259 lines
7.7 KiB
Go
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
|
|
}
|