package services import ( "errors" "fmt" "strings" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" "git.beifan.cn/trace-system/backend-go/database" "git.beifan.cn/trace-system/backend-go/models" ) // UsersService 用户管理服务 type UsersService struct{} func toUserDTO(user models.User) models.UserDTO { return models.UserDTO{ ID: user.ID, Username: user.Username, Name: user.Name, Email: user.Email, Phone: user.Phone, EmployeeNo: user.EmployeeNo, Position: user.Position, Role: user.Role, CreatedAt: user.CreatedAt, EmployeeSerials: user.EmployeeSerials, } } func hasBackendAccess(role string) bool { return role == "admin" || role == "technician" } func isValidEmployeeRole(role string) bool { return role == "admin" || role == "technician" || role == "employee" } // Create 创建用户 func (s *UsersService) Create(dto models.CreateUserDTO) (*models.UserDTO, error) { name := strings.TrimSpace(dto.Name) phone := strings.TrimSpace(dto.Phone) employeeNo := strings.TrimSpace(dto.EmployeeNo) position := strings.TrimSpace(dto.Position) role := strings.TrimSpace(dto.Role) username := strings.TrimSpace(dto.Username) if username == "" { username = employeeNo } if name == "" { return nil, errors.New("姓名不能为空") } if phone == "" { return nil, errors.New("电话不能为空") } if employeeNo == "" { return nil, errors.New("工号不能为空") } if position == "" { return nil, errors.New("岗位不能为空") } if !isValidEmployeeRole(role) { return nil, errors.New("角色不正确") } if hasBackendAccess(role) && len(dto.Password) < 6 { return nil, errors.New("管理员和技术员必须设置至少 6 位初始密码") } var existing models.User err := database.DB.Where("username = ?", username).First(&existing).Error if err == nil { return nil, errors.New("用户名已存在") } if !errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("查询用户失败: %w", err) } err = database.DB.Where("employee_no = ?", employeeNo).First(&existing).Error if err == nil { return nil, errors.New("工号已存在") } if !errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("查询员工工号失败: %w", err) } hashed := "" if hasBackendAccess(role) { hashBytes, err := bcrypt.GenerateFromPassword([]byte(dto.Password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("密码加密失败: %w", err) } hashed = string(hashBytes) } user := models.User{ Username: username, Password: hashed, Name: name, Email: dto.Email, Phone: phone, EmployeeNo: employeeNo, Position: position, Role: role, } err = database.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&user).Error; err != nil { return fmt.Errorf("创建员工失败: %w", err) } serialService := EmployeeSerialsService{} if _, err := serialService.GenerateForEmployee(tx, user, user.ID, ""); err != nil { return err } return tx.Preload("EmployeeSerials").First(&user, user.ID).Error }) if err != nil { return nil, err } dtoOut := toUserDTO(user) return &dtoOut, nil } // FindAll 分页 + 按角色过滤 func (s *UsersService) FindAll(page int, limit int, role string, search string) ([]models.UserDTO, int, int, error) { var users []models.User var total int64 db := database.DB.Model(&models.User{}).Preload("EmployeeSerials") if role != "" { db = db.Where("role = ?", role) } if search != "" { pattern := "%" + search + "%" db = db.Where("username LIKE ? OR name LIKE ? OR email LIKE ? OR phone LIKE ? OR employee_no LIKE ? OR position LIKE ?", pattern, pattern, pattern, pattern, pattern, pattern) } if err := db.Count(&total).Error; err != nil { return nil, 0, 0, fmt.Errorf("统计用户数失败: %w", err) } offset := (page - 1) * limit if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&users).Error; err != nil { return nil, 0, 0, fmt.Errorf("查询用户列表失败: %w", err) } result := make([]models.UserDTO, 0, len(users)) for _, u := range users { result = append(result, toUserDTO(u)) } totalPages := (int(total) + limit - 1) / limit return result, int(total), totalPages, nil } // FindAssignable 获取可分配的用户(admin + technician),用于售后工单分配 func (s *UsersService) FindAssignable() ([]models.UserDTO, error) { var users []models.User if err := database.DB.Where("role IN ?", []string{"admin", "technician"}). Order("role DESC, created_at ASC").Find(&users).Error; err != nil { return nil, fmt.Errorf("查询可分配用户失败: %w", err) } result := make([]models.UserDTO, 0, len(users)) for _, u := range users { result = append(result, toUserDTO(u)) } return result, nil } // Update 更新用户信息(不含密码) func (s *UsersService) Update(userId uint, dto models.UpdateUserDTO, currentUserId uint) (*models.UserDTO, error) { var user models.User if err := database.DB.First(&user, userId).Error; err != nil { return nil, errors.New("用户不存在") } if dto.Name != "" { user.Name = strings.TrimSpace(dto.Name) } if dto.Email != "" { user.Email = dto.Email } if dto.Phone != "" { user.Phone = strings.TrimSpace(dto.Phone) } if dto.EmployeeNo != "" { employeeNo := strings.TrimSpace(dto.EmployeeNo) var existing models.User err := database.DB.Where("employee_no = ? AND id <> ?", employeeNo, userId).First(&existing).Error if err == nil { return nil, errors.New("工号已存在") } if !errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("查询员工工号失败: %w", err) } user.EmployeeNo = employeeNo user.Username = employeeNo } if dto.Position != "" { user.Position = strings.TrimSpace(dto.Position) } if dto.Role != "" { if !isValidEmployeeRole(dto.Role) { return nil, errors.New("角色不正确") } // 防止管理员把自己降级 if user.ID == currentUserId && user.Role == "admin" && dto.Role != "admin" { return nil, errors.New("不能修改自己的管理员角色") } user.Role = dto.Role } if err := database.DB.Save(&user).Error; err != nil { return nil, fmt.Errorf("更新用户失败: %w", err) } dtoOut := toUserDTO(user) return &dtoOut, nil } // ResetPassword 管理员重置用户密码 func (s *UsersService) ResetPassword(userId uint, newPassword string) error { var user models.User if err := database.DB.First(&user, userId).Error; err != nil { return errors.New("用户不存在") } if !hasBackendAccess(user.Role) { return errors.New("员工无后台登录权限,不能设置密码") } hashed, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { return fmt.Errorf("密码加密失败: %w", err) } user.Password = string(hashed) if err := database.DB.Save(&user).Error; err != nil { return fmt.Errorf("重置密码失败: %w", err) } return nil } // Delete 删除用户 func (s *UsersService) Delete(userId uint, currentUserId uint) error { if userId == currentUserId { return errors.New("不能删除自己") } var user models.User if err := database.DB.First(&user, userId).Error; err != nil { return errors.New("用户不存在") } if user.Role == "admin" { // 防止删除最后一个 admin var adminCount int64 database.DB.Model(&models.User{}).Where("role = ?", "admin").Count(&adminCount) if adminCount <= 1 { return errors.New("不能删除最后一个管理员") } } if err := database.DB.Delete(&user).Error; err != nil { return fmt.Errorf("删除用户失败: %w", err) } return nil }