feat: release v1.0.0
This commit is contained in:
180
internal/service/auth.go
Normal file
180
internal/service/auth.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"server/internal/config"
|
||||
"server/internal/db"
|
||||
"server/internal/middleware"
|
||||
"server/internal/model/auth"
|
||||
"server/internal/model/request"
|
||||
"server/internal/model/response"
|
||||
"server/internal/pkg/cache"
|
||||
"server/internal/pkg/cache/cachekey"
|
||||
"server/internal/pkg/errs"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type AuthService struct {
|
||||
jwt *middleware.JWTMiddleware
|
||||
cache *cache.Caches
|
||||
store *db.Store
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func NewAuthService(jwt *middleware.JWTMiddleware, cache *cache.Caches, store *db.Store, cfg *config.Config) *AuthService {
|
||||
return &AuthService{
|
||||
jwt: jwt,
|
||||
cache: cache,
|
||||
store: store,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// generatePasswordHash 生成密码哈希
|
||||
func generatePasswordHash(password string) (string, error) {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(hashed), nil
|
||||
}
|
||||
|
||||
// comparePasswordHash 比较密码哈希
|
||||
func comparePasswordHash(passwordHash, inputPassword string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(inputPassword))
|
||||
}
|
||||
|
||||
func (s *AuthService) Login(ctx context.Context, req request.LoginRequest) (*response.LoginResponse, error) {
|
||||
user, err := s.store.GetSysUserByAccount(ctx, req.Account)
|
||||
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// 此处判断如果用户id不为1 且状态为0表示用户已被禁用
|
||||
if user.ID != 1 && user.Status == 0 {
|
||||
return nil, errs.ErrUserDisabled
|
||||
}
|
||||
|
||||
if err = comparePasswordHash(user.PasswordHash, req.Password); err != nil {
|
||||
return nil, errs.ErrInvalidCredentials
|
||||
}
|
||||
|
||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refreshToken, err := s.jwt.GenerateRefreshToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 哈希
|
||||
hash := s.jwt.HashRefreshToken(refreshToken)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
refreshTokenExp := now.Add(s.cfg.JWTConfig.RefreshExpire)
|
||||
refreshTokenRecord := &auth.RefreshTokenRecord{
|
||||
UserID: user.ID,
|
||||
CreatedAt: now,
|
||||
ExpiresAt: refreshTokenExp,
|
||||
}
|
||||
|
||||
// 存入redis
|
||||
err = s.cache.SetJSON(ctx, cachekey.AuthRefresh(hash), refreshTokenRecord, s.cfg.JWTConfig.RefreshExpire)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 反向索引
|
||||
err = s.cache.SAdd(ctx, cachekey.AuthRefreshUser(user.ID), hash)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 给反向索引设置过期时间 这个过期时间需要覆盖最后一个token的过期时间 所以用最新的就行了
|
||||
if err = s.cache.Expire(ctx, cachekey.AuthRefreshUser(user.ID), s.cfg.JWTConfig.RefreshExpire); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.LoginResponse{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExp: accessTokenExp,
|
||||
RefreshToken: refreshToken,
|
||||
RefreshTokenExp: refreshTokenExp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AuthService) Logout(ctx context.Context, refreshToken string) error {
|
||||
hash := s.jwt.HashRefreshToken(refreshToken)
|
||||
|
||||
// 获取用户信息
|
||||
user, ok, err := cache.GetJSON[auth.RefreshTokenRecord](ctx, s.cache, cachekey.AuthRefresh(hash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
// 清理反向索引
|
||||
if err = s.cache.SRem(ctx, cachekey.AuthRefreshUser(user.UserID), hash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 清理当前登录的token
|
||||
if err = s.cache.Del(ctx, cachekey.AuthRefresh(hash)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthService) GetActiveSysUser(ctx context.Context, id int32) error {
|
||||
var err error
|
||||
|
||||
_, err = s.store.GetActiveSysUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthService) RefreshToken(ctx context.Context, refreshToken string) (*response.LoginResponse, error) {
|
||||
// 获取哈希
|
||||
hash := s.jwt.HashRefreshToken(refreshToken)
|
||||
// 从redis中获取数据
|
||||
record, ok, err := cache.GetJSON[auth.RefreshTokenRecord](ctx, s.cache, cachekey.AuthRefresh(hash))
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidRefreshToken
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil, errs.ErrInvalidRefreshToken
|
||||
}
|
||||
|
||||
// 如果不是超级用户 需要判断用户状态
|
||||
if record.UserID != 1 {
|
||||
err = s.GetActiveSysUser(ctx, record.UserID)
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidRefreshToken
|
||||
}
|
||||
}
|
||||
|
||||
// 获取新的access token
|
||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(record.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.LoginResponse{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExp: accessTokenExp,
|
||||
}, nil
|
||||
}
|
||||
@@ -2,7 +2,8 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"server/internal/db"
|
||||
"server/internal/db/sqlc"
|
||||
"server/internal/model/common"
|
||||
"server/internal/model/request"
|
||||
"server/internal/pkg/dberr"
|
||||
@@ -10,27 +11,27 @@ import (
|
||||
)
|
||||
|
||||
type CategoryService struct {
|
||||
queries *db.Queries
|
||||
store *db.Store
|
||||
}
|
||||
|
||||
func NewCategoryService(queries *db.Queries) *CategoryService {
|
||||
func NewCategoryService(store *db.Store) *CategoryService {
|
||||
return &CategoryService{
|
||||
queries: queries,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([]db.Category, int64, error) {
|
||||
params := db.ListCategoriesParams{
|
||||
func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.Category, int64, error) {
|
||||
params := sqlc.ListCategoriesParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountCategories(ctx)
|
||||
total, err := s.store.CountCategories(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.ListCategories(ctx, params)
|
||||
list, err := s.store.ListCategories(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -38,27 +39,29 @@ func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *CategoryService) ListAll(ctx context.Context) ([]db.Category, error) {
|
||||
return s.queries.ListAllCategories(ctx)
|
||||
func (s *CategoryService) ListAll(ctx context.Context) ([]sqlc.Category, error) {
|
||||
return s.store.ListAllCategories(ctx)
|
||||
}
|
||||
|
||||
func (s *CategoryService) Create(ctx context.Context, req request.CreateCategoryRequest) error {
|
||||
params := db.CreateCategoryParams{
|
||||
params := sqlc.CreateCategoryParams{
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
|
||||
err := s.queries.CreateCategory(ctx, params)
|
||||
err := s.store.CreateCategory(ctx, params)
|
||||
return dberr.MapUniqueViolation(err, dberr.CategoryCodeKey, errs.ErrCategoryCodeAlreadyExists)
|
||||
}
|
||||
|
||||
func (s *CategoryService) Update(ctx context.Context, id int32, req request.UpdateCategoryRequest) error {
|
||||
params := db.UpdateCategoryParams{
|
||||
params := sqlc.UpdateCategoryParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
rows, err := s.queries.UpdateCategory(ctx, params)
|
||||
rows, err := s.store.UpdateCategory(ctx, params)
|
||||
// 先判断数据条目是否存在
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound); err != nil {
|
||||
// 再判断code是否重复
|
||||
@@ -69,6 +72,13 @@ func (s *CategoryService) Update(ctx context.Context, id int32, req request.Upda
|
||||
}
|
||||
|
||||
func (s *CategoryService) Delete(ctx context.Context, id int32) error {
|
||||
rows, err := s.queries.DeleteCategory(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound)
|
||||
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
err := q.DeletePostCategoryByCategoryID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := q.DeleteCategory(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ var Module = fx.Module("services",
|
||||
NewSysApiService,
|
||||
NewSysFileService,
|
||||
NewSysPostService,
|
||||
NewAuthService,
|
||||
NewTagService,
|
||||
|
||||
NewPostService,
|
||||
NewCategoryService,
|
||||
|
||||
@@ -3,7 +3,8 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
db "server/internal/db/sqlc"
|
||||
"server/internal/db"
|
||||
"server/internal/db/sqlc"
|
||||
"server/internal/model/common"
|
||||
"server/internal/model/response"
|
||||
"server/internal/pkg/dberr"
|
||||
@@ -12,27 +13,27 @@ import (
|
||||
)
|
||||
|
||||
type PostService struct {
|
||||
queries *db.Queries
|
||||
store *db.Store
|
||||
}
|
||||
|
||||
func NewPostService(queries *db.Queries) *PostService {
|
||||
func NewPostService(store *db.Store) *PostService {
|
||||
return &PostService{
|
||||
queries: queries,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListPublishedPostsRow, int64, error) {
|
||||
params := db.ListPublishedPostsParams{
|
||||
func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListPublishedPostsRow, int64, error) {
|
||||
params := sqlc.ListPublishedPostsParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountPublishedPosts(ctx)
|
||||
total, err := s.store.CountPublishedPosts(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.ListPublishedPosts(ctx, params)
|
||||
list, err := s.store.ListPublishedPosts(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -45,13 +46,13 @@ func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]db.
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (*db.GetPublicPostBySlugRow, error) {
|
||||
post, err := s.queries.GetPublicPostBySlug(ctx, slug)
|
||||
func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (*sqlc.GetPublicPostBySlugRow, error) {
|
||||
post, err := s.store.GetPublicPostBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return nil, dberr.MapNoRows(err, errs.ErrPostNotFound)
|
||||
}
|
||||
|
||||
_ = s.queries.IncrementPostStatsView(ctx, db.IncrementPostStatsViewParams{
|
||||
_ = s.store.IncrementPostStatsView(ctx, sqlc.IncrementPostStatsViewParams{
|
||||
PostID: post.ID,
|
||||
Ip: ip,
|
||||
})
|
||||
@@ -59,12 +60,12 @@ func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
func (s *PostService) ListCategoryStats(ctx context.Context) ([]db.ListCategoryStatsRow, error) {
|
||||
return s.queries.ListCategoryStats(ctx)
|
||||
func (s *PostService) ListCategoryStats(ctx context.Context) ([]sqlc.ListCategoryStatsRow, error) {
|
||||
return s.store.ListCategoryStats(ctx)
|
||||
}
|
||||
|
||||
func (s *PostService) ListArchives(ctx context.Context) ([]response.ArchiveYear, error) {
|
||||
list, err := s.queries.ListArchives(ctx)
|
||||
list, err := s.store.ListArchives(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -111,3 +112,7 @@ func (s *PostService) ListArchives(ctx context.Context) ([]response.ArchiveYear,
|
||||
|
||||
return archive, nil
|
||||
}
|
||||
|
||||
func (s *PostService) ListPostTags(ctx context.Context) ([]sqlc.Tag, error) {
|
||||
return s.store.ListAllTags(ctx)
|
||||
}
|
||||
|
||||
@@ -2,41 +2,46 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"server/internal/model/common"
|
||||
"server/internal/db"
|
||||
"server/internal/db/sqlc"
|
||||
"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"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type SysApiService struct {
|
||||
queries *db.Queries
|
||||
pool *pgxpool.Pool
|
||||
cache *cache.Caches
|
||||
store *db.Store
|
||||
cache *cache.Caches
|
||||
}
|
||||
|
||||
func NewSysApiService(queries *db.Queries, pool *pgxpool.Pool, cache *cache.Caches) *SysApiService {
|
||||
return &SysApiService{queries: queries, pool: pool, cache: cache}
|
||||
func NewSysApiService(store *db.Store, cache *cache.Caches) *SysApiService {
|
||||
return &SysApiService{
|
||||
store: store,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SysApiService) ListPage(ctx context.Context, p *common.Pagination) ([]db.SysApi, int64, error) {
|
||||
params := db.GetSysApisParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
func (s *SysApiService) ListPage(ctx context.Context, p request.SearchSysApiParams) ([]sqlc.SysApi, int64, error) {
|
||||
params := sqlc.GetSysApisParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
GroupName: p.GroupName,
|
||||
Method: p.Method,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountSysApis(ctx)
|
||||
total, err := s.store.CountSysApis(ctx, sqlc.CountSysApisParams{
|
||||
GroupName: p.GroupName,
|
||||
Method: p.Method,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.GetSysApis(ctx, params)
|
||||
list, err := s.store.GetSysApis(ctx, params)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -45,69 +50,54 @@ func (s *SysApiService) ListPage(ctx context.Context, p *common.Pagination) ([]d
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *SysApiService) GetAllSysApis(ctx context.Context) ([]db.SysApi, error) {
|
||||
return s.queries.GetAllSysApis(ctx)
|
||||
func (s *SysApiService) GetAllSysApis(ctx context.Context) ([]sqlc.SysApi, error) {
|
||||
return s.store.GetAllSysApis(ctx)
|
||||
}
|
||||
|
||||
func (s *SysApiService) GetApiGroupNames(ctx context.Context) ([]string, error) {
|
||||
return s.queries.GetSysApiGroupNames(ctx)
|
||||
return s.store.GetSysApiGroupNames(ctx)
|
||||
}
|
||||
|
||||
func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequest) error {
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
api := sqlc.CreateSysApiParams{
|
||||
Name: req.Name,
|
||||
GroupName: req.GroupName,
|
||||
Method: req.Method,
|
||||
Path: req.Path,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
|
||||
defer func(ctx context.Context) {
|
||||
_ = tx.Rollback(ctx)
|
||||
}(ctx)
|
||||
// 创建权限
|
||||
permissionId, err := q.CreateSysPermission(ctx, sqlc.CreateSysPermissionParams{
|
||||
Type: int16(enum.PermissionTypeApi),
|
||||
})
|
||||
|
||||
q := db.New(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api := db.CreateSysApiParams{
|
||||
Name: req.Name,
|
||||
GroupName: req.GroupName,
|
||||
Method: req.Method,
|
||||
Path: req.Path,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
// 创建api
|
||||
apiId, err := q.CreateSysApi(ctx, api)
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrSysApiMethodPathAlreadyExists)
|
||||
}
|
||||
|
||||
// 关联权限
|
||||
if err = q.CreateSysApiPermission(ctx, sqlc.CreateSysApiPermissionParams{
|
||||
ApiID: apiId,
|
||||
PermissionID: permissionId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// 创建权限
|
||||
permissionId, err := q.CreateSysPermission(ctx, db.CreateSysPermissionParams{
|
||||
Type: int16(enum.PermissionTypeApi),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建api
|
||||
apiId, err := q.CreateSysApi(ctx, api)
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrSysApiMethodPathAlreadyExists)
|
||||
}
|
||||
|
||||
// 关联权限
|
||||
if err = q.CreateSysApiPermission(ctx, db.CreateSysApiPermissionParams{
|
||||
ApiID: apiId,
|
||||
PermissionID: permissionId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysApiService) Update(ctx context.Context, id int32, req request.UpdateSysApiRequest) error {
|
||||
api := db.UpdateSysApiParams{
|
||||
api := sqlc.UpdateSysApiParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
GroupName: req.GroupName,
|
||||
@@ -116,43 +106,51 @@ func (s *SysApiService) Update(ctx context.Context, id int32, req request.Update
|
||||
Sort: req.Sort,
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
|
||||
rows, err := s.queries.UpdateSysApi(ctx, api)
|
||||
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)
|
||||
}
|
||||
|
||||
// 如果更新成功 则清理缓存
|
||||
_ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysApiService) Delete(ctx context.Context, id int32) error {
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
// 1. 找出权限id
|
||||
permissionID, err := q.GetSysPermissionIDBySysApiID(ctx, id)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrSysApiNotFound)
|
||||
}
|
||||
// 2. 根据权限id,删除角色权限关联数据
|
||||
if err = q.DeleteSysRolePermissionByPermissionID(ctx, permissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
// 3. 根据api id 删除sys_api_permission关联表数据
|
||||
if err = q.DeleteSysApiPermission(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
// 4. 根据权限id,删除权限表权限数据
|
||||
if err = q.DeleteSysPermission(ctx, permissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
// 5. 删除api
|
||||
rows, err := q.DeleteSysApi(ctx, id)
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer tx.Rollback(ctx)
|
||||
// 清理缓存 事务执行成功后才清理
|
||||
_ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern)
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
rows, err := q.DeleteSysApi(ctx, id)
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = q.DeleteSysPermissionBySysApiID(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = q.DeleteSysApiPermission(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
|
||||
return tx.Commit(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
db "server/internal/db/sqlc"
|
||||
"server/internal/db"
|
||||
"server/internal/db/sqlc"
|
||||
"server/internal/model/common"
|
||||
"server/internal/model/response"
|
||||
"server/internal/pkg/httputil"
|
||||
@@ -14,12 +15,12 @@ import (
|
||||
)
|
||||
|
||||
type SysFileService struct {
|
||||
queries *db.Queries
|
||||
store *db.Store
|
||||
}
|
||||
|
||||
func NewSysFileService(queries *db.Queries) *SysFileService {
|
||||
func NewSysFileService(store *db.Store) *SysFileService {
|
||||
return &SysFileService{
|
||||
queries: queries,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,19 +41,19 @@ func MakeSavedDir(folder string) (string, error) {
|
||||
return uploadDir, nil
|
||||
}
|
||||
|
||||
func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]db.File, int64, error) {
|
||||
params := db.GetFilesParams{
|
||||
func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.File, int64, error) {
|
||||
params := sqlc.GetFilesParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountFiles(ctx)
|
||||
total, err := s.store.CountFiles(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.GetFiles(ctx, params)
|
||||
list, err := s.store.GetFiles(ctx, params)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -61,7 +62,7 @@ func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
||||
return response.ToFiles(list), total, nil
|
||||
}
|
||||
|
||||
func (s *SysFileService) Upload(ctx context.Context, folder string, file *multipart.FileHeader) (*db.CreateFileRow, error) {
|
||||
func (s *SysFileService) Upload(ctx context.Context, folder string, file *multipart.FileHeader) (*sqlc.CreateFileRow, error) {
|
||||
// 生成文件名
|
||||
fileID, err := gonanoid.New()
|
||||
if err != nil {
|
||||
@@ -98,7 +99,7 @@ func (s *SysFileService) Upload(ctx context.Context, folder string, file *multip
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := db.CreateFileParams{
|
||||
params := sqlc.CreateFileParams{
|
||||
FileName: filename,
|
||||
FilePath: filePath,
|
||||
OriginalName: file.Filename,
|
||||
@@ -107,7 +108,7 @@ func (s *SysFileService) Upload(ctx context.Context, folder string, file *multip
|
||||
FileSize: file.Size,
|
||||
}
|
||||
|
||||
result, err := s.queries.CreateFile(ctx, params)
|
||||
result, err := s.store.CreateFile(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,75 +2,72 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"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"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type SysMenuService struct {
|
||||
pool *pgxpool.Pool
|
||||
queries *db.Queries
|
||||
store *db.Store
|
||||
cache *cache.Caches
|
||||
}
|
||||
|
||||
func NewSysMenuService(queries *db.Queries, pool *pgxpool.Pool) *SysMenuService {
|
||||
func NewSysMenuService(store *db.Store, cache *cache.Caches) *SysMenuService {
|
||||
return &SysMenuService{
|
||||
queries: queries,
|
||||
pool: pool,
|
||||
store: store,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRequest) error {
|
||||
// 开启事务
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
menu := sqlc.CreateSysMenuParams{
|
||||
Name: req.Name,
|
||||
Path: req.Path,
|
||||
Component: req.Component,
|
||||
Type: *req.Type,
|
||||
Status: *req.Status,
|
||||
Hidden: req.Hidden,
|
||||
Sort: req.Sort,
|
||||
Icon: req.Icon,
|
||||
ParentID: req.ParentID,
|
||||
}
|
||||
|
||||
defer func(ctx context.Context) {
|
||||
_ = tx.Rollback(ctx)
|
||||
}(ctx)
|
||||
// 先创建权限
|
||||
permissionId, err := q.CreateSysPermission(ctx, sqlc.CreateSysPermissionParams{
|
||||
Type: int16(enum.PermissionTypeMenu),
|
||||
Code: &req.PermissionCode,
|
||||
})
|
||||
|
||||
q := db.New(tx)
|
||||
// code重复性校验
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
||||
}
|
||||
|
||||
menu := db.CreateSysMenuParams{
|
||||
Name: req.Name,
|
||||
Path: req.Path,
|
||||
Component: req.Component,
|
||||
Type: *req.Type,
|
||||
Status: *req.Status,
|
||||
Hidden: req.Hidden,
|
||||
Sort: req.Sort,
|
||||
Icon: req.Icon,
|
||||
}
|
||||
// 创建菜单
|
||||
menuId, err := q.CreateSysMenu(ctx, menu)
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrSysMenusPathUniqueIdx)
|
||||
}
|
||||
|
||||
permissionId, err := q.CreateSysPermission(ctx, db.CreateSysPermissionParams{
|
||||
Type: int16(enum.PermissionTypeMenu),
|
||||
Code: &req.PermissionCode,
|
||||
// 绑定权限
|
||||
if err = q.CreateSysMenuPermission(ctx, sqlc.CreateSysMenuPermissionParams{
|
||||
MenuID: menuId,
|
||||
PermissionID: permissionId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
||||
}
|
||||
|
||||
menuId, err := q.CreateSysMenu(ctx, menu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = q.CreateSysMenuPermission(ctx, db.CreateSysMenuPermissionParams{
|
||||
MenuID: menuId,
|
||||
PermissionID: permissionId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -78,66 +75,76 @@ func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRe
|
||||
}
|
||||
|
||||
func (s *SysMenuService) Update(ctx context.Context, id int32, req request.UpdateSysMenuRequest) error {
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
// 构造 menu 参数
|
||||
menu := sqlc.UpdateSysMenuParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Path: req.Path,
|
||||
Component: req.Component,
|
||||
Hidden: req.Hidden,
|
||||
Sort: req.Sort,
|
||||
Type: req.Type,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
if req.Icon.Set {
|
||||
menu.UpdateIcon = true
|
||||
if req.Icon.Valid {
|
||||
menu.Icon = &req.Icon.Value
|
||||
}
|
||||
}
|
||||
|
||||
if req.ParentID.Set {
|
||||
menu.UpdateParentID = true
|
||||
if req.ParentID.Valid {
|
||||
menu.ParentID = &req.ParentID.Value
|
||||
}
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
rows, err := q.UpdateSysMenu(ctx, menu)
|
||||
|
||||
// 先判断数据是否存在 在判断path是否重复
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrSysMenusPathUniqueIdx)
|
||||
}
|
||||
|
||||
permission := sqlc.UpdateSysMenuPermissionCodeParams{
|
||||
MenuID: id,
|
||||
Code: req.PermissionCode,
|
||||
}
|
||||
|
||||
if err = q.UpdateSysMenuPermissionCode(ctx, permission); err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
q := db.New(tx)
|
||||
// 清理缓存
|
||||
_ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern)
|
||||
|
||||
// 构造 menu 参数
|
||||
menu := db.UpdateSysMenuParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Path: req.Path,
|
||||
Component: req.Component,
|
||||
Hidden: req.Hidden,
|
||||
Sort: req.Sort,
|
||||
Type: req.Type,
|
||||
Status: req.Status,
|
||||
Icon: req.Icon,
|
||||
}
|
||||
|
||||
if req.ParentID.Set {
|
||||
menu.UpdateParentID = true
|
||||
if req.ParentID.Valid {
|
||||
menu.ParentID = &req.ParentID.Value
|
||||
}
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
rows, err := q.UpdateSysMenu(ctx, menu)
|
||||
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
permission := db.UpdateSysMenuPermissionCodeParams{
|
||||
MenuID: id,
|
||||
Code: req.PermissionCode,
|
||||
}
|
||||
|
||||
if err = q.UpdateSysMenuPermissionCode(ctx, permission); err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
||||
}
|
||||
|
||||
return tx.Commit(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListSysMenusRow, int64, error) {
|
||||
params := db.ListSysMenusParams{
|
||||
func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListSysMenusRow, int64, error) {
|
||||
params := sqlc.ListSysMenusParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountSysMenus(ctx)
|
||||
total, err := s.store.CountSysMenus(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.ListSysMenus(ctx, params)
|
||||
list, err := s.store.ListSysMenus(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -145,34 +152,43 @@ func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *SysMenuService) GetMenus(ctx context.Context) ([]db.GetAllSysMenusRow, error) {
|
||||
return s.queries.GetAllSysMenus(ctx)
|
||||
func (s *SysMenuService) GetMenus(ctx context.Context) ([]sqlc.GetAllSysMenusRow, error) {
|
||||
return s.store.GetAllSysMenus(ctx)
|
||||
}
|
||||
|
||||
func (s *SysMenuService) Delete(ctx context.Context, id int32) error {
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
// 1. 根据菜单id, 找出对应的权限id
|
||||
permissionID, err := q.GetSysPermissionIDBySysMenuID(ctx, id)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrSysMenuNotFound)
|
||||
}
|
||||
// 2. 根据权限id 删除角色权限关联
|
||||
if err = q.DeleteSysRolePermissionByPermissionID(ctx, permissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
// 3. 根据菜单id 删除sys_menu_permission关联表数据
|
||||
if err = q.DeleteSysMenuPermission(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
// 4. 根据权限id删除权限表数据
|
||||
if err = q.DeleteSysPermission(ctx, permissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
// 5. 删除菜单
|
||||
rows, err := q.DeleteSysMenu(ctx, id)
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
rows, err := q.DeleteSysMenu(ctx, id)
|
||||
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = q.DeleteSysPermissionByMenuID(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = q.DeleteSysMenuPermission(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit(ctx)
|
||||
// 清理缓存
|
||||
_ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,41 +2,37 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"server/internal/db"
|
||||
"server/internal/db/sqlc"
|
||||
"server/internal/model/common"
|
||||
"server/internal/model/request"
|
||||
"server/internal/pkg/dberr"
|
||||
"server/internal/pkg/errs"
|
||||
"server/internal/pkg/httputil"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type SysPostService struct {
|
||||
queries *db.Queries
|
||||
pool *pgxpool.Pool
|
||||
store *db.Store
|
||||
}
|
||||
|
||||
func NewSysPostService(queries *db.Queries, pool *pgxpool.Pool) *SysPostService {
|
||||
func NewSysPostService(store *db.Store) *SysPostService {
|
||||
return &SysPostService{
|
||||
queries: queries,
|
||||
pool: pool,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListPostsRow, int64, error) {
|
||||
params := db.ListPostsParams{
|
||||
func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListPostsRow, int64, error) {
|
||||
params := sqlc.ListPostsParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountPosts(ctx)
|
||||
total, err := s.store.CountPosts(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.ListPosts(ctx, params)
|
||||
list, err := s.store.ListPosts(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -49,8 +45,8 @@ func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *SysPostService) FindByID(ctx context.Context, id int32) (*db.GetPostByIdRow, error) {
|
||||
post, err := s.queries.GetPostById(ctx, id)
|
||||
func (s *SysPostService) 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)
|
||||
}
|
||||
@@ -61,103 +57,117 @@ func (s *SysPostService) FindByID(ctx context.Context, id int32) (*db.GetPostByI
|
||||
}
|
||||
|
||||
func (s *SysPostService) Create(ctx context.Context, req request.CreatePostRequest) (int32, error) {
|
||||
// 开启事务
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
postID, err := db.WithTxResult(ctx, s.store, func(q *sqlc.Queries) (int32, error) {
|
||||
params := sqlc.CreatePostParams{
|
||||
Title: req.Title,
|
||||
CoverID: &req.CoverID,
|
||||
Slug: req.Slug,
|
||||
Content: req.Content,
|
||||
Summary: req.Summary,
|
||||
Status: *req.Status,
|
||||
Sort: req.Sort,
|
||||
PublishedAt: req.PublishedAt,
|
||||
}
|
||||
|
||||
// 创建博客
|
||||
postID, err := q.CreatePost(ctx, params)
|
||||
if err != nil {
|
||||
return 0, dberr.MapUniqueViolation(err, dberr.PostSlugKey, errs.ErrSlugAlreadyExists)
|
||||
}
|
||||
|
||||
// 关联分类
|
||||
if err = q.CreatePostCategory(ctx, sqlc.CreatePostCategoryParams{
|
||||
PostID: postID,
|
||||
CategoryID: *req.CategoryID,
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var tagsParams []sqlc.CreatePostTagParams
|
||||
for _, tagID := range req.Tags {
|
||||
tagsParams = append(tagsParams, sqlc.CreatePostTagParams{
|
||||
PostID: postID,
|
||||
TagID: tagID,
|
||||
})
|
||||
}
|
||||
|
||||
if _, err = q.CreatePostTag(ctx, tagsParams); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return postID, nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = tx.Rollback(ctx)
|
||||
}()
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
params := db.CreatePostParams{
|
||||
Title: req.Title,
|
||||
CoverID: req.CoverID,
|
||||
Slug: req.Slug,
|
||||
Content: req.Content,
|
||||
Summary: req.Summary,
|
||||
Status: *req.Status,
|
||||
Sort: req.Sort,
|
||||
PublishedAt: req.PublishedAt,
|
||||
}
|
||||
|
||||
postId, err := q.CreatePost(ctx, params)
|
||||
if err != nil {
|
||||
return 0, dberr.MapUniqueViolation(err, dberr.PostSlugKey, errs.ErrSlugAlreadyExists)
|
||||
}
|
||||
|
||||
if err = q.CreatePostCategory(ctx, db.CreatePostCategoryParams{
|
||||
PostID: postId,
|
||||
CategoryID: *req.CategoryID,
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 提交
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return postId, nil
|
||||
return postID, nil
|
||||
}
|
||||
|
||||
func (s *SysPostService) Update(ctx context.Context, id int32, req request.UpdatePostRequest) error {
|
||||
// 开启事务
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
params := sqlc.UpdatePostParams{
|
||||
Title: req.Title,
|
||||
CoverID: req.CoverID,
|
||||
Slug: req.Slug,
|
||||
Content: req.Content,
|
||||
Summary: req.Summary,
|
||||
Status: req.Status,
|
||||
Sort: req.Sort,
|
||||
PublishedAt: req.PublishedAt,
|
||||
ID: id,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = tx.Rollback(ctx)
|
||||
}()
|
||||
rows, err := q.UpdatePost(ctx, params)
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.PostSlugKey, errs.ErrSlugAlreadyExists)
|
||||
}
|
||||
|
||||
q := db.New(tx)
|
||||
if err = dberr.MapRowsAffected(rows, nil, errs.ErrPostNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := db.UpdatePostParams{
|
||||
Title: req.Title,
|
||||
CoverID: req.CoverID,
|
||||
Slug: req.Slug,
|
||||
Content: req.Content,
|
||||
Summary: req.Summary,
|
||||
Status: req.Status,
|
||||
Sort: req.Sort,
|
||||
PublishedAt: req.PublishedAt,
|
||||
ID: id,
|
||||
}
|
||||
// 先删除分类
|
||||
if err = q.DeletePostCategoryByPostID(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := q.UpdatePost(ctx, params)
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.PostSlugKey, errs.ErrSlugAlreadyExists)
|
||||
}
|
||||
if err = q.CreatePostCategory(ctx, sqlc.CreatePostCategoryParams{
|
||||
PostID: id,
|
||||
CategoryID: *req.CategoryID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = dberr.MapRowsAffected(rows, nil, errs.ErrPostNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
// 先删除标签
|
||||
if err = q.DeletePostTagByPostID(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = q.DeletePostCategory(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
var tagsParams []sqlc.CreatePostTagParams
|
||||
for _, tagID := range req.Tags {
|
||||
tagsParams = append(tagsParams, sqlc.CreatePostTagParams{
|
||||
PostID: id,
|
||||
TagID: tagID,
|
||||
})
|
||||
}
|
||||
|
||||
if err = q.CreatePostCategory(ctx, db.CreatePostCategoryParams{
|
||||
PostID: id,
|
||||
CategoryID: *req.CategoryID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = q.CreatePostTag(ctx, tagsParams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 提交
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SysPostService) Delete(ctx context.Context, id int32) error {
|
||||
rows, err := s.queries.DeletePost(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrPostNotFound)
|
||||
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
err := q.DeletePostCategoryByPostID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := q.DeletePost(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrPostNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,45 +2,56 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"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"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type SysRoleService struct {
|
||||
queries *db.Queries
|
||||
pool *pgxpool.Pool
|
||||
cache *cache.Caches
|
||||
store *db.Store
|
||||
cache *cache.Caches
|
||||
}
|
||||
|
||||
func NewSysRoleService(queries *db.Queries, pool *pgxpool.Pool, cache *cache.Caches) *SysRoleService {
|
||||
func NewSysRoleService(store *db.Store, cache *cache.Caches) *SysRoleService {
|
||||
return &SysRoleService{
|
||||
queries: queries,
|
||||
pool: pool,
|
||||
cache: cache,
|
||||
store: store,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SysRoleService) ListPage(ctx context.Context, p *common.Pagination) ([]db.SysRole, int64, error) {
|
||||
params := db.ListSysRolesParams{
|
||||
// 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.queries.CountSysRoles(ctx)
|
||||
total, err := s.store.CountSysRoles(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.queries.ListSysRoles(ctx, params)
|
||||
list, err := s.store.ListSysRoles(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -48,38 +59,36 @@ func (s *SysRoleService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *SysRoleService) GetRoles(ctx context.Context) ([]db.SysRole, error) {
|
||||
return s.queries.GetAllSysRoles(ctx)
|
||||
func (s *SysRoleService) GetRoles(ctx context.Context) ([]sqlc.SysRole, error) {
|
||||
return s.store.GetAllSysRoles(ctx)
|
||||
}
|
||||
|
||||
func (s *SysRoleService) GetRoleMenus(ctx context.Context, id int32) ([]db.GetSysRoleMenusRow, error) {
|
||||
_, err := s.queries.GetSysRoleByID(ctx, id)
|
||||
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.queries.GetSysRoleMenus(ctx, id)
|
||||
return s.store.GetSysRoleMenus(ctx, id)
|
||||
}
|
||||
|
||||
func (s *SysRoleService) GetRoleApis(ctx context.Context, id int32) ([]db.GetSysRoleApisRow, error) {
|
||||
_, err := s.queries.GetSysRoleByID(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.queries.GetSysRoleApis(ctx, id)
|
||||
return s.store.GetSysRoleApis(ctx, id)
|
||||
}
|
||||
|
||||
func (s *SysRoleService) Create(ctx context.Context, req request.CreateSysRoleRequest) error {
|
||||
params := db.CreateSysRoleParams{
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
params := sqlc.CreateSysRoleParams{
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
Status: *req.Status,
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
|
||||
err := s.queries.CreateSysRole(ctx, params)
|
||||
err := s.store.CreateSysRole(ctx, params)
|
||||
if err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysRoleCodeKey, errs.ErrCodeAlreadyExists)
|
||||
}
|
||||
@@ -87,125 +96,156 @@ func (s *SysRoleService) Create(ctx context.Context, req request.CreateSysRoleRe
|
||||
}
|
||||
|
||||
func (s *SysRoleService) Update(ctx context.Context, id int32, req request.UpdateSysRoleRequest) error {
|
||||
params := db.UpdateSysRoleParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
params := sqlc.UpdateSysRoleParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
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
|
||||
}
|
||||
|
||||
rows, err := s.queries.UpdateSysRole(ctx, params)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound)
|
||||
// 清理缓存 无论启用禁用 启用时也需要清理缓存以刷新权限
|
||||
_ = s.clearCache(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysRoleService) SetRoleMenus(ctx context.Context, roleID int32, req request.SetSysRoleMenusRequest) error {
|
||||
_, err := s.queries.GetSysRoleByID(ctx, roleID)
|
||||
// 先检查角色是否存在
|
||||
_, err := s.store.GetSysRoleByID(ctx, roleID)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
||||
}
|
||||
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
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
|
||||
}
|
||||
|
||||
defer func(ctx context.Context) {
|
||||
_ = tx.Rollback(ctx)
|
||||
}(ctx)
|
||||
// 清理缓存 这里只用清理用户信息
|
||||
_ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern)
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
permissionIds, err := s.queries.GetSysPermissionIdsByMenuIDs(ctx, req.MenuIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := make([]db.CreateSysRolePermissionParams, 0, len(permissionIds))
|
||||
for _, id := range permissionIds {
|
||||
params = append(params, db.CreateSysRolePermissionParams{
|
||||
RoleID: roleID,
|
||||
PermissionID: id,
|
||||
})
|
||||
}
|
||||
|
||||
if err = q.DeleteSysRolePermission(ctx, db.DeleteSysRolePermissionParams{
|
||||
RoleID: roleID,
|
||||
Type: int16(enum.PermissionTypeMenu),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = q.CreateSysRolePermission(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysRoleService) SetRoleApis(ctx context.Context, roleID int32, req request.SetSysRoleApisRequest) error {
|
||||
_, err := s.queries.GetSysRoleByID(ctx, roleID)
|
||||
_, err := s.store.GetSysRoleByID(ctx, roleID)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
||||
}
|
||||
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
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
|
||||
}
|
||||
|
||||
defer func(ctx context.Context) {
|
||||
_ = tx.Rollback(ctx)
|
||||
}(ctx)
|
||||
// 清理缓存 这里只用清理接口权限
|
||||
_ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern)
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
permissionIds, err := s.queries.GetSysPermissionIdsByApiIDs(ctx, req.ApiIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := make([]db.CreateSysRolePermissionParams, 0, len(permissionIds))
|
||||
for _, id := range permissionIds {
|
||||
params = append(params, db.CreateSysRolePermissionParams{
|
||||
RoleID: roleID,
|
||||
PermissionID: id,
|
||||
})
|
||||
}
|
||||
|
||||
if err = q.DeleteSysRolePermission(ctx, db.DeleteSysRolePermissionParams{
|
||||
RoleID: roleID,
|
||||
Type: int16(enum.PermissionTypeApi),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = q.CreateSysRolePermission(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearAllSysUserCache()
|
||||
|
||||
return err
|
||||
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.cache.ClearAllSysUserCache()
|
||||
rows, err := s.queries.DeleteSysRole(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound)
|
||||
_ = s.clearCache(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,165 +2,166 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"errors"
|
||||
"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"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type SysUserService struct {
|
||||
queries *db.Queries
|
||||
pool *pgxpool.Pool
|
||||
jwt *middleware.JWTMiddleware
|
||||
cache *cache.Caches
|
||||
store *db.Store
|
||||
jwt *middleware.JWTMiddleware
|
||||
cache *cache.Caches
|
||||
}
|
||||
|
||||
func NewSysUserService(queries *db.Queries, pool *pgxpool.Pool, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
|
||||
func NewSysUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
|
||||
return &SysUserService{
|
||||
queries: queries,
|
||||
pool: pool,
|
||||
jwt: jwt,
|
||||
cache: cache,
|
||||
store: store,
|
||||
jwt: jwt,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// generatePasswordHash 生成密码哈希
|
||||
func generatePasswordHash(password string) (string, error) {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// clearUserCache
|
||||
func (s *SysUserService) clearUserCache(ctx context.Context, id int32) error {
|
||||
if err := s.cache.Del(ctx, cachekey.UserApiPermissions(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
return string(hashed), nil
|
||||
|
||||
if err := s.cache.Del(ctx, cachekey.UserInfo(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// comparePasswordHash 比较密码哈希
|
||||
func comparePasswordHash(passwordHash, inputPassword string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(inputPassword))
|
||||
}
|
||||
|
||||
func (s *SysUserService) Login(ctx context.Context, req request.LoginRequest) (*response.LoginResponse, error) {
|
||||
user, err := s.queries.GetSysUserByAccount(ctx, req.Account)
|
||||
// clearRefreshToken
|
||||
func (s *SysUserService) clearRefreshToken(ctx context.Context, id int32) error {
|
||||
|
||||
keys, err := s.cache.SMembers(ctx, cachekey.AuthRefreshUser(id))
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidCredentials
|
||||
return err
|
||||
}
|
||||
|
||||
if err = comparePasswordHash(user.PasswordHash, req.Password); err != nil {
|
||||
return nil, errs.ErrInvalidCredentials
|
||||
// 循环删除所有的token
|
||||
for _, key := range keys {
|
||||
if err = s.cache.Del(ctx, cachekey.AuthRefresh(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refreshToken, refreshTokenExp, err := s.jwt.GenerateRefreshToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.LoginResponse{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExp: accessTokenExp,
|
||||
RefreshToken: refreshToken,
|
||||
RefreshTokenExp: refreshTokenExp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) RefreshToken(ctx context.Context, refreshToken string) (*response.LoginResponse, error) {
|
||||
claims, err := s.jwt.ParseRefreshToken(refreshToken)
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidRefreshToken
|
||||
}
|
||||
|
||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(claims.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.LoginResponse{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExp: accessTokenExp,
|
||||
}, nil
|
||||
// 删除反向索引
|
||||
err = s.cache.DelSetMembers(ctx, cachekey.AuthRefreshUser(id))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool) (*response.SysUserInfo, error) {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
return cache.GetOrSetJSON[*response.SysUserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.SysUserInfo, error) {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var (
|
||||
user db.GetSysUserByIDRow
|
||||
roles []db.SysRole
|
||||
menus []db.SysMenu
|
||||
)
|
||||
|
||||
g.Go(func() error {
|
||||
u, err := s.queries.GetSysUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
user = u
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
r, err := s.queries.GetSysUserRoles(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roles = r
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
var (
|
||||
m []db.SysMenu
|
||||
err error
|
||||
user sqlc.GetSysUserByIDRow
|
||||
roles []sqlc.SysRole
|
||||
menus []sqlc.SysMenu
|
||||
permissions []*string
|
||||
)
|
||||
if isAdmin {
|
||||
m, err = s.queries.GetSysAdminMenus(ctx)
|
||||
} else {
|
||||
m, err = s.queries.GetSysUserMenus(ctx, id)
|
||||
|
||||
g.Go(func() error {
|
||||
u, err := s.store.GetSysUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
user = u
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
r, err := s.store.GetSysUserRoles(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roles = r
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
var (
|
||||
p []*string
|
||||
err error
|
||||
)
|
||||
|
||||
if isAdmin {
|
||||
p, err = s.store.GetSysAdminPermissions(ctx)
|
||||
} else {
|
||||
p, err = s.store.GetSysUserPermissions(ctx, id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
permissions = p
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
var (
|
||||
m []sqlc.SysMenu
|
||||
err error
|
||||
)
|
||||
if isAdmin {
|
||||
m, err = s.store.GetSysAdminMenus(ctx)
|
||||
} else {
|
||||
m, err = s.store.GetSysUserMenus(ctx, id)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
menus = m
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
// 如果用户被禁用 返回错误 超管不用管状态
|
||||
if user.ID != 1 && user.Status != 1 {
|
||||
return nil, errs.ErrUserDisabled
|
||||
}
|
||||
menus = m
|
||||
return nil
|
||||
|
||||
// 处理角色
|
||||
userInfo := response.NewSysUserInfo(user, roles, menus, permissions)
|
||||
|
||||
return userInfo, nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 处理角色
|
||||
userInfo := response.NewSysUserInfo(user, roles, menus)
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListSysUsersRow, int64, error) {
|
||||
params := db.ListSysUsersParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
func (s *SysUserService) ListPage(ctx context.Context, p request.SearchSysUserParams) ([]sqlc.ListSysUsersRow, int64, error) {
|
||||
params := sqlc.ListSysUsersParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
Username: p.Username,
|
||||
}
|
||||
|
||||
total, err := s.queries.CountSysUsers(ctx)
|
||||
total, err := s.store.CountSysUsers(ctx, p.Username)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
users, err := s.queries.ListSysUsers(ctx, params)
|
||||
users, err := s.store.ListSysUsers(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -174,14 +175,14 @@ func (s *SysUserService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
||||
return users, total, nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]db.SysRole, error) {
|
||||
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) {
|
||||
// 先查询用户是否存在
|
||||
_, err := s.queries.GetSysUserByID(ctx, id)
|
||||
_, err := s.store.GetSysUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
return s.queries.GetSysUserRoles(ctx, id)
|
||||
return s.store.GetSysUserRoles(ctx, id)
|
||||
}
|
||||
|
||||
func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRequest) error {
|
||||
@@ -190,14 +191,15 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe
|
||||
return err
|
||||
}
|
||||
|
||||
user := db.CreateSysUserParams{
|
||||
user := sqlc.CreateSysUserParams{
|
||||
Account: req.Account,
|
||||
Username: req.Username,
|
||||
PasswordHash: passwordHash,
|
||||
AvatarID: req.AvatarID,
|
||||
Status: *req.Status,
|
||||
}
|
||||
|
||||
if err = s.queries.CreateSysUser(ctx, user); err != nil {
|
||||
if err = s.store.CreateSysUser(ctx, user); err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
|
||||
}
|
||||
|
||||
@@ -205,9 +207,10 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe
|
||||
}
|
||||
|
||||
func (s *SysUserService) Update(ctx context.Context, id int32, req request.UpdateSysUserRequest) error {
|
||||
user := db.UpdateSysUserParams{
|
||||
user := sqlc.UpdateSysUserParams{
|
||||
Username: req.Username,
|
||||
ID: id,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
if req.AvatarID.Set {
|
||||
@@ -217,47 +220,59 @@ func (s *SysUserService) Update(ctx context.Context, id int32, req request.Updat
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := s.queries.UpdateSysUser(ctx, user)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error {
|
||||
// 先查询用户是否存在
|
||||
_, err := s.queries.GetSysUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func(ctx context.Context) {
|
||||
_ = tx.Rollback(ctx)
|
||||
}(ctx)
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
if err = q.ClearSysUserRoles(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, roleID := range req.RoleIDs {
|
||||
if err = q.CreateSysUserRole(ctx, db.CreateSysUserRoleParams{
|
||||
UserID: userID,
|
||||
RoleID: roleID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
rows, err := s.store.UpdateSysUser(ctx, user)
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearSysUserCache(userID)
|
||||
_ = s.clearUserCache(ctx, id)
|
||||
|
||||
// 如果将用户的状态修改为0,则清除用户刷新令牌
|
||||
if req.Status != nil && *req.Status == 0 {
|
||||
_ = s.clearRefreshToken(ctx, id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error {
|
||||
// 先查询用户是否存在
|
||||
_, err := s.store.GetSysUserByID(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 {
|
||||
return err
|
||||
}
|
||||
|
||||
var params []sqlc.CreateSysUserRoleParams
|
||||
|
||||
for _, roleID := range req.RoleIDs {
|
||||
params = append(params, sqlc.CreateSysUserRoleParams{
|
||||
UserID: userID,
|
||||
RoleID: roleID,
|
||||
})
|
||||
}
|
||||
|
||||
// 2. 关联用户角色数据
|
||||
_, err = q.CreateSysUserRole(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 清理缓存
|
||||
_ = s.clearUserCache(ctx, userID)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -268,13 +283,21 @@ func (s *SysUserService) UpdatePassword(ctx context.Context, id int32, req reque
|
||||
return err
|
||||
}
|
||||
|
||||
params := db.UpdateSysUserPasswordParams{
|
||||
params := sqlc.UpdateSysUserPasswordParams{
|
||||
ID: id,
|
||||
PasswordHash: passwordHash,
|
||||
}
|
||||
|
||||
rows, err := s.queries.UpdateSysUserPassword(ctx, params)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
rows, err := s.store.UpdateSysUserPassword(ctx, params)
|
||||
err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 下线当前用户
|
||||
_ = s.clearRefreshToken(ctx, id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) Delete(ctx context.Context, id int32) error {
|
||||
@@ -282,9 +305,28 @@ func (s *SysUserService) Delete(ctx context.Context, id int32) error {
|
||||
return errs.ErrCannotDeleteSuperAdmin
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearSysUserCache(id)
|
||||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
// 清理用户角色关联数据
|
||||
err := q.ClearSysUserRoles(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := s.queries.DeleteSysUser(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
// 删除用户
|
||||
rows, err := q.DeleteSysUser(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, errs.ErrUserNotFound) {
|
||||
// 用户不存在也执行清理缓存操作
|
||||
_ = s.clearUserCache(ctx, id)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 事务执行成功后清理缓存
|
||||
_ = s.clearUserCache(ctx, id)
|
||||
_ = s.clearRefreshToken(ctx, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
87
internal/service/tag.go
Normal file
87
internal/service/tag.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"server/internal/db"
|
||||
"server/internal/db/sqlc"
|
||||
"server/internal/model/common"
|
||||
"server/internal/model/request"
|
||||
"server/internal/pkg/dberr"
|
||||
"server/internal/pkg/errs"
|
||||
)
|
||||
|
||||
type TagService struct {
|
||||
store *db.Store
|
||||
}
|
||||
|
||||
func NewTagService(store *db.Store) *TagService {
|
||||
return &TagService{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TagService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.Tag, int64, error) {
|
||||
params := sqlc.ListTagsParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
}
|
||||
|
||||
total, err := s.store.CountTags(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list, err := s.store.ListTags(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (s *TagService) ListAll(ctx context.Context) ([]sqlc.Tag, error) {
|
||||
return s.store.ListAllTags(ctx)
|
||||
}
|
||||
|
||||
func (s *TagService) Create(ctx context.Context, req request.CreateTagRequest) error {
|
||||
params := sqlc.CreateTagParams{
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
|
||||
err := s.store.CreateTag(ctx, params)
|
||||
return dberr.MapUniqueViolation(err, dberr.TagCodeKey, errs.ErrTagCodeAlreadyExists)
|
||||
}
|
||||
|
||||
func (s *TagService) Update(ctx context.Context, id int32, req request.UpdateTagRequest) error {
|
||||
params := sqlc.UpdateTagParams{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Code: req.Code,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
rows, err := s.store.UpdateTag(ctx, params)
|
||||
// 先判断数据条目是否存在
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrTagNotFound); err != nil {
|
||||
// 再判断code是否重复
|
||||
return dberr.MapUniqueViolation(err, dberr.TagCodeKey, errs.ErrTagCodeAlreadyExists)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TagService) Delete(ctx context.Context, id int32) error {
|
||||
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
// 删除中间表数据
|
||||
err := s.store.DeletePostTagByTagID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除标签数据
|
||||
rows, err := s.store.DeleteTag(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrTagNotFound)
|
||||
})
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user