package service import ( "errors" "fmt" "time" "licserver/internal/model" "licserver/internal/utils" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" "github.com/golang-jwt/jwt/v5" "github.com/mojocn/base64Captcha" ) type UserService struct { db *gorm.DB config *utils.Config captchaService *CaptchaService } type UserProfile struct { ID uint `json:"id"` Username string `json:"username"` Email string `json:"email"` Role string `json:"role"` } // LoginInput 登录输入 type LoginInput struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` CaptchaId string `json:"captchaId" binding:"required"` Captcha string `json:"captcha" binding:"required"` } func NewUserService(db *gorm.DB, config *utils.Config) *UserService { return &UserService{ db: db, config: config, captchaService: NewCaptchaService(db, &config.Email), } } func (s *UserService) Register(username, password, email, captcha string) error { // 验证验证码 if err := s.captchaService.VerifyCaptcha(email, "register", captcha); err != nil { return err } // 检查用户名是否已存在 var count int64 s.db.Model(&model.User{}).Where("username = ?", username).Count(&count) if count > 0 { return errors.New("用户名已存在") } // 检查邮箱是否已存在 s.db.Model(&model.User{}).Where("email = ?", email).Count(&count) if count > 0 { return errors.New("邮箱已被注册") } // 原有的注册逻辑 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return err } user := model.User{ Username: username, Password: string(hashedPassword), Email: email, Role: "user", } return s.db.Create(&user).Error } func (s *UserService) Login(username, password string) (string, error) { var user model.User if err := s.db.Where("username = ?", username).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return "", errors.New("用户不存在") } return "", err } if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { return "", errors.New("密码错误") } // 生成 JWT token token, err := utils.GenerateToken(user.ID, user.Username, user.Role, &s.config.JWT) if err != nil { return "", err } // 更新最后登录时间 s.db.Model(&user).Update("last_login", gorm.Expr("CURRENT_TIMESTAMP")) return token, nil } func (s *UserService) GetUserByID(id uint) (*UserProfile, error) { var user model.User if err := s.db.First(&user, id).Error; err != nil { return nil, err } return &UserProfile{ ID: user.ID, Username: user.Username, Email: user.Email, Role: user.Role, }, nil } func (s *UserService) UpdateProfile(userID uint, email string) error { // 检查邮箱是否被其他用户使用 var count int64 if err := s.db.Model(&model.User{}).Where("email = ? AND id != ?", email, userID).Count(&count).Error; err != nil { return err } if count > 0 { return errors.New("邮箱已被其他用户使用") } // 更新用户信息 return s.db.Model(&model.User{}).Where("id = ?", userID).Update("email", email).Error } func (s *UserService) ChangePassword(userID uint, oldPassword, newPassword string) error { var user model.User if err := s.db.First(&user, userID).Error; err != nil { return err } // 验证旧密码 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword)); err != nil { return errors.New("旧密码错误") } // 加密新密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { return err } return s.db.Model(&user).Update("password", string(hashedPassword)).Error } func (s *UserService) ResetPassword(email, captcha string) error { // 验证验证码 if err := s.captchaService.VerifyCaptcha(email, "reset", captcha); err != nil { return err } // 原有的重置密码逻辑 var user model.User if err := s.db.Where("email = ?", email).First(&user).Error; err != nil { return errors.New("邮箱不存在") } // 生成重置令牌 token, err := utils.GenerateResetToken() if err != nil { return err } // 保存重置令牌 resetToken := model.PasswordResetToken{ UserID: user.ID, Token: token, ExpiresAt: time.Now().Add(24 * time.Hour), Used: false, } if err := s.db.Create(&resetToken).Error; err != nil { return err } // 发送重置邮件 emailService := utils.NewEmailService(&s.config.Email) resetLink := fmt.Sprintf("http://localhost:%s/reset-password?token=%s", s.config.Server.Port, token) emailBody := fmt.Sprintf(`

密码重置

您好,%s

请点击以下链接重置您的密码:

重置密码

此链接将在24小时后失效。

如果您没有请求重置密码,请忽略此邮件。

`, user.Username, resetLink) return emailService.SendEmail(user.Email, "密码重置", emailBody) } func (s *UserService) ValidateResetToken(token string) (*model.User, error) { var resetToken model.PasswordResetToken if err := s.db.Where("token = ? AND used = ? AND expires_at > ?", token, false, time.Now()).First(&resetToken).Error; err != nil { return nil, errors.New("无效或已过期的重置令牌") } var user model.User if err := s.db.First(&user, resetToken.UserID).Error; err != nil { return nil, err } return &user, nil } func (s *UserService) ResetPasswordWithToken(token, newPassword string) error { user, err := s.ValidateResetToken(token) if err != nil { return err } // 更新密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { return err } // 使用事务确保原子性 return s.db.Transaction(func(tx *gorm.DB) error { if err := tx.Model(&user).Update("password", string(hashedPassword)).Error; err != nil { return err } // 标记令牌为已使用 if err := tx.Model(&model.PasswordResetToken{}). Where("token = ?", token). Update("used", true).Error; err != nil { return err } return nil }) } func (s *UserService) SendRegisterCaptcha(email string) error { // 检查邮箱是否已被注册 var count int64 s.db.Model(&model.User{}).Where("email = ?", email).Count(&count) if count > 0 { return errors.New("邮箱已被注册") } return s.captchaService.SendEmailCaptcha(email, "register") } func (s *UserService) SendResetPasswordCaptcha(email string) error { var user model.User if err := s.db.Where("email = ?", email).First(&user).Error; err != nil { return errors.New("邮箱不存在") } return s.captchaService.SendEmailCaptcha(email, "reset") } func (s *UserService) GetCaptchaService() *CaptchaService { return s.captchaService } // GetUsers 获取用户列表 func (s *UserService) GetUsers(username, role string, page, pageSize int) ([]UserProfile, int64, error) { var users []model.User var total int64 var profiles []UserProfile query := s.db.Model(&model.User{}) if username != "" { query = query.Where("username LIKE ?", "%"+username+"%") } if role != "" { query = query.Where("role = ?", role) } // 获取总数 query.Count(&total) // 分页查询 if page > 0 && pageSize > 0 { offset := (page - 1) * pageSize query = query.Offset(offset).Limit(pageSize) } if err := query.Find(&users).Error; err != nil { return nil, 0, err } // 转换为 UserProfile for _, user := range users { profiles = append(profiles, UserProfile{ ID: user.ID, Username: user.Username, Email: user.Email, Role: user.Role, }) } return profiles, total, nil } // CreateUser 创建新用户 func (s *UserService) CreateUser(username, password, email, role string) error { // 检查用户名是否已存在 var count int64 s.db.Model(&model.User{}).Where("username = ?", username).Count(&count) if count > 0 { return errors.New("用户名已存在") } // 检查邮箱是否已存在 s.db.Model(&model.User{}).Where("email = ?", email).Count(&count) if count > 0 { return errors.New("邮箱已被注册") } // 加密密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return err } user := model.User{ Username: username, Password: string(hashedPassword), Email: email, Role: role, } return s.db.Create(&user).Error } // UpdateUser 更新用户信息 func (s *UserService) UpdateUser(id uint, email, role string) error { // 检查邮箱是否被其他用户使用 var count int64 s.db.Model(&model.User{}).Where("email = ? AND id != ?", email, id).Count(&count) if count > 0 { return errors.New("邮箱已被其他用户使用") } return s.db.Model(&model.User{}).Where("id = ?", id).Updates(map[string]interface{}{ "email": email, "role": role, }).Error } // DeleteUser 删除用户 func (s *UserService) DeleteUser(id uint) error { // 检查是否为最后一个管理员 var adminCount int64 s.db.Model(&model.User{}).Where("role = ?", "admin").Count(&adminCount) var user model.User if err := s.db.First(&user, id).Error; err != nil { return err } if user.Role == "admin" && adminCount <= 1 { return errors.New("不能删除最后一个管理员") } return s.db.Delete(&model.User{}, id).Error } // ValidateUser 验证用户名密码 func (s *UserService) ValidateUser(username, password string) (*model.User, error) { var user model.User if err := s.db.Where("username = ?", username).First(&user).Error; err != nil { return nil, errors.New("用户不存在") } // 使用 bcrypt 比较密码 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { return nil, errors.New("密码错误") } return &user, nil } // GenerateToken 生成JWT token func (s *UserService) GenerateToken(user *model.User) (string, error) { claims := jwt.MapClaims{ "user_id": user.ID, "username": user.Username, "role": user.Role, "exp": time.Now().Add(time.Hour * 24).Unix(), // 24小时过期 } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(s.config.JWT.Secret)) } // VerifyCaptcha 验证验证码 func (s *UserService) VerifyCaptcha(captchaId, captcha string) bool { return base64Captcha.DefaultMemStore.Verify(captchaId, captcha, true) }