feat: update template

This commit is contained in:
2026-08-19 22:05:49 +08:00
parent 0e674e9d56
commit 5a71093b0c
67 changed files with 2070 additions and 585 deletions

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS access_logs;

View File

@@ -0,0 +1,25 @@
CREATE TABLE access_logs
(
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
request_id TEXT,
user_id INTEGER,
ip INET,
user_agent TEXT,
request_method VARCHAR(10) NOT NULL,
request_path TEXT NOT NULL,
referer TEXT,
message TEXT,
status_code INTEGER,
response_time_ms INTEGER,
started_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_access_logs_created_at
ON access_logs (created_at);
CREATE INDEX idx_access_logs_request_path
ON access_logs (request_path);
CREATE INDEX idx_access_logs_ip
ON access_logs (ip);

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS file_image_metadata;

View File

@@ -0,0 +1,9 @@
CREATE TABLE file_image_metadata
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
file_id INTEGER UNIQUE NOT NULL,
width INTEGER NOT NULL,
height INTEGER NOT NULL,
format TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

View File

@@ -0,0 +1,3 @@
-- down
ALTER TABLE post_stats
RENAME COLUMN view_count TO view;

View File

@@ -0,0 +1,4 @@
ALTER TABLE post_stats
RENAME COLUMN view TO view_count;
COMMENT ON COLUMN post_stats.view_count IS '文章阅读量';

View File

@@ -0,0 +1,2 @@
ALTER TABLE sys_users
DROP COLUMN IF EXISTS token_version;

View File

@@ -0,0 +1,4 @@
ALTER TABLE sys_users
ADD COLUMN IF NOT EXISTS token_version INTEGER NOT NULL DEFAULT 0;
COMMENT ON COLUMN sys_users.token_version IS 'Token version';

View File

@@ -0,0 +1,16 @@
-- 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);
-- name: ListAccessLogs :many
SELECT a.*, 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;
-- name: CountAccessLogs :one
SELECT COUNT(*)
FROM access_logs;

View File

@@ -0,0 +1,10 @@
-- name: CreateFileImageMetadata :exec
INSERT INTO file_image_metadata(file_id, width, height, format)
VALUES ($1, $2, $3, $4);
-- name: CopyFileImageMetadata :copyfrom
INSERT INTO file_image_metadata(file_id, width, height, format)
VALUES ($1, $2, $3, $4);
-- name: TruncateFileImageMetadata :exec
TRUNCATE TABLE file_image_metadata;

View File

@@ -1,14 +1,29 @@
-- 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;
-- name: ListFiles :many
SELECT *
FROM files
ORDER BY id
SELECT f.*,
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;
-- name: CountFiles :one
SELECT COUNT(*)
FROM files;
-- name: ListImageFiles :many
SELECT id, file_path
FROM files
WHERE mime_type LIKE 'image/%';

View File

@@ -18,25 +18,25 @@ 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)
-- 第二步:用这极少量的记录去进行 JOIN
SELECT p.*,
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
@@ -55,9 +55,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;
-- name: CountPosts :one
@@ -66,13 +67,14 @@ FROM posts;
-- name: GetPostByID :one
SELECT p.*,
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
@@ -81,6 +83,7 @@ FROM posts p
WHERE p.id = $1
GROUP BY p.id,
f.file_url,
f.file_size,
c.name,
c.id
LIMIT 1;
@@ -88,7 +91,7 @@ LIMIT 1;
-- name: UpdatePost :execrows
UPDATE posts
SET title = coalesce(sqlc.narg('title'), title),
cover_id = coalesce(sqlc.narg('cover_id'), cover_id),
cover_id = CASE WHEN @update_cover_id::boolean THEN @cover_id ELSE cover_id END,
slug = coalesce(sqlc.narg('slug'), slug),
content = coalesce(sqlc.narg('content'), content),
summary = coalesce(sqlc.narg('summary'), summary),
@@ -105,12 +108,30 @@ WHERE id = $1;
-- web -------------------------------------------------------
-- name: GetPublicPostBySlug :one
SELECT p.*,
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()
@@ -132,21 +153,22 @@ WITH paginated_posts AS (
ORDER BY sort DESC, published_at DESC, id DESC
LIMIT $1 OFFSET $2)
SELECT p.*,
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
@@ -164,7 +186,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;
-- name: CountPublishedPosts :one
@@ -174,35 +197,90 @@ WHERE status = 1
AND published_at < NOW();
-- 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;
-- 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
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;
-- 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()
GROUP BY
c.id,
c.sort,
c.name
ORDER BY c.sort DESC, c.id;
AND
-- category code 过滤NULL 或空字符串时不过滤)
(
COALESCE(sqlc.narg('category_code')::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 = sqlc.narg('category_code')::text)
)
AND
-- tag code 过滤NULL 或空字符串时不过滤)
(
COALESCE(sqlc.narg('tag_code')::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 = sqlc.narg('tag_code')::text)
)
ORDER BY p.sort DESC,
p.published_at DESC,
p.id DESC;
-- 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;

View File

@@ -18,7 +18,7 @@ WITH visitor AS (
)
INSERT INTO post_stats (
post_id,
view
view_count
)
SELECT
post_id,
@@ -26,4 +26,4 @@ SELECT
FROM visitor
ON CONFLICT (post_id)
DO UPDATE
SET view = post_stats.view + 1;
SET view_count = post_stats.view_count + 1;

View File

@@ -2,27 +2,13 @@
INSERT INTO sys_users (account, username, password_hash, status, avatar_id)
VALUES ($1, $2, $3, $4, $5);
-- name: GetActiveUserByID :one
-- 场景:用户登录、获取个人信息、刷新 Token严格校验 status = 1
SELECT id, account, username, status
-- name: GetUserAuthState :one
SELECT status,
token_version
FROM sys_users
WHERE id = $1
AND status = 1;
WHERE id = $1;
-- 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;
-- name: GetUserByAccount :one
-- name: GetUser :one
SELECT u.id,
u.account,
u.username,
@@ -30,11 +16,13 @@ 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;
-- name: ListUsers :many
SELECT u.id,
@@ -63,6 +51,15 @@ SET username = coalesce(sqlc.narg('username'), username),
avatar_id = CASE WHEN @update_avatar_id::boolean THEN @avatar_id ELSE avatar_id END
WHERE id = sqlc.arg('id');
-- name: IncrementUserTokenVersion :exec
UPDATE sys_users
SET token_version = token_version + 1
WHERE id = $1;
-- name: IncrementTokenVersionForAllUsers :exec
UPDATE sys_users
SET token_version = token_version + 1;
-- name: UpdateUserPassword :execrows
UPDATE sys_users
SET password_hash = $2

View File

@@ -0,0 +1,25 @@
CREATE TABLE access_logs
(
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
request_id TEXT,
user_id INTEGER,
ip INET,
user_agent TEXT,
request_method VARCHAR(10) NOT NULL,
request_path TEXT NOT NULL,
referer TEXT,
message TEXT,
status_code INTEGER,
response_time_ms INTEGER,
started_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_access_logs_created_at
ON access_logs (created_at);
CREATE INDEX idx_access_logs_request_path
ON access_logs (request_path);
CREATE INDEX idx_access_logs_ip
ON access_logs (ip);

View File

@@ -0,0 +1,9 @@
CREATE TABLE file_image_metadata
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
file_id INTEGER UNIQUE NOT NULL,
width INTEGER NOT NULL,
height INTEGER NOT NULL,
format TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

View File

@@ -1,8 +1,8 @@
CREATE TABLE post_stats
(
post_id INTEGER PRIMARY KEY,
view INTEGER NOT NULL DEFAULT 0
post_id INTEGER PRIMARY KEY,
view_count INTEGER NOT NULL DEFAULT 0
);
COMMENT ON COLUMN post_stats.post_id IS '文章id';
COMMENT ON COLUMN post_stats.view IS '文章阅读量';
COMMENT ON COLUMN post_stats.view_count IS '文章阅读量';

View File

@@ -6,6 +6,7 @@ CREATE TABLE sys_users
password_hash TEXT NOT NULL,
status SMALLINT NOT NULL DEFAULT 1,
avatar_id INTEGER,
token_version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ,
@@ -20,12 +21,12 @@ CREATE TRIGGER update_sys_users_updated_at
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN sys_users.id IS '主键ID';
COMMENT ON COLUMN sys_users.account IS '用户账号';
COMMENT ON COLUMN sys_users.username IS '用户名称';
COMMENT ON COLUMN sys_users.password_hash IS 'hash密码';
COMMENT ON COLUMN sys_users.status IS '用户状态 0:禁用 1:启用';
COMMENT ON COLUMN sys_users.avatar_id IS '头像文件id';
COMMENT ON COLUMN sys_users.token_version IS 'Token Version';
COMMENT ON COLUMN sys_users.created_at IS '创建时间';
COMMENT ON COLUMN sys_users.updated_at IS '更新时间';

View 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
}

View File

@@ -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

View 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
}

View File

@@ -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
}

View File

@@ -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"`
// 更新时间

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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