package service import ( "fmt" "os" "path/filepath" "strings" "gorm.io/gorm" "filestoragesystem/internal/config" "filestoragesystem/internal/model" "filestoragesystem/internal/repository" "filestoragesystem/internal/utils" "filestoragesystem/pkg/apperr" ) // UserService 用户管理服务(个人资料 + 管理员用户管理) type UserService struct { cfg *config.Config userRepo *repository.UserRepo roleRepo *repository.RoleRepo settingRepo *repository.SettingRepo opLogRepo *repository.OpLogRepo fileRepo *repository.FileRepo projectRepo *repository.ProjectRepo } // GetProfile 获取个人资料 func (s *UserService) GetProfile(userID uint) (*model.User, error) { u, err := s.userRepo.FindByID(userID) if err != nil { return nil, apperr.ErrNotFound } return u, nil } // UpdateProfile 更新个人资料(邮箱) func (s *UserService) UpdateProfile(userID uint, email string) error { email = strings.TrimSpace(strings.ToLower(email)) if email != "" && !utils.IsEmail(email) { return fmt.Errorf("邮箱格式不正确") } if email != "" { if other, err := s.userRepo.FindByEmail(email); err == nil && other.ID != userID { return apperr.ErrUserExists } return s.userRepo.UpdateFields(userID, map[string]interface{}{"email": email}) } return nil } // ChangePassword 修改自己的密码 func (s *UserService) ChangePassword(userID uint, oldPwd, newPwd string) error { if !utils.IsValidPassword(newPwd) { return fmt.Errorf("新密码至少8位且需包含字母和数字") } u, err := s.userRepo.FindByID(userID) if err != nil { return apperr.ErrNotFound } if !utils.CheckPassword(u.PasswordHash, oldPwd) { return fmt.Errorf("原密码错误") } hash, err := utils.HashPassword(newPwd) if err != nil { return err } return s.userRepo.UpdateFields(userID, map[string]interface{}{"password_hash": hash}) } // ---- 管理员操作 ---- // AdminListUsers 用户列表 func (s *UserService) AdminListUsers(keyword, roleID, status string, page, pageSize int) ([]model.User, int64, error) { return s.userRepo.List(keyword, roleID, status, page, pageSize) } // AdminGetUser 用户详情 func (s *UserService) AdminGetUser(id uint) (*model.User, error) { u, err := s.userRepo.FindByID(id) if err != nil { return nil, apperr.ErrNotFound } return u, nil } // AdminCreateUser 管理员创建用户 func (s *UserService) AdminCreateUser(username, email, password string, roleID uint, storageLimit int64) (*model.User, error) { username = strings.TrimSpace(username) email = strings.TrimSpace(strings.ToLower(email)) if len(username) < 3 { return nil, fmt.Errorf("用户名至少3个字符") } if !utils.IsEmail(email) { return nil, fmt.Errorf("邮箱格式不正确") } if !utils.IsValidPassword(password) { return nil, fmt.Errorf("密码至少8位且需包含字母和数字") } if _, err := s.userRepo.FindByUsername(username); err == nil { return nil, apperr.ErrUserExists } if _, err := s.userRepo.FindByEmail(email); err == nil { return nil, apperr.ErrUserExists } role, err := s.roleRepo.FindByID(roleID) if err != nil { return nil, fmt.Errorf("角色不存在") } if storageLimit <= 0 { storageLimit = 10737418240 if v, err := s.settingRepo.GetValue("default_storage_limit"); err == nil { var n int64 if _, e := fmt.Sscanf(v, "%d", &n); e == nil && n > 0 { storageLimit = n } } } hash, err := utils.HashPassword(password) if err != nil { return nil, err } u := &model.User{ Username: username, Email: email, PasswordHash: hash, RoleID: role.ID, Status: 1, StorageLimit: storageLimit, } if err := s.userRepo.Create(u); err != nil { return nil, err } utils.Info("user", "管理员创建用户: %s", username) return u, nil } // AdminUpdateUser 编辑用户(邮箱/角色/配额) func (s *UserService) AdminUpdateUser(id uint, email string, roleID uint, storageLimit int64) (*model.User, error) { u, err := s.userRepo.FindByID(id) if err != nil { return nil, apperr.ErrNotFound } updates := map[string]interface{}{} if email = strings.TrimSpace(strings.ToLower(email)); email != "" { if !utils.IsEmail(email) { return nil, fmt.Errorf("邮箱格式不正确") } if other, err := s.userRepo.FindByEmail(email); err == nil && other.ID != id { return nil, apperr.ErrUserExists } updates["email"] = email } if roleID > 0 { role, err := s.roleRepo.FindByID(roleID) if err != nil { return nil, fmt.Errorf("角色不存在") } // 防止移除最后一个超管 if u.Role != nil && u.Role.Code == "super_admin" && role.Code != "super_admin" { if n, _ := s.roleRepo.CountUsersByRole(u.RoleID); n <= 1 { return nil, fmt.Errorf("系统至少需要保留一个超级管理员") } } updates["role_id"] = role.ID } if storageLimit > 0 { updates["storage_limit"] = storageLimit } if len(updates) == 0 { return u, nil } if err := s.userRepo.UpdateFields(id, updates); err != nil { return nil, err } return s.userRepo.FindByID(id) } // AdminSetUserStatus 启用/禁用用户 func (s *UserService) AdminSetUserStatus(id uint, status int8) error { u, err := s.userRepo.FindByID(id) if err != nil { return apperr.ErrNotFound } if u.Role != nil && u.Role.Code == "super_admin" && status == 0 { if n, _ := s.roleRepo.CountUsersByRole(u.RoleID); n <= 1 { return fmt.Errorf("不能禁用最后一个超级管理员") } } return s.userRepo.UpdateFields(id, map[string]interface{}{"status": status}) } // AdminAssignRole 修改用户角色 func (s *UserService) AdminAssignRole(id, roleID uint) error { role, err := s.roleRepo.FindByID(roleID) if err != nil { return fmt.Errorf("角色不存在") } u, err := s.userRepo.FindByID(id) if err != nil { return apperr.ErrNotFound } if u.Role != nil && u.Role.Code == "super_admin" && role.Code != "super_admin" { if n, _ := s.roleRepo.CountUsersByRole(u.RoleID); n <= 1 { return fmt.Errorf("系统至少需要保留一个超级管理员") } } return s.userRepo.UpdateFields(id, map[string]interface{}{"role_id": role.ID}) } // AdminResetPassword 重置用户密码 func (s *UserService) AdminResetPassword(id uint, newPassword string) error { if !utils.IsValidPassword(newPassword) { return fmt.Errorf("密码至少8位且需包含字母和数字") } hash, err := utils.HashPassword(newPassword) if err != nil { return err } return s.userRepo.UpdateFields(id, map[string]interface{}{"password_hash": hash}) } // AdminDeleteUser 删除用户及其全部数据 func (s *UserService) AdminDeleteUser(id uint) error { u, err := s.userRepo.FindByID(id) if err != nil { if err == gorm.ErrRecordNotFound { return apperr.ErrNotFound } return err } if u.Role != nil && u.Role.Code == "super_admin" { if n, _ := s.roleRepo.CountUsersByRole(u.RoleID); n <= 1 { return fmt.Errorf("不能删除最后一个超级管理员") } } // 删除物理文件 files, _ := s.fileRepo.ListByUser(id) seen := make(map[string]bool) for _, f := range files { if f.StoredPath != "" && !seen[f.StoredPath] { seen[f.StoredPath] = true _ = os.Remove(filepath.Join(s.cfg.Storage.Root, f.StoredPath)) } } // 级联清理(文件/项目/密钥由外键无级联,手动删除) _ = s.fileRepo.DB.Exec("DELETE FROM files WHERE user_id = ?", id).Error _ = s.fileRepo.DB.Exec("DELETE FROM temp_links WHERE user_id = ?", id).Error _ = s.fileRepo.DB.Exec("DELETE FROM projects WHERE user_id = ?", id).Error _ = s.fileRepo.DB.Exec("DELETE FROM api_keys WHERE user_id = ?", id).Error _ = s.fileRepo.DB.Exec("DELETE FROM webhooks WHERE user_id = ?", id).Error _ = s.fileRepo.DB.Exec("DELETE FROM traffic_logs WHERE user_id = ?", id).Error if err := s.userRepo.Delete(id); err != nil { return err } utils.Info("user", "管理员删除用户: %s", u.Username) return nil }