package service import ( "context" "errors" "fmt" "strconv" "time" "gofaster/internal/auth/model" "gofaster/internal/auth/repository" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" ) type AuthService struct { userRepo repository.UserRepository captchaRepo repository.CaptchaRepository } func NewAuthService(userRepo repository.UserRepository, captchaRepo repository.CaptchaRepository) *AuthService { return &AuthService{ userRepo: userRepo, captchaRepo: captchaRepo, } } // Login 用户登录 func (s *AuthService) Login(ctx context.Context, username, password, captcha, captchaID string) (*model.LoginResponse, error) { // 验证验证码 if captcha != "" && captchaID != "" { expectedCaptcha, err := s.captchaRepo.Get(ctx, captchaID) if err != nil { return nil, errors.New("验证码错误或已过期") } if captcha != expectedCaptcha { return nil, errors.New("验证码错误") } } // 根据用户名查找用户 user, err := s.userRepo.GetByUsername(ctx, username) if err != nil { return nil, errors.New("用户名或密码错误") } // 验证密码 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { return nil, errors.New("用户名或密码错误") } // 检查用户状态 if !user.CanLogin() { return nil, errors.New("账户已被禁用或锁定") } // 更新最后登录信息 now := time.Now() user.LastLoginAt = &now user.LastLoginIP = "127.0.0.1" // 这里应该从请求中获取真实IP user.ResetPasswordError() // 重置密码错误次数 if err := s.userRepo.Update(ctx, user); err != nil { // 登录失败,但不影响登录流程 } // 生成JWT token token, err := s.generateJWTToken(user) if err != nil { return nil, errors.New("生成认证令牌失败") } // 检查是否需要强制修改密码 forceChangePassword := s.checkForceChangePassword(user) // 转换为UserInfo userInfo := model.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, Phone: user.Phone, Status: user.Status, CreatedAt: user.CreatedAt, LastLoginAt: user.LastLoginAt, LastLoginIP: user.LastLoginIP, Roles: []model.RoleInfo{}, // 暂时为空,后续可以加载角色信息 } return &model.LoginResponse{ Token: token, TokenType: "Bearer", ExpiresIn: 86400, // 24小时 RefreshToken: "", // 暂时为空 User: userInfo, ForceChangePassword: forceChangePassword, }, nil } // GetCaptchaRepo 获取验证码仓库 func (s *AuthService) GetCaptchaRepo() repository.CaptchaRepository { return s.captchaRepo } // RefreshToken 刷新JWT token func (s *AuthService) RefreshToken(ctx context.Context, userID interface{}) (string, error) { // 安全地转换userID var uid uint switch v := userID.(type) { case uint: uid = v case int: if v < 0 { return "", errors.New("无效的用户ID") } uid = uint(v) case int64: if v < 0 { return "", errors.New("无效的用户ID") } uid = uint(v) case float64: if v < 0 || v > float64(^uint(0)) { return "", errors.New("无效的用户ID") } uid = uint(v) case string: if parsed, err := strconv.ParseUint(v, 10, 64); err == nil { uid = uint(parsed) } else { return "", errors.New("无效的用户ID") } default: return "", errors.New("无效的用户ID类型") } // 获取用户信息 user, err := s.userRepo.GetByID(ctx, uid) if err != nil { return "", errors.New("用户不存在") } // 生成新的JWT token return s.generateJWTToken(user) } // generateJWTToken 生成JWT token func (s *AuthService) generateJWTToken(user *model.User) (string, error) { // 创建claims claims := jwt.MapClaims{ "user_id": user.ID, "username": user.Username, "email": user.Email, "exp": time.Now().Add(time.Hour * 24).Unix(), // 24小时过期 "iat": time.Now().Unix(), "iss": "gofaster", } // 创建token token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 签名token tokenString, err := token.SignedString([]byte("your-secret-key")) if err != nil { return "", fmt.Errorf("签名token失败: %w", err) } return tokenString, nil } // checkForceChangePassword 检查是否需要强制修改密码 func (s *AuthService) checkForceChangePassword(user *model.User) bool { return user.ForceChangePassword } // GetUserInfo 获取用户信息 func (s *AuthService) GetUserInfo(ctx context.Context, userID uint) (*model.UserInfo, error) { // 根据用户ID获取用户信息 user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return nil, errors.New("用户不存在") } // 检查用户状态 if !user.CanLogin() { return nil, errors.New("账户已被禁用或锁定") } // 转换为UserInfo结构 userInfo := &model.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, Phone: user.Phone, Status: user.Status, CreatedAt: user.CreatedAt, LastLoginAt: user.LastLoginAt, LastLoginIP: user.LastLoginIP, Roles: []model.RoleInfo{}, // 暂时为空,后续可以加载角色信息 } return userInfo, nil } // Logout 用户登出 func (s *AuthService) Logout(ctx context.Context, token string) error { // 这里可以实现令牌黑名单逻辑 // 暂时简单返回成功 return nil }