feat: update template
This commit is contained in:
127
internal/db/sqlc/access_logs.sql.go
Normal file
127
internal/db/sqlc/access_logs.sql.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: access_logs.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"time"
|
||||
)
|
||||
|
||||
const countAccessLogs = `-- name: CountAccessLogs :one
|
||||
SELECT COUNT(*)
|
||||
FROM access_logs
|
||||
`
|
||||
|
||||
func (q *Queries) CountAccessLogs(ctx context.Context) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countAccessLogs)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
const createAccessLog = `-- name: CreateAccessLog :exec
|
||||
INSERT INTO access_logs(request_id, user_id, ip, user_agent, request_method, request_path, referer, status_code,
|
||||
response_time_ms, started_at, message)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
`
|
||||
|
||||
type CreateAccessLogParams struct {
|
||||
RequestID *string `json:"request_id"`
|
||||
UserID *int32 `json:"user_id"`
|
||||
Ip *netip.Addr `json:"ip"`
|
||||
UserAgent *string `json:"user_agent"`
|
||||
RequestMethod string `json:"request_method"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Referer *string `json:"referer"`
|
||||
StatusCode *int32 `json:"status_code"`
|
||||
ResponseTimeMs *int32 `json:"response_time_ms"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
Message *string `json:"message"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAccessLog(ctx context.Context, arg CreateAccessLogParams) error {
|
||||
_, err := q.db.Exec(ctx, createAccessLog,
|
||||
arg.RequestID,
|
||||
arg.UserID,
|
||||
arg.Ip,
|
||||
arg.UserAgent,
|
||||
arg.RequestMethod,
|
||||
arg.RequestPath,
|
||||
arg.Referer,
|
||||
arg.StatusCode,
|
||||
arg.ResponseTimeMs,
|
||||
arg.StartedAt,
|
||||
arg.Message,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const listAccessLogs = `-- name: ListAccessLogs :many
|
||||
SELECT a.id, a.request_id, a.user_id, a.ip, a.user_agent, a.request_method, a.request_path, a.referer, a.message, a.status_code, a.response_time_ms, a.started_at, a.created_at, u.username
|
||||
FROM access_logs a
|
||||
LEFT JOIN sys_users u
|
||||
ON u.id = a.user_id
|
||||
ORDER BY a.started_at DESC, a.id
|
||||
LIMIT $1 OFFSET $2
|
||||
`
|
||||
|
||||
type ListAccessLogsParams struct {
|
||||
Limit int32 `json:"limit"`
|
||||
Offset int32 `json:"offset"`
|
||||
}
|
||||
|
||||
type ListAccessLogsRow struct {
|
||||
ID int64 `json:"id"`
|
||||
RequestID *string `json:"request_id"`
|
||||
UserID *int32 `json:"user_id"`
|
||||
Ip *netip.Addr `json:"ip"`
|
||||
UserAgent *string `json:"user_agent"`
|
||||
RequestMethod string `json:"request_method"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Referer *string `json:"referer"`
|
||||
Message *string `json:"message"`
|
||||
StatusCode *int32 `json:"status_code"`
|
||||
ResponseTimeMs *int32 `json:"response_time_ms"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Username *string `json:"username"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListAccessLogs(ctx context.Context, arg ListAccessLogsParams) ([]ListAccessLogsRow, error) {
|
||||
rows, err := q.db.Query(ctx, listAccessLogs, arg.Limit, arg.Offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListAccessLogsRow{}
|
||||
for rows.Next() {
|
||||
var i ListAccessLogsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.RequestID,
|
||||
&i.UserID,
|
||||
&i.Ip,
|
||||
&i.UserAgent,
|
||||
&i.RequestMethod,
|
||||
&i.RequestPath,
|
||||
&i.Referer,
|
||||
&i.Message,
|
||||
&i.StatusCode,
|
||||
&i.ResponseTimeMs,
|
||||
&i.StartedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Username,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
@@ -9,6 +9,41 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// iteratorForCopyFileImageMetadata implements pgx.CopyFromSource.
|
||||
type iteratorForCopyFileImageMetadata struct {
|
||||
rows []CopyFileImageMetadataParams
|
||||
skippedFirstNextCall bool
|
||||
}
|
||||
|
||||
func (r *iteratorForCopyFileImageMetadata) Next() bool {
|
||||
if len(r.rows) == 0 {
|
||||
return false
|
||||
}
|
||||
if !r.skippedFirstNextCall {
|
||||
r.skippedFirstNextCall = true
|
||||
return true
|
||||
}
|
||||
r.rows = r.rows[1:]
|
||||
return len(r.rows) > 0
|
||||
}
|
||||
|
||||
func (r iteratorForCopyFileImageMetadata) Values() ([]interface{}, error) {
|
||||
return []interface{}{
|
||||
r.rows[0].FileID,
|
||||
r.rows[0].Width,
|
||||
r.rows[0].Height,
|
||||
r.rows[0].Format,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r iteratorForCopyFileImageMetadata) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Queries) CopyFileImageMetadata(ctx context.Context, arg []CopyFileImageMetadataParams) (int64, error) {
|
||||
return q.db.CopyFrom(ctx, []string{"file_image_metadata"}, []string{"file_id", "width", "height", "format"}, &iteratorForCopyFileImageMetadata{rows: arg})
|
||||
}
|
||||
|
||||
// iteratorForCreatePostTag implements pgx.CopyFromSource.
|
||||
type iteratorForCreatePostTag struct {
|
||||
rows []CreatePostTagParams
|
||||
|
||||
48
internal/db/sqlc/file_image_metadata.sql.go
Normal file
48
internal/db/sqlc/file_image_metadata.sql.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: file_image_metadata.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type CopyFileImageMetadataParams struct {
|
||||
FileID int32 `json:"file_id"`
|
||||
Width int32 `json:"width"`
|
||||
Height int32 `json:"height"`
|
||||
Format string `json:"format"`
|
||||
}
|
||||
|
||||
const createFileImageMetadata = `-- name: CreateFileImageMetadata :exec
|
||||
INSERT INTO file_image_metadata(file_id, width, height, format)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`
|
||||
|
||||
type CreateFileImageMetadataParams struct {
|
||||
FileID int32 `json:"file_id"`
|
||||
Width int32 `json:"width"`
|
||||
Height int32 `json:"height"`
|
||||
Format string `json:"format"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateFileImageMetadata(ctx context.Context, arg CreateFileImageMetadataParams) error {
|
||||
_, err := q.db.Exec(ctx, createFileImageMetadata,
|
||||
arg.FileID,
|
||||
arg.Width,
|
||||
arg.Height,
|
||||
arg.Format,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const truncateFileImageMetadata = `-- name: TruncateFileImageMetadata :exec
|
||||
TRUNCATE TABLE file_image_metadata
|
||||
`
|
||||
|
||||
func (q *Queries) TruncateFileImageMetadata(ctx context.Context) error {
|
||||
_, err := q.db.Exec(ctx, truncateFileImageMetadata)
|
||||
return err
|
||||
}
|
||||
@@ -7,6 +7,7 @@ package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const countFiles = `-- name: CountFiles :one
|
||||
@@ -24,7 +25,7 @@ func (q *Queries) CountFiles(ctx context.Context) (int64, error) {
|
||||
const createFile = `-- name: CreateFile :one
|
||||
INSERT INTO files(file_name, file_path, original_name, folder_name, mime_type, file_size, file_url)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id,file_url,file_name
|
||||
RETURNING id,file_url,file_name,file_size
|
||||
`
|
||||
|
||||
type CreateFileParams struct {
|
||||
@@ -41,6 +42,7 @@ type CreateFileRow struct {
|
||||
ID int32 `json:"id"`
|
||||
FileUrl string `json:"file_url"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (CreateFileRow, error) {
|
||||
@@ -54,14 +56,29 @@ func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (CreateF
|
||||
arg.FileUrl,
|
||||
)
|
||||
var i CreateFileRow
|
||||
err := row.Scan(&i.ID, &i.FileUrl, &i.FileName)
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.FileUrl,
|
||||
&i.FileName,
|
||||
&i.FileSize,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listFiles = `-- name: ListFiles :many
|
||||
SELECT id, file_name, file_path, file_url, original_name, folder_name, mime_type, file_size, created_at, updated_at
|
||||
FROM files
|
||||
ORDER BY id
|
||||
SELECT f.id, f.file_name, f.file_path, f.file_url, f.original_name, f.folder_name, f.mime_type, f.file_size, f.created_at, f.updated_at,
|
||||
CASE
|
||||
WHEN m.file_id IS NULL THEN NULL
|
||||
ELSE jsonb_build_object(
|
||||
'width', m.width,
|
||||
'height', m.height,
|
||||
'format', m.format
|
||||
)
|
||||
END AS metadata
|
||||
FROM files f
|
||||
LEFT JOIN file_image_metadata m
|
||||
ON f.id = m.file_id
|
||||
ORDER BY f.id DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
`
|
||||
|
||||
@@ -70,15 +87,29 @@ type ListFilesParams struct {
|
||||
Offset int32 `json:"offset"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListFiles(ctx context.Context, arg ListFilesParams) ([]File, error) {
|
||||
type ListFilesRow struct {
|
||||
ID int32 `json:"id"`
|
||||
FileName string `json:"file_name"`
|
||||
FilePath string `json:"file_path"`
|
||||
FileUrl string `json:"file_url"`
|
||||
OriginalName string `json:"original_name"`
|
||||
FolderName string `json:"folder_name"`
|
||||
MimeType string `json:"mime_type"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
Metadata interface{} `json:"metadata"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListFiles(ctx context.Context, arg ListFilesParams) ([]ListFilesRow, error) {
|
||||
rows, err := q.db.Query(ctx, listFiles, arg.Limit, arg.Offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []File{}
|
||||
items := []ListFilesRow{}
|
||||
for rows.Next() {
|
||||
var i File
|
||||
var i ListFilesRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.FileName,
|
||||
@@ -90,6 +121,7 @@ func (q *Queries) ListFiles(ctx context.Context, arg ListFilesParams) ([]File, e
|
||||
&i.FileSize,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Metadata,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -100,3 +132,34 @@ func (q *Queries) ListFiles(ctx context.Context, arg ListFilesParams) ([]File, e
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listImageFiles = `-- name: ListImageFiles :many
|
||||
SELECT id, file_path
|
||||
FROM files
|
||||
WHERE mime_type LIKE 'image/%'
|
||||
`
|
||||
|
||||
type ListImageFilesRow struct {
|
||||
ID int32 `json:"id"`
|
||||
FilePath string `json:"file_path"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListImageFiles(ctx context.Context) ([]ListImageFilesRow, error) {
|
||||
rows, err := q.db.Query(ctx, listImageFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListImageFilesRow{}
|
||||
for rows.Next() {
|
||||
var i ListImageFilesRow
|
||||
if err := rows.Scan(&i.ID, &i.FilePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,22 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type AccessLog struct {
|
||||
ID int64 `json:"id"`
|
||||
RequestID *string `json:"request_id"`
|
||||
UserID *int32 `json:"user_id"`
|
||||
Ip *netip.Addr `json:"ip"`
|
||||
UserAgent *string `json:"user_agent"`
|
||||
RequestMethod string `json:"request_method"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Referer *string `json:"referer"`
|
||||
Message *string `json:"message"`
|
||||
StatusCode *int32 `json:"status_code"`
|
||||
ResponseTimeMs *int32 `json:"response_time_ms"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
// 分类ID
|
||||
ID int32 `json:"id"`
|
||||
@@ -45,6 +61,15 @@ type File struct {
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type FileImageMetadatum struct {
|
||||
ID int32 `json:"id"`
|
||||
FileID int32 `json:"file_id"`
|
||||
Width int32 `json:"width"`
|
||||
Height int32 `json:"height"`
|
||||
Format string `json:"format"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
// 主键ID
|
||||
ID int32 `json:"id"`
|
||||
@@ -81,7 +106,7 @@ type PostStat struct {
|
||||
// 文章id
|
||||
PostID int32 `json:"post_id"`
|
||||
// 文章阅读量
|
||||
View int32 `json:"view"`
|
||||
ViewCount int32 `json:"view_count"`
|
||||
}
|
||||
|
||||
type PostTag struct {
|
||||
@@ -207,6 +232,8 @@ type SysUser struct {
|
||||
Status int16 `json:"status"`
|
||||
// 头像文件id
|
||||
AvatarID *int32 `json:"avatar_id"`
|
||||
// Token Version
|
||||
TokenVersion int32 `json:"token_version"`
|
||||
// 创建时间
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
// 更新时间
|
||||
|
||||
@@ -7,6 +7,7 @@ package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -85,13 +86,14 @@ func (q *Queries) DeletePost(ctx context.Context, id int32) (int64, error) {
|
||||
|
||||
const getPostByID = `-- name: GetPostByID :one
|
||||
SELECT p.id, p.title, p.cover_id, p.slug, p.content, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
||||
f.file_url AS cover,
|
||||
f.file_url AS cover,
|
||||
f.file_size AS cover_size,
|
||||
c.name AS category_name,
|
||||
c.id AS category_id,
|
||||
COALESCE(
|
||||
array_agg(pt.tag_id ORDER BY pt.tag_id) FILTER (WHERE pt.tag_id IS NOT NULL),
|
||||
ARRAY[]::integer[]
|
||||
) AS tags
|
||||
array_agg(pt.tag_id ORDER BY pt.tag_id) FILTER (WHERE pt.tag_id IS NOT NULL),
|
||||
ARRAY []::integer[]
|
||||
) AS tags
|
||||
FROM posts p
|
||||
LEFT JOIN files f ON f.id = p.cover_id
|
||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||
@@ -100,6 +102,7 @@ FROM posts p
|
||||
WHERE p.id = $1
|
||||
GROUP BY p.id,
|
||||
f.file_url,
|
||||
f.file_size,
|
||||
c.name,
|
||||
c.id
|
||||
LIMIT 1
|
||||
@@ -118,6 +121,7 @@ type GetPostByIDRow struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
Cover *string `json:"cover"`
|
||||
CoverSize *int64 `json:"cover_size"`
|
||||
CategoryName *string `json:"category_name"`
|
||||
CategoryID *int32 `json:"category_id"`
|
||||
Tags interface{} `json:"tags"`
|
||||
@@ -139,6 +143,7 @@ func (q *Queries) GetPostByID(ctx context.Context, id int32) (GetPostByIDRow, er
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Cover,
|
||||
&i.CoverSize,
|
||||
&i.CategoryName,
|
||||
&i.CategoryID,
|
||||
&i.Tags,
|
||||
@@ -146,14 +151,66 @@ func (q *Queries) GetPostByID(ctx context.Context, id int32) (GetPostByIDRow, er
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getPostsForSitemap = `-- name: GetPostsForSitemap :many
|
||||
SELECT slug, created_at, updated_at
|
||||
FROM posts
|
||||
WHERE status = 1
|
||||
AND published_at < NOW()
|
||||
ORDER BY sort DESC, published_at DESC, id DESC
|
||||
`
|
||||
|
||||
type GetPostsForSitemapRow struct {
|
||||
Slug string `json:"slug"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetPostsForSitemap(ctx context.Context) ([]GetPostsForSitemapRow, error) {
|
||||
rows, err := q.db.Query(ctx, getPostsForSitemap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetPostsForSitemapRow{}
|
||||
for rows.Next() {
|
||||
var i GetPostsForSitemapRow
|
||||
if err := rows.Scan(&i.Slug, &i.CreatedAt, &i.UpdatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getPublicPostBySlug = `-- name: GetPublicPostBySlug :one
|
||||
|
||||
SELECT p.id, p.title, p.cover_id, p.slug, p.content, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
||||
f.file_url AS cover,
|
||||
COALESCE(ps.view, 0) AS view
|
||||
SELECT p.id,
|
||||
p.title,
|
||||
p.slug,
|
||||
p.content,
|
||||
p.summary,
|
||||
p.published_at,
|
||||
f.file_url AS cover,
|
||||
COALESCE(ps.view_count, 0) AS view_count,
|
||||
c.code AS category_code,
|
||||
c.name AS category_name,
|
||||
COALESCE(t.tags, '[]') AS tags
|
||||
FROM posts p
|
||||
LEFT JOIN files f ON f.id = p.cover_id
|
||||
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||
LEFT JOIN categories c ON pc.category_id = c.id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT jsonb_agg(
|
||||
jsonb_build_object('name', tag.name, 'code', tag.code)
|
||||
) AS tags
|
||||
FROM post_tag pt
|
||||
JOIN tags tag ON tag.id = pt.tag_id
|
||||
WHERE pt.post_id = p.id
|
||||
) t ON true
|
||||
WHERE p.slug = $1
|
||||
AND p.status = 1
|
||||
AND p.published_at < NOW()
|
||||
@@ -161,19 +218,17 @@ LIMIT 1
|
||||
`
|
||||
|
||||
type GetPublicPostBySlugRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
CoverID *int32 `json:"cover_id"`
|
||||
Slug string `json:"slug"`
|
||||
Content string `json:"content"`
|
||||
Summary string `json:"summary"`
|
||||
Status int16 `json:"status"`
|
||||
Sort *int32 `json:"sort"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
Cover *string `json:"cover"`
|
||||
View int32 `json:"view"`
|
||||
ID int32 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Content string `json:"content"`
|
||||
Summary string `json:"summary"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
Cover *string `json:"cover"`
|
||||
ViewCount int32 `json:"view_count"`
|
||||
CategoryCode *string `json:"category_code"`
|
||||
CategoryName *string `json:"category_name"`
|
||||
Tags json.RawMessage `json:"tags"`
|
||||
}
|
||||
|
||||
// web -------------------------------------------------------
|
||||
@@ -183,37 +238,31 @@ func (q *Queries) GetPublicPostBySlug(ctx context.Context, slug string) (GetPubl
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Title,
|
||||
&i.CoverID,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.Summary,
|
||||
&i.Status,
|
||||
&i.Sort,
|
||||
&i.PublishedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Cover,
|
||||
&i.View,
|
||||
&i.ViewCount,
|
||||
&i.CategoryCode,
|
||||
&i.CategoryName,
|
||||
&i.Tags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listArchives = `-- name: ListArchives :many
|
||||
SELECT
|
||||
p.id,
|
||||
p.slug,
|
||||
p.title,
|
||||
p.published_at,
|
||||
c."name" AS category_name
|
||||
FROM
|
||||
posts p
|
||||
LEFT JOIN post_category pc ON p.id = pc.post_id
|
||||
LEFT JOIN categories c ON c.id = pc.category_id
|
||||
WHERE
|
||||
p.status = 1
|
||||
SELECT p.id,
|
||||
p.slug,
|
||||
p.title,
|
||||
p.published_at,
|
||||
c."name" AS category_name
|
||||
FROM posts p
|
||||
LEFT JOIN post_category pc ON p.id = pc.post_id
|
||||
LEFT JOIN categories c ON c.id = pc.category_id
|
||||
WHERE p.status = 1
|
||||
AND p.published_at < NOW()
|
||||
ORDER BY
|
||||
p.published_at DESC, p.id DESC
|
||||
ORDER BY p.published_at DESC, p.id DESC
|
||||
`
|
||||
|
||||
type ListArchivesRow struct {
|
||||
@@ -251,26 +300,26 @@ func (q *Queries) ListArchives(ctx context.Context) ([]ListArchivesRow, error) {
|
||||
}
|
||||
|
||||
const listCategoryStats = `-- name: ListCategoryStats :many
|
||||
SELECT
|
||||
c.id,
|
||||
c.name,
|
||||
COUNT(p.id) AS post_count
|
||||
FROM
|
||||
categories c
|
||||
LEFT JOIN post_category pc ON c.id = pc.category_id
|
||||
LEFT JOIN posts p ON p.id = pc.post_id
|
||||
AND p.status = 1
|
||||
AND p.published_at < NOW()
|
||||
GROUP BY
|
||||
c.id,
|
||||
c.sort,
|
||||
c.name
|
||||
SELECT c.id,
|
||||
c.name,
|
||||
c.code,
|
||||
COUNT(p.id) AS post_count
|
||||
FROM categories c
|
||||
LEFT JOIN post_category pc ON c.id = pc.category_id
|
||||
LEFT JOIN posts p ON p.id = pc.post_id
|
||||
AND p.status = 1
|
||||
AND p.published_at < NOW()
|
||||
GROUP BY c.id,
|
||||
c.sort,
|
||||
c.name,
|
||||
c.code
|
||||
ORDER BY c.sort DESC, c.id
|
||||
`
|
||||
|
||||
type ListCategoryStatsRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
PostCount int64 `json:"post_count"`
|
||||
}
|
||||
|
||||
@@ -283,7 +332,12 @@ func (q *Queries) ListCategoryStats(ctx context.Context) ([]ListCategoryStatsRow
|
||||
items := []ListCategoryStatsRow{}
|
||||
for rows.Next() {
|
||||
var i ListCategoryStatsRow
|
||||
if err := rows.Scan(&i.ID, &i.Name, &i.PostCount); err != nil {
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Code,
|
||||
&i.PostCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
@@ -309,24 +363,24 @@ WITH paginated_posts AS (
|
||||
updated_at
|
||||
FROM posts
|
||||
ORDER BY sort DESC, published_at DESC, id DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
)
|
||||
LIMIT $1 OFFSET $2)
|
||||
SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
||||
f.file_url AS cover,
|
||||
c.name AS category_name,
|
||||
c.id AS category_id,
|
||||
COALESCE(ps.view, 0) AS view,
|
||||
f.file_url AS cover,
|
||||
f.file_size AS cover_size,
|
||||
c.name AS category_name,
|
||||
c.id AS category_id,
|
||||
COALESCE(ps.view_count, 0) AS view_count,
|
||||
COALESCE(
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', t.id,
|
||||
'name', t.name,
|
||||
'code', t.code
|
||||
)
|
||||
ORDER BY t.sort DESC, t.id
|
||||
) FILTER (WHERE t.id IS NOT NULL),
|
||||
'[]'::jsonb
|
||||
) AS tags
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', t.id,
|
||||
'name', t.name,
|
||||
'code', t.code
|
||||
)
|
||||
ORDER BY t.sort DESC, t.id
|
||||
) FILTER (WHERE t.id IS NOT NULL),
|
||||
'[]'::jsonb
|
||||
) AS tags
|
||||
FROM paginated_posts p
|
||||
LEFT JOIN files f ON f.id = p.cover_id
|
||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||
@@ -345,9 +399,10 @@ GROUP BY p.id,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
f.file_url,
|
||||
f.file_size,
|
||||
c.name,
|
||||
c.id,
|
||||
ps.view
|
||||
ps.view_count
|
||||
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC
|
||||
`
|
||||
|
||||
@@ -368,9 +423,10 @@ type ListPostsRow struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
Cover *string `json:"cover"`
|
||||
CoverSize *int64 `json:"cover_size"`
|
||||
CategoryName *string `json:"category_name"`
|
||||
CategoryID *int32 `json:"category_id"`
|
||||
View int32 `json:"view"`
|
||||
ViewCount int32 `json:"view_count"`
|
||||
Tags interface{} `json:"tags"`
|
||||
}
|
||||
|
||||
@@ -396,9 +452,10 @@ func (q *Queries) ListPosts(ctx context.Context, arg ListPostsParams) ([]ListPos
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Cover,
|
||||
&i.CoverSize,
|
||||
&i.CategoryName,
|
||||
&i.CategoryID,
|
||||
&i.View,
|
||||
&i.ViewCount,
|
||||
&i.Tags,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -427,21 +484,22 @@ WITH paginated_posts AS (
|
||||
ORDER BY sort DESC, published_at DESC, id DESC
|
||||
LIMIT $1 OFFSET $2)
|
||||
SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.sort, p.published_at,
|
||||
f.file_url AS cover,
|
||||
c.name AS category_name,
|
||||
c.id AS category_id,
|
||||
COALESCE(ps.view, 0) AS view,
|
||||
f.file_url AS cover,
|
||||
c.name AS category_name,
|
||||
c.id AS category_id,
|
||||
c.code AS category_code,
|
||||
COALESCE(ps.view_count, 0) AS view_count,
|
||||
COALESCE(
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', t.id,
|
||||
'name', t.name,
|
||||
'code', t.code
|
||||
)
|
||||
ORDER BY t.sort DESC, t.id
|
||||
) FILTER (WHERE t.id IS NOT NULL),
|
||||
'[]'::jsonb
|
||||
) AS tags
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'id', t.id,
|
||||
'name', t.name,
|
||||
'code', t.code
|
||||
)
|
||||
ORDER BY t.sort DESC, t.id
|
||||
) FILTER (WHERE t.id IS NOT NULL),
|
||||
'[]'::jsonb
|
||||
) AS tags
|
||||
FROM paginated_posts p
|
||||
LEFT JOIN files f ON f.id = p.cover_id
|
||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||
@@ -459,7 +517,8 @@ GROUP BY p.id,
|
||||
f.file_url,
|
||||
c.name,
|
||||
c.id,
|
||||
ps.view
|
||||
c.code,
|
||||
ps.view_count
|
||||
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC
|
||||
`
|
||||
|
||||
@@ -479,7 +538,8 @@ type ListPublishedPostsRow struct {
|
||||
Cover *string `json:"cover"`
|
||||
CategoryName *string `json:"category_name"`
|
||||
CategoryID *int32 `json:"category_id"`
|
||||
View int32 `json:"view"`
|
||||
CategoryCode *string `json:"category_code"`
|
||||
ViewCount int32 `json:"view_count"`
|
||||
Tags interface{} `json:"tags"`
|
||||
}
|
||||
|
||||
@@ -503,7 +563,8 @@ func (q *Queries) ListPublishedPosts(ctx context.Context, arg ListPublishedPosts
|
||||
&i.Cover,
|
||||
&i.CategoryName,
|
||||
&i.CategoryID,
|
||||
&i.View,
|
||||
&i.CategoryCode,
|
||||
&i.ViewCount,
|
||||
&i.Tags,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -516,34 +577,141 @@ func (q *Queries) ListPublishedPosts(ctx context.Context, arg ListPublishedPosts
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listPublishedPostsWithFilters = `-- name: ListPublishedPostsWithFilters :many
|
||||
SELECT p.id,
|
||||
p.title,
|
||||
p.slug,
|
||||
p.summary,
|
||||
p.sort,
|
||||
p.published_at,
|
||||
f.file_url AS cover,
|
||||
c."name" AS category_name,
|
||||
c.code AS category_code,
|
||||
COALESCE(t.tags, '[]') AS tags,
|
||||
COALESCE(ps."view_count", 0) AS view_count
|
||||
FROM posts p
|
||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||
LEFT JOIN categories c ON c.id = pc.category_id
|
||||
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
||||
LEFT JOIN files f ON f.id = p.cover_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT jsonb_agg(
|
||||
jsonb_build_object('name', tag.name, 'code', tag.code)
|
||||
) AS tags
|
||||
FROM post_tag pt
|
||||
JOIN tags tag ON tag.id = pt.tag_id
|
||||
WHERE pt.post_id = p.id
|
||||
) t ON true
|
||||
WHERE
|
||||
-- 只显示已发布的文章
|
||||
p.status = 1
|
||||
AND p.published_at < NOW()
|
||||
AND
|
||||
-- category code 过滤(NULL 或空字符串时不过滤)
|
||||
(
|
||||
COALESCE($1::text, '') = ''
|
||||
OR EXISTS (SELECT 1
|
||||
FROM post_category pc2
|
||||
JOIN categories c2 ON c2.id = pc2.category_id
|
||||
WHERE pc2.post_id = p.id
|
||||
AND c2.code = $1::text)
|
||||
)
|
||||
AND
|
||||
-- tag code 过滤(NULL 或空字符串时不过滤)
|
||||
(
|
||||
COALESCE($2::text, '') = ''
|
||||
OR EXISTS (SELECT 1
|
||||
FROM post_tag pt2
|
||||
JOIN tags t2 ON t2.id = pt2.tag_id
|
||||
WHERE pt2.post_id = p.id
|
||||
AND t2.code = $2::text)
|
||||
)
|
||||
ORDER BY p.sort DESC,
|
||||
p.published_at DESC,
|
||||
p.id DESC
|
||||
`
|
||||
|
||||
type ListPublishedPostsWithFiltersParams struct {
|
||||
CategoryCode *string `json:"category_code"`
|
||||
TagCode *string `json:"tag_code"`
|
||||
}
|
||||
|
||||
type ListPublishedPostsWithFiltersRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Summary string `json:"summary"`
|
||||
Sort *int32 `json:"sort"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
Cover *string `json:"cover"`
|
||||
CategoryName *string `json:"category_name"`
|
||||
CategoryCode *string `json:"category_code"`
|
||||
Tags json.RawMessage `json:"tags"`
|
||||
ViewCount int32 `json:"view_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListPublishedPostsWithFilters(ctx context.Context, arg ListPublishedPostsWithFiltersParams) ([]ListPublishedPostsWithFiltersRow, error) {
|
||||
rows, err := q.db.Query(ctx, listPublishedPostsWithFilters, arg.CategoryCode, arg.TagCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListPublishedPostsWithFiltersRow{}
|
||||
for rows.Next() {
|
||||
var i ListPublishedPostsWithFiltersRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Summary,
|
||||
&i.Sort,
|
||||
&i.PublishedAt,
|
||||
&i.Cover,
|
||||
&i.CategoryName,
|
||||
&i.CategoryCode,
|
||||
&i.Tags,
|
||||
&i.ViewCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updatePost = `-- name: UpdatePost :execrows
|
||||
UPDATE posts
|
||||
SET title = coalesce($1, title),
|
||||
cover_id = coalesce($2, cover_id),
|
||||
slug = coalesce($3, slug),
|
||||
content = coalesce($4, content),
|
||||
summary = coalesce($5, summary),
|
||||
status = coalesce($6, status),
|
||||
sort = coalesce($7, sort),
|
||||
published_at = coalesce($8, published_at)
|
||||
WHERE id = $9
|
||||
cover_id = CASE WHEN $2::boolean THEN $3 ELSE cover_id END,
|
||||
slug = coalesce($4, slug),
|
||||
content = coalesce($5, content),
|
||||
summary = coalesce($6, summary),
|
||||
status = coalesce($7, status),
|
||||
sort = coalesce($8, sort),
|
||||
published_at = coalesce($9, published_at)
|
||||
WHERE id = $10
|
||||
`
|
||||
|
||||
type UpdatePostParams struct {
|
||||
Title *string `json:"title"`
|
||||
CoverID *int32 `json:"cover_id"`
|
||||
Slug *string `json:"slug"`
|
||||
Content *string `json:"content"`
|
||||
Summary *string `json:"summary"`
|
||||
Status *int16 `json:"status"`
|
||||
Sort *int32 `json:"sort"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
ID int32 `json:"id"`
|
||||
Title *string `json:"title"`
|
||||
UpdateCoverID bool `json:"update_cover_id"`
|
||||
CoverID *int32 `json:"cover_id"`
|
||||
Slug *string `json:"slug"`
|
||||
Content *string `json:"content"`
|
||||
Summary *string `json:"summary"`
|
||||
Status *int16 `json:"status"`
|
||||
Sort *int32 `json:"sort"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
ID int32 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePost(ctx context.Context, arg UpdatePostParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, updatePost,
|
||||
arg.Title,
|
||||
arg.UpdateCoverID,
|
||||
arg.CoverID,
|
||||
arg.Slug,
|
||||
arg.Content,
|
||||
|
||||
@@ -30,7 +30,7 @@ WITH visitor AS (
|
||||
)
|
||||
INSERT INTO post_stats (
|
||||
post_id,
|
||||
view
|
||||
view_count
|
||||
)
|
||||
SELECT
|
||||
post_id,
|
||||
@@ -38,7 +38,7 @@ SELECT
|
||||
FROM visitor
|
||||
ON CONFLICT (post_id)
|
||||
DO UPDATE
|
||||
SET view = post_stats.view + 1
|
||||
SET view_count = post_stats.view_count + 1
|
||||
`
|
||||
|
||||
type IncrementPostStatsViewParams struct {
|
||||
|
||||
@@ -78,34 +78,7 @@ func (q *Queries) DeleteUser(ctx context.Context, id int32) (int64, error) {
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const getActiveUserByID = `-- name: GetActiveUserByID :one
|
||||
SELECT id, account, username, status
|
||||
FROM sys_users
|
||||
WHERE id = $1
|
||||
AND status = 1
|
||||
`
|
||||
|
||||
type GetActiveUserByIDRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Username string `json:"username"`
|
||||
Status int16 `json:"status"`
|
||||
}
|
||||
|
||||
// 场景:用户登录、获取个人信息、刷新 Token(严格校验 status = 1)
|
||||
func (q *Queries) GetActiveUserByID(ctx context.Context, id int32) (GetActiveUserByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, getActiveUserByID, id)
|
||||
var i GetActiveUserByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Account,
|
||||
&i.Username,
|
||||
&i.Status,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByAccount = `-- name: GetUserByAccount :one
|
||||
const getUser = `-- name: GetUser :one
|
||||
SELECT u.id,
|
||||
u.account,
|
||||
u.username,
|
||||
@@ -113,14 +86,21 @@ SELECT u.id,
|
||||
u.status,
|
||||
u.avatar_id,
|
||||
f.file_url AS avatar_url,
|
||||
u.token_version,
|
||||
u.created_at,
|
||||
u.updated_at
|
||||
FROM sys_users u
|
||||
LEFT JOIN files f ON u.avatar_id = f.id
|
||||
WHERE u.account = $1
|
||||
WHERE u.id = $1
|
||||
OR u.account = $2
|
||||
`
|
||||
|
||||
type GetUserByAccountRow struct {
|
||||
type GetUserParams struct {
|
||||
ID int32 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
}
|
||||
|
||||
type GetUserRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Username string `json:"username"`
|
||||
@@ -128,13 +108,14 @@ type GetUserByAccountRow struct {
|
||||
Status int16 `json:"status"`
|
||||
AvatarID *int32 `json:"avatar_id"`
|
||||
AvatarUrl *string `json:"avatar_url"`
|
||||
TokenVersion int32 `json:"token_version"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByAccount(ctx context.Context, account string) (GetUserByAccountRow, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByAccount, account)
|
||||
var i GetUserByAccountRow
|
||||
func (q *Queries) GetUser(ctx context.Context, arg GetUserParams) (GetUserRow, error) {
|
||||
row := q.db.QueryRow(ctx, getUser, arg.ID, arg.Account)
|
||||
var i GetUserRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Account,
|
||||
@@ -143,53 +124,53 @@ func (q *Queries) GetUserByAccount(ctx context.Context, account string) (GetUser
|
||||
&i.Status,
|
||||
&i.AvatarID,
|
||||
&i.AvatarUrl,
|
||||
&i.TokenVersion,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByID = `-- name: GetUserByID :one
|
||||
SELECT u.id,
|
||||
u.account,
|
||||
u.username,
|
||||
u.avatar_id,
|
||||
u.status,
|
||||
f.file_url AS avatar_url,
|
||||
u.created_at,
|
||||
u.updated_at
|
||||
FROM sys_users u
|
||||
LEFT JOIN files f ON u.avatar_id = f.id
|
||||
WHERE u.id = $1
|
||||
const getUserAuthState = `-- name: GetUserAuthState :one
|
||||
SELECT status,
|
||||
token_version
|
||||
FROM sys_users
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
type GetUserByIDRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Username string `json:"username"`
|
||||
AvatarID *int32 `json:"avatar_id"`
|
||||
Status int16 `json:"status"`
|
||||
AvatarUrl *string `json:"avatar_url"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
type GetUserAuthStateRow struct {
|
||||
Status int16 `json:"status"`
|
||||
TokenVersion int32 `json:"token_version"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, id int32) (GetUserByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByID, id)
|
||||
var i GetUserByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Account,
|
||||
&i.Username,
|
||||
&i.AvatarID,
|
||||
&i.Status,
|
||||
&i.AvatarUrl,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
func (q *Queries) GetUserAuthState(ctx context.Context, id int32) (GetUserAuthStateRow, error) {
|
||||
row := q.db.QueryRow(ctx, getUserAuthState, id)
|
||||
var i GetUserAuthStateRow
|
||||
err := row.Scan(&i.Status, &i.TokenVersion)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const incrementTokenVersionForAllUsers = `-- name: IncrementTokenVersionForAllUsers :exec
|
||||
UPDATE sys_users
|
||||
SET token_version = token_version + 1
|
||||
`
|
||||
|
||||
func (q *Queries) IncrementTokenVersionForAllUsers(ctx context.Context) error {
|
||||
_, err := q.db.Exec(ctx, incrementTokenVersionForAllUsers)
|
||||
return err
|
||||
}
|
||||
|
||||
const incrementUserTokenVersion = `-- name: IncrementUserTokenVersion :exec
|
||||
UPDATE sys_users
|
||||
SET token_version = token_version + 1
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) IncrementUserTokenVersion(ctx context.Context, id int32) error {
|
||||
_, err := q.db.Exec(ctx, incrementUserTokenVersion, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const listAdminMenus = `-- name: ListAdminMenus :many
|
||||
SELECT id, name, path, component, type, hidden, sort, status, parent_id, icon, created_at, updated_at
|
||||
FROM sys_menus
|
||||
|
||||
Reference in New Issue
Block a user