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 }