Files
2026-08-23 00:48:10 +08:00

259 lines
7.7 KiB
Go

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
}