// Package service 提供 admin 模块的核心业务逻辑 package service import ( "context" "errors" "math" adminDAO "github.com/echochat/backend/app/admin/dao" authDAO "github.com/echochat/backend/app/auth/dao" "github.com/echochat/backend/app/auth/model" "github.com/echochat/backend/app/constants" "github.com/echochat/backend/app/dto" "github.com/echochat/backend/pkg/logs" "github.com/echochat/backend/pkg/utils" "go.uber.org/zap" "gorm.io/gorm" ) var ( ErrUserNotFound = errors.New("用户不存在") ErrUserExists = errors.New("用户名或邮箱已被注册") ErrInvalidStatus = errors.New("无效的用户状态") ErrInvalidRole = errors.New("无效的角色代码") ErrCannotDisableSelf = errors.New("不能禁用自己的账号") ErrInsufficientPermission = errors.New("权限不足,无法操作更高等级的用户") ErrCannotAssignHigherRole = errors.New("不能分配高于自身等级的角色") ) // UserManageService 管理端用户管理服务 type UserManageService struct { userManageDAO *adminDAO.UserManageDAO userDAO *authDAO.UserDAO roleDAO *authDAO.RoleDAO } // NewUserManageService 创建用户管理服务实例 func NewUserManageService( userManageDAO *adminDAO.UserManageDAO, userDAO *authDAO.UserDAO, roleDAO *authDAO.RoleDAO, ) *UserManageService { return &UserManageService{ userManageDAO: userManageDAO, userDAO: userDAO, roleDAO: roleDAO, } } // GetUserList 获取用户列表(分页 + 搜索 + 筛选) func (s *UserManageService) GetUserList(ctx context.Context, req *dto.UserListRequest) (*dto.UserListResponse, error) { funcName := "service.user_manage_service.GetUserList" logs.Info(ctx, funcName, "获取用户列表") users, total, err := s.userManageDAO.ListUsers(ctx, req) if err != nil { return nil, err } list := make([]dto.AdminUserInfo, 0, len(users)) for _, user := range users { roles, roleErr := s.roleDAO.GetUserRoles(ctx, user.ID) if roleErr != nil { logs.Warn(ctx, funcName, "获取用户角色失败,降级为空角色列表", zap.Int64("user_id", user.ID), zap.Error(roleErr)) roles = []model.Role{} } list = append(list, *s.buildAdminUserInfo(&user, roles)) } return &dto.UserListResponse{ Total: total, List: list, }, nil } // GetUserDetail 获取单个用户详情(含角色信息) func (s *UserManageService) GetUserDetail(ctx context.Context, userID int64) (*dto.AdminUserInfo, error) { funcName := "service.user_manage_service.GetUserDetail" logs.Info(ctx, funcName, "获取用户详情", zap.Int64("user_id", userID)) user, err := s.userDAO.FindByID(ctx, userID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrUserNotFound } return nil, err } roles, roleErr := s.roleDAO.GetUserRoles(ctx, user.ID) if roleErr != nil { logs.Warn(ctx, funcName, "获取用户角色失败,降级为空角色列表", zap.Int64("user_id", user.ID), zap.Error(roleErr)) roles = []model.Role{} } return s.buildAdminUserInfo(user, roles), nil } // UpdateUserStatus 启用/禁用用户 // adminUserID 用于防止管理员禁用自己;同时校验操作者权限等级必须高于目标用户 func (s *UserManageService) UpdateUserStatus(ctx context.Context, userID int64, status int, adminUserID int64) error { funcName := "service.user_manage_service.UpdateUserStatus" logs.Info(ctx, funcName, "更新用户状态", zap.Int64("target_user_id", userID), zap.Int("status", status), zap.Int64("admin_user_id", adminUserID), ) if userID == adminUserID { return ErrCannotDisableSelf } if status != constants.UserStatusActive && status != constants.UserStatusDisabled { return ErrInvalidStatus } _, err := s.userDAO.FindByID(ctx, userID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrUserNotFound } return err } if err := s.checkPermissionLevel(ctx, adminUserID, userID); err != nil { return err } if err := s.userManageDAO.UpdateUserStatus(ctx, userID, status); err != nil { return err } logs.Info(ctx, funcName, "用户状态更新成功", zap.Int64("user_id", userID), zap.Int("new_status", status), ) return nil } // SetUserRoles 批量设置用户角色(事务内先清后设) // adminUserID 用于校验操作者等级,防止越权分配高等级角色 func (s *UserManageService) SetUserRoles(ctx context.Context, userID int64, roleCodes []string, adminUserID int64) error { funcName := "service.user_manage_service.SetUserRoles" logs.Info(ctx, funcName, "设置用户角色", zap.Int64("target_user_id", userID), zap.Strings("role_codes", roleCodes), zap.Int64("admin_user_id", adminUserID), ) _, err := s.userDAO.FindByID(ctx, userID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrUserNotFound } return err } if err := s.checkPermissionLevel(ctx, adminUserID, userID); err != nil { return err } roles, err := s.roleDAO.FindByCodeList(ctx, roleCodes) if err != nil { return err } if len(roles) != len(roleCodes) { return ErrInvalidRole } adminLevel, err := s.roleDAO.GetUserMaxLevel(ctx, adminUserID) if err != nil { return err } for _, r := range roles { if r.Level <= adminLevel { logs.Warn(ctx, funcName, "尝试分配高于自身等级的角色", zap.String("role_code", r.Code), zap.Int("role_level", r.Level), zap.Int("admin_level", adminLevel), ) return ErrCannotAssignHigherRole } } roleIDs := make([]int, 0, len(roles)) for _, r := range roles { roleIDs = append(roleIDs, r.ID) } if err := s.roleDAO.SetUserRoles(ctx, userID, roleIDs); err != nil { return err } logs.Info(ctx, funcName, "用户角色设置成功", zap.Int64("user_id", userID), zap.Strings("role_codes", roleCodes), ) return nil } // GetAllRoles 获取所有角色列表 func (s *UserManageService) GetAllRoles(ctx context.Context) ([]dto.RoleInfo, error) { funcName := "service.user_manage_service.GetAllRoles" logs.Info(ctx, funcName, "获取所有角色列表") roles, err := s.roleDAO.GetAllRoles(ctx) if err != nil { return nil, err } result := make([]dto.RoleInfo, 0, len(roles)) for _, r := range roles { result = append(result, dto.RoleInfo{ Code: r.Code, Name: r.Name, Level: r.Level, }) } return result, nil } // checkPermissionLevel 校验操作者的权限等级是否高于目标用户 // 操作者 level 必须 < 目标用户 level(数值更小 = 权限更高) func (s *UserManageService) checkPermissionLevel(ctx context.Context, adminUserID, targetUserID int64) error { funcName := "service.user_manage_service.checkPermissionLevel" adminLevel, err := s.roleDAO.GetUserMaxLevel(ctx, adminUserID) if err != nil { logs.Error(ctx, funcName, "获取操作者权限等级失败", zap.Error(err)) return err } targetLevel, err := s.roleDAO.GetUserMaxLevel(ctx, targetUserID) if err != nil { logs.Error(ctx, funcName, "获取目标用户权限等级失败", zap.Error(err)) return err } if adminLevel >= targetLevel { logs.Warn(ctx, funcName, "权限不足", zap.Int64("admin_user_id", adminUserID), zap.Int("admin_level", adminLevel), zap.Int64("target_user_id", targetUserID), zap.Int("target_level", targetLevel), ) return ErrInsufficientPermission } return nil } // CreateUser 管理员手动创建用户 // adminUserID 用于校验操作者权限等级,防止越权分配高等级角色 func (s *UserManageService) CreateUser(ctx context.Context, req *dto.AdminCreateUserRequest, adminUserID int64) (*dto.AdminUserInfo, error) { funcName := "service.user_manage_service.CreateUser" logs.Info(ctx, funcName, "管理员创建用户", zap.String("username", req.Username), zap.String("email", logs.MaskEmail(req.Email)), zap.Int64("admin_user_id", adminUserID), ) existing, findErr := s.userDAO.FindByUsername(ctx, req.Username) if findErr != nil && !errors.Is(findErr, gorm.ErrRecordNotFound) { return nil, findErr } if existing != nil { return nil, ErrUserExists } existing, findErr = s.userDAO.FindByEmail(ctx, req.Email) if findErr != nil && !errors.Is(findErr, gorm.ErrRecordNotFound) { return nil, findErr } if existing != nil { return nil, ErrUserExists } roleCode := req.RoleCode if roleCode == "" { roleCode = constants.RoleUser } role, roleErr := s.roleDAO.FindByCode(ctx, roleCode) if roleErr != nil { return nil, ErrInvalidRole } adminLevel, err := s.roleDAO.GetUserMaxLevel(ctx, adminUserID) if err != nil { return nil, err } if role.Level <= adminLevel { logs.Warn(ctx, funcName, "尝试为新用户分配高于自身等级的角色", zap.String("role_code", roleCode), zap.Int("role_level", role.Level), zap.Int("admin_level", adminLevel), ) return nil, ErrCannotAssignHigherRole } hashedPassword, err := utils.HashPassword(req.Password) if err != nil { return nil, err } nickname := req.Nickname if nickname == "" { nickname = req.Username } user := &model.User{ Username: req.Username, Email: req.Email, PasswordHash: hashedPassword, Nickname: nickname, Status: constants.UserStatusActive, } if err := s.userDAO.Create(ctx, user); err != nil { return nil, err } if assignErr := s.roleDAO.AssignRole(ctx, user.ID, role.ID); assignErr != nil { logs.Warn(ctx, funcName, "角色分配失败", zap.Int64("user_id", user.ID), zap.Error(assignErr)) } userRoles, rolesErr := s.roleDAO.GetUserRoles(ctx, user.ID) if rolesErr != nil { logs.Warn(ctx, funcName, "获取新建用户角色失败", zap.Int64("user_id", user.ID), zap.Error(rolesErr)) userRoles = []model.Role{} } logs.Info(ctx, funcName, "管理员创建用户成功", zap.Int64("user_id", user.ID)) return s.buildAdminUserInfo(user, userRoles), nil } // buildAdminUserInfo 从 model.User + []model.Role 构建 dto.AdminUserInfo func (s *UserManageService) buildAdminUserInfo(user *model.User, roles []model.Role) *dto.AdminUserInfo { roleInfos := make([]dto.RoleInfo, 0, len(roles)) minLevel := math.MaxInt32 for _, r := range roles { roleInfos = append(roleInfos, dto.RoleInfo{ Code: r.Code, Name: r.Name, Level: r.Level, }) if r.Level < minLevel { minLevel = r.Level } } info := &dto.AdminUserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, Nickname: user.Nickname, Avatar: user.Avatar, Gender: user.Gender, Status: user.Status, StatusText: constants.UserStatusMap[user.Status], Roles: roleInfos, MaxLevel: minLevel, CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"), UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"), } if user.Phone != nil { info.Phone = *user.Phone } if user.LastLoginAt != nil { info.LastLoginAt = user.LastLoginAt.Format("2006-01-02 15:04:05") } if user.LastLoginIP != nil { info.LastLoginIP = *user.LastLoginIP } return info }