From 0e674e9d560e924467a4485f4cc41a208d5510a4 Mon Sep 17 00:00:00 2001 From: xy <10816187@qq.com> Date: Sun, 2 Aug 2026 12:02:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80sql=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E3=80=81=E5=87=BD=E6=95=B0=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/main.go | 3 +- .../000011_add_column_to_files.down.sql | 1 + .../000011_add_column_to_files.up.sql | 10 + internal/db/query/files.sql | 11 +- internal/db/query/post.sql | 16 +- internal/db/query/sys_apis.sql | 33 +- internal/db/query/sys_menus.sql | 22 +- internal/db/query/sys_permissions.sql | 10 +- internal/db/query/sys_roles.sql | 40 +- internal/db/query/sys_users.sql | 40 +- internal/db/schema/files.sql | 14 +- internal/db/sqlc/copyfrom.go | 32 +- internal/db/sqlc/files.sql.go | 26 +- internal/db/sqlc/models.go | 2 + internal/db/sqlc/post.sql.go | 24 +- internal/db/sqlc/sys_apis.sql.go | 121 +++--- internal/db/sqlc/sys_menus.sql.go | 212 +++++----- internal/db/sqlc/sys_permissions.sql.go | 34 +- internal/db/sqlc/sys_roles.sql.go | 392 +++++++++--------- internal/db/sqlc/sys_users.sql.go | 282 ++++++------- internal/handler/{sys_api.go => admin/api.go} | 57 ++- internal/handler/{ => admin}/auth.go | 21 +- internal/handler/{ => admin}/category.go | 31 +- internal/handler/admin/file.go | 70 ++++ .../handler/{sys_menu.go => admin/menu.go} | 49 ++- internal/handler/admin/module.go | 24 ++ internal/handler/admin/post.go | 123 ++++++ .../handler/{sys_role.go => admin/role.go} | 69 +-- internal/handler/admin/system.go | 24 ++ internal/handler/{ => admin}/tag.go | 32 +- .../handler/{sys_user.go => admin/user.go} | 103 +++-- internal/handler/module.go | 19 +- internal/handler/site/module.go | 13 + internal/handler/{ => site}/post.go | 28 +- internal/handler/sys_file.go | 58 --- internal/handler/sys_post.go | 108 ----- internal/handler/web.go | 30 -- internal/middleware/auth.go | 104 +++-- internal/middleware/logger.go | 4 +- internal/model/common/response.go | 5 + internal/model/request/{sys_api.go => api.go} | 7 +- .../model/request/{sys_menu.go => menu.go} | 4 +- .../model/request/{sys_role.go => role.go} | 8 +- .../model/request/{sys_user.go => user.go} | 10 +- .../model/response/{sys_file.go => file.go} | 0 .../model/response/{sys_user.go => user.go} | 11 +- internal/pkg/errs/errs.go | 61 +-- internal/pkg/httputil/request.go | 24 ++ internal/router/client.go | 19 - internal/router/router.go | 154 ++++--- internal/router/system.go | 84 ---- internal/service/{sys_api.go => admin/api.go} | 75 ++-- internal/service/{ => admin}/auth.go | 6 +- internal/service/{ => admin}/category.go | 13 +- .../service/{sys_file.go => admin/file.go} | 29 +- .../service/{sys_menu.go => admin/menu.go} | 71 ++-- internal/service/admin/module.go | 19 + .../service/{sys_post.go => admin/post.go} | 37 +- internal/service/admin/role.go | 254 ++++++++++++ internal/service/{ => admin}/tag.go | 13 +- .../service/{sys_user.go => admin/user.go} | 103 +++-- internal/service/module.go | 18 +- internal/service/sys_role.go | 251 ----------- internal/service/web/module.go | 11 + internal/service/{ => web}/post.go | 19 +- internal/utils/utils.go | 28 -- scripts/Dockerfile | 33 ++ scripts/deploy.sh | 46 ++ 68 files changed, 1985 insertions(+), 1720 deletions(-) create mode 100644 internal/db/migrations/000011_add_column_to_files.down.sql create mode 100644 internal/db/migrations/000011_add_column_to_files.up.sql rename internal/handler/{sys_api.go => admin/api.go} (50%) rename internal/handler/{ => admin}/auth.go (81%) rename internal/handler/{ => admin}/category.go (70%) create mode 100644 internal/handler/admin/file.go rename internal/handler/{sys_menu.go => admin/menu.go} (50%) create mode 100644 internal/handler/admin/module.go create mode 100644 internal/handler/admin/post.go rename internal/handler/{sys_role.go => admin/role.go} (55%) create mode 100644 internal/handler/admin/system.go rename internal/handler/{ => admin}/tag.go (71%) rename internal/handler/{sys_user.go => admin/user.go} (57%) create mode 100644 internal/handler/site/module.go rename internal/handler/{ => site}/post.go (71%) delete mode 100644 internal/handler/sys_file.go delete mode 100644 internal/handler/sys_post.go delete mode 100644 internal/handler/web.go rename internal/model/request/{sys_api.go => api.go} (85%) rename internal/model/request/{sys_menu.go => menu.go} (95%) rename internal/model/request/{sys_role.go => role.go} (76%) rename internal/model/request/{sys_user.go => user.go} (85%) rename internal/model/response/{sys_file.go => file.go} (100%) rename internal/model/response/{sys_user.go => user.go} (79%) delete mode 100644 internal/router/client.go delete mode 100644 internal/router/system.go rename internal/service/{sys_api.go => admin/api.go} (50%) rename internal/service/{ => admin}/auth.go (97%) rename internal/service/{ => admin}/category.go (88%) rename internal/service/{sys_file.go => admin/file.go} (73%) rename internal/service/{sys_menu.go => admin/menu.go} (59%) create mode 100644 internal/service/admin/module.go rename internal/service/{sys_post.go => admin/post.go} (77%) create mode 100644 internal/service/admin/role.go rename internal/service/{ => admin}/tag.go (88%) rename internal/service/{sys_user.go => admin/user.go} (61%) delete mode 100644 internal/service/sys_role.go create mode 100644 internal/service/web/module.go rename internal/service/{ => web}/post.go (91%) delete mode 100644 internal/utils/utils.go create mode 100644 scripts/Dockerfile create mode 100755 scripts/deploy.sh diff --git a/cmd/main.go b/cmd/main.go index a60a6c4..dcafada 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,11 +8,12 @@ import ( "server/internal/config" "server/internal/db" "server/internal/handler" + "server/internal/service" + "server/internal/middleware" "server/internal/pkg/cache" "server/internal/pkg/logger" "server/internal/router" - "server/internal/service" "github.com/go-chi/chi/v5" "go.uber.org/fx" diff --git a/internal/db/migrations/000011_add_column_to_files.down.sql b/internal/db/migrations/000011_add_column_to_files.down.sql new file mode 100644 index 0000000..cafd5c9 --- /dev/null +++ b/internal/db/migrations/000011_add_column_to_files.down.sql @@ -0,0 +1 @@ +ALTER TABLE files DROP COLUMN IF EXISTS file_url; \ No newline at end of file diff --git a/internal/db/migrations/000011_add_column_to_files.up.sql b/internal/db/migrations/000011_add_column_to_files.up.sql new file mode 100644 index 0000000..ea4ac4e --- /dev/null +++ b/internal/db/migrations/000011_add_column_to_files.up.sql @@ -0,0 +1,10 @@ +ALTER TABLE files +ADD COLUMN IF NOT EXISTS file_url TEXT; + +UPDATE files +SET file_url = ''; + +ALTER TABLE files +ALTER COLUMN file_url SET NOT NULL; + +COMMENT ON COLUMN files.file_url IS '完整路径'; \ No newline at end of file diff --git a/internal/db/query/files.sql b/internal/db/query/files.sql index 00bfcae..8cf8acb 100644 --- a/internal/db/query/files.sql +++ b/internal/db/query/files.sql @@ -1,12 +1,13 @@ -- name: CreateFile :one -INSERT INTO files(file_name, file_path, original_name, folder_name, mime_type, file_size) -VALUES ($1, $2, $3, $4, $5, $6) RETURNING id,file_path,file_name; +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; --- name: GetFiles :many +-- name: ListFiles :many SELECT * FROM files -ORDER BY id LIMIT $1 -OFFSET $2; +ORDER BY id +LIMIT $1 OFFSET $2; -- name: CountFiles :one SELECT COUNT(*) diff --git a/internal/db/query/post.sql b/internal/db/query/post.sql index ec14cb0..7e5f7d9 100644 --- a/internal/db/query/post.sql +++ b/internal/db/query/post.sql @@ -22,7 +22,7 @@ WITH paginated_posts AS ( ) -- 第二步:用这极少量的记录去进行 JOIN SELECT p.*, - f.file_path AS cover, + f.file_url AS cover, c.name AS category_name, c.id AS category_id, COALESCE(ps.view, 0) AS view, @@ -54,7 +54,7 @@ GROUP BY p.id, p.published_at, p.created_at, p.updated_at, - f.file_path, + f.file_url, c.name, c.id, ps.view @@ -64,9 +64,9 @@ ORDER BY p.sort DESC, p.published_at DESC, p.id DESC; SELECT COUNT(*) FROM posts; --- name: GetPostById :one +-- name: GetPostByID :one SELECT p.*, - f.file_path AS cover, + f.file_url AS cover, c.name AS category_name, c.id AS category_id, COALESCE( @@ -80,7 +80,7 @@ FROM posts p LEFT JOIN post_tag pt ON pt.post_id = p.id WHERE p.id = $1 GROUP BY p.id, - f.file_path, + f.file_url, c.name, c.id LIMIT 1; @@ -106,7 +106,7 @@ WHERE id = $1; -- name: GetPublicPostBySlug :one SELECT p.*, - f.file_path AS cover, + f.file_url AS cover, COALESCE(ps.view, 0) AS view FROM posts p LEFT JOIN files f ON f.id = p.cover_id @@ -132,7 +132,7 @@ WITH paginated_posts AS ( ORDER BY sort DESC, published_at DESC, id DESC LIMIT $1 OFFSET $2) SELECT p.*, - f.file_path AS cover, + f.file_url AS cover, c.name AS category_name, c.id AS category_id, COALESCE(ps.view, 0) AS view, @@ -161,7 +161,7 @@ GROUP BY p.id, p.summary, p.sort, p.published_at, - f.file_path, + f.file_url, c.name, c.id, ps.view diff --git a/internal/db/query/sys_apis.sql b/internal/db/query/sys_apis.sql index 5b176b1..06a6785 100644 --- a/internal/db/query/sys_apis.sql +++ b/internal/db/query/sys_apis.sql @@ -1,28 +1,35 @@ --- name: CreateSysApi :one +-- name: CreateApi :one INSERT INTO sys_apis (name, group_name, method, path, sort) VALUES ($1, $2, $3, $4, $5) RETURNING id; --- name: GetSysApis :many +-- name: ListAllApis :many SELECT * FROM sys_apis -WHERE (sqlc.arg('group_name')::text = '' OR group_name ILIKE '%' || sqlc.arg('group_name') || '%') +ORDER BY sort ASC; + +-- name: ListApis :many +SELECT * +FROM sys_apis +WHERE (sqlc.arg('name')::text = '' OR name ILIKE '%' || sqlc.arg('name') || '%') + AND (sqlc.arg('group_name')::text = '' OR group_name ILIKE '%' || sqlc.arg('group_name') || '%') AND (sqlc.arg('method')::text = '' OR method = sqlc.arg('method')) ORDER BY id LIMIT $1 OFFSET $2; --- name: CountSysApis :one +-- name: CountApis :one SELECT COUNT(*) FROM sys_apis -WHERE (sqlc.arg('group_name')::text = '' OR group_name ILIKE '%' || sqlc.arg('group_name') || '%') +WHERE (sqlc.arg('name')::text = '' OR name ILIKE '%' || sqlc.arg('name') || '%') + AND (sqlc.arg('group_name')::text = '' OR group_name ILIKE '%' || sqlc.arg('group_name') || '%') AND (sqlc.arg('method')::text = '' OR method = sqlc.arg('method')); --- name: GetSysApiByID :one +-- name: GetApiByID :one SELECT * FROM sys_apis WHERE id = $1; --- name: UpdateSysApi :execrows +-- name: UpdateApi :execrows UPDATE sys_apis SET name = $2, method = $3, @@ -31,25 +38,21 @@ SET name = $2, group_name = $6 WHERE id = $1; --- name: DeleteSysApi :execrows +-- name: DeleteApi :execrows DELETE FROM sys_apis WHERE id = $1; --- name: CreateSysApiPermission :exec +-- name: CreateApiPermission :exec INSERT INTO sys_api_permission (api_id, permission_id) VALUES ($1, $2); --- name: DeleteSysApiPermission :exec +-- name: DeleteApiPermission :exec DELETE FROM sys_api_permission WHERE api_id = $1; --- name: GetSysApiGroupNames :many +-- name: ListApiGroups :many SELECT DISTINCT group_name FROM sys_apis; --- name: GetAllSysApis :many -SELECT * -FROM sys_apis -ORDER BY sort ASC; \ No newline at end of file diff --git a/internal/db/query/sys_menus.sql b/internal/db/query/sys_menus.sql index 60dac97..90c57f6 100644 --- a/internal/db/query/sys_menus.sql +++ b/internal/db/query/sys_menus.sql @@ -1,9 +1,9 @@ --- name: CreateSysMenu :one +-- name: CreateMenu :one INSERT INTO sys_menus (name, path, component, type, hidden, sort, status, parent_id, icon) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id; --- name: ListSysMenus :many +-- name: ListMenus :many SELECT m.*, p.code AS permission_code FROM sys_menus m LEFT JOIN sys_menu_permission mp ON m.id = mp.menu_id @@ -11,11 +11,11 @@ FROM sys_menus m ORDER BY m.id LIMIT $1 OFFSET $2; --- name: CountSysMenus :one +-- name: CountMenus :one SELECT COUNT(*) FROM sys_menus; --- name: GetAllSysMenus :many +-- name: ListAllMenus :many SELECT m.*, p.code AS permission_code FROM sys_menus m @@ -24,12 +24,12 @@ FROM sys_menus m ORDER BY m.sort ASC, m.id ASC; --- name: GetSysMenuByID :one +-- name: GetMenuByID :one SELECT * FROM sys_menus WHERE id = $1; --- name: UpdateSysMenu :execrows +-- name: UpdateMenu :execrows UPDATE sys_menus SET name = coalesce(sqlc.narg('name'), name), path = coalesce(sqlc.narg('path'), path), @@ -42,28 +42,28 @@ SET name = coalesce(sqlc.narg('name'), name), parent_id = CASE WHEN @update_parent_id::boolean THEN @parent_id ELSE parent_id END WHERE id = sqlc.arg('id'); --- name: DeleteSysMenu :execrows +-- name: DeleteMenu :execrows DELETE FROM sys_menus WHERE id = $1; --- name: CreateSysMenuPermission :exec +-- name: CreateMenuPermission :exec INSERT INTO sys_menu_permission (menu_id, permission_id) VALUES ($1, $2); --- name: DeleteSysMenuPermission :exec +-- name: DeleteMenuPermission :exec DELETE FROM sys_menu_permission WHERE menu_id = $1; --- name: DeleteSysPermissionByMenuID :exec +-- name: DeletePermissionsByMenuID :exec DELETE FROM sys_permissions WHERE id IN (SELECT permission_id FROM sys_menu_permission WHERE menu_id = $1); --- name: UpdateSysMenuPermissionCode :exec +-- name: UpdateMenuPermissionCode :exec UPDATE sys_permissions p SET code = coalesce(sqlc.narg('code'), code) FROM sys_menu_permission mp diff --git a/internal/db/query/sys_permissions.sql b/internal/db/query/sys_permissions.sql index ee33e63..e23384d 100644 --- a/internal/db/query/sys_permissions.sql +++ b/internal/db/query/sys_permissions.sql @@ -1,23 +1,23 @@ --- name: CreateSysPermission :one +-- name: CreatePermission :one INSERT INTO sys_permissions(type,code) VALUES ($1,$2) RETURNING id; --- name: UpdateSysPermission :exec +-- name: UpdatePermission :exec UPDATE sys_permissions SET code = $2 WHERE id = $1; --- name: DeleteSysPermission :exec +-- name: DeletePermission :exec DELETE FROM sys_permissions WHERE id = $1; --- name: GetSysPermissionIDBySysApiID :one +-- name: GetPermissionIDBySysApiID :one SELECT permission_id FROM sys_api_permission WHERE api_id = $1; --- name: GetSysPermissionIDBySysMenuID :one +-- name: GetPermissionIDBySysMenuID :one SELECT permission_id FROM sys_menu_permission WHERE menu_id = $1; \ No newline at end of file diff --git a/internal/db/query/sys_roles.sql b/internal/db/query/sys_roles.sql index 2b03e99..e865230 100644 --- a/internal/db/query/sys_roles.sql +++ b/internal/db/query/sys_roles.sql @@ -1,23 +1,27 @@ --- name: CreateSysRole :exec +-- name: CreateRole :exec INSERT INTO sys_roles (name, code, status) VALUES ($1, $2, $3); --- name: ListSysRoles :many +-- name: ListAllRoles :many +SELECT * +from sys_roles; + +-- name: ListRoles :many SELECT * FROM sys_roles ORDER BY id LIMIT $1 OFFSET $2; --- name: CountSysRoles :one +-- name: CountRoles :one SELECT COUNT(*) FROM sys_roles; --- name: GetSysRoleByID :one +-- name: GetRoleByID :one SELECT * FROM sys_roles WHERE id = $1; --- name: ExistsSysRoleCode :one +-- name: ExistsRoleCode :one SELECT EXISTS(SELECT 1 FROM sys_roles WHERE code = sqlc.arg(code) @@ -26,67 +30,65 @@ SELECT EXISTS(SELECT 1 OR id <> sqlc.narg(id)::integer )); --- name: UpdateSysRole :execrows +-- name: UpdateRole :execrows UPDATE sys_roles SET name = coalesce(sqlc.narg('name'), name), status = coalesce(sqlc.narg('status'), status) WHERE id = sqlc.arg('id'); --- name: DeleteSysRole :execrows +-- name: DeleteRole :execrows DELETE FROM sys_roles WHERE id = $1; --- name: CreateSysRolePermission :copyfrom +-- name: CreateRolePermission :copyfrom INSERT INTO sys_role_permission (role_id, permission_id) VALUES ($1, $2); --- name: GetSysPermissionIdsByMenuIDs :many +-- name: ListPermissionIDsByMenuIDs :many SELECT permission_id FROM sys_menu_permission WHERE menu_id = ANY ($1::int[]); --- name: GetSysPermissionIdsByApiIDs :many +-- name: ListPermissionIDsByApiIDs :many SELECT permission_id FROM sys_api_permission WHERE api_id = ANY ($1::int[]); --- name: GetAllSysRoles :many -SELECT * -from sys_roles; --- name: GetSysRoleMenus :many + +-- name: ListRoleMenus :many SELECT m.id, m.name FROM sys_role_permission rp JOIN sys_menu_permission mp ON mp.permission_id = rp.permission_id JOIN sys_menus m ON m.id = mp.menu_id WHERE rp.role_id = $1; --- name: GetSysRoleApis :many +-- name: ListRoleApis :many SELECT a.id, a."name" FROM sys_role_permission rp JOIN sys_api_permission ap ON ap.permission_id = rp.permission_id JOIN sys_apis a ON a.id = ap.api_id WHERE rp.role_id = $1; --- name: DeleteSysRolePermissionByType :exec +-- name: DeleteRolePermissionsByType :exec DELETE FROM sys_role_permission rp USING sys_permissions p WHERE rp.permission_id = p.id AND rp.role_id = $1 AND p."type" = $2; --- name: DeleteSysRolePermissionByRoleID :exec +-- name: DeleteRolePermissionsByRoleID :exec DELETE FROM sys_role_permission WHERE role_id = $1; --- name: DeleteSysRolePermissionByPermissionID :exec +-- name: DeleteRolePermissionByPermissionID :exec DELETE FROM sys_role_permission WHERE permission_id = $1; --- name: DeleteSysUserRoleByRoleID :exec +-- name: DeleteUserRoleByRoleID :exec DELETE FROM sys_user_role WHERE role_id = $1; \ No newline at end of file diff --git a/internal/db/query/sys_users.sql b/internal/db/query/sys_users.sql index b664024..1f9cd51 100644 --- a/internal/db/query/sys_users.sql +++ b/internal/db/query/sys_users.sql @@ -1,48 +1,48 @@ --- name: CreateSysUser :exec +-- name: CreateUser :exec INSERT INTO sys_users (account, username, password_hash, status, avatar_id) VALUES ($1, $2, $3, $4, $5); --- name: GetActiveSysUserByID :one +-- name: GetActiveUserByID :one -- 场景:用户登录、获取个人信息、刷新 Token(严格校验 status = 1) SELECT id, account, username, status FROM sys_users WHERE id = $1 AND status = 1; --- name: GetSysUserByID :one +-- name: GetUserByID :one SELECT u.id, u.account, u.username, u.avatar_id, u.status, - f.file_path AS avatar_url, + 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: GetSysUserByAccount :one +-- name: GetUserByAccount :one SELECT u.id, u.account, u.username, u.password_hash, u.status, u.avatar_id, - f.file_path AS avatar_url, + 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.account = $1; --- name: ListSysUsers :many +-- name: ListUsers :many SELECT u.id, u.account, u.username, u.status, u.avatar_id, - f.file_path AS avatar_url, + f.file_url AS avatar_url, u.created_at, u.updated_at FROM sys_users u @@ -51,57 +51,57 @@ WHERE (sqlc.arg('username')::text = '' OR u.username ILIKE '%' || sqlc.arg('user ORDER BY u.id LIMIT $1 OFFSET $2; --- name: CountSysUsers :one +-- name: CountUsers :one SELECT COUNT(*) FROM sys_users WHERE (sqlc.arg('username')::text = '' OR username ILIKE '%' || sqlc.arg('username') || '%'); --- name: UpdateSysUser :execrows +-- name: UpdateUser :execrows UPDATE sys_users SET username = coalesce(sqlc.narg('username'), username), status = coalesce(sqlc.narg('status'), status), avatar_id = CASE WHEN @update_avatar_id::boolean THEN @avatar_id ELSE avatar_id END WHERE id = sqlc.arg('id'); --- name: UpdateSysUserPassword :execrows +-- name: UpdateUserPassword :execrows UPDATE sys_users SET password_hash = $2 WHERE id = $1; --- name: DeleteSysUser :execrows +-- name: DeleteUser :execrows DELETE FROM sys_users WHERE id = $1 AND id <> 1; --- name: ClearSysUserRoles :exec +-- name: ClearUserRoles :exec DELETE FROM sys_user_role WHERE user_id = $1; --- name: CreateSysUserRole :copyfrom +-- name: CreateUserRole :copyfrom INSERT INTO sys_user_role (user_id, role_id) VALUES ($1, $2); --- name: GetSysUserRoles :many +-- name: ListUserRoles :many SELECT r.* FROM sys_roles r JOIN sys_user_role ur ON r.id = ur.role_id WHERE ur.user_id = $1; --- name: GetSysAdminMenus :many +-- name: ListAdminMenus :many SELECT * FROM sys_menus WHERE status = 1 AND type <> 2 ORDER BY sort, id; --- name: GetSysAdminPermissions :many +-- name: ListAdminPermissionCodes :many SELECT code FROM sys_permissions WHERE type = 0; --- name: GetSysUserMenus :many +-- name: ListUserMenus :many SELECT DISTINCT m.* FROM sys_users u JOIN sys_user_role ur ON ur.user_id = u.id @@ -116,7 +116,7 @@ WHERE u.id = $1 AND m.type <> 2 ORDER BY m.sort, m.id; --- name: GetSysUserPermissions :many +-- name: ListUserPermissionCodes :many SELECT DISTINCT p.code FROM sys_users u JOIN sys_user_role ur ON u.id = ur.user_id @@ -131,7 +131,7 @@ WHERE u.id = $1 AND m.status = 1 AND p."type" = 0; --- name: GetSysUserApis :many +-- name: ListUserApis :many SELECT DISTINCT a.id, a.PATH, a.METHOD diff --git a/internal/db/schema/files.sql b/internal/db/schema/files.sql index 9666ffa..33ebf4a 100644 --- a/internal/db/schema/files.sql +++ b/internal/db/schema/files.sql @@ -1,12 +1,13 @@ CREATE TABLE files ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - file_name TEXT NOT NULL, - file_path TEXT NOT NULL, - original_name TEXT NOT NULL, - folder_name TEXT NOT NULL, - mime_type TEXT NOT NULL, - file_size BIGINT NOT NULL, + file_name TEXT NOT NULL, + file_path TEXT NOT NULL, + file_url TEXT NOT NULL, + original_name TEXT NOT NULL, + folder_name TEXT NOT NULL, + mime_type TEXT NOT NULL, + file_size BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ ); @@ -20,6 +21,7 @@ EXECUTE FUNCTION update_updated_at_column(); COMMENT ON COLUMN files.id IS '主键ID'; COMMENT ON COLUMN files.file_name IS '文件名 xxx.jpg'; COMMENT ON COLUMN files.file_path IS '文件路径 带path /xxx/xxx.jpg'; +COMMENT ON COLUMN files.file_url IS '完整路径'; COMMENT ON COLUMN files.original_name IS '原始文件名'; COMMENT ON COLUMN files.folder_name IS '文件夹名称'; COMMENT ON COLUMN files.mime_type IS 'mime_type'; diff --git a/internal/db/sqlc/copyfrom.go b/internal/db/sqlc/copyfrom.go index ca0c4f2..3e855a2 100644 --- a/internal/db/sqlc/copyfrom.go +++ b/internal/db/sqlc/copyfrom.go @@ -42,13 +42,13 @@ func (q *Queries) CreatePostTag(ctx context.Context, arg []CreatePostTagParams) return q.db.CopyFrom(ctx, []string{"post_tag"}, []string{"post_id", "tag_id"}, &iteratorForCreatePostTag{rows: arg}) } -// iteratorForCreateSysRolePermission implements pgx.CopyFromSource. -type iteratorForCreateSysRolePermission struct { - rows []CreateSysRolePermissionParams +// iteratorForCreateRolePermission implements pgx.CopyFromSource. +type iteratorForCreateRolePermission struct { + rows []CreateRolePermissionParams skippedFirstNextCall bool } -func (r *iteratorForCreateSysRolePermission) Next() bool { +func (r *iteratorForCreateRolePermission) Next() bool { if len(r.rows) == 0 { return false } @@ -60,28 +60,28 @@ func (r *iteratorForCreateSysRolePermission) Next() bool { return len(r.rows) > 0 } -func (r iteratorForCreateSysRolePermission) Values() ([]interface{}, error) { +func (r iteratorForCreateRolePermission) Values() ([]interface{}, error) { return []interface{}{ r.rows[0].RoleID, r.rows[0].PermissionID, }, nil } -func (r iteratorForCreateSysRolePermission) Err() error { +func (r iteratorForCreateRolePermission) Err() error { return nil } -func (q *Queries) CreateSysRolePermission(ctx context.Context, arg []CreateSysRolePermissionParams) (int64, error) { - return q.db.CopyFrom(ctx, []string{"sys_role_permission"}, []string{"role_id", "permission_id"}, &iteratorForCreateSysRolePermission{rows: arg}) +func (q *Queries) CreateRolePermission(ctx context.Context, arg []CreateRolePermissionParams) (int64, error) { + return q.db.CopyFrom(ctx, []string{"sys_role_permission"}, []string{"role_id", "permission_id"}, &iteratorForCreateRolePermission{rows: arg}) } -// iteratorForCreateSysUserRole implements pgx.CopyFromSource. -type iteratorForCreateSysUserRole struct { - rows []CreateSysUserRoleParams +// iteratorForCreateUserRole implements pgx.CopyFromSource. +type iteratorForCreateUserRole struct { + rows []CreateUserRoleParams skippedFirstNextCall bool } -func (r *iteratorForCreateSysUserRole) Next() bool { +func (r *iteratorForCreateUserRole) Next() bool { if len(r.rows) == 0 { return false } @@ -93,17 +93,17 @@ func (r *iteratorForCreateSysUserRole) Next() bool { return len(r.rows) > 0 } -func (r iteratorForCreateSysUserRole) Values() ([]interface{}, error) { +func (r iteratorForCreateUserRole) Values() ([]interface{}, error) { return []interface{}{ r.rows[0].UserID, r.rows[0].RoleID, }, nil } -func (r iteratorForCreateSysUserRole) Err() error { +func (r iteratorForCreateUserRole) Err() error { return nil } -func (q *Queries) CreateSysUserRole(ctx context.Context, arg []CreateSysUserRoleParams) (int64, error) { - return q.db.CopyFrom(ctx, []string{"sys_user_role"}, []string{"user_id", "role_id"}, &iteratorForCreateSysUserRole{rows: arg}) +func (q *Queries) CreateUserRole(ctx context.Context, arg []CreateUserRoleParams) (int64, error) { + return q.db.CopyFrom(ctx, []string{"sys_user_role"}, []string{"user_id", "role_id"}, &iteratorForCreateUserRole{rows: arg}) } diff --git a/internal/db/sqlc/files.sql.go b/internal/db/sqlc/files.sql.go index eccfea2..3f3eb0a 100644 --- a/internal/db/sqlc/files.sql.go +++ b/internal/db/sqlc/files.sql.go @@ -22,8 +22,9 @@ 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) -VALUES ($1, $2, $3, $4, $5, $6) RETURNING id,file_path,file_name +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 ` type CreateFileParams struct { @@ -33,11 +34,12 @@ type CreateFileParams struct { FolderName string `json:"folder_name"` MimeType string `json:"mime_type"` FileSize int64 `json:"file_size"` + FileUrl string `json:"file_url"` } type CreateFileRow struct { ID int32 `json:"id"` - FilePath string `json:"file_path"` + FileUrl string `json:"file_url"` FileName string `json:"file_name"` } @@ -49,26 +51,27 @@ func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (CreateF arg.FolderName, arg.MimeType, arg.FileSize, + arg.FileUrl, ) var i CreateFileRow - err := row.Scan(&i.ID, &i.FilePath, &i.FileName) + err := row.Scan(&i.ID, &i.FileUrl, &i.FileName) return i, err } -const getFiles = `-- name: GetFiles :many -SELECT id, file_name, file_path, original_name, folder_name, mime_type, file_size, created_at, updated_at +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 LIMIT $1 -OFFSET $2 +ORDER BY id +LIMIT $1 OFFSET $2 ` -type GetFilesParams struct { +type ListFilesParams struct { Limit int32 `json:"limit"` Offset int32 `json:"offset"` } -func (q *Queries) GetFiles(ctx context.Context, arg GetFilesParams) ([]File, error) { - rows, err := q.db.Query(ctx, getFiles, arg.Limit, arg.Offset) +func (q *Queries) ListFiles(ctx context.Context, arg ListFilesParams) ([]File, error) { + rows, err := q.db.Query(ctx, listFiles, arg.Limit, arg.Offset) if err != nil { return nil, err } @@ -80,6 +83,7 @@ func (q *Queries) GetFiles(ctx context.Context, arg GetFilesParams) ([]File, err &i.ID, &i.FileName, &i.FilePath, + &i.FileUrl, &i.OriginalName, &i.FolderName, &i.MimeType, diff --git a/internal/db/sqlc/models.go b/internal/db/sqlc/models.go index 2922832..30ac92a 100644 --- a/internal/db/sqlc/models.go +++ b/internal/db/sqlc/models.go @@ -29,6 +29,8 @@ type File struct { FileName string `json:"file_name"` // 文件路径 带path /xxx/xxx.jpg FilePath string `json:"file_path"` + // 完整路径 + FileUrl string `json:"file_url"` // 原始文件名 OriginalName string `json:"original_name"` // 文件夹名称 diff --git a/internal/db/sqlc/post.sql.go b/internal/db/sqlc/post.sql.go index 99befb3..54e6655 100644 --- a/internal/db/sqlc/post.sql.go +++ b/internal/db/sqlc/post.sql.go @@ -83,9 +83,9 @@ func (q *Queries) DeletePost(ctx context.Context, id int32) (int64, error) { return result.RowsAffected(), nil } -const getPostById = `-- name: GetPostById :one +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_path AS cover, + f.file_url AS cover, c.name AS category_name, c.id AS category_id, COALESCE( @@ -99,13 +99,13 @@ FROM posts p LEFT JOIN post_tag pt ON pt.post_id = p.id WHERE p.id = $1 GROUP BY p.id, - f.file_path, + f.file_url, c.name, c.id LIMIT 1 ` -type GetPostByIdRow struct { +type GetPostByIDRow struct { ID int32 `json:"id"` Title string `json:"title"` CoverID *int32 `json:"cover_id"` @@ -123,9 +123,9 @@ type GetPostByIdRow struct { Tags interface{} `json:"tags"` } -func (q *Queries) GetPostById(ctx context.Context, id int32) (GetPostByIdRow, error) { - row := q.db.QueryRow(ctx, getPostById, id) - var i GetPostByIdRow +func (q *Queries) GetPostByID(ctx context.Context, id int32) (GetPostByIDRow, error) { + row := q.db.QueryRow(ctx, getPostByID, id) + var i GetPostByIDRow err := row.Scan( &i.ID, &i.Title, @@ -149,7 +149,7 @@ func (q *Queries) GetPostById(ctx context.Context, id int32) (GetPostByIdRow, er 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_path AS cover, + f.file_url AS cover, COALESCE(ps.view, 0) AS view FROM posts p LEFT JOIN files f ON f.id = p.cover_id @@ -312,7 +312,7 @@ WITH paginated_posts AS ( 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_path AS cover, + f.file_url AS cover, c.name AS category_name, c.id AS category_id, COALESCE(ps.view, 0) AS view, @@ -344,7 +344,7 @@ GROUP BY p.id, p.published_at, p.created_at, p.updated_at, - f.file_path, + f.file_url, c.name, c.id, ps.view @@ -427,7 +427,7 @@ 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_path AS cover, + f.file_url AS cover, c.name AS category_name, c.id AS category_id, COALESCE(ps.view, 0) AS view, @@ -456,7 +456,7 @@ GROUP BY p.id, p.summary, p.sort, p.published_at, - f.file_path, + f.file_url, c.name, c.id, ps.view diff --git a/internal/db/sqlc/sys_apis.sql.go b/internal/db/sqlc/sys_apis.sql.go index f65486a..cc91b0e 100644 --- a/internal/db/sqlc/sys_apis.sql.go +++ b/internal/db/sqlc/sys_apis.sql.go @@ -9,32 +9,34 @@ import ( "context" ) -const countSysApis = `-- name: CountSysApis :one +const countApis = `-- name: CountApis :one SELECT COUNT(*) FROM sys_apis -WHERE ($1::text = '' OR group_name ILIKE '%' || $1 || '%') - AND ($2::text = '' OR method = $2) +WHERE ($1::text = '' OR name ILIKE '%' || $1 || '%') + AND ($2::text = '' OR group_name ILIKE '%' || $2 || '%') + AND ($3::text = '' OR method = $3) ` -type CountSysApisParams struct { +type CountApisParams struct { + Name string `json:"name"` GroupName string `json:"group_name"` Method string `json:"method"` } -func (q *Queries) CountSysApis(ctx context.Context, arg CountSysApisParams) (int64, error) { - row := q.db.QueryRow(ctx, countSysApis, arg.GroupName, arg.Method) +func (q *Queries) CountApis(ctx context.Context, arg CountApisParams) (int64, error) { + row := q.db.QueryRow(ctx, countApis, arg.Name, arg.GroupName, arg.Method) var count int64 err := row.Scan(&count) return count, err } -const createSysApi = `-- name: CreateSysApi :one +const createApi = `-- name: CreateApi :one INSERT INTO sys_apis (name, group_name, method, path, sort) VALUES ($1, $2, $3, $4, $5) RETURNING id ` -type CreateSysApiParams struct { +type CreateApiParams struct { Name string `json:"name"` GroupName string `json:"group_name"` Method string `json:"method"` @@ -42,8 +44,8 @@ type CreateSysApiParams struct { Sort *int32 `json:"sort"` } -func (q *Queries) CreateSysApi(ctx context.Context, arg CreateSysApiParams) (int32, error) { - row := q.db.QueryRow(ctx, createSysApi, +func (q *Queries) CreateApi(ctx context.Context, arg CreateApiParams) (int32, error) { + row := q.db.QueryRow(ctx, createApi, arg.Name, arg.GroupName, arg.Method, @@ -55,54 +57,76 @@ func (q *Queries) CreateSysApi(ctx context.Context, arg CreateSysApiParams) (int return id, err } -const createSysApiPermission = `-- name: CreateSysApiPermission :exec +const createApiPermission = `-- name: CreateApiPermission :exec INSERT INTO sys_api_permission (api_id, permission_id) VALUES ($1, $2) ` -type CreateSysApiPermissionParams struct { +type CreateApiPermissionParams struct { ApiID int32 `json:"api_id"` PermissionID int32 `json:"permission_id"` } -func (q *Queries) CreateSysApiPermission(ctx context.Context, arg CreateSysApiPermissionParams) error { - _, err := q.db.Exec(ctx, createSysApiPermission, arg.ApiID, arg.PermissionID) +func (q *Queries) CreateApiPermission(ctx context.Context, arg CreateApiPermissionParams) error { + _, err := q.db.Exec(ctx, createApiPermission, arg.ApiID, arg.PermissionID) return err } -const deleteSysApi = `-- name: DeleteSysApi :execrows +const deleteApi = `-- name: DeleteApi :execrows DELETE FROM sys_apis WHERE id = $1 ` -func (q *Queries) DeleteSysApi(ctx context.Context, id int32) (int64, error) { - result, err := q.db.Exec(ctx, deleteSysApi, id) +func (q *Queries) DeleteApi(ctx context.Context, id int32) (int64, error) { + result, err := q.db.Exec(ctx, deleteApi, id) if err != nil { return 0, err } return result.RowsAffected(), nil } -const deleteSysApiPermission = `-- name: DeleteSysApiPermission :exec +const deleteApiPermission = `-- name: DeleteApiPermission :exec DELETE FROM sys_api_permission WHERE api_id = $1 ` -func (q *Queries) DeleteSysApiPermission(ctx context.Context, apiID int32) error { - _, err := q.db.Exec(ctx, deleteSysApiPermission, apiID) +func (q *Queries) DeleteApiPermission(ctx context.Context, apiID int32) error { + _, err := q.db.Exec(ctx, deleteApiPermission, apiID) return err } -const getAllSysApis = `-- name: GetAllSysApis :many +const getApiByID = `-- name: GetApiByID :one +SELECT id, group_name, name, method, path, sort, created_at, updated_at +FROM sys_apis +WHERE id = $1 +` + +func (q *Queries) GetApiByID(ctx context.Context, id int32) (SysApi, error) { + row := q.db.QueryRow(ctx, getApiByID, id) + var i SysApi + err := row.Scan( + &i.ID, + &i.GroupName, + &i.Name, + &i.Method, + &i.Path, + &i.Sort, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const listAllApis = `-- name: ListAllApis :many SELECT id, group_name, name, method, path, sort, created_at, updated_at FROM sys_apis ORDER BY sort ASC ` -func (q *Queries) GetAllSysApis(ctx context.Context) ([]SysApi, error) { - rows, err := q.db.Query(ctx, getAllSysApis) +func (q *Queries) ListAllApis(ctx context.Context) ([]SysApi, error) { + rows, err := q.db.Query(ctx, listAllApis) if err != nil { return nil, err } @@ -130,35 +154,13 @@ func (q *Queries) GetAllSysApis(ctx context.Context) ([]SysApi, error) { return items, nil } -const getSysApiByID = `-- name: GetSysApiByID :one -SELECT id, group_name, name, method, path, sort, created_at, updated_at -FROM sys_apis -WHERE id = $1 -` - -func (q *Queries) GetSysApiByID(ctx context.Context, id int32) (SysApi, error) { - row := q.db.QueryRow(ctx, getSysApiByID, id) - var i SysApi - err := row.Scan( - &i.ID, - &i.GroupName, - &i.Name, - &i.Method, - &i.Path, - &i.Sort, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getSysApiGroupNames = `-- name: GetSysApiGroupNames :many +const listApiGroups = `-- name: ListApiGroups :many SELECT DISTINCT group_name FROM sys_apis ` -func (q *Queries) GetSysApiGroupNames(ctx context.Context) ([]string, error) { - rows, err := q.db.Query(ctx, getSysApiGroupNames) +func (q *Queries) ListApiGroups(ctx context.Context) ([]string, error) { + rows, err := q.db.Query(ctx, listApiGroups) if err != nil { return nil, err } @@ -177,26 +179,29 @@ func (q *Queries) GetSysApiGroupNames(ctx context.Context) ([]string, error) { return items, nil } -const getSysApis = `-- name: GetSysApis :many +const listApis = `-- name: ListApis :many SELECT id, group_name, name, method, path, sort, created_at, updated_at FROM sys_apis -WHERE ($3::text = '' OR group_name ILIKE '%' || $3 || '%') - AND ($4::text = '' OR method = $4) +WHERE ($3::text = '' OR name ILIKE '%' || $3 || '%') + AND ($4::text = '' OR group_name ILIKE '%' || $4 || '%') + AND ($5::text = '' OR method = $5) ORDER BY id LIMIT $1 OFFSET $2 ` -type GetSysApisParams struct { +type ListApisParams struct { Limit int32 `json:"limit"` Offset int32 `json:"offset"` + Name string `json:"name"` GroupName string `json:"group_name"` Method string `json:"method"` } -func (q *Queries) GetSysApis(ctx context.Context, arg GetSysApisParams) ([]SysApi, error) { - rows, err := q.db.Query(ctx, getSysApis, +func (q *Queries) ListApis(ctx context.Context, arg ListApisParams) ([]SysApi, error) { + rows, err := q.db.Query(ctx, listApis, arg.Limit, arg.Offset, + arg.Name, arg.GroupName, arg.Method, ) @@ -227,7 +232,7 @@ func (q *Queries) GetSysApis(ctx context.Context, arg GetSysApisParams) ([]SysAp return items, nil } -const updateSysApi = `-- name: UpdateSysApi :execrows +const updateApi = `-- name: UpdateApi :execrows UPDATE sys_apis SET name = $2, method = $3, @@ -237,7 +242,7 @@ SET name = $2, WHERE id = $1 ` -type UpdateSysApiParams struct { +type UpdateApiParams struct { ID int32 `json:"id"` Name string `json:"name"` Method string `json:"method"` @@ -246,8 +251,8 @@ type UpdateSysApiParams struct { GroupName string `json:"group_name"` } -func (q *Queries) UpdateSysApi(ctx context.Context, arg UpdateSysApiParams) (int64, error) { - result, err := q.db.Exec(ctx, updateSysApi, +func (q *Queries) UpdateApi(ctx context.Context, arg UpdateApiParams) (int64, error) { + result, err := q.db.Exec(ctx, updateApi, arg.ID, arg.Name, arg.Method, diff --git a/internal/db/sqlc/sys_menus.sql.go b/internal/db/sqlc/sys_menus.sql.go index 3f6cd31..120bf92 100644 --- a/internal/db/sqlc/sys_menus.sql.go +++ b/internal/db/sqlc/sys_menus.sql.go @@ -10,25 +10,25 @@ import ( "time" ) -const countSysMenus = `-- name: CountSysMenus :one +const countMenus = `-- name: CountMenus :one SELECT COUNT(*) FROM sys_menus ` -func (q *Queries) CountSysMenus(ctx context.Context) (int64, error) { - row := q.db.QueryRow(ctx, countSysMenus) +func (q *Queries) CountMenus(ctx context.Context) (int64, error) { + row := q.db.QueryRow(ctx, countMenus) var count int64 err := row.Scan(&count) return count, err } -const createSysMenu = `-- name: CreateSysMenu :one +const createMenu = `-- name: CreateMenu :one INSERT INTO sys_menus (name, path, component, type, hidden, sort, status, parent_id, icon) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id ` -type CreateSysMenuParams struct { +type CreateMenuParams struct { Name string `json:"name"` Path *string `json:"path"` Component *string `json:"component"` @@ -40,8 +40,8 @@ type CreateSysMenuParams struct { Icon *int32 `json:"icon"` } -func (q *Queries) CreateSysMenu(ctx context.Context, arg CreateSysMenuParams) (int32, error) { - row := q.db.QueryRow(ctx, createSysMenu, +func (q *Queries) CreateMenu(ctx context.Context, arg CreateMenuParams) (int32, error) { + row := q.db.QueryRow(ctx, createMenu, arg.Name, arg.Path, arg.Component, @@ -57,47 +57,47 @@ func (q *Queries) CreateSysMenu(ctx context.Context, arg CreateSysMenuParams) (i return id, err } -const createSysMenuPermission = `-- name: CreateSysMenuPermission :exec +const createMenuPermission = `-- name: CreateMenuPermission :exec INSERT INTO sys_menu_permission (menu_id, permission_id) VALUES ($1, $2) ` -type CreateSysMenuPermissionParams struct { +type CreateMenuPermissionParams struct { MenuID int32 `json:"menu_id"` PermissionID int32 `json:"permission_id"` } -func (q *Queries) CreateSysMenuPermission(ctx context.Context, arg CreateSysMenuPermissionParams) error { - _, err := q.db.Exec(ctx, createSysMenuPermission, arg.MenuID, arg.PermissionID) +func (q *Queries) CreateMenuPermission(ctx context.Context, arg CreateMenuPermissionParams) error { + _, err := q.db.Exec(ctx, createMenuPermission, arg.MenuID, arg.PermissionID) return err } -const deleteSysMenu = `-- name: DeleteSysMenu :execrows +const deleteMenu = `-- name: DeleteMenu :execrows DELETE FROM sys_menus WHERE id = $1 ` -func (q *Queries) DeleteSysMenu(ctx context.Context, id int32) (int64, error) { - result, err := q.db.Exec(ctx, deleteSysMenu, id) +func (q *Queries) DeleteMenu(ctx context.Context, id int32) (int64, error) { + result, err := q.db.Exec(ctx, deleteMenu, id) if err != nil { return 0, err } return result.RowsAffected(), nil } -const deleteSysMenuPermission = `-- name: DeleteSysMenuPermission :exec +const deleteMenuPermission = `-- name: DeleteMenuPermission :exec DELETE FROM sys_menu_permission WHERE menu_id = $1 ` -func (q *Queries) DeleteSysMenuPermission(ctx context.Context, menuID int32) error { - _, err := q.db.Exec(ctx, deleteSysMenuPermission, menuID) +func (q *Queries) DeleteMenuPermission(ctx context.Context, menuID int32) error { + _, err := q.db.Exec(ctx, deleteMenuPermission, menuID) return err } -const deleteSysPermissionByMenuID = `-- name: DeleteSysPermissionByMenuID :exec +const deletePermissionsByMenuID = `-- name: DeletePermissionsByMenuID :exec DELETE FROM sys_permissions WHERE id IN (SELECT permission_id @@ -105,79 +105,19 @@ WHERE id IN (SELECT permission_id WHERE menu_id = $1) ` -func (q *Queries) DeleteSysPermissionByMenuID(ctx context.Context, menuID int32) error { - _, err := q.db.Exec(ctx, deleteSysPermissionByMenuID, menuID) +func (q *Queries) DeletePermissionsByMenuID(ctx context.Context, menuID int32) error { + _, err := q.db.Exec(ctx, deletePermissionsByMenuID, menuID) return err } -const getAllSysMenus = `-- name: GetAllSysMenus :many -SELECT m.id, m.name, m.path, m.component, m.type, m.hidden, m.sort, m.status, m.parent_id, m.icon, m.created_at, m.updated_at, - p.code AS permission_code -FROM sys_menus m - LEFT JOIN sys_menu_permission mp ON m.id = mp.menu_id - LEFT JOIN sys_permissions p ON p.id = mp.permission_id -ORDER BY m.sort ASC, - m.id ASC -` - -type GetAllSysMenusRow struct { - ID int32 `json:"id"` - Name string `json:"name"` - Path *string `json:"path"` - Component *string `json:"component"` - Type int16 `json:"type"` - Hidden *bool `json:"hidden"` - Sort *int32 `json:"sort"` - Status int16 `json:"status"` - ParentID *int32 `json:"parent_id"` - Icon *int32 `json:"icon"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt *time.Time `json:"updated_at"` - PermissionCode *string `json:"permission_code"` -} - -func (q *Queries) GetAllSysMenus(ctx context.Context) ([]GetAllSysMenusRow, error) { - rows, err := q.db.Query(ctx, getAllSysMenus) - if err != nil { - return nil, err - } - defer rows.Close() - items := []GetAllSysMenusRow{} - for rows.Next() { - var i GetAllSysMenusRow - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Path, - &i.Component, - &i.Type, - &i.Hidden, - &i.Sort, - &i.Status, - &i.ParentID, - &i.Icon, - &i.CreatedAt, - &i.UpdatedAt, - &i.PermissionCode, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const getSysMenuByID = `-- name: GetSysMenuByID :one +const getMenuByID = `-- name: GetMenuByID :one SELECT id, name, path, component, type, hidden, sort, status, parent_id, icon, created_at, updated_at FROM sys_menus WHERE id = $1 ` -func (q *Queries) GetSysMenuByID(ctx context.Context, id int32) (SysMenu, error) { - row := q.db.QueryRow(ctx, getSysMenuByID, id) +func (q *Queries) GetMenuByID(ctx context.Context, id int32) (SysMenu, error) { + row := q.db.QueryRow(ctx, getMenuByID, id) var i SysMenu err := row.Scan( &i.ID, @@ -196,21 +136,17 @@ func (q *Queries) GetSysMenuByID(ctx context.Context, id int32) (SysMenu, error) return i, err } -const listSysMenus = `-- name: ListSysMenus :many -SELECT m.id, m.name, m.path, m.component, m.type, m.hidden, m.sort, m.status, m.parent_id, m.icon, m.created_at, m.updated_at, p.code AS permission_code +const listAllMenus = `-- name: ListAllMenus :many +SELECT m.id, m.name, m.path, m.component, m.type, m.hidden, m.sort, m.status, m.parent_id, m.icon, m.created_at, m.updated_at, + p.code AS permission_code FROM sys_menus m LEFT JOIN sys_menu_permission mp ON m.id = mp.menu_id - LEFT JOIN sys_permissions p ON mp.permission_id = p.id -ORDER BY m.id -LIMIT $1 OFFSET $2 + LEFT JOIN sys_permissions p ON p.id = mp.permission_id +ORDER BY m.sort ASC, + m.id ASC ` -type ListSysMenusParams struct { - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -type ListSysMenusRow struct { +type ListAllMenusRow struct { ID int32 `json:"id"` Name string `json:"name"` Path *string `json:"path"` @@ -226,15 +162,15 @@ type ListSysMenusRow struct { PermissionCode *string `json:"permission_code"` } -func (q *Queries) ListSysMenus(ctx context.Context, arg ListSysMenusParams) ([]ListSysMenusRow, error) { - rows, err := q.db.Query(ctx, listSysMenus, arg.Limit, arg.Offset) +func (q *Queries) ListAllMenus(ctx context.Context) ([]ListAllMenusRow, error) { + rows, err := q.db.Query(ctx, listAllMenus) if err != nil { return nil, err } defer rows.Close() - items := []ListSysMenusRow{} + items := []ListAllMenusRow{} for rows.Next() { - var i ListSysMenusRow + var i ListAllMenusRow if err := rows.Scan( &i.ID, &i.Name, @@ -260,7 +196,71 @@ func (q *Queries) ListSysMenus(ctx context.Context, arg ListSysMenusParams) ([]L return items, nil } -const updateSysMenu = `-- name: UpdateSysMenu :execrows +const listMenus = `-- name: ListMenus :many +SELECT m.id, m.name, m.path, m.component, m.type, m.hidden, m.sort, m.status, m.parent_id, m.icon, m.created_at, m.updated_at, p.code AS permission_code +FROM sys_menus m + LEFT JOIN sys_menu_permission mp ON m.id = mp.menu_id + LEFT JOIN sys_permissions p ON mp.permission_id = p.id +ORDER BY m.id +LIMIT $1 OFFSET $2 +` + +type ListMenusParams struct { + Limit int32 `json:"limit"` + Offset int32 `json:"offset"` +} + +type ListMenusRow struct { + ID int32 `json:"id"` + Name string `json:"name"` + Path *string `json:"path"` + Component *string `json:"component"` + Type int16 `json:"type"` + Hidden *bool `json:"hidden"` + Sort *int32 `json:"sort"` + Status int16 `json:"status"` + ParentID *int32 `json:"parent_id"` + Icon *int32 `json:"icon"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` + PermissionCode *string `json:"permission_code"` +} + +func (q *Queries) ListMenus(ctx context.Context, arg ListMenusParams) ([]ListMenusRow, error) { + rows, err := q.db.Query(ctx, listMenus, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListMenusRow{} + for rows.Next() { + var i ListMenusRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Path, + &i.Component, + &i.Type, + &i.Hidden, + &i.Sort, + &i.Status, + &i.ParentID, + &i.Icon, + &i.CreatedAt, + &i.UpdatedAt, + &i.PermissionCode, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateMenu = `-- name: UpdateMenu :execrows UPDATE sys_menus SET name = coalesce($1, name), path = coalesce($2, path), @@ -274,7 +274,7 @@ SET name = coalesce($1, name), WHERE id = $12 ` -type UpdateSysMenuParams struct { +type UpdateMenuParams struct { Name *string `json:"name"` Path *string `json:"path"` Component *string `json:"component"` @@ -289,8 +289,8 @@ type UpdateSysMenuParams struct { ID int32 `json:"id"` } -func (q *Queries) UpdateSysMenu(ctx context.Context, arg UpdateSysMenuParams) (int64, error) { - result, err := q.db.Exec(ctx, updateSysMenu, +func (q *Queries) UpdateMenu(ctx context.Context, arg UpdateMenuParams) (int64, error) { + result, err := q.db.Exec(ctx, updateMenu, arg.Name, arg.Path, arg.Component, @@ -310,7 +310,7 @@ func (q *Queries) UpdateSysMenu(ctx context.Context, arg UpdateSysMenuParams) (i return result.RowsAffected(), nil } -const updateSysMenuPermissionCode = `-- name: UpdateSysMenuPermissionCode :exec +const updateMenuPermissionCode = `-- name: UpdateMenuPermissionCode :exec UPDATE sys_permissions p SET code = coalesce($2, code) FROM sys_menu_permission mp @@ -318,12 +318,12 @@ WHERE p.id = mp.permission_id AND mp.menu_id = $1 ` -type UpdateSysMenuPermissionCodeParams struct { +type UpdateMenuPermissionCodeParams struct { MenuID int32 `json:"menu_id"` Code *string `json:"code"` } -func (q *Queries) UpdateSysMenuPermissionCode(ctx context.Context, arg UpdateSysMenuPermissionCodeParams) error { - _, err := q.db.Exec(ctx, updateSysMenuPermissionCode, arg.MenuID, arg.Code) +func (q *Queries) UpdateMenuPermissionCode(ctx context.Context, arg UpdateMenuPermissionCodeParams) error { + _, err := q.db.Exec(ctx, updateMenuPermissionCode, arg.MenuID, arg.Code) return err } diff --git a/internal/db/sqlc/sys_permissions.sql.go b/internal/db/sqlc/sys_permissions.sql.go index 70619cf..7ab31ba 100644 --- a/internal/db/sqlc/sys_permissions.sql.go +++ b/internal/db/sqlc/sys_permissions.sql.go @@ -9,72 +9,72 @@ import ( "context" ) -const createSysPermission = `-- name: CreateSysPermission :one +const createPermission = `-- name: CreatePermission :one INSERT INTO sys_permissions(type,code) VALUES ($1,$2) RETURNING id ` -type CreateSysPermissionParams struct { +type CreatePermissionParams struct { Type int16 `json:"type"` Code *string `json:"code"` } -func (q *Queries) CreateSysPermission(ctx context.Context, arg CreateSysPermissionParams) (int32, error) { - row := q.db.QueryRow(ctx, createSysPermission, arg.Type, arg.Code) +func (q *Queries) CreatePermission(ctx context.Context, arg CreatePermissionParams) (int32, error) { + row := q.db.QueryRow(ctx, createPermission, arg.Type, arg.Code) var id int32 err := row.Scan(&id) return id, err } -const deleteSysPermission = `-- name: DeleteSysPermission :exec +const deletePermission = `-- name: DeletePermission :exec DELETE FROM sys_permissions WHERE id = $1 ` -func (q *Queries) DeleteSysPermission(ctx context.Context, id int32) error { - _, err := q.db.Exec(ctx, deleteSysPermission, id) +func (q *Queries) DeletePermission(ctx context.Context, id int32) error { + _, err := q.db.Exec(ctx, deletePermission, id) return err } -const getSysPermissionIDBySysApiID = `-- name: GetSysPermissionIDBySysApiID :one +const getPermissionIDBySysApiID = `-- name: GetPermissionIDBySysApiID :one SELECT permission_id FROM sys_api_permission WHERE api_id = $1 ` -func (q *Queries) GetSysPermissionIDBySysApiID(ctx context.Context, apiID int32) (int32, error) { - row := q.db.QueryRow(ctx, getSysPermissionIDBySysApiID, apiID) +func (q *Queries) GetPermissionIDBySysApiID(ctx context.Context, apiID int32) (int32, error) { + row := q.db.QueryRow(ctx, getPermissionIDBySysApiID, apiID) var permission_id int32 err := row.Scan(&permission_id) return permission_id, err } -const getSysPermissionIDBySysMenuID = `-- name: GetSysPermissionIDBySysMenuID :one +const getPermissionIDBySysMenuID = `-- name: GetPermissionIDBySysMenuID :one SELECT permission_id FROM sys_menu_permission WHERE menu_id = $1 ` -func (q *Queries) GetSysPermissionIDBySysMenuID(ctx context.Context, menuID int32) (int32, error) { - row := q.db.QueryRow(ctx, getSysPermissionIDBySysMenuID, menuID) +func (q *Queries) GetPermissionIDBySysMenuID(ctx context.Context, menuID int32) (int32, error) { + row := q.db.QueryRow(ctx, getPermissionIDBySysMenuID, menuID) var permission_id int32 err := row.Scan(&permission_id) return permission_id, err } -const updateSysPermission = `-- name: UpdateSysPermission :exec +const updatePermission = `-- name: UpdatePermission :exec UPDATE sys_permissions SET code = $2 WHERE id = $1 ` -type UpdateSysPermissionParams struct { +type UpdatePermissionParams struct { ID int32 `json:"id"` Code *string `json:"code"` } -func (q *Queries) UpdateSysPermission(ctx context.Context, arg UpdateSysPermissionParams) error { - _, err := q.db.Exec(ctx, updateSysPermission, arg.ID, arg.Code) +func (q *Queries) UpdatePermission(ctx context.Context, arg UpdatePermissionParams) error { + _, err := q.db.Exec(ctx, updatePermission, arg.ID, arg.Code) return err } diff --git a/internal/db/sqlc/sys_roles.sql.go b/internal/db/sqlc/sys_roles.sql.go index 0bd22c0..b29cc07 100644 --- a/internal/db/sqlc/sys_roles.sql.go +++ b/internal/db/sqlc/sys_roles.sql.go @@ -9,76 +9,76 @@ import ( "context" ) -const countSysRoles = `-- name: CountSysRoles :one +const countRoles = `-- name: CountRoles :one SELECT COUNT(*) FROM sys_roles ` -func (q *Queries) CountSysRoles(ctx context.Context) (int64, error) { - row := q.db.QueryRow(ctx, countSysRoles) +func (q *Queries) CountRoles(ctx context.Context) (int64, error) { + row := q.db.QueryRow(ctx, countRoles) var count int64 err := row.Scan(&count) return count, err } -const createSysRole = `-- name: CreateSysRole :exec +const createRole = `-- name: CreateRole :exec INSERT INTO sys_roles (name, code, status) VALUES ($1, $2, $3) ` -type CreateSysRoleParams struct { +type CreateRoleParams struct { Name string `json:"name"` Code string `json:"code"` Status int16 `json:"status"` } -func (q *Queries) CreateSysRole(ctx context.Context, arg CreateSysRoleParams) error { - _, err := q.db.Exec(ctx, createSysRole, arg.Name, arg.Code, arg.Status) +func (q *Queries) CreateRole(ctx context.Context, arg CreateRoleParams) error { + _, err := q.db.Exec(ctx, createRole, arg.Name, arg.Code, arg.Status) return err } -type CreateSysRolePermissionParams struct { +type CreateRolePermissionParams struct { RoleID int32 `json:"role_id"` PermissionID int32 `json:"permission_id"` } -const deleteSysRole = `-- name: DeleteSysRole :execrows +const deleteRole = `-- name: DeleteRole :execrows DELETE FROM sys_roles WHERE id = $1 ` -func (q *Queries) DeleteSysRole(ctx context.Context, id int32) (int64, error) { - result, err := q.db.Exec(ctx, deleteSysRole, id) +func (q *Queries) DeleteRole(ctx context.Context, id int32) (int64, error) { + result, err := q.db.Exec(ctx, deleteRole, id) if err != nil { return 0, err } return result.RowsAffected(), nil } -const deleteSysRolePermissionByPermissionID = `-- name: DeleteSysRolePermissionByPermissionID :exec +const deleteRolePermissionByPermissionID = `-- name: DeleteRolePermissionByPermissionID :exec DELETE FROM sys_role_permission WHERE permission_id = $1 ` -func (q *Queries) DeleteSysRolePermissionByPermissionID(ctx context.Context, permissionID int32) error { - _, err := q.db.Exec(ctx, deleteSysRolePermissionByPermissionID, permissionID) +func (q *Queries) DeleteRolePermissionByPermissionID(ctx context.Context, permissionID int32) error { + _, err := q.db.Exec(ctx, deleteRolePermissionByPermissionID, permissionID) return err } -const deleteSysRolePermissionByRoleID = `-- name: DeleteSysRolePermissionByRoleID :exec +const deleteRolePermissionsByRoleID = `-- name: DeleteRolePermissionsByRoleID :exec DELETE FROM sys_role_permission WHERE role_id = $1 ` -func (q *Queries) DeleteSysRolePermissionByRoleID(ctx context.Context, roleID int32) error { - _, err := q.db.Exec(ctx, deleteSysRolePermissionByRoleID, roleID) +func (q *Queries) DeleteRolePermissionsByRoleID(ctx context.Context, roleID int32) error { + _, err := q.db.Exec(ctx, deleteRolePermissionsByRoleID, roleID) return err } -const deleteSysRolePermissionByType = `-- name: DeleteSysRolePermissionByType :exec +const deleteRolePermissionsByType = `-- name: DeleteRolePermissionsByType :exec DELETE FROM sys_role_permission rp USING sys_permissions p WHERE rp.permission_id = p.id @@ -86,28 +86,28 @@ WHERE rp.permission_id = p.id AND p."type" = $2 ` -type DeleteSysRolePermissionByTypeParams struct { +type DeleteRolePermissionsByTypeParams struct { RoleID int32 `json:"role_id"` Type int16 `json:"type"` } -func (q *Queries) DeleteSysRolePermissionByType(ctx context.Context, arg DeleteSysRolePermissionByTypeParams) error { - _, err := q.db.Exec(ctx, deleteSysRolePermissionByType, arg.RoleID, arg.Type) +func (q *Queries) DeleteRolePermissionsByType(ctx context.Context, arg DeleteRolePermissionsByTypeParams) error { + _, err := q.db.Exec(ctx, deleteRolePermissionsByType, arg.RoleID, arg.Type) return err } -const deleteSysUserRoleByRoleID = `-- name: DeleteSysUserRoleByRoleID :exec +const deleteUserRoleByRoleID = `-- name: DeleteUserRoleByRoleID :exec DELETE FROM sys_user_role WHERE role_id = $1 ` -func (q *Queries) DeleteSysUserRoleByRoleID(ctx context.Context, roleID int32) error { - _, err := q.db.Exec(ctx, deleteSysUserRoleByRoleID, roleID) +func (q *Queries) DeleteUserRoleByRoleID(ctx context.Context, roleID int32) error { + _, err := q.db.Exec(ctx, deleteUserRoleByRoleID, roleID) return err } -const existsSysRoleCode = `-- name: ExistsSysRoleCode :one +const existsRoleCode = `-- name: ExistsRoleCode :one SELECT EXISTS(SELECT 1 FROM sys_roles WHERE code = $1 @@ -117,143 +117,26 @@ SELECT EXISTS(SELECT 1 )) ` -type ExistsSysRoleCodeParams struct { +type ExistsRoleCodeParams struct { Code string `json:"code"` ID *int32 `json:"id"` } -func (q *Queries) ExistsSysRoleCode(ctx context.Context, arg ExistsSysRoleCodeParams) (bool, error) { - row := q.db.QueryRow(ctx, existsSysRoleCode, arg.Code, arg.ID) +func (q *Queries) ExistsRoleCode(ctx context.Context, arg ExistsRoleCodeParams) (bool, error) { + row := q.db.QueryRow(ctx, existsRoleCode, arg.Code, arg.ID) var exists bool err := row.Scan(&exists) return exists, err } -const getAllSysRoles = `-- name: GetAllSysRoles :many -SELECT id, name, code, status, created_at, updated_at -from sys_roles -` - -func (q *Queries) GetAllSysRoles(ctx context.Context) ([]SysRole, error) { - rows, err := q.db.Query(ctx, getAllSysRoles) - if err != nil { - return nil, err - } - defer rows.Close() - items := []SysRole{} - for rows.Next() { - var i SysRole - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Code, - &i.Status, - &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 getSysPermissionIdsByApiIDs = `-- name: GetSysPermissionIdsByApiIDs :many -SELECT permission_id -FROM sys_api_permission -WHERE api_id = ANY ($1::int[]) -` - -func (q *Queries) GetSysPermissionIdsByApiIDs(ctx context.Context, dollar_1 []int32) ([]int32, error) { - rows, err := q.db.Query(ctx, getSysPermissionIdsByApiIDs, dollar_1) - if err != nil { - return nil, err - } - defer rows.Close() - items := []int32{} - for rows.Next() { - var permission_id int32 - if err := rows.Scan(&permission_id); err != nil { - return nil, err - } - items = append(items, permission_id) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const getSysPermissionIdsByMenuIDs = `-- name: GetSysPermissionIdsByMenuIDs :many -SELECT permission_id -FROM sys_menu_permission -WHERE menu_id = ANY ($1::int[]) -` - -func (q *Queries) GetSysPermissionIdsByMenuIDs(ctx context.Context, dollar_1 []int32) ([]int32, error) { - rows, err := q.db.Query(ctx, getSysPermissionIdsByMenuIDs, dollar_1) - if err != nil { - return nil, err - } - defer rows.Close() - items := []int32{} - for rows.Next() { - var permission_id int32 - if err := rows.Scan(&permission_id); err != nil { - return nil, err - } - items = append(items, permission_id) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const getSysRoleApis = `-- name: GetSysRoleApis :many -SELECT a.id, a."name" -FROM sys_role_permission rp - JOIN sys_api_permission ap ON ap.permission_id = rp.permission_id - JOIN sys_apis a ON a.id = ap.api_id -WHERE rp.role_id = $1 -` - -type GetSysRoleApisRow struct { - ID int32 `json:"id"` - Name string `json:"name"` -} - -func (q *Queries) GetSysRoleApis(ctx context.Context, roleID int32) ([]GetSysRoleApisRow, error) { - rows, err := q.db.Query(ctx, getSysRoleApis, roleID) - if err != nil { - return nil, err - } - defer rows.Close() - items := []GetSysRoleApisRow{} - for rows.Next() { - var i GetSysRoleApisRow - if err := rows.Scan(&i.ID, &i.Name); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const getSysRoleByID = `-- name: GetSysRoleByID :one +const getRoleByID = `-- name: GetRoleByID :one SELECT id, name, code, status, created_at, updated_at FROM sys_roles WHERE id = $1 ` -func (q *Queries) GetSysRoleByID(ctx context.Context, id int32) (SysRole, error) { - row := q.db.QueryRow(ctx, getSysRoleByID, id) +func (q *Queries) GetRoleByID(ctx context.Context, id int32) (SysRole, error) { + row := q.db.QueryRow(ctx, getRoleByID, id) var i SysRole err := row.Scan( &i.ID, @@ -266,53 +149,13 @@ func (q *Queries) GetSysRoleByID(ctx context.Context, id int32) (SysRole, error) return i, err } -const getSysRoleMenus = `-- name: GetSysRoleMenus :many -SELECT m.id, m.name -FROM sys_role_permission rp - JOIN sys_menu_permission mp ON mp.permission_id = rp.permission_id - JOIN sys_menus m ON m.id = mp.menu_id -WHERE rp.role_id = $1 -` - -type GetSysRoleMenusRow struct { - ID int32 `json:"id"` - Name string `json:"name"` -} - -func (q *Queries) GetSysRoleMenus(ctx context.Context, roleID int32) ([]GetSysRoleMenusRow, error) { - rows, err := q.db.Query(ctx, getSysRoleMenus, roleID) - if err != nil { - return nil, err - } - defer rows.Close() - items := []GetSysRoleMenusRow{} - for rows.Next() { - var i GetSysRoleMenusRow - if err := rows.Scan(&i.ID, &i.Name); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const listSysRoles = `-- name: ListSysRoles :many +const listAllRoles = `-- name: ListAllRoles :many SELECT id, name, code, status, created_at, updated_at -FROM sys_roles -ORDER BY id -LIMIT $1 OFFSET $2 +from sys_roles ` -type ListSysRolesParams struct { - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) ListSysRoles(ctx context.Context, arg ListSysRolesParams) ([]SysRole, error) { - rows, err := q.db.Query(ctx, listSysRoles, arg.Limit, arg.Offset) +func (q *Queries) ListAllRoles(ctx context.Context) ([]SysRole, error) { + rows, err := q.db.Query(ctx, listAllRoles) if err != nil { return nil, err } @@ -338,21 +181,178 @@ func (q *Queries) ListSysRoles(ctx context.Context, arg ListSysRolesParams) ([]S return items, nil } -const updateSysRole = `-- name: UpdateSysRole :execrows +const listPermissionIDsByApiIDs = `-- name: ListPermissionIDsByApiIDs :many +SELECT permission_id +FROM sys_api_permission +WHERE api_id = ANY ($1::int[]) +` + +func (q *Queries) ListPermissionIDsByApiIDs(ctx context.Context, dollar_1 []int32) ([]int32, error) { + rows, err := q.db.Query(ctx, listPermissionIDsByApiIDs, dollar_1) + if err != nil { + return nil, err + } + defer rows.Close() + items := []int32{} + for rows.Next() { + var permission_id int32 + if err := rows.Scan(&permission_id); err != nil { + return nil, err + } + items = append(items, permission_id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listPermissionIDsByMenuIDs = `-- name: ListPermissionIDsByMenuIDs :many +SELECT permission_id +FROM sys_menu_permission +WHERE menu_id = ANY ($1::int[]) +` + +func (q *Queries) ListPermissionIDsByMenuIDs(ctx context.Context, dollar_1 []int32) ([]int32, error) { + rows, err := q.db.Query(ctx, listPermissionIDsByMenuIDs, dollar_1) + if err != nil { + return nil, err + } + defer rows.Close() + items := []int32{} + for rows.Next() { + var permission_id int32 + if err := rows.Scan(&permission_id); err != nil { + return nil, err + } + items = append(items, permission_id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listRoleApis = `-- name: ListRoleApis :many +SELECT a.id, a."name" +FROM sys_role_permission rp + JOIN sys_api_permission ap ON ap.permission_id = rp.permission_id + JOIN sys_apis a ON a.id = ap.api_id +WHERE rp.role_id = $1 +` + +type ListRoleApisRow struct { + ID int32 `json:"id"` + Name string `json:"name"` +} + +func (q *Queries) ListRoleApis(ctx context.Context, roleID int32) ([]ListRoleApisRow, error) { + rows, err := q.db.Query(ctx, listRoleApis, roleID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListRoleApisRow{} + for rows.Next() { + var i ListRoleApisRow + if err := rows.Scan(&i.ID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listRoleMenus = `-- name: ListRoleMenus :many +SELECT m.id, m.name +FROM sys_role_permission rp + JOIN sys_menu_permission mp ON mp.permission_id = rp.permission_id + JOIN sys_menus m ON m.id = mp.menu_id +WHERE rp.role_id = $1 +` + +type ListRoleMenusRow struct { + ID int32 `json:"id"` + Name string `json:"name"` +} + +func (q *Queries) ListRoleMenus(ctx context.Context, roleID int32) ([]ListRoleMenusRow, error) { + rows, err := q.db.Query(ctx, listRoleMenus, roleID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListRoleMenusRow{} + for rows.Next() { + var i ListRoleMenusRow + if err := rows.Scan(&i.ID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listRoles = `-- name: ListRoles :many +SELECT id, name, code, status, created_at, updated_at +FROM sys_roles +ORDER BY id +LIMIT $1 OFFSET $2 +` + +type ListRolesParams struct { + Limit int32 `json:"limit"` + Offset int32 `json:"offset"` +} + +func (q *Queries) ListRoles(ctx context.Context, arg ListRolesParams) ([]SysRole, error) { + rows, err := q.db.Query(ctx, listRoles, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + items := []SysRole{} + for rows.Next() { + var i SysRole + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Code, + &i.Status, + &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 updateRole = `-- name: UpdateRole :execrows UPDATE sys_roles SET name = coalesce($1, name), status = coalesce($2, status) WHERE id = $3 ` -type UpdateSysRoleParams struct { +type UpdateRoleParams struct { Name *string `json:"name"` Status *int16 `json:"status"` ID int32 `json:"id"` } -func (q *Queries) UpdateSysRole(ctx context.Context, arg UpdateSysRoleParams) (int64, error) { - result, err := q.db.Exec(ctx, updateSysRole, arg.Name, arg.Status, arg.ID) +func (q *Queries) UpdateRole(ctx context.Context, arg UpdateRoleParams) (int64, error) { + result, err := q.db.Exec(ctx, updateRole, arg.Name, arg.Status, arg.ID) if err != nil { return 0, err } diff --git a/internal/db/sqlc/sys_users.sql.go b/internal/db/sqlc/sys_users.sql.go index 87d9f13..c58180b 100644 --- a/internal/db/sqlc/sys_users.sql.go +++ b/internal/db/sqlc/sys_users.sql.go @@ -10,36 +10,36 @@ import ( "time" ) -const clearSysUserRoles = `-- name: ClearSysUserRoles :exec +const clearUserRoles = `-- name: ClearUserRoles :exec DELETE FROM sys_user_role WHERE user_id = $1 ` -func (q *Queries) ClearSysUserRoles(ctx context.Context, userID int32) error { - _, err := q.db.Exec(ctx, clearSysUserRoles, userID) +func (q *Queries) ClearUserRoles(ctx context.Context, userID int32) error { + _, err := q.db.Exec(ctx, clearUserRoles, userID) return err } -const countSysUsers = `-- name: CountSysUsers :one +const countUsers = `-- name: CountUsers :one SELECT COUNT(*) FROM sys_users WHERE ($1::text = '' OR username ILIKE '%' || $1 || '%') ` -func (q *Queries) CountSysUsers(ctx context.Context, username string) (int64, error) { - row := q.db.QueryRow(ctx, countSysUsers, username) +func (q *Queries) CountUsers(ctx context.Context, username string) (int64, error) { + row := q.db.QueryRow(ctx, countUsers, username) var count int64 err := row.Scan(&count) return count, err } -const createSysUser = `-- name: CreateSysUser :exec +const createUser = `-- name: CreateUser :exec INSERT INTO sys_users (account, username, password_hash, status, avatar_id) VALUES ($1, $2, $3, $4, $5) ` -type CreateSysUserParams struct { +type CreateUserParams struct { Account string `json:"account"` Username string `json:"username"` PasswordHash string `json:"password_hash"` @@ -47,8 +47,8 @@ type CreateSysUserParams struct { AvatarID *int32 `json:"avatar_id"` } -func (q *Queries) CreateSysUser(ctx context.Context, arg CreateSysUserParams) error { - _, err := q.db.Exec(ctx, createSysUser, +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) error { + _, err := q.db.Exec(ctx, createUser, arg.Account, arg.Username, arg.PasswordHash, @@ -58,34 +58,34 @@ func (q *Queries) CreateSysUser(ctx context.Context, arg CreateSysUserParams) er return err } -type CreateSysUserRoleParams struct { +type CreateUserRoleParams struct { UserID int32 `json:"user_id"` RoleID int32 `json:"role_id"` } -const deleteSysUser = `-- name: DeleteSysUser :execrows +const deleteUser = `-- name: DeleteUser :execrows DELETE FROM sys_users WHERE id = $1 AND id <> 1 ` -func (q *Queries) DeleteSysUser(ctx context.Context, id int32) (int64, error) { - result, err := q.db.Exec(ctx, deleteSysUser, id) +func (q *Queries) DeleteUser(ctx context.Context, id int32) (int64, error) { + result, err := q.db.Exec(ctx, deleteUser, id) if err != nil { return 0, err } return result.RowsAffected(), nil } -const getActiveSysUserByID = `-- name: GetActiveSysUserByID :one +const getActiveUserByID = `-- name: GetActiveUserByID :one SELECT id, account, username, status FROM sys_users WHERE id = $1 AND status = 1 ` -type GetActiveSysUserByIDRow struct { +type GetActiveUserByIDRow struct { ID int32 `json:"id"` Account string `json:"account"` Username string `json:"username"` @@ -93,9 +93,9 @@ type GetActiveSysUserByIDRow struct { } // 场景:用户登录、获取个人信息、刷新 Token(严格校验 status = 1) -func (q *Queries) GetActiveSysUserByID(ctx context.Context, id int32) (GetActiveSysUserByIDRow, error) { - row := q.db.QueryRow(ctx, getActiveSysUserByID, id) - var i GetActiveSysUserByIDRow +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, @@ -105,7 +105,92 @@ func (q *Queries) GetActiveSysUserByID(ctx context.Context, id int32) (GetActive return i, err } -const getSysAdminMenus = `-- name: GetSysAdminMenus :many +const getUserByAccount = `-- name: GetUserByAccount :one +SELECT u.id, + u.account, + u.username, + u.password_hash, + u.status, + u.avatar_id, + 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.account = $1 +` + +type GetUserByAccountRow struct { + ID int32 `json:"id"` + Account string `json:"account"` + Username string `json:"username"` + PasswordHash string `json:"password_hash"` + Status int16 `json:"status"` + AvatarID *int32 `json:"avatar_id"` + AvatarUrl *string `json:"avatar_url"` + 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 + err := row.Scan( + &i.ID, + &i.Account, + &i.Username, + &i.PasswordHash, + &i.Status, + &i.AvatarID, + &i.AvatarUrl, + &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 +` + +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"` +} + +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, + ) + return i, 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 WHERE status = 1 @@ -113,8 +198,8 @@ WHERE status = 1 ORDER BY sort, id ` -func (q *Queries) GetSysAdminMenus(ctx context.Context) ([]SysMenu, error) { - rows, err := q.db.Query(ctx, getSysAdminMenus) +func (q *Queries) ListAdminMenus(ctx context.Context) ([]SysMenu, error) { + rows, err := q.db.Query(ctx, listAdminMenus) if err != nil { return nil, err } @@ -146,14 +231,14 @@ func (q *Queries) GetSysAdminMenus(ctx context.Context) ([]SysMenu, error) { return items, nil } -const getSysAdminPermissions = `-- name: GetSysAdminPermissions :many +const listAdminPermissionCodes = `-- name: ListAdminPermissionCodes :many SELECT code FROM sys_permissions WHERE type = 0 ` -func (q *Queries) GetSysAdminPermissions(ctx context.Context) ([]*string, error) { - rows, err := q.db.Query(ctx, getSysAdminPermissions) +func (q *Queries) ListAdminPermissionCodes(ctx context.Context) ([]*string, error) { + rows, err := q.db.Query(ctx, listAdminPermissionCodes) if err != nil { return nil, err } @@ -172,7 +257,7 @@ func (q *Queries) GetSysAdminPermissions(ctx context.Context) ([]*string, error) return items, nil } -const getSysUserApis = `-- name: GetSysUserApis :many +const listUserApis = `-- name: ListUserApis :many SELECT DISTINCT a.id, a.PATH, a.METHOD @@ -187,21 +272,21 @@ WHERE u.id = $1 AND r.status = 1 ` -type GetSysUserApisRow struct { +type ListUserApisRow struct { ID int32 `json:"id"` Path string `json:"path"` Method string `json:"method"` } -func (q *Queries) GetSysUserApis(ctx context.Context, id int32) ([]GetSysUserApisRow, error) { - rows, err := q.db.Query(ctx, getSysUserApis, id) +func (q *Queries) ListUserApis(ctx context.Context, id int32) ([]ListUserApisRow, error) { + rows, err := q.db.Query(ctx, listUserApis, id) if err != nil { return nil, err } defer rows.Close() - items := []GetSysUserApisRow{} + items := []ListUserApisRow{} for rows.Next() { - var i GetSysUserApisRow + var i ListUserApisRow if err := rows.Scan(&i.ID, &i.Path, &i.Method); err != nil { return nil, err } @@ -213,92 +298,7 @@ func (q *Queries) GetSysUserApis(ctx context.Context, id int32) ([]GetSysUserApi return items, nil } -const getSysUserByAccount = `-- name: GetSysUserByAccount :one -SELECT u.id, - u.account, - u.username, - u.password_hash, - u.status, - u.avatar_id, - f.file_path 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.account = $1 -` - -type GetSysUserByAccountRow struct { - ID int32 `json:"id"` - Account string `json:"account"` - Username string `json:"username"` - PasswordHash string `json:"password_hash"` - Status int16 `json:"status"` - AvatarID *int32 `json:"avatar_id"` - AvatarUrl *string `json:"avatar_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt *time.Time `json:"updated_at"` -} - -func (q *Queries) GetSysUserByAccount(ctx context.Context, account string) (GetSysUserByAccountRow, error) { - row := q.db.QueryRow(ctx, getSysUserByAccount, account) - var i GetSysUserByAccountRow - err := row.Scan( - &i.ID, - &i.Account, - &i.Username, - &i.PasswordHash, - &i.Status, - &i.AvatarID, - &i.AvatarUrl, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getSysUserByID = `-- name: GetSysUserByID :one -SELECT u.id, - u.account, - u.username, - u.avatar_id, - u.status, - f.file_path 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 -` - -type GetSysUserByIDRow 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"` -} - -func (q *Queries) GetSysUserByID(ctx context.Context, id int32) (GetSysUserByIDRow, error) { - row := q.db.QueryRow(ctx, getSysUserByID, id) - var i GetSysUserByIDRow - err := row.Scan( - &i.ID, - &i.Account, - &i.Username, - &i.AvatarID, - &i.Status, - &i.AvatarUrl, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getSysUserMenus = `-- name: GetSysUserMenus :many +const listUserMenus = `-- name: ListUserMenus :many SELECT DISTINCT m.id, m.name, m.path, m.component, m.type, m.hidden, m.sort, m.status, m.parent_id, m.icon, m.created_at, m.updated_at FROM sys_users u JOIN sys_user_role ur ON ur.user_id = u.id @@ -314,8 +314,8 @@ WHERE u.id = $1 ORDER BY m.sort, m.id ` -func (q *Queries) GetSysUserMenus(ctx context.Context, id int32) ([]SysMenu, error) { - rows, err := q.db.Query(ctx, getSysUserMenus, id) +func (q *Queries) ListUserMenus(ctx context.Context, id int32) ([]SysMenu, error) { + rows, err := q.db.Query(ctx, listUserMenus, id) if err != nil { return nil, err } @@ -347,7 +347,7 @@ func (q *Queries) GetSysUserMenus(ctx context.Context, id int32) ([]SysMenu, err return items, nil } -const getSysUserPermissions = `-- name: GetSysUserPermissions :many +const listUserPermissionCodes = `-- name: ListUserPermissionCodes :many SELECT DISTINCT p.code FROM sys_users u JOIN sys_user_role ur ON u.id = ur.user_id @@ -363,8 +363,8 @@ WHERE u.id = $1 AND p."type" = 0 ` -func (q *Queries) GetSysUserPermissions(ctx context.Context, id int32) ([]*string, error) { - rows, err := q.db.Query(ctx, getSysUserPermissions, id) +func (q *Queries) ListUserPermissionCodes(ctx context.Context, id int32) ([]*string, error) { + rows, err := q.db.Query(ctx, listUserPermissionCodes, id) if err != nil { return nil, err } @@ -383,15 +383,15 @@ func (q *Queries) GetSysUserPermissions(ctx context.Context, id int32) ([]*strin return items, nil } -const getSysUserRoles = `-- name: GetSysUserRoles :many +const listUserRoles = `-- name: ListUserRoles :many SELECT r.id, r.name, r.code, r.status, r.created_at, r.updated_at FROM sys_roles r JOIN sys_user_role ur ON r.id = ur.role_id WHERE ur.user_id = $1 ` -func (q *Queries) GetSysUserRoles(ctx context.Context, userID int32) ([]SysRole, error) { - rows, err := q.db.Query(ctx, getSysUserRoles, userID) +func (q *Queries) ListUserRoles(ctx context.Context, userID int32) ([]SysRole, error) { + rows, err := q.db.Query(ctx, listUserRoles, userID) if err != nil { return nil, err } @@ -417,13 +417,13 @@ func (q *Queries) GetSysUserRoles(ctx context.Context, userID int32) ([]SysRole, return items, nil } -const listSysUsers = `-- name: ListSysUsers :many +const listUsers = `-- name: ListUsers :many SELECT u.id, u.account, u.username, u.status, u.avatar_id, - f.file_path AS avatar_url, + f.file_url AS avatar_url, u.created_at, u.updated_at FROM sys_users u @@ -433,13 +433,13 @@ ORDER BY u.id LIMIT $1 OFFSET $2 ` -type ListSysUsersParams struct { +type ListUsersParams struct { Limit int32 `json:"limit"` Offset int32 `json:"offset"` Username string `json:"username"` } -type ListSysUsersRow struct { +type ListUsersRow struct { ID int32 `json:"id"` Account string `json:"account"` Username string `json:"username"` @@ -450,15 +450,15 @@ type ListSysUsersRow struct { UpdatedAt *time.Time `json:"updated_at"` } -func (q *Queries) ListSysUsers(ctx context.Context, arg ListSysUsersParams) ([]ListSysUsersRow, error) { - rows, err := q.db.Query(ctx, listSysUsers, arg.Limit, arg.Offset, arg.Username) +func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]ListUsersRow, error) { + rows, err := q.db.Query(ctx, listUsers, arg.Limit, arg.Offset, arg.Username) if err != nil { return nil, err } defer rows.Close() - items := []ListSysUsersRow{} + items := []ListUsersRow{} for rows.Next() { - var i ListSysUsersRow + var i ListUsersRow if err := rows.Scan( &i.ID, &i.Account, @@ -479,7 +479,7 @@ func (q *Queries) ListSysUsers(ctx context.Context, arg ListSysUsersParams) ([]L return items, nil } -const updateSysUser = `-- name: UpdateSysUser :execrows +const updateUser = `-- name: UpdateUser :execrows UPDATE sys_users SET username = coalesce($1, username), status = coalesce($2, status), @@ -487,7 +487,7 @@ SET username = coalesce($1, username), WHERE id = $5 ` -type UpdateSysUserParams struct { +type UpdateUserParams struct { Username *string `json:"username"` Status *int16 `json:"status"` UpdateAvatarID bool `json:"update_avatar_id"` @@ -495,8 +495,8 @@ type UpdateSysUserParams struct { ID int32 `json:"id"` } -func (q *Queries) UpdateSysUser(ctx context.Context, arg UpdateSysUserParams) (int64, error) { - result, err := q.db.Exec(ctx, updateSysUser, +func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (int64, error) { + result, err := q.db.Exec(ctx, updateUser, arg.Username, arg.Status, arg.UpdateAvatarID, @@ -509,19 +509,19 @@ func (q *Queries) UpdateSysUser(ctx context.Context, arg UpdateSysUserParams) (i return result.RowsAffected(), nil } -const updateSysUserPassword = `-- name: UpdateSysUserPassword :execrows +const updateUserPassword = `-- name: UpdateUserPassword :execrows UPDATE sys_users SET password_hash = $2 WHERE id = $1 ` -type UpdateSysUserPasswordParams struct { +type UpdateUserPasswordParams struct { ID int32 `json:"id"` PasswordHash string `json:"password_hash"` } -func (q *Queries) UpdateSysUserPassword(ctx context.Context, arg UpdateSysUserPasswordParams) (int64, error) { - result, err := q.db.Exec(ctx, updateSysUserPassword, arg.ID, arg.PasswordHash) +func (q *Queries) UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) (int64, error) { + result, err := q.db.Exec(ctx, updateUserPassword, arg.ID, arg.PasswordHash) if err != nil { return 0, err } diff --git a/internal/handler/sys_api.go b/internal/handler/admin/api.go similarity index 50% rename from internal/handler/sys_api.go rename to internal/handler/admin/api.go index 9b412d6..f33e6af 100644 --- a/internal/handler/sys_api.go +++ b/internal/handler/admin/api.go @@ -1,31 +1,48 @@ -package handler +package admin import ( "net/http" "server/internal/model/common" "server/internal/model/request" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" ) -type SysApiHandler struct { - apiService *service.SysApiService +type ApiHandler struct { + apiService *admin.ApiService } -func NewSysApiHandler(apiService *service.SysApiService) *SysApiHandler { - return &SysApiHandler{apiService: apiService} +var _ router.Registrar = (*ApiHandler)(nil) + +func NewApiHandler(apiService *admin.ApiService) *ApiHandler { + return &ApiHandler{apiService: apiService} } -func (h *SysApiHandler) ListPage(w http.ResponseWriter, r *http.Request) { +func (h *ApiHandler) Register(r chi.Router) { + r.Route("/apis", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/all", h.ListAll) + r.Get("/groups", h.ListApiGroups) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Delete("/{id}", h.Delete) + }) +} + +func (h *ApiHandler) List(w http.ResponseWriter, r *http.Request) { pagination := httputil.Pagination(r) - params := request.SearchSysApiParams{ + params := request.SearchApiParams{ Pagination: *pagination, + Name: r.URL.Query().Get("name"), GroupName: r.URL.Query().Get("group_name"), Method: r.URL.Query().Get("method"), } - list, total, err := h.apiService.ListPage(r.Context(), params) + result, err := h.apiService.List(r.Context(), params) if err != nil { httputil.Fail(w, err) @@ -35,15 +52,15 @@ func (h *SysApiHandler) ListPage(w http.ResponseWriter, r *http.Request) { resp := common.PageResponse{ Page: pagination.Page, PageSize: pagination.PageSize, - List: list, - Total: total, + List: result.List, + Total: result.Total, } httputil.OkWithPage(w, &resp) } -func (h *SysApiHandler) GetAllSysApis(w http.ResponseWriter, r *http.Request) { - list, err := h.apiService.GetAllSysApis(r.Context()) +func (h *ApiHandler) ListAll(w http.ResponseWriter, r *http.Request) { + list, err := h.apiService.ListAll(r.Context()) if err != nil { httputil.Fail(w, err) @@ -53,8 +70,8 @@ func (h *SysApiHandler) GetAllSysApis(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, list) } -func (h *SysApiHandler) GetApiGroupNames(w http.ResponseWriter, r *http.Request) { - list, err := h.apiService.GetApiGroupNames(r.Context()) +func (h *ApiHandler) ListApiGroups(w http.ResponseWriter, r *http.Request) { + list, err := h.apiService.ListApiGroups(r.Context()) if err != nil { httputil.Fail(w, err) @@ -64,8 +81,8 @@ func (h *SysApiHandler) GetApiGroupNames(w http.ResponseWriter, r *http.Request) httputil.Ok(w, list) } -func (h *SysApiHandler) Create(w http.ResponseWriter, r *http.Request) { - var req request.CreateSysApiRequest +func (h *ApiHandler) Create(w http.ResponseWriter, r *http.Request) { + var req request.CreateApiRequest if err := httputil.BindJson(r, &req); err != nil { httputil.Fail(w, err) @@ -80,8 +97,8 @@ func (h *SysApiHandler) Create(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysApiHandler) Update(w http.ResponseWriter, r *http.Request) { - var req request.UpdateSysApiRequest +func (h *ApiHandler) Update(w http.ResponseWriter, r *http.Request) { + var req request.UpdateApiRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -102,7 +119,7 @@ func (h *SysApiHandler) Update(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysApiHandler) Delete(w http.ResponseWriter, r *http.Request) { +func (h *ApiHandler) Delete(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) diff --git a/internal/handler/auth.go b/internal/handler/admin/auth.go similarity index 81% rename from internal/handler/auth.go rename to internal/handler/admin/auth.go index 0e90a29..23208d2 100644 --- a/internal/handler/auth.go +++ b/internal/handler/admin/auth.go @@ -1,4 +1,4 @@ -package handler +package admin import ( "net/http" @@ -6,19 +6,32 @@ import ( "server/internal/model/request" "server/internal/pkg/errs" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" "time" + + "github.com/go-chi/chi/v5" ) type AuthHandler struct { - authService *service.AuthService + authService *admin.AuthService cfg *config.Config } -func NewAuthHandler(authService *service.AuthService, cfg *config.Config) *AuthHandler { +var _ router.Registrar = (*AuthHandler)(nil) + +func NewAuthHandler(authService *admin.AuthService, cfg *config.Config) *AuthHandler { return &AuthHandler{authService: authService, cfg: cfg} } +func (h *AuthHandler) Register(r chi.Router) { + r.Route("/auth", func(r chi.Router) { + r.Post("/login", h.Login) + r.Post("/refresh-token", h.RefreshToken) + r.Post("/logout", h.Logout) + }) +} + func (h *AuthHandler) clearRefreshTokenCookie(w http.ResponseWriter) { http.SetCookie(w, &http.Cookie{ Name: h.cfg.JWTConfig.CookieName, diff --git a/internal/handler/category.go b/internal/handler/admin/category.go similarity index 70% rename from internal/handler/category.go rename to internal/handler/admin/category.go index 1e0e046..98306c9 100644 --- a/internal/handler/category.go +++ b/internal/handler/admin/category.go @@ -1,24 +1,39 @@ -package handler +package admin import ( "net/http" "server/internal/model/common" "server/internal/model/request" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" ) type CategoryHandler struct { - categoryService *service.CategoryService + categoryService *admin.CategoryService } -func NewCategoryHandler(categoryService *service.CategoryService) *CategoryHandler { +var _ router.Registrar = (*CategoryHandler)(nil) + +func NewCategoryHandler(categoryService *admin.CategoryService) *CategoryHandler { return &CategoryHandler{categoryService: categoryService} } -func (h *CategoryHandler) ListPage(w http.ResponseWriter, r *http.Request) { +func (h *CategoryHandler) Register(r chi.Router) { + r.Route("/categories", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/all", h.ListAll) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Delete("/{id}", h.Delete) + }) +} + +func (h *CategoryHandler) List(w http.ResponseWriter, r *http.Request) { pagination := httputil.Pagination(r) - list, total, err := h.categoryService.ListPage(r.Context(), pagination) + result, err := h.categoryService.List(r.Context(), pagination) if err != nil { httputil.Fail(w, err) return @@ -27,8 +42,8 @@ func (h *CategoryHandler) ListPage(w http.ResponseWriter, r *http.Request) { resp := common.PageResponse{ Page: pagination.Page, PageSize: pagination.PageSize, - List: list, - Total: total, + List: result.List, + Total: result.Total, } httputil.OkWithPage(w, &resp) } diff --git a/internal/handler/admin/file.go b/internal/handler/admin/file.go new file mode 100644 index 0000000..36fe73a --- /dev/null +++ b/internal/handler/admin/file.go @@ -0,0 +1,70 @@ +package admin + +import ( + "net/http" + "server/internal/model/common" + "server/internal/pkg/httputil" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" +) + +type FileHandler struct { + service *admin.FileService +} + +var _ router.Registrar = (*FileHandler)(nil) + +func NewFileHandler(service *admin.FileService) *FileHandler { + return &FileHandler{ + service: service, + } +} + +func (h *FileHandler) Register(r chi.Router) { + r.Route("/files", func(r chi.Router) { + r.Get("/", h.List) + r.Post("/", h.Upload) + }) +} + +func (h *FileHandler) List(w http.ResponseWriter, r *http.Request) { + pagination := httputil.Pagination(r) + + result, err := h.service.List(r.Context(), pagination) + if err != nil { + httputil.Fail(w, err) + return + } + + resp := common.PageResponse{ + Page: pagination.Page, + PageSize: pagination.PageSize, + List: result.List, + Total: result.Total, + } + + httputil.OkWithPage(w, &resp) +} + +func (h *FileHandler) Upload(w http.ResponseWriter, r *http.Request) { + folder := r.FormValue("folder") + if folder == "" { + folder = "/" + } + + _, header, err := r.FormFile("file") + if err != nil { + httputil.Fail(w, err) + return + } + + file, err := h.service.Upload(r.Context(), folder, header) + if err != nil { + httputil.Fail(w, err) + return + } + + httputil.Ok(w, file) +} diff --git a/internal/handler/sys_menu.go b/internal/handler/admin/menu.go similarity index 50% rename from internal/handler/sys_menu.go rename to internal/handler/admin/menu.go index d703281..39d11ff 100644 --- a/internal/handler/sys_menu.go +++ b/internal/handler/admin/menu.go @@ -1,25 +1,40 @@ -package handler +package admin import ( "net/http" "server/internal/model/common" "server/internal/model/request" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" ) -type SysMenuHandler struct { - menuService *service.SysMenuService +type MenuHandler struct { + menuService *admin.MenuService } -func NewSysMenuHandler(menuService *service.SysMenuService) *SysMenuHandler { - return &SysMenuHandler{menuService: menuService} +var _ router.Registrar = (*MenuHandler)(nil) + +func NewMenuHandler(menuService *admin.MenuService) *MenuHandler { + return &MenuHandler{menuService: menuService} } -func (h *SysMenuHandler) ListPage(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) Register(r chi.Router) { + r.Route("/menus", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/all", h.ListAll) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Delete("/{id}", h.Delete) + }) +} + +func (h *MenuHandler) List(w http.ResponseWriter, r *http.Request) { pagination := httputil.Pagination(r) - list, total, err := h.menuService.ListPage(r.Context(), pagination) + result, err := h.menuService.List(r.Context(), pagination) if err != nil { httputil.Fail(w, err) return @@ -28,15 +43,15 @@ func (h *SysMenuHandler) ListPage(w http.ResponseWriter, r *http.Request) { resp := common.PageResponse{ Page: pagination.Page, PageSize: pagination.PageSize, - List: list, - Total: total, + List: result.List, + Total: result.Total, } httputil.OkWithPage(w, &resp) } -func (h *SysMenuHandler) GetMenus(w http.ResponseWriter, r *http.Request) { - list, err := h.menuService.GetMenus(r.Context()) +func (h *MenuHandler) ListAll(w http.ResponseWriter, r *http.Request) { + list, err := h.menuService.ListAll(r.Context()) if err != nil { httputil.Fail(w, err) return @@ -45,8 +60,8 @@ func (h *SysMenuHandler) GetMenus(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, list) } -func (h *SysMenuHandler) Create(w http.ResponseWriter, r *http.Request) { - var req request.CreateSysMenuRequest +func (h *MenuHandler) Create(w http.ResponseWriter, r *http.Request) { + var req request.CreateMenuRequest if err := httputil.BindJson(r, &req); err != nil { httputil.Fail(w, err) @@ -61,8 +76,8 @@ func (h *SysMenuHandler) Create(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysMenuHandler) Update(w http.ResponseWriter, r *http.Request) { - var req request.UpdateSysMenuRequest +func (h *MenuHandler) Update(w http.ResponseWriter, r *http.Request) { + var req request.UpdateMenuRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -83,7 +98,7 @@ func (h *SysMenuHandler) Update(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysMenuHandler) Delete(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) Delete(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) diff --git a/internal/handler/admin/module.go b/internal/handler/admin/module.go new file mode 100644 index 0000000..c433885 --- /dev/null +++ b/internal/handler/admin/module.go @@ -0,0 +1,24 @@ +package admin + +import ( + "server/internal/router" + + "go.uber.org/fx" +) + +var Module = fx.Module("admin-handlers", + fx.Provide( + // 特殊路由 不用鉴权 + router.AsRegistrar(router.AdminPublicRoutes, NewAuthHandler), + + router.AsRegistrar(router.AdminRoutes, NewApiHandler), + router.AsRegistrar(router.AdminRoutes, NewCategoryHandler), + router.AsRegistrar(router.AdminRoutes, NewFileHandler), + router.AsRegistrar(router.AdminRoutes, NewMenuHandler), + router.AsRegistrar(router.AdminRoutes, NewPostHandler), + router.AsRegistrar(router.AdminRoutes, NewRoleHandler), + router.AsRegistrar(router.AdminRoutes, NewTagHandler), + router.AsRegistrar(router.AdminRoutes, NewUserHandler), + router.AsRegistrar(router.AdminRoutes, NewSystemHandler), + ), +) diff --git a/internal/handler/admin/post.go b/internal/handler/admin/post.go new file mode 100644 index 0000000..24beb2d --- /dev/null +++ b/internal/handler/admin/post.go @@ -0,0 +1,123 @@ +package admin + +import ( + "net/http" + "server/internal/model/common" + "server/internal/model/request" + "server/internal/pkg/httputil" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" +) + +type PostHandler struct { + postService *admin.PostService +} + +var _ router.Registrar = (*PostHandler)(nil) + +func NewPostHandler(postService *admin.PostService) *PostHandler { + return &PostHandler{postService: postService} +} + +func (h *PostHandler) Register(r chi.Router) { + r.Route("/posts", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/{id}", h.GetPostByID) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Delete("/{id}", h.Delete) + }) +} + +func (h *PostHandler) List(w http.ResponseWriter, r *http.Request) { + pagination := httputil.Pagination(r) + + result, err := h.postService.List(r.Context(), pagination) + if err != nil { + httputil.Fail(w, err) + } + + resp := common.PageResponse{ + Page: pagination.Page, + PageSize: pagination.PageSize, + List: result.List, + Total: result.Total, + } + + httputil.OkWithPage(w, &resp) +} + +func (h *PostHandler) GetPostByID(w http.ResponseWriter, r *http.Request) { + id, err := httputil.URLParamInt32(r, "id") + if err != nil { + httputil.Fail(w, err) + return + } + + post, err := h.postService.FindByID(r.Context(), id) + if err != nil { + httputil.Fail(w, err) + return + } + + httputil.Ok(w, post) +} + +func (h *PostHandler) Create(w http.ResponseWriter, r *http.Request) { + var req request.CreatePostRequest + + if err := httputil.BindJson(r, &req); err != nil { + httputil.Fail(w, err) + return + } + + postID, err := h.postService.Create(r.Context(), req) + + if err != nil { + httputil.Fail(w, err) + return + } + + httputil.Ok(w, map[string]int32{ + "post_id": postID, + }) +} + +func (h *PostHandler) Update(w http.ResponseWriter, r *http.Request) { + id, err := httputil.URLParamInt32(r, "id") + if err != nil { + httputil.Fail(w, err) + return + } + + var req request.UpdatePostRequest + + if err = httputil.BindJson(r, &req); err != nil { + httputil.Fail(w, err) + return + } + + if err = h.postService.Update(r.Context(), id, req); err != nil { + httputil.Fail(w, err) + return + } + + httputil.Ok(w) +} + +func (h *PostHandler) Delete(w http.ResponseWriter, r *http.Request) { + id, err := httputil.URLParamInt32(r, "id") + if err != nil { + httputil.Fail(w, err) + return + } + + if err = h.postService.Delete(r.Context(), id); err != nil { + httputil.Fail(w, err) + return + } + + httputil.Ok(w) +} diff --git a/internal/handler/sys_role.go b/internal/handler/admin/role.go similarity index 55% rename from internal/handler/sys_role.go rename to internal/handler/admin/role.go index 55c3b57..70e6732 100644 --- a/internal/handler/sys_role.go +++ b/internal/handler/admin/role.go @@ -1,25 +1,44 @@ -package handler +package admin import ( "net/http" "server/internal/model/common" "server/internal/model/request" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" ) -type SysRoleHandler struct { - roleService *service.SysRoleService +type RoleHandler struct { + roleService *admin.RoleService } -func NewSysRoleHandler(roleService *service.SysRoleService) *SysRoleHandler { - return &SysRoleHandler{roleService: roleService} +var _ router.Registrar = (*RoleHandler)(nil) + +func NewRoleHandler(roleService *admin.RoleService) *RoleHandler { + return &RoleHandler{roleService: roleService} } -func (h *SysRoleHandler) ListPage(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) Register(r chi.Router) { + r.Route("/roles", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/{id}/menus", h.ListRoleMenus) + r.Get("/{id}/apis", h.ListRoleApis) + r.Get("/all", h.ListAll) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Put("/{id}/menus", h.SetRoleMenus) + r.Put("/{id}/apis", h.SetRoleApis) + r.Delete("/{id}", h.Delete) + }) +} + +func (h *RoleHandler) List(w http.ResponseWriter, r *http.Request) { pagination := httputil.Pagination(r) - list, total, err := h.roleService.ListPage(r.Context(), pagination) + result, err := h.roleService.List(r.Context(), pagination) if err != nil { httputil.Fail(w, err) @@ -29,21 +48,21 @@ func (h *SysRoleHandler) ListPage(w http.ResponseWriter, r *http.Request) { resp := common.PageResponse{ Page: pagination.Page, PageSize: pagination.PageSize, - List: list, - Total: total, + List: result.List, + Total: result.Total, } httputil.OkWithPage(w, &resp) } -func (h *SysRoleHandler) GetRoleMenus(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) ListRoleMenus(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) return } - list, err := h.roleService.GetRoleMenus(r.Context(), id) + list, err := h.roleService.ListRoleMenus(r.Context(), id) if err != nil { httputil.Fail(w, err) return @@ -52,14 +71,14 @@ func (h *SysRoleHandler) GetRoleMenus(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, list) } -func (h *SysRoleHandler) GetRoleApis(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) ListRoleApis(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) return } - list, err := h.roleService.GetRoleApis(r.Context(), id) + list, err := h.roleService.ListRoleApis(r.Context(), id) if err != nil { httputil.Fail(w, err) return @@ -68,8 +87,8 @@ func (h *SysRoleHandler) GetRoleApis(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, list) } -func (h *SysRoleHandler) GetRoles(w http.ResponseWriter, r *http.Request) { - roles, err := h.roleService.GetRoles(r.Context()) +func (h *RoleHandler) ListAll(w http.ResponseWriter, r *http.Request) { + roles, err := h.roleService.ListAll(r.Context()) if err != nil { httputil.Fail(w, err) return @@ -78,8 +97,8 @@ func (h *SysRoleHandler) GetRoles(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, roles) } -func (h *SysRoleHandler) Create(w http.ResponseWriter, r *http.Request) { - var req request.CreateSysRoleRequest +func (h *RoleHandler) Create(w http.ResponseWriter, r *http.Request) { + var req request.CreateRoleRequest if err := httputil.BindJson(r, &req); err != nil { httputil.Fail(w, err) @@ -94,8 +113,8 @@ func (h *SysRoleHandler) Create(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysRoleHandler) Update(w http.ResponseWriter, r *http.Request) { - var req request.UpdateSysRoleRequest +func (h *RoleHandler) Update(w http.ResponseWriter, r *http.Request) { + var req request.UpdateRoleRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -116,8 +135,8 @@ func (h *SysRoleHandler) Update(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysRoleHandler) SetRoleMenus(w http.ResponseWriter, r *http.Request) { - var req request.SetSysRoleMenusRequest +func (h *RoleHandler) SetRoleMenus(w http.ResponseWriter, r *http.Request) { + var req request.SetRoleMenusRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -138,8 +157,8 @@ func (h *SysRoleHandler) SetRoleMenus(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysRoleHandler) SetRoleApis(w http.ResponseWriter, r *http.Request) { - var req request.SetSysRoleApisRequest +func (h *RoleHandler) SetRoleApis(w http.ResponseWriter, r *http.Request) { + var req request.SetRoleApisRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -161,7 +180,7 @@ func (h *SysRoleHandler) SetRoleApis(w http.ResponseWriter, r *http.Request) { } -func (h *SysRoleHandler) Delete(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) Delete(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) diff --git a/internal/handler/admin/system.go b/internal/handler/admin/system.go new file mode 100644 index 0000000..1a5e4c2 --- /dev/null +++ b/internal/handler/admin/system.go @@ -0,0 +1,24 @@ +package admin + +import ( + "server/internal/router" + + "github.com/go-chi/chi/v5" +) + +type SystemHandler struct { +} + +func NewSystemHandler() *SystemHandler { + return &SystemHandler{} +} + +var _ router.Registrar = (*SystemHandler)(nil) + +func (h *SystemHandler) Register(r chi.Router) { + +} + +func (h *SystemHandler) ListRoutes() { + +} diff --git a/internal/handler/tag.go b/internal/handler/admin/tag.go similarity index 71% rename from internal/handler/tag.go rename to internal/handler/admin/tag.go index 6812ff3..56fd441 100644 --- a/internal/handler/tag.go +++ b/internal/handler/admin/tag.go @@ -1,26 +1,41 @@ -package handler +package admin import ( "net/http" "server/internal/model/common" "server/internal/model/request" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" ) type TagHandler struct { - tagService *service.TagService + tagService *admin.TagService } -func NewTagHandler(tagService *service.TagService) *TagHandler { +var _ router.Registrar = (*TagHandler)(nil) + +func NewTagHandler(tagService *admin.TagService) *TagHandler { return &TagHandler{ tagService: tagService, } } -func (h *TagHandler) ListPage(w http.ResponseWriter, r *http.Request) { +func (h *TagHandler) Register(r chi.Router) { + r.Route("/tags", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/all", h.ListAll) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Delete("/{id}", h.Delete) + }) +} + +func (h *TagHandler) List(w http.ResponseWriter, r *http.Request) { pagination := httputil.Pagination(r) - list, total, err := h.tagService.ListPage(r.Context(), pagination) + result, err := h.tagService.List(r.Context(), pagination) if err != nil { httputil.Fail(w, err) return @@ -29,9 +44,10 @@ func (h *TagHandler) ListPage(w http.ResponseWriter, r *http.Request) { resp := common.PageResponse{ Page: pagination.Page, PageSize: pagination.PageSize, - List: list, - Total: total, + List: result.List, + Total: result.Total, } + httputil.OkWithPage(w, &resp) } diff --git a/internal/handler/sys_user.go b/internal/handler/admin/user.go similarity index 57% rename from internal/handler/sys_user.go rename to internal/handler/admin/user.go index faea7a0..661d6dd 100644 --- a/internal/handler/sys_user.go +++ b/internal/handler/admin/user.go @@ -1,4 +1,4 @@ -package handler +package admin import ( "net/http" @@ -7,44 +7,39 @@ import ( "server/internal/model/request" "server/internal/pkg/errs" "server/internal/pkg/httputil" - "server/internal/service" + "server/internal/router" + "server/internal/service/admin" + + "github.com/go-chi/chi/v5" ) -type SysUserHandler struct { - userService *service.SysUserService +type UserHandler struct { + userService *admin.UserService } -func NewSysUserHandler(userService *service.SysUserService) *SysUserHandler { - return &SysUserHandler{ +// 编译期接口断言 +var _ router.Registrar = (*UserHandler)(nil) + +func NewUserHandler(userService *admin.UserService) *UserHandler { + return &UserHandler{ userService: userService, } } -func (h *SysUserHandler) ListPage(w http.ResponseWriter, r *http.Request) { - pagination := httputil.Pagination(r) - - params := request.SearchSysUserParams{ - Pagination: *pagination, - Username: r.URL.Query().Get("username"), - } - - list, total, err := h.userService.ListPage(r.Context(), params) - if err != nil { - httputil.Fail(w, err) - return - } - - resp := common.PageResponse{ - Page: pagination.Page, - PageSize: pagination.PageSize, - List: list, - Total: total, - } - - httputil.OkWithPage(w, &resp) +func (h *UserHandler) Register(r chi.Router) { + r.Route("/users", func(r chi.Router) { + r.Get("/", h.List) + r.Get("/me", h.GetCurrentUser) + r.Get("/{id}/roles", h.ListRoles) + r.Post("/", h.Create) + r.Patch("/{id}", h.Update) + r.Put("/{id}/roles", h.SetRoles) + r.Patch("/{id}/password", h.UpdatePassword) + r.Delete("/{id}", h.Delete) + }) } -func (h *SysUserHandler) GetUserInfo(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) GetCurrentUser(w http.ResponseWriter, r *http.Request) { claims, ok := middleware.GetClaims(r.Context()) if !ok || claims.UserID == 0 { @@ -52,10 +47,10 @@ func (h *SysUserHandler) GetUserInfo(w http.ResponseWriter, r *http.Request) { return } - isAdmin, _ := r.Context().Value(middleware.IsAdminKey).(bool) + isAdmin := middleware.IsAdmin(r.Context()) uid := claims.UserID - user, err := h.userService.GetUserInfo(r.Context(), uid, isAdmin) + user, err := h.userService.GetCurrentUser(r.Context(), uid, isAdmin) if err != nil { httputil.Fail(w, err) return @@ -64,14 +59,38 @@ func (h *SysUserHandler) GetUserInfo(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, user) } -func (h *SysUserHandler) GetRoles(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) List(w http.ResponseWriter, r *http.Request) { + pagination := httputil.Pagination(r) + + params := request.SearchUserParams{ + Pagination: *pagination, + Username: r.URL.Query().Get("username"), + } + + result, err := h.userService.List(r.Context(), params) + if err != nil { + httputil.Fail(w, err) + return + } + + resp := common.PageResponse{ + Page: pagination.Page, + PageSize: pagination.PageSize, + List: result.List, + Total: result.Total, + } + + httputil.OkWithPage(w, &resp) +} + +func (h *UserHandler) ListRoles(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) return } - list, err := h.userService.GetRoles(r.Context(), id) + list, err := h.userService.ListRoles(r.Context(), id) if err != nil { httputil.Fail(w, err) return @@ -80,8 +99,8 @@ func (h *SysUserHandler) GetRoles(w http.ResponseWriter, r *http.Request) { httputil.Ok(w, list) } -func (h *SysUserHandler) Create(w http.ResponseWriter, r *http.Request) { - var req request.CreateSysUserRequest +func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) { + var req request.CreateUserRequest if err := httputil.BindJson(r, &req); err != nil { httputil.Fail(w, err) @@ -96,8 +115,8 @@ func (h *SysUserHandler) Create(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysUserHandler) Update(w http.ResponseWriter, r *http.Request) { - var req request.UpdateSysUserRequest +func (h *UserHandler) Update(w http.ResponseWriter, r *http.Request) { + var req request.UpdateUserRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -118,8 +137,8 @@ func (h *SysUserHandler) Update(w http.ResponseWriter, r *http.Request) { httputil.Ok(w) } -func (h *SysUserHandler) SetRoles(w http.ResponseWriter, r *http.Request) { - var req request.SetSysUserRolesRequest +func (h *UserHandler) SetRoles(w http.ResponseWriter, r *http.Request) { + var req request.SetUserRolesRequest id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -141,8 +160,8 @@ func (h *SysUserHandler) SetRoles(w http.ResponseWriter, r *http.Request) { } -func (h *SysUserHandler) UpdatePassword(w http.ResponseWriter, r *http.Request) { - var req request.UpdateSysUserPassword +func (h *UserHandler) UpdatePassword(w http.ResponseWriter, r *http.Request) { + var req request.UpdateUserPassword id, err := httputil.URLParamInt32(r, "id") if err != nil { @@ -163,7 +182,7 @@ func (h *SysUserHandler) UpdatePassword(w http.ResponseWriter, r *http.Request) httputil.Ok(w) } -func (h *SysUserHandler) Delete(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) Delete(w http.ResponseWriter, r *http.Request) { id, err := httputil.URLParamInt32(r, "id") if err != nil { httputil.Fail(w, err) diff --git a/internal/handler/module.go b/internal/handler/module.go index 4266555..c7f5d15 100644 --- a/internal/handler/module.go +++ b/internal/handler/module.go @@ -1,22 +1,15 @@ package handler import ( + "server/internal/handler/admin" + "server/internal/handler/site" + "go.uber.org/fx" ) var Module = fx.Module("handlers", - fx.Provide( - NewSysUserHandler, - NewSysRoleHandler, - NewSysMenuHandler, - NewSysApiHandler, - NewSysFileHandler, - NewSysPostHandler, - NewCategoryHandler, - NewAuthHandler, - NewTagHandler, - - NewPostHandler, - NewWebHandler, + fx.Options( + admin.Module, + site.Module, ), ) diff --git a/internal/handler/site/module.go b/internal/handler/site/module.go new file mode 100644 index 0000000..c189a74 --- /dev/null +++ b/internal/handler/site/module.go @@ -0,0 +1,13 @@ +package site + +import ( + "server/internal/router" + + "go.uber.org/fx" +) + +var Module = fx.Module("site-handlers", + fx.Provide( + router.AsRegistrar(router.SiteRoutes, NewPostHandler), + ), +) diff --git a/internal/handler/post.go b/internal/handler/site/post.go similarity index 71% rename from internal/handler/post.go rename to internal/handler/site/post.go index d90a967..2d49338 100644 --- a/internal/handler/post.go +++ b/internal/handler/site/post.go @@ -1,4 +1,4 @@ -package handler +package site import ( "net/http" @@ -6,24 +6,34 @@ import ( "server/internal/model/common" "server/internal/pkg/errs" "server/internal/pkg/httputil" - "server/internal/service" - "server/internal/utils" + "server/internal/router" + "server/internal/service/web" "github.com/go-chi/chi/v5" ) +var _ router.Registrar = (*PostHandler)(nil) + type PostHandler struct { - postService *service.PostService + postService *web.PostService } -func NewPostHandler(postService *service.PostService) *PostHandler { +func (h *PostHandler) Register(r chi.Router) { + r.Get("/posts", h.ListPage) + r.Get("/posts/{slug}", h.GetPost) + r.Get("/categories/stats", h.ListCategoryStats) + r.Get("/posts/archives", h.ListArchives) + r.Get("/posts/tags", h.ListPostTags) +} + +func NewPostHandler(postService *web.PostService) *PostHandler { return &PostHandler{postService: postService} } func (h *PostHandler) ListPage(w http.ResponseWriter, r *http.Request) { pagination := httputil.Pagination(r) - list, total, err := h.postService.ListPage(r.Context(), pagination) + result, err := h.postService.ListPage(r.Context(), pagination) if err != nil { httputil.Fail(w, err) return @@ -32,8 +42,8 @@ func (h *PostHandler) ListPage(w http.ResponseWriter, r *http.Request) { resp := common.PageResponse{ Page: pagination.Page, PageSize: pagination.PageSize, - List: list, - Total: total, + List: result.List, + Total: result.Total, } httputil.OkWithPage(w, &resp) @@ -47,7 +57,7 @@ func (h *PostHandler) GetPost(w http.ResponseWriter, r *http.Request) { return } - ip, err := netip.ParseAddr(utils.ClientIP(r)) + ip, err := netip.ParseAddr(httputil.ClientIP(r)) if err != nil { httputil.Fail(w, err) return diff --git a/internal/handler/sys_file.go b/internal/handler/sys_file.go deleted file mode 100644 index a561189..0000000 --- a/internal/handler/sys_file.go +++ /dev/null @@ -1,58 +0,0 @@ -package handler - -import ( - "net/http" - "server/internal/model/common" - "server/internal/pkg/httputil" - "server/internal/service" -) - -type SysFileHandler struct { - service *service.SysFileService -} - -func NewSysFileHandler(service *service.SysFileService) *SysFileHandler { - return &SysFileHandler{ - service: service, - } -} - -func (h *SysFileHandler) ListPage(w http.ResponseWriter, r *http.Request) { - pagination := httputil.Pagination(r) - - list, total, err := h.service.ListPage(r.Context(), pagination) - if err != nil { - httputil.Fail(w, err) - return - } - - resp := common.PageResponse{ - Page: pagination.Page, - PageSize: pagination.PageSize, - List: list, - Total: total, - } - - httputil.OkWithPage(w, &resp) -} - -func (h *SysFileHandler) Upload(w http.ResponseWriter, r *http.Request) { - folder := r.FormValue("folder") - if folder == "" { - folder = "/" - } - - _, header, err := r.FormFile("file") - if err != nil { - httputil.Fail(w, err) - return - } - - file, err := h.service.Upload(r.Context(), folder, header) - if err != nil { - httputil.Fail(w, err) - return - } - - httputil.Ok(w, file) -} diff --git a/internal/handler/sys_post.go b/internal/handler/sys_post.go deleted file mode 100644 index 72357a3..0000000 --- a/internal/handler/sys_post.go +++ /dev/null @@ -1,108 +0,0 @@ -package handler - -import ( - "net/http" - "server/internal/model/common" - "server/internal/model/request" - "server/internal/pkg/httputil" - "server/internal/service" -) - -type SysPostHandler struct { - sysPostService *service.SysPostService -} - -func NewSysPostHandler(postService *service.SysPostService) *SysPostHandler { - return &SysPostHandler{sysPostService: postService} -} - -func (h *SysPostHandler) ListPage(w http.ResponseWriter, r *http.Request) { - pagination := httputil.Pagination(r) - - list, total, err := h.sysPostService.ListPage(r.Context(), pagination) - if err != nil { - httputil.Fail(w, err) - } - - resp := common.PageResponse{ - Page: pagination.Page, - PageSize: pagination.PageSize, - List: list, - Total: total, - } - - httputil.OkWithPage(w, &resp) -} - -func (h *SysPostHandler) GetPostById(w http.ResponseWriter, r *http.Request) { - id, err := httputil.URLParamInt32(r, "id") - if err != nil { - httputil.Fail(w, err) - return - } - - post, err := h.sysPostService.FindByID(r.Context(), id) - if err != nil { - httputil.Fail(w, err) - return - } - - httputil.Ok(w, post) -} - -func (h *SysPostHandler) Create(w http.ResponseWriter, r *http.Request) { - var req request.CreatePostRequest - - if err := httputil.BindJson(r, &req); err != nil { - httputil.Fail(w, err) - return - } - - postID, err := h.sysPostService.Create(r.Context(), req) - - if err != nil { - httputil.Fail(w, err) - return - } - - httputil.Ok(w, map[string]int32{ - "post_id": postID, - }) -} - -func (h *SysPostHandler) Update(w http.ResponseWriter, r *http.Request) { - id, err := httputil.URLParamInt32(r, "id") - if err != nil { - httputil.Fail(w, err) - return - } - - var req request.UpdatePostRequest - - if err = httputil.BindJson(r, &req); err != nil { - httputil.Fail(w, err) - return - } - - if err = h.sysPostService.Update(r.Context(), id, req); err != nil { - httputil.Fail(w, err) - return - } - - httputil.Ok(w) -} - -func (h *SysPostHandler) Delete(w http.ResponseWriter, r *http.Request) { - id, err := httputil.URLParamInt32(r, "id") - if err != nil { - httputil.Fail(w, err) - return - } - - if err = h.sysPostService.Delete(r.Context(), id); err != nil { - httputil.Fail(w, err) - return - } - - httputil.Ok(w) -} diff --git a/internal/handler/web.go b/internal/handler/web.go deleted file mode 100644 index 72e4bbc..0000000 --- a/internal/handler/web.go +++ /dev/null @@ -1,30 +0,0 @@ -package handler - -import ( - "net/http" - "server/internal/pkg/httputil" - "server/internal/utils" -) - -type WebHandler struct { -} - -func NewWebHandler() *WebHandler { - return &WebHandler{} -} - -func (h *WebHandler) GetClientInfo(w http.ResponseWriter, r *http.Request) { - ip := utils.ClientIP(r) - userAgent := r.UserAgent() - - httputil.Ok(w, map[string]any{ - "ip": ip, - "userAgent": userAgent, - "method": r.Method, - "host": r.Host, - "path": r.URL.Path, - "origin": r.Header.Get("Origin"), - "accept": r.Header.Get("Accept"), - "acceptLanguage": r.Header.Get("Accept-Language"), - }) -} diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index 06ed2f7..afe8c5d 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -30,43 +30,7 @@ func NewAuthMiddleware(store *db.Store, cache *cache.Caches) *AuthMiddleware { } } -func (m *AuthMiddleware) Middleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - var err error - - claims, ok := GetClaims(ctx) - if !ok || claims.UserID == 0 { - httputil.Fail(w, errs.ErrUnauthorized) - return - } - - // 判断是否有管理员权限 目前只判断uid是否为1 - isAdmin := userIsAdmin(claims.UserID) - - if isAdmin { - ctx = context.WithValue(ctx, IsAdminKey, isAdmin) - next.ServeHTTP(w, r.WithContext(ctx)) - return - } - - // 不是管理员 判断api权限 - hasPermission, err := userHasApiPermission(ctx, r, m.store, claims.UserID, m.cache) - if err != nil { - httputil.Fail(w, err) - return - } - - if !hasPermission { - httputil.Fail(w, errs.ErrPermissionDenied) - return - } - - next.ServeHTTP(w, r) - }) -} - -func userIsAdmin(uid int32) bool { +func UserIsAdmin(uid int32) bool { if uid == 1 { return true } @@ -74,31 +38,79 @@ func userIsAdmin(uid int32) bool { return false } -func userHasApiPermission(ctx context.Context, r *http.Request, store *db.Store, uid int32, c *cache.Caches) (bool, error) { +func IsAdmin(ctx context.Context) bool { + isAdmin, ok := ctx.Value(IsAdminKey).(bool) + return ok && isAdmin +} + +func (m *AuthMiddleware) hasApiPermission(ctx context.Context, uid int32, requestMethod string, requestPath string) (bool, error) { var ( - apis []sqlc.GetSysUserApisRow + apis []sqlc.ListUserApisRow err error ) // 先从缓存中获取api数据 k := cachekey.UserApiPermissions(uid) - apis, err = cache.GetOrSetJSON[[]sqlc.GetSysUserApisRow](ctx, c, k, 0, func() ([]sqlc.GetSysUserApisRow, error) { - return store.GetSysUserApis(ctx, uid) + apis, err = cache.GetOrSetJSON[[]sqlc.ListUserApisRow](ctx, m.cache, k, 0, func() ([]sqlc.ListUserApisRow, error) { + return m.store.ListUserApis(ctx, uid) }) if err != nil { return false, err } - requestPath := chi.RouteContext(r.Context()).RoutePattern() - requestPath = strings.TrimPrefix(requestPath, "/api") - requestMethod := r.Method - for _, api := range apis { - if api.Path == requestPath && api.Method == requestMethod { + if api.Method != requestMethod { + continue + } + + if api.Path == requestPath { return true, nil } } return false, nil } + +func (m *AuthMiddleware) Middleware(router chi.Router) func(handler http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + claims, ok := GetClaims(ctx) + if !ok || claims.UserID == 0 { + httputil.Fail(w, errs.ErrUnauthorized) + return + } + + // 判断是否有管理员权限 + isAdmin := UserIsAdmin(claims.UserID) + + if isAdmin { + ctx = context.WithValue(ctx, IsAdminKey, isAdmin) + next.ServeHTTP(w, r.WithContext(ctx)) + return + } + + // 如果不是管理员 需要判断api权限 + rctx := chi.NewRouteContext() + requestMethod := r.Method + // https://github.com/go-chi/chi/pull/872 + api := router.Find(rctx, requestMethod, r.URL.Path) + requestPath := strings.TrimPrefix(api, "/api") + + hasPermission, err := m.hasApiPermission(ctx, claims.UserID, requestMethod, requestPath) + if err != nil { + httputil.Fail(w, err) + return + } + + if !hasPermission { + httputil.Fail(w, errs.ErrPermissionDenied) + return + } + + next.ServeHTTP(w, r) + }) + } +} diff --git a/internal/middleware/logger.go b/internal/middleware/logger.go index 142c4e9..eec1a7b 100644 --- a/internal/middleware/logger.go +++ b/internal/middleware/logger.go @@ -5,7 +5,7 @@ import ( "log/slog" "net/http" "server/internal/model/common" - "server/internal/utils" + "server/internal/pkg/httputil" "time" gonanoid "github.com/matoous/go-nanoid/v2" @@ -71,7 +71,7 @@ func (m *LoggerMiddleware) Middleware(next http.Handler) http.Handler { duration := time.Since(start) - ip := utils.ClientIP(r) + ip := httputil.ClientIP(r) fullPath := r.URL.Path if r.URL.RawQuery != "" { diff --git a/internal/model/common/response.go b/internal/model/common/response.go index f9c749b..7febfa4 100644 --- a/internal/model/common/response.go +++ b/internal/model/common/response.go @@ -14,3 +14,8 @@ type PageResponse struct { List interface{} `json:"list,omitempty"` // 业务数据 Total int64 `json:"total"` // 总记录数 } + +type PageResult[T any] struct { + List []T + Total int64 +} diff --git a/internal/model/request/sys_api.go b/internal/model/request/api.go similarity index 85% rename from internal/model/request/sys_api.go rename to internal/model/request/api.go index 9a3067d..fc05d16 100644 --- a/internal/model/request/sys_api.go +++ b/internal/model/request/api.go @@ -2,7 +2,7 @@ package request import "server/internal/model/common" -type CreateSysApiRequest struct { +type CreateApiRequest struct { Name string `json:"name" validate:"required,min=1,max=100"` GroupName string `json:"group_name" validate:"required,min=1,max=100"` Path string `json:"path" validate:"required,min=1,max=100"` @@ -10,7 +10,7 @@ type CreateSysApiRequest struct { Sort *int32 `json:"sort" validate:"required,min=0"` } -type UpdateSysApiRequest struct { +type UpdateApiRequest struct { Name string `json:"name" validate:"required,min=1,max=100"` GroupName string `json:"group_name" validate:"required,min=1,max=100"` Path string `json:"path" validate:"required,min=1,max=100"` @@ -18,8 +18,9 @@ type UpdateSysApiRequest struct { Sort *int32 `json:"sort" validate:"required,min=0"` } -type SearchSysApiParams struct { +type SearchApiParams struct { common.Pagination + Name string `json:"name" form:"name" validate:"omitempty,max=100"` Method string `json:"method" form:"method" validate:"omitempty,oneof=GET POST PUT PATCH DELETE"` GroupName string `json:"group_name" form:"group_name" validate:"omitempty,max=100"` } diff --git a/internal/model/request/sys_menu.go b/internal/model/request/menu.go similarity index 95% rename from internal/model/request/sys_menu.go rename to internal/model/request/menu.go index d0628eb..9ecb24d 100644 --- a/internal/model/request/sys_menu.go +++ b/internal/model/request/menu.go @@ -4,7 +4,7 @@ import ( "server/internal/pkg/validator" ) -type CreateSysMenuRequest struct { +type CreateMenuRequest struct { Name string `json:"name" validate:"required,min=1,max=100"` Path *string `json:"path" validate:"omitempty,max=100"` Type *int16 `json:"type" validate:"required,oneof=0 1 2"` @@ -17,7 +17,7 @@ type CreateSysMenuRequest struct { PermissionCode string `json:"permission_code" validate:"required,min=1,max=100"` } -type UpdateSysMenuRequest struct { +type UpdateMenuRequest struct { Name *string `json:"name" validate:"omitempty,min=1,max=100"` Path *string `json:"path" validate:"omitempty,max=100"` Type *int16 `json:"type" validate:"omitempty,oneof=0 1 2"` diff --git a/internal/model/request/sys_role.go b/internal/model/request/role.go similarity index 76% rename from internal/model/request/sys_role.go rename to internal/model/request/role.go index 9d4c74d..7970c50 100644 --- a/internal/model/request/sys_role.go +++ b/internal/model/request/role.go @@ -1,20 +1,20 @@ package request -type CreateSysRoleRequest struct { +type CreateRoleRequest struct { Name string `json:"name" validate:"required,min=1,max=100"` Code string `json:"code" validate:"required,max=100"` Status *int16 `json:"status" validate:"required,oneof=0 1"` } -type UpdateSysRoleRequest struct { +type UpdateRoleRequest struct { Name *string `json:"name" validate:"min=1,max=100"` Status *int16 `json:"status" validate:"omitempty,oneof=0 1"` } -type SetSysRoleMenusRequest struct { +type SetRoleMenusRequest struct { MenuIDs []int32 `json:"menu_ids" validate:"required,dive,gt=0"` } -type SetSysRoleApisRequest struct { +type SetRoleApisRequest struct { ApiIDs []int32 `json:"api_ids" validate:"required,dive,gt=0"` } diff --git a/internal/model/request/sys_user.go b/internal/model/request/user.go similarity index 85% rename from internal/model/request/sys_user.go rename to internal/model/request/user.go index 6004d82..eebcd93 100644 --- a/internal/model/request/sys_user.go +++ b/internal/model/request/user.go @@ -5,7 +5,7 @@ import ( "server/internal/pkg/validator" ) -type CreateSysUserRequest struct { +type CreateUserRequest struct { Username string `json:"username" validate:"required,min=1,max=50"` Account string `json:"account" validate:"required,min=5,max=100"` Password string `json:"password" validate:"required,min=6,max=255"` @@ -13,17 +13,17 @@ type CreateSysUserRequest struct { Status *int16 `json:"status" validate:"oneof=0 1"` } -type UpdateSysUserRequest struct { +type UpdateUserRequest struct { Username *string `json:"username" validate:"omitempty,min=1,max=50"` AvatarID validator.NullInt32 `json:"avatar_id" validate:"omitempty"` Status *int16 `json:"status" validate:"omitempty,oneof=0 1"` } -type UpdateSysUserPassword struct { +type UpdateUserPassword struct { Password string `json:"password" validate:"required,min=6,max=255"` } -type SetSysUserRolesRequest struct { +type SetUserRolesRequest struct { RoleIDs []int32 `json:"role_ids" validate:"required,dive,gt=0"` } @@ -32,7 +32,7 @@ type LoginRequest struct { Password string `json:"password" validate:"required,min=6,max=255"` } -type SearchSysUserParams struct { +type SearchUserParams struct { common.Pagination Username string `json:"username" validate:"omitempty,max=50"` } diff --git a/internal/model/response/sys_file.go b/internal/model/response/file.go similarity index 100% rename from internal/model/response/sys_file.go rename to internal/model/response/file.go diff --git a/internal/model/response/sys_user.go b/internal/model/response/user.go similarity index 79% rename from internal/model/response/sys_user.go rename to internal/model/response/user.go index ea46f3e..49c8403 100644 --- a/internal/model/response/sys_user.go +++ b/internal/model/response/user.go @@ -2,17 +2,16 @@ package response import ( "server/internal/db/sqlc" - "server/internal/pkg/httputil" "time" ) -type SysUserRolesResponse struct { +type UserRolesResponse struct { ID uint `json:"id"` Code string `json:"code"` Description string `json:"description"` } -type SysUserInfo struct { +type UserInfo struct { ID int32 `json:"id"` Account string `json:"account"` Username string `json:"username"` @@ -29,7 +28,7 @@ type LoginResponse struct { RefreshTokenExp time.Time `json:"refresh_token_exp"` } -func NewSysUserInfo(user sqlc.GetSysUserByIDRow, roles []sqlc.SysRole, menus []sqlc.SysMenu, p []*string) *SysUserInfo { +func NewUserInfo(user sqlc.GetUserByIDRow, roles []sqlc.SysRole, menus []sqlc.SysMenu, p []*string) *UserInfo { roleCodes := make([]string, len(roles)) for i, role := range roles { roleCodes[i] = role.Code @@ -42,11 +41,11 @@ func NewSysUserInfo(user sqlc.GetSysUserByIDRow, roles []sqlc.SysRole, menus []s } } - return &SysUserInfo{ + return &UserInfo{ ID: user.ID, Account: user.Account, Username: user.Username, - AvatarUrl: httputil.BuildFileUrl(user.AvatarUrl), + AvatarUrl: *user.AvatarUrl, Roles: roleCodes, Menus: menus, Permissions: permissions, diff --git a/internal/pkg/errs/errs.go b/internal/pkg/errs/errs.go index 6290bf9..65df3af 100644 --- a/internal/pkg/errs/errs.go +++ b/internal/pkg/errs/errs.go @@ -19,34 +19,35 @@ func New(httpCode int, msg string) *AppError { } var ( - ErrInvalidCredentials = New(http.StatusUnauthorized, "用户名或密码错误") - ErrUserDisabled = New(http.StatusForbidden, "用户已被禁用") - ErrUnauthenticated = New(http.StatusUnauthorized, "用户未登录或登录已失效") - ErrUnauthorized = New(http.StatusUnauthorized, "认证失败:无法获取当前用户信息") - ErrInvalidToken = New(http.StatusUnauthorized, "登录凭证无效") - ErrInvalidTokenClaims = New(http.StatusUnauthorized, "登录凭证解析失败") - ErrInvalidRefreshToken = New(http.StatusBadRequest, "invalid_grant") - ErrExpiredRefreshToken = New(http.StatusBadRequest, "invalid_grant") - ErrUserNotFound = New(http.StatusNotFound, "用户数据不存在") - ErrCategoryNotFound = New(http.StatusNotFound, "分类数据不存在") - ErrTagNotFound = New(http.StatusNotFound, "标签数据不存在") - ErrSysApiNotFound = New(http.StatusNotFound, "接口数据不存在") - ErrPostNotFound = New(http.StatusNotFound, "文章数据不存在") - ErrSysMenuNotFound = New(http.StatusNotFound, "菜单数据不存在") - ErrSysRoleNotFound = New(http.StatusNotFound, "角色数据不存在") - ErrCannotDeleteSuperAdmin = New(http.StatusForbidden, "超级管理员账号无法被删除") - ErrSlugRequired = New(http.StatusBadRequest, "slug不能为空") - ErrIDRequired = New(http.StatusBadRequest, "id不能为空") - ErrPermissionDenied = New(http.StatusForbidden, "没有权限访问该资源") - ErrInvalidID = New(http.StatusBadRequest, "id非法请检查传入的id") - ErrEmptyBody = New(http.StatusBadRequest, "请求内容(body)不能为空") - ErrInvalidJSON = New(http.StatusBadRequest, "请求数据格式错误") - ErrAccountAlreadyExists = New(http.StatusBadRequest, "账号已存在") - ErrCodeAlreadyExists = New(http.StatusBadRequest, "角色编码不允许重复") - ErrSlugAlreadyExists = New(http.StatusBadRequest, "slug不允许重复") - ErrPermissionCodeAlreadyExists = New(http.StatusBadRequest, "权限编码不允许重复") - ErrCategoryCodeAlreadyExists = New(http.StatusBadRequest, "分类编码不允许重复") - ErrTagCodeAlreadyExists = New(http.StatusBadRequest, "标签编码不允许重复") - ErrSysMenusPathUniqueIdx = New(http.StatusBadRequest, "菜单路径不允许重复") - ErrSysApiMethodPathAlreadyExists = New(http.StatusBadRequest, "接口方法(method)路径(path)不允许重复") + ErrInvalidCredentials = New(http.StatusUnauthorized, "用户名或密码错误") + ErrUserDisabled = New(http.StatusForbidden, "用户已被禁用") + ErrUnauthenticated = New(http.StatusUnauthorized, "用户未登录或登录已失效") + ErrUnauthorized = New(http.StatusUnauthorized, "认证失败:无法获取当前用户信息") + ErrInvalidToken = New(http.StatusUnauthorized, "登录凭证无效") + ErrInvalidTokenClaims = New(http.StatusUnauthorized, "登录凭证解析失败") + ErrInvalidRefreshToken = New(http.StatusBadRequest, "invalid_grant") + ErrExpiredRefreshToken = New(http.StatusBadRequest, "invalid_grant") + ErrUserNotFound = New(http.StatusNotFound, "用户数据不存在") + ErrCategoryNotFound = New(http.StatusNotFound, "分类数据不存在") + ErrTagNotFound = New(http.StatusNotFound, "标签数据不存在") + ErrApiNotFound = New(http.StatusNotFound, "接口数据不存在") + ErrPostNotFound = New(http.StatusNotFound, "文章数据不存在") + ErrMenuNotFound = New(http.StatusNotFound, "菜单数据不存在") + ErrRoleNotFound = New(http.StatusNotFound, "角色数据不存在") + ErrCannotDeleteSuperAdmin = New(http.StatusForbidden, "超级管理员账号无法被删除") + ErrSlugRequired = New(http.StatusBadRequest, "slug不能为空") + ErrIDRequired = New(http.StatusBadRequest, "id不能为空") + ErrPermissionDenied = New(http.StatusForbidden, "没有权限访问该资源") + ErrInvalidID = New(http.StatusBadRequest, "id非法请检查传入的id") + ErrEmptyBody = New(http.StatusBadRequest, "请求内容(body)不能为空") + ErrInvalidJSON = New(http.StatusBadRequest, "请求数据格式错误") + ErrAccountAlreadyExists = New(http.StatusBadRequest, "账号已存在") + ErrCodeAlreadyExists = New(http.StatusBadRequest, "角色编码不允许重复") + ErrSlugAlreadyExists = New(http.StatusBadRequest, "slug不允许重复") + ErrPermissionCodeAlreadyExists = New(http.StatusBadRequest, "权限编码不允许重复") + ErrCategoryCodeAlreadyExists = New(http.StatusBadRequest, "分类编码不允许重复") + ErrTagCodeAlreadyExists = New(http.StatusBadRequest, "标签编码不允许重复") + ErrMenusPathUniqueIdx = New(http.StatusBadRequest, "菜单路径不允许重复") + ErrApiMethodPathAlreadyExists = New(http.StatusBadRequest, "接口方法(method)路径(path)不允许重复") + ErrInvalidApiPath = New(http.StatusBadRequest, "接口路径格式不正确,必须以/开头,参数段需为{name}格式") ) diff --git a/internal/pkg/httputil/request.go b/internal/pkg/httputil/request.go index 35b8204..595be73 100644 --- a/internal/pkg/httputil/request.go +++ b/internal/pkg/httputil/request.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "io" + "net" "net/http" "net/url" "server/internal/config" @@ -11,6 +12,7 @@ import ( "server/internal/pkg/errs" "server/internal/pkg/validator" "strconv" + "strings" "github.com/go-chi/chi/v5" ) @@ -71,6 +73,28 @@ func BindJson(r *http.Request, dest any) error { return nil } +// ClientIP 获取客户端 IP,依次从 X-Forwarded-For、X-Real-IP、RemoteAddr 取值 +func ClientIP(r *http.Request) string { + if xff := r.Header.Get("X-Forwarded-For"); xff != "" { + parts := strings.Split(xff, ",") + if len(parts) > 0 { + return strings.TrimSpace(parts[0]) + } + } + + if ip := r.Header.Get("X-Real-IP"); ip != "" { + return ip + } + + // RemoteAddr: IP:port + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil { + return host + } + + return r.RemoteAddr +} + func BuildFileUrl(path *string) string { if path == nil || *path == "" { return "" diff --git a/internal/router/client.go b/internal/router/client.go deleted file mode 100644 index 7405c62..0000000 --- a/internal/router/client.go +++ /dev/null @@ -1,19 +0,0 @@ -package router - -import ( - "server/internal/handler" - - "github.com/go-chi/chi/v5" -) - -func SetupPostRoutes(r chi.Router, h *handler.PostHandler) { - r.Get("/post", h.ListPage) - r.Get("/post/{slug}", h.GetPost) - r.Get("/category/stats", h.ListCategoryStats) - r.Get("/post/archive", h.ListArchives) - r.Get("/post/tags", h.ListPostTags) -} - -func SetupWebRoutes(r chi.Router, h *handler.WebHandler) { - -} diff --git a/internal/router/router.go b/internal/router/router.go index dbbb880..f128e4a 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -5,35 +5,78 @@ import ( "os" "path/filepath" "server/internal/config" - "server/internal/handler" "server/internal/middleware" + "server/internal/pkg/httputil" "github.com/go-chi/chi/v5" "go.uber.org/fx" ) -type Handlers struct { - fx.In - // auth - JwtMiddleware *middleware.JWTMiddleware - AuthMiddleware *middleware.AuthMiddleware - LoggerMiddleware *middleware.LoggerMiddleware - // system - AuthHandler *handler.AuthHandler - SysUserHandler *handler.SysUserHandler - SysRoleHandler *handler.SysRoleHandler - SysMenuHandler *handler.SysMenuHandler - SysApiHandler *handler.SysApiHandler - SysFileHandler *handler.SysFileHandler - SysPostHandler *handler.SysPostHandler - CategoryHandler *handler.CategoryHandler - TagHandler *handler.TagHandler - // web - PostHandler *handler.PostHandler - WebHandler *handler.WebHandler +type Group string + +const ( + AdminPublicRoutes Group = "admin_public_routes" + AdminRoutes Group = "admin_routes" + SiteRoutes Group = "site_routes" +) + +type Registrar interface { + Register(r chi.Router) } -func setupStaticFiles(r chi.Router) { +type Params struct { + fx.In + + Jwt *middleware.JWTMiddleware + Auth *middleware.AuthMiddleware + Logger *middleware.LoggerMiddleware + + AdminPublicRoutes []Registrar `group:"admin_public_routes"` + AdminRoutes []Registrar `group:"admin_routes"` + SiteRoutes []Registrar `group:"site_routes"` +} + +func NewRouter(p Params) *chi.Mux { + mux := chi.NewRouter() + + mux.Use(p.Logger.Middleware) + + if config.IsDev() { + // 开放静态目录 + registerStaticFiles(mux) + + // 开发阶段:遍历所有已注册路由的清单接口(无鉴权) + mux.Get("/api/admin/routes", ListRoutes(mux)) + } + + mux.Route("/api/admin", func(r chi.Router) { + // 免鉴权接口 + for _, route := range p.AdminPublicRoutes { + route.Register(r) + } + + r.Group(func(r chi.Router) { + // jwt 和 auth中间件 + r.Use(p.Jwt.Middleware) + r.Use(p.Auth.Middleware(mux)) + + // 循环挂载所有后台业务模块 + for _, route := range p.AdminRoutes { + route.Register(r) + } + }) + }) + + mux.Route("/api", func(r chi.Router) { + for _, route := range p.SiteRoutes { + route.Register(r) + } + }) + + return mux +} + +func registerStaticFiles(r chi.Router) { rootDir, _ := os.Getwd() uploadsDir := filepath.Join(rootDir, "uploads") @@ -41,43 +84,34 @@ func setupStaticFiles(r chi.Router) { r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir(uploadsDir)))) } -func NewRouter(m Handlers) *chi.Mux { - r := chi.NewRouter() - - r.Use(m.LoggerMiddleware.Middleware) - - if config.IsDev() { - // 开放静态目录 - setupStaticFiles(r) - } - - r.Route("/api/admin", func(r chi.Router) { - r.Post("/auth/login", m.AuthHandler.Login) - r.Post("/auth/refresh-token", m.AuthHandler.RefreshToken) - r.Post("/auth/logout", m.AuthHandler.Logout) - - r.Group(func(r chi.Router) { - r.Use(m.JwtMiddleware.Middleware) - r.Use(m.AuthMiddleware.Middleware) - - SetupSysUserRoutes(r, m.SysUserHandler) - SetupSysRoleRoutes(r, m.SysRoleHandler) - SetupSysMenuRoutes(r, m.SysMenuHandler) - SetupSysApiRoutes(r, m.SysApiHandler) - SetupSysFileRoutes(r, m.SysFileHandler) - SetupCategoryRoutes(r, m.CategoryHandler) - SetupSysPostRoutes(r, m.SysPostHandler) - SetupTagRoutes(r, m.TagHandler) - }) - - }) - - // web api - r.Route("/api", func(r chi.Router) { - r.Group(func(r chi.Router) { - SetupPostRoutes(r, m.PostHandler) - SetupWebRoutes(r, m.WebHandler) - }) - }) - return r +// RouteInfo 路由信息 +type RouteInfo struct { + Method string `json:"method"` + Path string `json:"path"` +} + +// ListRoutes 遍历所有已注册的路由 +func ListRoutes(mux *chi.Mux) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var routes []RouteInfo + + err := chi.Walk(mux, func(method string, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error { + routes = append(routes, RouteInfo{Method: method, Path: route}) + return nil + }) + if err != nil { + httputil.Fail(w, err) + return + } + + httputil.Ok(w, routes) + } +} + +func AsRegistrar(group Group, f any) any { + return fx.Annotate( + f, + fx.As(new(Registrar)), + fx.ResultTags(`group:"`+string(group)+`"`), + ) } diff --git a/internal/router/system.go b/internal/router/system.go deleted file mode 100644 index 56ec531..0000000 --- a/internal/router/system.go +++ /dev/null @@ -1,84 +0,0 @@ -package router - -import ( - "server/internal/handler" - - "github.com/go-chi/chi/v5" -) - -// SetupSysUserRoutes 8 -func SetupSysUserRoutes(r chi.Router, h *handler.SysUserHandler) { - r.Get("/user", h.ListPage) - r.Get("/user/info", h.GetUserInfo) - r.Get("/user/{id}/roles", h.GetRoles) - r.Post("/user", h.Create) - r.Patch("/user/{id}", h.Update) - r.Put("/user/{id}/roles", h.SetRoles) - r.Patch("/user/{id}/password", h.UpdatePassword) - r.Delete("/user/{id}", h.Delete) -} - -// SetupSysRoleRoutes 9 -func SetupSysRoleRoutes(r chi.Router, h *handler.SysRoleHandler) { - r.Get("/role", h.ListPage) - r.Get("/role/{id}/menus", h.GetRoleMenus) - r.Get("/role/{id}/apis", h.GetRoleApis) - r.Get("/role/all", h.GetRoles) - r.Post("/role", h.Create) - r.Patch("/role/{id}", h.Update) - r.Put("/role/{id}/menus", h.SetRoleMenus) - r.Put("/role/{id}/apis", h.SetRoleApis) - r.Delete("/role/{id}", h.Delete) -} - -// SetupSysMenuRoutes 5 -func SetupSysMenuRoutes(r chi.Router, h *handler.SysMenuHandler) { - r.Get("/menu", h.ListPage) - r.Get("/menu/all", h.GetMenus) - r.Post("/menu", h.Create) - r.Patch("/menu/{id}", h.Update) - r.Delete("/menu/{id}", h.Delete) -} - -// SetupSysApiRoutes 6 -func SetupSysApiRoutes(r chi.Router, h *handler.SysApiHandler) { - r.Get("/api", h.ListPage) - r.Get("/api/all", h.GetAllSysApis) - r.Get("/api/group-names", h.GetApiGroupNames) - r.Post("/api", h.Create) - r.Patch("/api/{id}", h.Update) - r.Delete("/api/{id}", h.Delete) -} - -// SetupSysFileRoutes 2 -func SetupSysFileRoutes(r chi.Router, h *handler.SysFileHandler) { - r.Get("/file", h.ListPage) - r.Post("/file", h.Upload) -} - -// SetupSysPostRoutes 5 -func SetupSysPostRoutes(r chi.Router, h *handler.SysPostHandler) { - r.Get("/post", h.ListPage) - r.Get("/post/{id}", h.GetPostById) - r.Post("/post", h.Create) - r.Patch("/post/{id}", h.Update) - r.Delete("/post/{id}", h.Delete) -} - -// SetupCategoryRoutes 5 -func SetupCategoryRoutes(r chi.Router, h *handler.CategoryHandler) { - r.Get("/category", h.ListPage) - r.Get("/category/all", h.ListAll) - r.Post("/category", h.Create) - r.Patch("/category/{id}", h.Update) - r.Delete("/category/{id}", h.Delete) -} - -// SetupTagRoutes 5 -func SetupTagRoutes(r chi.Router, h *handler.TagHandler) { - r.Get("/tag", h.ListPage) - r.Get("/tag/all", h.ListAll) - r.Post("/tag", h.Create) - r.Patch("/tag/{id}", h.Update) - r.Delete("/tag/{id}", h.Delete) -} diff --git a/internal/service/sys_api.go b/internal/service/admin/api.go similarity index 50% rename from internal/service/sys_api.go rename to internal/service/admin/api.go index 6bb291c..35a06de 100644 --- a/internal/service/sys_api.go +++ b/internal/service/admin/api.go @@ -1,9 +1,10 @@ -package service +package admin import ( "context" "server/internal/db" "server/internal/db/sqlc" + "server/internal/model/common" "server/internal/model/enum" "server/internal/model/request" "server/internal/pkg/cache" @@ -12,55 +13,61 @@ import ( "server/internal/pkg/errs" ) -type SysApiService struct { +type ApiService struct { store *db.Store cache *cache.Caches } -func NewSysApiService(store *db.Store, cache *cache.Caches) *SysApiService { - return &SysApiService{ +func NewApiService(store *db.Store, cache *cache.Caches) *ApiService { + return &ApiService{ store: store, cache: cache, } } -func (s *SysApiService) ListPage(ctx context.Context, p request.SearchSysApiParams) ([]sqlc.SysApi, int64, error) { - params := sqlc.GetSysApisParams{ +func (s *ApiService) List(ctx context.Context, p request.SearchApiParams) (*common.PageResult[sqlc.SysApi], error) { + params := sqlc.ListApisParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, + Name: p.Name, GroupName: p.GroupName, Method: p.Method, } - total, err := s.store.CountSysApis(ctx, sqlc.CountSysApisParams{ + total, err := s.store.CountApis(ctx, sqlc.CountApisParams{ + Name: p.Name, GroupName: p.GroupName, Method: p.Method, }) if err != nil { - return nil, 0, err + return nil, err } - list, err := s.store.GetSysApis(ctx, params) + list, err := s.store.ListApis(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - return list, total, nil + return &common.PageResult[sqlc.SysApi]{ + List: list, + Total: total, + }, nil } -func (s *SysApiService) GetAllSysApis(ctx context.Context) ([]sqlc.SysApi, error) { - return s.store.GetAllSysApis(ctx) +func (s *ApiService) ListAll(ctx context.Context) ([]sqlc.SysApi, error) { + return s.store.ListAllApis(ctx) } -func (s *SysApiService) GetApiGroupNames(ctx context.Context) ([]string, error) { - return s.store.GetSysApiGroupNames(ctx) +func (s *ApiService) ListApiGroups(ctx context.Context) ([]string, error) { + return s.store.ListApiGroups(ctx) } -func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequest) error { +func (s *ApiService) Create(ctx context.Context, req request.CreateApiRequest) error { + return s.store.WithTx(ctx, func(q *sqlc.Queries) error { - api := sqlc.CreateSysApiParams{ + api := sqlc.CreateApiParams{ Name: req.Name, GroupName: req.GroupName, Method: req.Method, @@ -69,7 +76,7 @@ func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequ } // 创建权限 - permissionId, err := q.CreateSysPermission(ctx, sqlc.CreateSysPermissionParams{ + permissionId, err := q.CreatePermission(ctx, sqlc.CreatePermissionParams{ Type: int16(enum.PermissionTypeApi), }) @@ -78,13 +85,13 @@ func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequ } // 创建api - apiId, err := q.CreateSysApi(ctx, api) + apiId, err := q.CreateApi(ctx, api) if err != nil { - return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrSysApiMethodPathAlreadyExists) + return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrApiMethodPathAlreadyExists) } // 关联权限 - if err = q.CreateSysApiPermission(ctx, sqlc.CreateSysApiPermissionParams{ + if err = q.CreateApiPermission(ctx, sqlc.CreateApiPermissionParams{ ApiID: apiId, PermissionID: permissionId, }); err != nil { @@ -96,8 +103,8 @@ func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequ }) } -func (s *SysApiService) Update(ctx context.Context, id int32, req request.UpdateSysApiRequest) error { - api := sqlc.UpdateSysApiParams{ +func (s *ApiService) Update(ctx context.Context, id int32, req request.UpdateApiRequest) error { + api := sqlc.UpdateApiParams{ ID: id, Name: req.Name, GroupName: req.GroupName, @@ -106,9 +113,9 @@ func (s *SysApiService) Update(ctx context.Context, id int32, req request.Update Sort: req.Sort, } - rows, err := s.store.UpdateSysApi(ctx, api) - if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil { - return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrSysApiMethodPathAlreadyExists) + rows, err := s.store.UpdateApi(ctx, api) + if err = dberr.MapRowsAffected(rows, err, errs.ErrApiNotFound); err != nil { + return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrApiMethodPathAlreadyExists) } // 如果更新成功 则清理缓存 @@ -117,28 +124,28 @@ func (s *SysApiService) Update(ctx context.Context, id int32, req request.Update return nil } -func (s *SysApiService) Delete(ctx context.Context, id int32) error { +func (s *ApiService) Delete(ctx context.Context, id int32) error { err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { // 1. 找出权限id - permissionID, err := q.GetSysPermissionIDBySysApiID(ctx, id) + permissionID, err := q.GetPermissionIDBySysApiID(ctx, id) if err != nil { - return dberr.MapNoRows(err, errs.ErrSysApiNotFound) + return dberr.MapNoRows(err, errs.ErrApiNotFound) } // 2. 根据权限id,删除角色权限关联数据 - if err = q.DeleteSysRolePermissionByPermissionID(ctx, permissionID); err != nil { + if err = q.DeleteRolePermissionByPermissionID(ctx, permissionID); err != nil { return err } // 3. 根据api id 删除sys_api_permission关联表数据 - if err = q.DeleteSysApiPermission(ctx, id); err != nil { + if err = q.DeleteApiPermission(ctx, id); err != nil { return err } // 4. 根据权限id,删除权限表权限数据 - if err = q.DeleteSysPermission(ctx, permissionID); err != nil { + if err = q.DeletePermission(ctx, permissionID); err != nil { return err } // 5. 删除api - rows, err := q.DeleteSysApi(ctx, id) - if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil { + rows, err := q.DeleteApi(ctx, id) + if err = dberr.MapRowsAffected(rows, err, errs.ErrApiNotFound); err != nil { return err } diff --git a/internal/service/auth.go b/internal/service/admin/auth.go similarity index 97% rename from internal/service/auth.go rename to internal/service/admin/auth.go index af6c2e3..0845d45 100644 --- a/internal/service/auth.go +++ b/internal/service/admin/auth.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -47,7 +47,7 @@ func comparePasswordHash(passwordHash, inputPassword string) error { } func (s *AuthService) Login(ctx context.Context, req request.LoginRequest) (*response.LoginResponse, error) { - user, err := s.store.GetSysUserByAccount(ctx, req.Account) + user, err := s.store.GetUserByAccount(ctx, req.Account) if err != nil { return nil, errs.ErrInvalidCredentials @@ -138,7 +138,7 @@ func (s *AuthService) Logout(ctx context.Context, refreshToken string) error { func (s *AuthService) GetActiveSysUser(ctx context.Context, id int32) error { var err error - _, err = s.store.GetActiveSysUserByID(ctx, id) + _, err = s.store.GetActiveUserByID(ctx, id) if err != nil { return err } diff --git a/internal/service/category.go b/internal/service/admin/category.go similarity index 88% rename from internal/service/category.go rename to internal/service/admin/category.go index a5f7dce..342c270 100644 --- a/internal/service/category.go +++ b/internal/service/admin/category.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -20,7 +20,7 @@ func NewCategoryService(store *db.Store) *CategoryService { } } -func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.Category, int64, error) { +func (s *CategoryService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.Category], error) { params := sqlc.ListCategoriesParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, @@ -28,15 +28,18 @@ func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([ total, err := s.store.CountCategories(ctx) if err != nil { - return nil, 0, err + return nil, err } list, err := s.store.ListCategories(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - return list, total, nil + return &common.PageResult[sqlc.Category]{ + List: list, + Total: total, + }, nil } func (s *CategoryService) ListAll(ctx context.Context) ([]sqlc.Category, error) { diff --git a/internal/service/sys_file.go b/internal/service/admin/file.go similarity index 73% rename from internal/service/sys_file.go rename to internal/service/admin/file.go index 0a0b0fe..67dc98f 100644 --- a/internal/service/sys_file.go +++ b/internal/service/admin/file.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -8,18 +8,17 @@ import ( "server/internal/db" "server/internal/db/sqlc" "server/internal/model/common" - "server/internal/model/response" "server/internal/pkg/httputil" gonanoid "github.com/matoous/go-nanoid/v2" ) -type SysFileService struct { +type FileService struct { store *db.Store } -func NewSysFileService(store *db.Store) *SysFileService { - return &SysFileService{ +func NewFileService(store *db.Store) *FileService { + return &FileService{ store: store, } } @@ -41,8 +40,8 @@ func MakeSavedDir(folder string) (string, error) { return uploadDir, nil } -func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.File, int64, error) { - params := sqlc.GetFilesParams{ +func (s *FileService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.File], error) { + params := sqlc.ListFilesParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, } @@ -50,19 +49,22 @@ func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([] total, err := s.store.CountFiles(ctx) if err != nil { - return nil, 0, err + return nil, err } - list, err := s.store.GetFiles(ctx, params) + list, err := s.store.ListFiles(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - return response.ToFiles(list), total, nil + return &common.PageResult[sqlc.File]{ + List: list, + Total: total, + }, nil } -func (s *SysFileService) Upload(ctx context.Context, folder string, file *multipart.FileHeader) (*sqlc.CreateFileRow, error) { +func (s *FileService) Upload(ctx context.Context, folder string, file *multipart.FileHeader) (*sqlc.CreateFileRow, error) { // 生成文件名 fileID, err := gonanoid.New() if err != nil { @@ -102,6 +104,7 @@ func (s *SysFileService) Upload(ctx context.Context, folder string, file *multip params := sqlc.CreateFileParams{ FileName: filename, FilePath: filePath, + FileUrl: httputil.BuildFileUrl(&filePath), OriginalName: file.Filename, FolderName: folder, MimeType: file.Header.Get("Content-Type"), @@ -113,7 +116,5 @@ func (s *SysFileService) Upload(ctx context.Context, folder string, file *multip return nil, err } - result.FilePath = httputil.BuildFileUrl(&result.FilePath) - return &result, nil } diff --git a/internal/service/sys_menu.go b/internal/service/admin/menu.go similarity index 59% rename from internal/service/sys_menu.go rename to internal/service/admin/menu.go index 04997cf..23905c2 100644 --- a/internal/service/sys_menu.go +++ b/internal/service/admin/menu.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -13,21 +13,21 @@ import ( "server/internal/pkg/errs" ) -type SysMenuService struct { +type MenuService struct { store *db.Store cache *cache.Caches } -func NewSysMenuService(store *db.Store, cache *cache.Caches) *SysMenuService { - return &SysMenuService{ +func NewMenuService(store *db.Store, cache *cache.Caches) *MenuService { + return &MenuService{ store: store, cache: cache, } } -func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRequest) error { +func (s *MenuService) Create(ctx context.Context, req request.CreateMenuRequest) error { err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { - menu := sqlc.CreateSysMenuParams{ + menu := sqlc.CreateMenuParams{ Name: req.Name, Path: req.Path, Component: req.Component, @@ -40,7 +40,7 @@ func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRe } // 先创建权限 - permissionId, err := q.CreateSysPermission(ctx, sqlc.CreateSysPermissionParams{ + permissionId, err := q.CreatePermission(ctx, sqlc.CreatePermissionParams{ Type: int16(enum.PermissionTypeMenu), Code: &req.PermissionCode, }) @@ -51,13 +51,13 @@ func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRe } // 创建菜单 - menuId, err := q.CreateSysMenu(ctx, menu) + menuId, err := q.CreateMenu(ctx, menu) if err != nil { - return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrSysMenusPathUniqueIdx) + return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrMenusPathUniqueIdx) } // 绑定权限 - if err = q.CreateSysMenuPermission(ctx, sqlc.CreateSysMenuPermissionParams{ + if err = q.CreateMenuPermission(ctx, sqlc.CreateMenuPermissionParams{ MenuID: menuId, PermissionID: permissionId, }); err != nil { @@ -74,10 +74,10 @@ func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRe return nil } -func (s *SysMenuService) Update(ctx context.Context, id int32, req request.UpdateSysMenuRequest) error { +func (s *MenuService) Update(ctx context.Context, id int32, req request.UpdateMenuRequest) error { err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { // 构造 menu 参数 - menu := sqlc.UpdateSysMenuParams{ + menu := sqlc.UpdateMenuParams{ ID: id, Name: req.Name, Path: req.Path, @@ -103,19 +103,19 @@ func (s *SysMenuService) Update(ctx context.Context, id int32, req request.Updat } // 执行更新 - rows, err := q.UpdateSysMenu(ctx, menu) + rows, err := q.UpdateMenu(ctx, menu) // 先判断数据是否存在 在判断path是否重复 - if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil { - return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrSysMenusPathUniqueIdx) + if err = dberr.MapRowsAffected(rows, err, errs.ErrMenuNotFound); err != nil { + return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrMenusPathUniqueIdx) } - permission := sqlc.UpdateSysMenuPermissionCodeParams{ + permission := sqlc.UpdateMenuPermissionCodeParams{ MenuID: id, Code: req.PermissionCode, } - if err = q.UpdateSysMenuPermissionCode(ctx, permission); err != nil { + if err = q.UpdateMenuPermissionCode(ctx, permission); err != nil { return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists) } @@ -132,52 +132,55 @@ func (s *SysMenuService) Update(ctx context.Context, id int32, req request.Updat return nil } -func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListSysMenusRow, int64, error) { - params := sqlc.ListSysMenusParams{ +func (s *MenuService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.ListMenusRow], error) { + params := sqlc.ListMenusParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, } - total, err := s.store.CountSysMenus(ctx) + total, err := s.store.CountMenus(ctx) if err != nil { - return nil, 0, err + return nil, err } - list, err := s.store.ListSysMenus(ctx, params) + list, err := s.store.ListMenus(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - return list, total, nil + return &common.PageResult[sqlc.ListMenusRow]{ + List: list, + Total: total, + }, nil } -func (s *SysMenuService) GetMenus(ctx context.Context) ([]sqlc.GetAllSysMenusRow, error) { - return s.store.GetAllSysMenus(ctx) +func (s *MenuService) ListAll(ctx context.Context) ([]sqlc.ListAllMenusRow, error) { + return s.store.ListAllMenus(ctx) } -func (s *SysMenuService) Delete(ctx context.Context, id int32) error { +func (s *MenuService) Delete(ctx context.Context, id int32) error { err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { // 1. 根据菜单id, 找出对应的权限id - permissionID, err := q.GetSysPermissionIDBySysMenuID(ctx, id) + permissionID, err := q.GetPermissionIDBySysMenuID(ctx, id) if err != nil { - return dberr.MapNoRows(err, errs.ErrSysMenuNotFound) + return dberr.MapNoRows(err, errs.ErrMenuNotFound) } // 2. 根据权限id 删除角色权限关联 - if err = q.DeleteSysRolePermissionByPermissionID(ctx, permissionID); err != nil { + if err = q.DeleteRolePermissionByPermissionID(ctx, permissionID); err != nil { return err } // 3. 根据菜单id 删除sys_menu_permission关联表数据 - if err = q.DeleteSysMenuPermission(ctx, id); err != nil { + if err = q.DeleteMenuPermission(ctx, id); err != nil { return err } // 4. 根据权限id删除权限表数据 - if err = q.DeleteSysPermission(ctx, permissionID); err != nil { + if err = q.DeletePermission(ctx, permissionID); err != nil { return err } // 5. 删除菜单 - rows, err := q.DeleteSysMenu(ctx, id) - if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil { + rows, err := q.DeleteMenu(ctx, id) + if err = dberr.MapRowsAffected(rows, err, errs.ErrMenuNotFound); err != nil { return err } diff --git a/internal/service/admin/module.go b/internal/service/admin/module.go new file mode 100644 index 0000000..32e6411 --- /dev/null +++ b/internal/service/admin/module.go @@ -0,0 +1,19 @@ +package admin + +import ( + "go.uber.org/fx" +) + +var Module = fx.Module("sys-services", + fx.Provide( + NewUserService, + NewRoleService, + NewMenuService, + NewApiService, + NewFileService, + NewPostService, + NewAuthService, + NewTagService, + NewCategoryService, + ), +) diff --git a/internal/service/sys_post.go b/internal/service/admin/post.go similarity index 77% rename from internal/service/sys_post.go rename to internal/service/admin/post.go index e6d7606..b8a8d8b 100644 --- a/internal/service/sys_post.go +++ b/internal/service/admin/post.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -8,20 +8,19 @@ import ( "server/internal/model/request" "server/internal/pkg/dberr" "server/internal/pkg/errs" - "server/internal/pkg/httputil" ) -type SysPostService struct { +type PostService struct { store *db.Store } -func NewSysPostService(store *db.Store) *SysPostService { - return &SysPostService{ +func NewPostService(store *db.Store) *PostService { + return &PostService{ store: store, } } -func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListPostsRow, int64, error) { +func (s *PostService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.ListPostsRow], error) { params := sqlc.ListPostsParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, @@ -29,34 +28,30 @@ func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([] total, err := s.store.CountPosts(ctx) if err != nil { - return nil, 0, err + return nil, err } list, err := s.store.ListPosts(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - for i := range list { - url := httputil.BuildFileUrl(list[i].Cover) - list[i].Cover = &url - } - - return list, total, nil + return &common.PageResult[sqlc.ListPostsRow]{ + List: list, + Total: total, + }, nil } -func (s *SysPostService) FindByID(ctx context.Context, id int32) (*sqlc.GetPostByIdRow, error) { - post, err := s.store.GetPostById(ctx, id) +func (s *PostService) FindByID(ctx context.Context, id int32) (*sqlc.GetPostByIDRow, error) { + post, err := s.store.GetPostByID(ctx, id) if err != nil { return nil, dberr.MapNoRows(err, errs.ErrPostNotFound) } - url := httputil.BuildFileUrl(post.Cover) - post.Cover = &url return &post, nil } -func (s *SysPostService) Create(ctx context.Context, req request.CreatePostRequest) (int32, error) { +func (s *PostService) Create(ctx context.Context, req request.CreatePostRequest) (int32, error) { postID, err := db.WithTxResult(ctx, s.store, func(q *sqlc.Queries) (int32, error) { params := sqlc.CreatePostParams{ Title: req.Title, @@ -104,7 +99,7 @@ func (s *SysPostService) Create(ctx context.Context, req request.CreatePostReque return postID, nil } -func (s *SysPostService) Update(ctx context.Context, id int32, req request.UpdatePostRequest) error { +func (s *PostService) Update(ctx context.Context, id int32, req request.UpdatePostRequest) error { return s.store.WithTx(ctx, func(q *sqlc.Queries) error { params := sqlc.UpdatePostParams{ Title: req.Title, @@ -160,7 +155,7 @@ func (s *SysPostService) Update(ctx context.Context, id int32, req request.Updat }) } -func (s *SysPostService) Delete(ctx context.Context, id int32) error { +func (s *PostService) Delete(ctx context.Context, id int32) error { return s.store.WithTx(ctx, func(q *sqlc.Queries) error { err := q.DeletePostCategoryByPostID(ctx, id) if err != nil { diff --git a/internal/service/admin/role.go b/internal/service/admin/role.go new file mode 100644 index 0000000..2d32b4d --- /dev/null +++ b/internal/service/admin/role.go @@ -0,0 +1,254 @@ +package admin + +import ( + "context" + "errors" + "server/internal/db" + "server/internal/db/sqlc" + "server/internal/model/common" + "server/internal/model/enum" + "server/internal/model/request" + "server/internal/pkg/cache" + "server/internal/pkg/cache/cachekey" + "server/internal/pkg/dberr" + "server/internal/pkg/errs" +) + +type RoleService struct { + store *db.Store + cache *cache.Caches +} + +func NewRoleService(store *db.Store, cache *cache.Caches) *RoleService { + return &RoleService{ + store: store, + cache: cache, + } +} + +// clearCache 清理所有api权限和用户信息 禁用角色/删除角色需要清理 +func (s *RoleService) clearCache(ctx context.Context) error { + if err := s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern); err != nil { + return err + } + + if err := s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern); err != nil { + return err + } + + return nil +} + +func (s *RoleService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.SysRole], error) { + params := sqlc.ListRolesParams{ + Limit: p.PageSize, + Offset: (p.Page - 1) * p.PageSize, + } + + total, err := s.store.CountRoles(ctx) + + if err != nil { + return nil, err + } + + list, err := s.store.ListRoles(ctx, params) + if err != nil { + return nil, err + } + + return &common.PageResult[sqlc.SysRole]{ + List: list, + Total: total, + }, nil +} + +func (s *RoleService) ListAll(ctx context.Context) ([]sqlc.SysRole, error) { + return s.store.ListAllRoles(ctx) +} + +func (s *RoleService) ListRoleMenus(ctx context.Context, id int32) ([]sqlc.ListRoleMenusRow, error) { + _, err := s.store.GetRoleByID(ctx, id) + if err != nil { + return nil, dberr.MapNoRows(err, errs.ErrRoleNotFound) + } + + return s.store.ListRoleMenus(ctx, id) +} + +func (s *RoleService) ListRoleApis(ctx context.Context, id int32) ([]sqlc.ListRoleApisRow, error) { + _, err := s.store.GetRoleByID(ctx, id) + if err != nil { + return nil, dberr.MapNoRows(err, errs.ErrRoleNotFound) + } + + return s.store.ListRoleApis(ctx, id) +} + +func (s *RoleService) Create(ctx context.Context, req request.CreateRoleRequest) error { + params := sqlc.CreateRoleParams{ + Name: req.Name, + Code: req.Code, + Status: *req.Status, + } + + err := s.store.CreateRole(ctx, params) + if err != nil { + return dberr.MapUniqueViolation(err, dberr.SysRoleCodeKey, errs.ErrCodeAlreadyExists) + } + return nil +} + +func (s *RoleService) Update(ctx context.Context, id int32, req request.UpdateRoleRequest) error { + params := sqlc.UpdateRoleParams{ + ID: id, + Name: req.Name, + Status: req.Status, + } + + rows, err := s.store.UpdateRole(ctx, params) + err = dberr.MapRowsAffected(rows, err, errs.ErrRoleNotFound) + if err != nil { + if errors.Is(err, errs.ErrRoleNotFound) { + } + return err + } + + // 清理缓存 无论启用禁用 启用时也需要清理缓存以刷新权限 + _ = s.clearCache(ctx) + + return nil +} + +func (s *RoleService) SetRoleMenus(ctx context.Context, roleID int32, req request.SetRoleMenusRequest) error { + // 先检查角色是否存在 + _, err := s.store.GetRoleByID(ctx, roleID) + if err != nil { + return dberr.MapNoRows(err, errs.ErrRoleNotFound) + } + + err = s.store.WithTx(ctx, func(q *sqlc.Queries) error { + // 1. 先删除所有关联表数据 + if err = q.DeleteRolePermissionsByType(ctx, sqlc.DeleteRolePermissionsByTypeParams{ + RoleID: roleID, + Type: int16(enum.PermissionTypeMenu), + }); err != nil { + return err + } + + // 2. 查出所有权限id并组装数据 + permissionIds, err := q.ListPermissionIDsByMenuIDs(ctx, req.MenuIDs) + if err != nil { + return err + } + + params := make([]sqlc.CreateRolePermissionParams, 0, len(permissionIds)) + for _, id := range permissionIds { + params = append(params, sqlc.CreateRolePermissionParams{ + RoleID: roleID, + PermissionID: id, + }) + } + + // 批量插入 + _, err = q.CreateRolePermission(ctx, params) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + return err + } + + // 清理缓存 这里只用清理用户信息 + _ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern) + + return nil +} + +func (s *RoleService) SetRoleApis(ctx context.Context, roleID int32, req request.SetRoleApisRequest) error { + _, err := s.store.GetRoleByID(ctx, roleID) + if err != nil { + return dberr.MapNoRows(err, errs.ErrRoleNotFound) + } + + err = s.store.WithTx(ctx, func(q *sqlc.Queries) error { + // 先删除所有旧权限数据 + if err = q.DeleteRolePermissionsByType(ctx, sqlc.DeleteRolePermissionsByTypeParams{ + RoleID: roleID, + Type: int16(enum.PermissionTypeApi), + }); err != nil { + return err + } + + // 查出数据并组装 + permissionIds, err := q.ListPermissionIDsByApiIDs(ctx, req.ApiIDs) + if err != nil { + return err + } + + params := make([]sqlc.CreateRolePermissionParams, 0, len(permissionIds)) + for _, id := range permissionIds { + params = append(params, sqlc.CreateRolePermissionParams{ + RoleID: roleID, + PermissionID: id, + }) + } + + // 批量插入 + _, err = q.CreateRolePermission(ctx, params) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + return err + } + + // 清理缓存 这里只用清理接口权限 + _ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern) + + return nil +} + +func (s *RoleService) Delete(ctx context.Context, id int32) error { + err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { + // 1.删除角色关联权限 + err := q.DeleteRolePermissionsByRoleID(ctx, id) + if err != nil { + return err + } + + // 2. 根据角色id删除用户关联的角色 + err = q.DeleteUserRoleByRoleID(ctx, id) + if err != nil { + return err + } + + // 3. 删除角色信息 + rows, err := q.DeleteRole(ctx, id) + err = dberr.MapRowsAffected(rows, err, errs.ErrRoleNotFound) + if err != nil { + // 如果是数据库不存在这条数据 此时也执行清理 + if errors.Is(err, errs.ErrRoleNotFound) { + } + return err + } + + return nil + }) + + if err != nil { + return err + } + + // 清理缓存 + _ = s.clearCache(ctx) + + return nil +} diff --git a/internal/service/tag.go b/internal/service/admin/tag.go similarity index 88% rename from internal/service/tag.go rename to internal/service/admin/tag.go index cd6398d..b175722 100644 --- a/internal/service/tag.go +++ b/internal/service/admin/tag.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -20,7 +20,7 @@ func NewTagService(store *db.Store) *TagService { } } -func (s *TagService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.Tag, int64, error) { +func (s *TagService) List(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.Tag], error) { params := sqlc.ListTagsParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, @@ -28,15 +28,18 @@ func (s *TagService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc total, err := s.store.CountTags(ctx) if err != nil { - return nil, 0, err + return nil, err } list, err := s.store.ListTags(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - return list, total, nil + return &common.PageResult[sqlc.Tag]{ + List: list, + Total: total, + }, nil } func (s *TagService) ListAll(ctx context.Context) ([]sqlc.Tag, error) { diff --git a/internal/service/sys_user.go b/internal/service/admin/user.go similarity index 61% rename from internal/service/sys_user.go rename to internal/service/admin/user.go index b2403a5..8c231fa 100644 --- a/internal/service/sys_user.go +++ b/internal/service/admin/user.go @@ -1,4 +1,4 @@ -package service +package admin import ( "context" @@ -6,25 +6,25 @@ import ( "server/internal/db" "server/internal/db/sqlc" "server/internal/middleware" + "server/internal/model/common" "server/internal/model/request" "server/internal/model/response" "server/internal/pkg/cache" "server/internal/pkg/cache/cachekey" "server/internal/pkg/dberr" "server/internal/pkg/errs" - "server/internal/pkg/httputil" "golang.org/x/sync/errgroup" ) -type SysUserService struct { +type UserService struct { store *db.Store jwt *middleware.JWTMiddleware cache *cache.Caches } -func NewSysUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService { - return &SysUserService{ +func NewUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *UserService { + return &UserService{ store: store, jwt: jwt, cache: cache, @@ -32,7 +32,7 @@ func NewSysUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *ca } // clearUserCache -func (s *SysUserService) clearUserCache(ctx context.Context, id int32) error { +func (s *UserService) clearUserCache(ctx context.Context, id int32) error { if err := s.cache.Del(ctx, cachekey.UserApiPermissions(id)); err != nil { return err } @@ -45,7 +45,7 @@ func (s *SysUserService) clearUserCache(ctx context.Context, id int32) error { } // clearRefreshToken -func (s *SysUserService) clearRefreshToken(ctx context.Context, id int32) error { +func (s *UserService) clearRefreshToken(ctx context.Context, id int32) error { keys, err := s.cache.SMembers(ctx, cachekey.AuthRefreshUser(id)) if err != nil { @@ -64,19 +64,19 @@ func (s *SysUserService) clearRefreshToken(ctx context.Context, id int32) error return err } -func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool) (*response.SysUserInfo, error) { - return cache.GetOrSetJSON[*response.SysUserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.SysUserInfo, error) { +func (s *UserService) GetCurrentUser(ctx context.Context, id int32, isAdmin bool) (*response.UserInfo, error) { + return cache.GetOrSetJSON[*response.UserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.UserInfo, error) { g, ctx := errgroup.WithContext(ctx) var ( - user sqlc.GetSysUserByIDRow + user sqlc.GetUserByIDRow roles []sqlc.SysRole menus []sqlc.SysMenu permissions []*string ) g.Go(func() error { - u, err := s.store.GetSysUserByID(ctx, id) + u, err := s.store.GetUserByID(ctx, id) if err != nil { return dberr.MapNoRows(err, errs.ErrUserNotFound) } @@ -86,7 +86,7 @@ func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool }) g.Go(func() error { - r, err := s.store.GetSysUserRoles(ctx, id) + r, err := s.store.ListUserRoles(ctx, id) if err != nil { return err } @@ -101,9 +101,9 @@ func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool ) if isAdmin { - p, err = s.store.GetSysAdminPermissions(ctx) + p, err = s.store.ListAdminPermissionCodes(ctx) } else { - p, err = s.store.GetSysUserPermissions(ctx, id) + p, err = s.store.ListUserPermissionCodes(ctx, id) } if err != nil { @@ -121,9 +121,9 @@ func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool err error ) if isAdmin { - m, err = s.store.GetSysAdminMenus(ctx) + m, err = s.store.ListAdminMenus(ctx) } else { - m, err = s.store.GetSysUserMenus(ctx, id) + m, err = s.store.ListUserMenus(ctx, id) } if err != nil { return err @@ -142,56 +142,53 @@ func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool } // 处理角色 - userInfo := response.NewSysUserInfo(user, roles, menus, permissions) + userInfo := response.NewUserInfo(user, roles, menus, permissions) return userInfo, nil }) } -func (s *SysUserService) ListPage(ctx context.Context, p request.SearchSysUserParams) ([]sqlc.ListSysUsersRow, int64, error) { - params := sqlc.ListSysUsersParams{ +func (s *UserService) List(ctx context.Context, p request.SearchUserParams) (*common.PageResult[sqlc.ListUsersRow], error) { + params := sqlc.ListUsersParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, Username: p.Username, } - total, err := s.store.CountSysUsers(ctx, p.Username) + total, err := s.store.CountUsers(ctx, p.Username) if err != nil { - return nil, 0, err + return nil, err } - users, err := s.store.ListSysUsers(ctx, params) + list, err := s.store.ListUsers(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - // 处理每个用户的头像URL - for i := range users { - url := httputil.BuildFileUrl(users[i].AvatarUrl) - users[i].AvatarUrl = &url - } - - return users, total, nil + return &common.PageResult[sqlc.ListUsersRow]{ + List: list, + Total: total, + }, nil } -func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) { +func (s *UserService) ListRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) { // 先查询用户是否存在 - _, err := s.store.GetSysUserByID(ctx, id) + _, err := s.store.GetUserByID(ctx, id) if err != nil { return nil, dberr.MapNoRows(err, errs.ErrUserNotFound) } - return s.store.GetSysUserRoles(ctx, id) + return s.store.ListUserRoles(ctx, id) } -func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRequest) error { +func (s *UserService) Create(ctx context.Context, req request.CreateUserRequest) error { passwordHash, err := generatePasswordHash(req.Password) if err != nil { return err } - user := sqlc.CreateSysUserParams{ + user := sqlc.CreateUserParams{ Account: req.Account, Username: req.Username, PasswordHash: passwordHash, @@ -199,15 +196,15 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe Status: *req.Status, } - if err = s.store.CreateSysUser(ctx, user); err != nil { + if err = s.store.CreateUser(ctx, user); err != nil { return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists) } return nil } -func (s *SysUserService) Update(ctx context.Context, id int32, req request.UpdateSysUserRequest) error { - user := sqlc.UpdateSysUserParams{ +func (s *UserService) Update(ctx context.Context, id int32, req request.UpdateUserRequest) error { + user := sqlc.UpdateUserParams{ Username: req.Username, ID: id, Status: req.Status, @@ -220,7 +217,7 @@ func (s *SysUserService) Update(ctx context.Context, id int32, req request.Updat } } - rows, err := s.store.UpdateSysUser(ctx, user) + rows, err := s.store.UpdateUser(ctx, user) if err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound); err != nil { return err } @@ -236,30 +233,30 @@ func (s *SysUserService) Update(ctx context.Context, id int32, req request.Updat return nil } -func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error { +func (s *UserService) SetRoles(ctx context.Context, userID int32, req request.SetUserRolesRequest) error { // 先查询用户是否存在 - _, err := s.store.GetSysUserByID(ctx, userID) + _, err := s.store.GetUserByID(ctx, userID) if err != nil { return dberr.MapNoRows(err, errs.ErrUserNotFound) } err = s.store.WithTx(ctx, func(q *sqlc.Queries) error { // 1. 先删除关联数据 - if err = q.ClearSysUserRoles(ctx, userID); err != nil { + if err = q.ClearUserRoles(ctx, userID); err != nil { return err } - var params []sqlc.CreateSysUserRoleParams + var params []sqlc.CreateUserRoleParams for _, roleID := range req.RoleIDs { - params = append(params, sqlc.CreateSysUserRoleParams{ + params = append(params, sqlc.CreateUserRoleParams{ UserID: userID, RoleID: roleID, }) } // 2. 关联用户角色数据 - _, err = q.CreateSysUserRole(ctx, params) + _, err = q.CreateUserRole(ctx, params) if err != nil { return err } @@ -277,18 +274,18 @@ func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request return nil } -func (s *SysUserService) UpdatePassword(ctx context.Context, id int32, req request.UpdateSysUserPassword) error { +func (s *UserService) UpdatePassword(ctx context.Context, id int32, req request.UpdateUserPassword) error { passwordHash, err := generatePasswordHash(req.Password) if err != nil { return err } - params := sqlc.UpdateSysUserPasswordParams{ + params := sqlc.UpdateUserPasswordParams{ ID: id, PasswordHash: passwordHash, } - rows, err := s.store.UpdateSysUserPassword(ctx, params) + rows, err := s.store.UpdateUserPassword(ctx, params) err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound) if err != nil { return err @@ -300,20 +297,22 @@ func (s *SysUserService) UpdatePassword(ctx context.Context, id int32, req reque return nil } -func (s *SysUserService) Delete(ctx context.Context, id int32) error { - if id == 1 { +func (s *UserService) Delete(ctx context.Context, id int32) error { + isAdmin := middleware.UserIsAdmin(id) + + if isAdmin { return errs.ErrCannotDeleteSuperAdmin } err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { // 清理用户角色关联数据 - err := q.ClearSysUserRoles(ctx, id) + err := q.ClearUserRoles(ctx, id) if err != nil { return err } // 删除用户 - rows, err := q.DeleteSysUser(ctx, id) + rows, err := q.DeleteUser(ctx, id) return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound) }) diff --git a/internal/service/module.go b/internal/service/module.go index e3e6c28..5c10bb1 100644 --- a/internal/service/module.go +++ b/internal/service/module.go @@ -1,21 +1,15 @@ package service import ( + "server/internal/service/admin" + "server/internal/service/web" + "go.uber.org/fx" ) var Module = fx.Module("services", - fx.Provide( - NewSysUserService, - NewSysRoleService, - NewSysMenuService, - NewSysApiService, - NewSysFileService, - NewSysPostService, - NewAuthService, - NewTagService, - - NewPostService, - NewCategoryService, + fx.Options( + admin.Module, + web.Module, ), ) diff --git a/internal/service/sys_role.go b/internal/service/sys_role.go deleted file mode 100644 index 7bb3d5f..0000000 --- a/internal/service/sys_role.go +++ /dev/null @@ -1,251 +0,0 @@ -package service - -import ( - "context" - "errors" - "server/internal/db" - "server/internal/db/sqlc" - "server/internal/model/common" - "server/internal/model/enum" - "server/internal/model/request" - "server/internal/pkg/cache" - "server/internal/pkg/cache/cachekey" - "server/internal/pkg/dberr" - "server/internal/pkg/errs" -) - -type SysRoleService struct { - store *db.Store - cache *cache.Caches -} - -func NewSysRoleService(store *db.Store, cache *cache.Caches) *SysRoleService { - return &SysRoleService{ - store: store, - cache: cache, - } -} - -// clearCache 清理所有api权限和用户信息 禁用角色/删除角色需要清理 -func (s *SysRoleService) clearCache(ctx context.Context) error { - if err := s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern); err != nil { - return err - } - - if err := s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern); err != nil { - return err - } - - return nil -} - -func (s *SysRoleService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.SysRole, int64, error) { - params := sqlc.ListSysRolesParams{ - Limit: p.PageSize, - Offset: (p.Page - 1) * p.PageSize, - } - - total, err := s.store.CountSysRoles(ctx) - - if err != nil { - return nil, 0, err - } - - list, err := s.store.ListSysRoles(ctx, params) - if err != nil { - return nil, 0, err - } - - return list, total, nil -} - -func (s *SysRoleService) GetRoles(ctx context.Context) ([]sqlc.SysRole, error) { - return s.store.GetAllSysRoles(ctx) -} - -func (s *SysRoleService) GetRoleMenus(ctx context.Context, id int32) ([]sqlc.GetSysRoleMenusRow, error) { - _, err := s.store.GetSysRoleByID(ctx, id) - if err != nil { - return nil, dberr.MapNoRows(err, errs.ErrSysRoleNotFound) - } - - return s.store.GetSysRoleMenus(ctx, id) -} - -func (s *SysRoleService) GetRoleApis(ctx context.Context, id int32) ([]sqlc.GetSysRoleApisRow, error) { - _, err := s.store.GetSysRoleByID(ctx, id) - if err != nil { - return nil, dberr.MapNoRows(err, errs.ErrSysRoleNotFound) - } - - return s.store.GetSysRoleApis(ctx, id) -} - -func (s *SysRoleService) Create(ctx context.Context, req request.CreateSysRoleRequest) error { - params := sqlc.CreateSysRoleParams{ - Name: req.Name, - Code: req.Code, - Status: *req.Status, - } - - err := s.store.CreateSysRole(ctx, params) - if err != nil { - return dberr.MapUniqueViolation(err, dberr.SysRoleCodeKey, errs.ErrCodeAlreadyExists) - } - return nil -} - -func (s *SysRoleService) Update(ctx context.Context, id int32, req request.UpdateSysRoleRequest) error { - params := sqlc.UpdateSysRoleParams{ - ID: id, - Name: req.Name, - Status: req.Status, - } - - rows, err := s.store.UpdateSysRole(ctx, params) - err = dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound) - if err != nil { - if errors.Is(err, errs.ErrSysRoleNotFound) { - } - return err - } - - // 清理缓存 无论启用禁用 启用时也需要清理缓存以刷新权限 - _ = s.clearCache(ctx) - - return nil -} - -func (s *SysRoleService) SetRoleMenus(ctx context.Context, roleID int32, req request.SetSysRoleMenusRequest) error { - // 先检查角色是否存在 - _, err := s.store.GetSysRoleByID(ctx, roleID) - if err != nil { - return dberr.MapNoRows(err, errs.ErrSysRoleNotFound) - } - - err = s.store.WithTx(ctx, func(q *sqlc.Queries) error { - // 1. 先删除所有关联表数据 - if err = q.DeleteSysRolePermissionByType(ctx, sqlc.DeleteSysRolePermissionByTypeParams{ - RoleID: roleID, - Type: int16(enum.PermissionTypeMenu), - }); err != nil { - return err - } - - // 2. 查出所有权限id并组装数据 - permissionIds, err := q.GetSysPermissionIdsByMenuIDs(ctx, req.MenuIDs) - if err != nil { - return err - } - - params := make([]sqlc.CreateSysRolePermissionParams, 0, len(permissionIds)) - for _, id := range permissionIds { - params = append(params, sqlc.CreateSysRolePermissionParams{ - RoleID: roleID, - PermissionID: id, - }) - } - - // 批量插入 - _, err = q.CreateSysRolePermission(ctx, params) - if err != nil { - return err - } - - return nil - }) - - if err != nil { - return err - } - - // 清理缓存 这里只用清理用户信息 - _ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern) - - return nil -} - -func (s *SysRoleService) SetRoleApis(ctx context.Context, roleID int32, req request.SetSysRoleApisRequest) error { - _, err := s.store.GetSysRoleByID(ctx, roleID) - if err != nil { - return dberr.MapNoRows(err, errs.ErrSysRoleNotFound) - } - - err = s.store.WithTx(ctx, func(q *sqlc.Queries) error { - // 先删除所有旧权限数据 - if err = q.DeleteSysRolePermissionByType(ctx, sqlc.DeleteSysRolePermissionByTypeParams{ - RoleID: roleID, - Type: int16(enum.PermissionTypeApi), - }); err != nil { - return err - } - - // 查出数据并组装 - permissionIds, err := q.GetSysPermissionIdsByApiIDs(ctx, req.ApiIDs) - if err != nil { - return err - } - - params := make([]sqlc.CreateSysRolePermissionParams, 0, len(permissionIds)) - for _, id := range permissionIds { - params = append(params, sqlc.CreateSysRolePermissionParams{ - RoleID: roleID, - PermissionID: id, - }) - } - - // 批量插入 - _, err = q.CreateSysRolePermission(ctx, params) - if err != nil { - return err - } - - return nil - }) - - if err != nil { - return err - } - - // 清理缓存 这里只用清理接口权限 - _ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern) - - return nil -} - -func (s *SysRoleService) Delete(ctx context.Context, id int32) error { - err := s.store.WithTx(ctx, func(q *sqlc.Queries) error { - // 1.删除角色关联权限 - err := q.DeleteSysRolePermissionByRoleID(ctx, id) - if err != nil { - return err - } - - // 2. 根据角色id删除用户关联的角色 - err = q.DeleteSysUserRoleByRoleID(ctx, id) - if err != nil { - return err - } - - // 3. 删除角色信息 - rows, err := q.DeleteSysRole(ctx, id) - err = dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound) - if err != nil { - // 如果是数据库不存在这条数据 此时也执行清理 - if errors.Is(err, errs.ErrSysRoleNotFound) { - } - return err - } - - return nil - }) - - if err != nil { - return err - } - - // 清理缓存 - _ = s.clearCache(ctx) - - return nil -} diff --git a/internal/service/web/module.go b/internal/service/web/module.go new file mode 100644 index 0000000..e13bfc6 --- /dev/null +++ b/internal/service/web/module.go @@ -0,0 +1,11 @@ +package web + +import ( + "go.uber.org/fx" +) + +var Module = fx.Module("web-services", + fx.Provide( + NewPostService, + ), +) diff --git a/internal/service/post.go b/internal/service/web/post.go similarity index 91% rename from internal/service/post.go rename to internal/service/web/post.go index a5f70b8..3016498 100644 --- a/internal/service/post.go +++ b/internal/service/web/post.go @@ -1,4 +1,4 @@ -package service +package web import ( "context" @@ -9,7 +9,6 @@ import ( "server/internal/model/response" "server/internal/pkg/dberr" "server/internal/pkg/errs" - "server/internal/pkg/httputil" ) type PostService struct { @@ -22,7 +21,7 @@ func NewPostService(store *db.Store) *PostService { } } -func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListPublishedPostsRow, int64, error) { +func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) (*common.PageResult[sqlc.ListPublishedPostsRow], error) { params := sqlc.ListPublishedPostsParams{ Limit: p.PageSize, Offset: (p.Page - 1) * p.PageSize, @@ -30,20 +29,18 @@ func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]sql total, err := s.store.CountPublishedPosts(ctx) if err != nil { - return nil, 0, err + return nil, err } list, err := s.store.ListPublishedPosts(ctx, params) if err != nil { - return nil, 0, err + return nil, err } - for i := range list { - url := httputil.BuildFileUrl(list[i].Cover) - list[i].Cover = &url - } - - return list, total, nil + return &common.PageResult[sqlc.ListPublishedPostsRow]{ + List: list, + Total: total, + }, nil } func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (*sqlc.GetPublicPostBySlugRow, error) { diff --git a/internal/utils/utils.go b/internal/utils/utils.go deleted file mode 100644 index 0853f1e..0000000 --- a/internal/utils/utils.go +++ /dev/null @@ -1,28 +0,0 @@ -package utils - -import ( - "net" - "net/http" - "strings" -) - -func ClientIP(r *http.Request) string { - if xff := r.Header.Get("X-Forwarded-For"); xff != "" { - parts := strings.Split(xff, ",") - if len(parts) > 0 { - return strings.TrimSpace(parts[0]) - } - } - - if ip := r.Header.Get("X-Real-IP"); ip != "" { - return ip - } - - // RemoteAddr: IP:port - host, _, err := net.SplitHostPort(r.RemoteAddr) - if err == nil { - return host - } - - return r.RemoteAddr -} diff --git a/scripts/Dockerfile b/scripts/Dockerfile new file mode 100644 index 0000000..aeb5d0d --- /dev/null +++ b/scripts/Dockerfile @@ -0,0 +1,33 @@ +FROM golang:1.24-alpine AS builder + +# 安装 UPX +RUN apk add --no-cache upx + +WORKDIR /app + +# 复制依赖文件 +COPY go.mod go.sum ./ +RUN go mod download + +# 复制代码 +COPY . . + +# 构建静态链接的二进制文件 +RUN go build -ldflags="-s -w" -o server ./cmd \ +&& upx -9 server + +# alpine:3.19 scratch 最小体积 +FROM scratch + +WORKDIR /app + +# 复制二进制文件 +COPY --from=builder /app/server . + +# 环境变量 +ENV APP_ENV=production + +EXPOSE 8080 + +# 运行服务 +CMD ["./server"] \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..a8c3fea --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -euo pipefail + +PROJECT_ROOT=$(cd -- "$(dirname "${BASH_SOURCE[0]}")/.." &> /dev/null && pwd) +REMOTE_SERVER="01" +REMOTE_DIR="/srv/docker/go-server" +IMAGE_NAME="go-server" +CONTAINER_NAME="go-server" +IMAGE_TAG="latest" +IMAGE_FILE="${IMAGE_NAME}-${IMAGE_TAG}.tar" +DOCKER_NETWORK="docker_app-bridge" + +cd "$PROJECT_ROOT" + +docker build -f ./scripts/Dockerfile --platform linux/amd64 -t "${IMAGE_NAME}":"${IMAGE_TAG}" . + +docker save -o "${IMAGE_FILE}" "${IMAGE_NAME}":"${IMAGE_TAG}" + +scp ${IMAGE_FILE} ${REMOTE_SERVER}:${REMOTE_DIR} + +ssh "${REMOTE_SERVER}" << EOF +set -euo pipefail + +cd "${REMOTE_DIR}" + +docker stop "${CONTAINER_NAME}" || true +docker rm "${CONTAINER_NAME}" || true +docker rmi "${IMAGE_NAME}":"${IMAGE_TAG}" || true + +docker load -i "${IMAGE_FILE}" +docker run -dit \ + --name "${CONTAINER_NAME}" \ + --restart unless-stopped \ + -v "${REMOTE_DIR}"/production.yaml:/app/production.yaml:ro \ + -v "${REMOTE_DIR}"/static:/app/uploads \ + -v "${REMOTE_DIR}"/logs:/app/logs \ + --network "${DOCKER_NETWORK}" \ + ${IMAGE_NAME}:${IMAGE_TAG} + +rm -f "${IMAGE_FILE}" +EOF + +rm -f "${IMAGE_FILE}" +docker rmi "${IMAGE_NAME}":"${IMAGE_TAG}" || true + +echo "部署完成" \ No newline at end of file