feat: 统一sql命名、函数命名

This commit is contained in:
2026-08-02 12:02:04 +08:00
parent d39342ce5f
commit 0e674e9d56
68 changed files with 1985 additions and 1720 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,11 @@
package web
import (
"go.uber.org/fx"
)
var Module = fx.Module("web-services",
fx.Provide(
NewPostService,
),
)

View File

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