Files
blog-server/internal/service/admin/user.go

332 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package admin
import (
"context"
"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"
"golang.org/x/sync/errgroup"
)
type UserService struct {
store *db.Store
jwt *middleware.JWTMiddleware
cache *cache.Caches
}
func NewUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *UserService {
return &UserService{
store: store,
jwt: jwt,
cache: cache,
}
}
// clearUserCache
func (s *UserService) clearUserCache(ctx context.Context, id int32) error {
if err := s.cache.Del(ctx, cachekey.UserApiPermissions(id)); err != nil {
return err
}
if err := s.cache.Del(ctx, cachekey.UserInfo(id)); err != nil {
return err
}
return nil
}
// clearRefreshToken
func (s *UserService) clearRefreshToken(ctx context.Context, id int32) error {
keys, err := s.cache.SMembers(ctx, cachekey.AuthRefreshUser(id))
if err != nil {
return err
}
// 循环删除所有的token
for _, key := range keys {
if err = s.cache.Del(ctx, cachekey.AuthRefresh(key)); err != nil {
return err
}
}
// 删除反向索引
err = s.cache.DelSetMembers(ctx, cachekey.AuthRefreshUser(id))
return err
}
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.GetUserByIDRow
roles []sqlc.SysRole
menus []sqlc.SysMenu
permissions []*string
)
g.Go(func() error {
u, err := s.store.GetUserByID(ctx, id)
if err != nil {
return dberr.MapNoRows(err, errs.ErrUserNotFound)
}
user = u
return nil
})
g.Go(func() error {
r, err := s.store.ListUserRoles(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.ListAdminPermissionCodes(ctx)
} else {
p, err = s.store.ListUserPermissionCodes(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.ListAdminMenus(ctx)
} else {
m, err = s.store.ListUserMenus(ctx, id)
}
if err != nil {
return err
}
menus = m
return nil
})
if err := g.Wait(); err != nil {
return nil, err
}
// 如果用户被禁用 返回错误 超管不用管状态
if user.ID != 1 && user.Status != 1 {
return nil, errs.ErrUserDisabled
}
// 处理角色
userInfo := response.NewUserInfo(user, roles, menus, permissions)
return userInfo, nil
})
}
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.CountUsers(ctx, p.Username)
if err != nil {
return nil, err
}
list, err := s.store.ListUsers(ctx, params)
if err != nil {
return nil, err
}
return &common.PageResult[sqlc.ListUsersRow]{
List: list,
Total: total,
}, nil
}
func (s *UserService) ListRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) {
// 先查询用户是否存在
_, err := s.store.GetUserByID(ctx, id)
if err != nil {
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
}
return s.store.ListUserRoles(ctx, id)
}
func (s *UserService) Create(ctx context.Context, req request.CreateUserRequest) error {
passwordHash, err := generatePasswordHash(req.Password)
if err != nil {
return err
}
user := sqlc.CreateUserParams{
Account: req.Account,
Username: req.Username,
PasswordHash: passwordHash,
AvatarID: req.AvatarID,
Status: *req.Status,
}
if err = s.store.CreateUser(ctx, user); err != nil {
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
}
return nil
}
func (s *UserService) Update(ctx context.Context, id int32, req request.UpdateUserRequest) error {
user := sqlc.UpdateUserParams{
Username: req.Username,
ID: id,
Status: req.Status,
}
if req.AvatarID.Set {
user.UpdateAvatarID = true
if req.AvatarID.Valid {
user.AvatarID = &req.AvatarID.Value
}
}
rows, err := s.store.UpdateUser(ctx, user)
if err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound); err != nil {
return err
}
// 清理缓存
_ = s.clearUserCache(ctx, id)
// 如果将用户的状态修改为0则清除用户刷新令牌
if req.Status != nil && *req.Status == 0 {
_ = s.clearRefreshToken(ctx, id)
}
return nil
}
func (s *UserService) SetRoles(ctx context.Context, userID int32, req request.SetUserRolesRequest) error {
// 先查询用户是否存在
_, 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.ClearUserRoles(ctx, userID); err != nil {
return err
}
var params []sqlc.CreateUserRoleParams
for _, roleID := range req.RoleIDs {
params = append(params, sqlc.CreateUserRoleParams{
UserID: userID,
RoleID: roleID,
})
}
// 2. 关联用户角色数据
_, err = q.CreateUserRole(ctx, params)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// 3. 清理缓存
_ = s.clearUserCache(ctx, userID)
return nil
}
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.UpdateUserPasswordParams{
ID: id,
PasswordHash: passwordHash,
}
rows, err := s.store.UpdateUserPassword(ctx, params)
err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
if err != nil {
return err
}
// 下线当前用户
_ = s.clearRefreshToken(ctx, id)
return nil
}
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.ClearUserRoles(ctx, id)
if err != nil {
return err
}
// 删除用户
rows, err := q.DeleteUser(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
}