Files
ai_english/serve/api/handlers/auth_handler.go
2025-11-17 13:39:05 +08:00

357 lines
8.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"net/http"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/common"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/middleware"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/services"
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/utils"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type AuthHandler struct {
userService *services.UserService
validator *validator.Validate
}
func NewAuthHandler(userService *services.UserService) *AuthHandler {
return &AuthHandler{
userService: userService,
validator: validator.New(),
}
}
// RegisterRequest 注册请求结构
type RegisterRequest struct {
Username string `json:"username" validate:"required,min=3,max=20"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=6"`
Nickname string `json:"nickname" validate:"required,min=1,max=50"`
}
// LoginRequest 登录请求结构
type LoginRequest struct {
Account string `json:"account" validate:"required"` // 用户名或邮箱
Password string `json:"password" validate:"required"`
}
// RefreshTokenRequest 刷新令牌请求结构
type RefreshTokenRequest struct {
RefreshToken string `json:"refresh_token" validate:"required"`
}
// ChangePasswordRequest 修改密码请求结构
type ChangePasswordRequest struct {
OldPassword string `json:"old_password" validate:"required"`
NewPassword string `json:"new_password" validate:"required,min=6"`
}
// AuthResponse 认证响应结构
type AuthResponse struct {
User *UserInfo `json:"user"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int64 `json:"expires_in"`
}
// UserInfo 用户信息结构
type UserInfo struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Level string `json:"level"`
Status string `json:"status"`
}
// Register 用户注册
func (h *AuthHandler) Register(c *gin.Context) {
var req RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 验证请求参数
if err := h.validator.Struct(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 验证邮箱格式
if !utils.IsValidEmail(req.Email) {
common.BadRequestResponse(c, "邮箱格式不正确")
return
}
// 验证密码强度
if !utils.IsStrongPassword(req.Password) {
common.BadRequestResponse(c, "密码强度不足至少8位且包含大小写字母、数字和特殊字符")
return
}
// 创建用户
user, err := h.userService.CreateUser(req.Username, req.Email, req.Password)
if err != nil {
if businessErr, ok := err.(*common.BusinessError); ok {
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
return
}
common.InternalServerErrorResponse(c, "用户创建失败")
return
}
// 生成令牌
accessToken, refreshToken, err := middleware.GenerateTokens(user.ID, user.Username, user.Email)
if err != nil {
common.InternalServerErrorResponse(c, "令牌生成失败")
return
}
// 更新用户登录信息
h.userService.UpdateLoginInfo(user.ID, utils.GetClientIP(c))
// 构造响应
nickname := ""
if user.Nickname != nil {
nickname = *user.Nickname
}
avatar := ""
if user.Avatar != nil {
avatar = *user.Avatar
}
userInfo := &UserInfo{
ID: user.ID,
Username: user.Username,
Email: user.Email,
Nickname: nickname,
Avatar: avatar,
Level: "beginner",
Status: user.Status,
}
response := &AuthResponse{
User: userInfo,
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: 7200, // 2小时
}
common.SuccessResponse(c, response)
}
// Login 用户登录
func (h *AuthHandler) Login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 验证请求参数
if err := h.validator.Struct(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 根据账号类型获取用户
var user *models.User
var err error
if utils.IsValidEmail(req.Account) {
user, err = h.userService.GetUserByEmail(req.Account)
} else {
user, err = h.userService.GetUserByUsername(req.Account)
}
if err != nil {
if businessErr, ok := err.(*common.BusinessError); ok {
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
return
}
common.InternalServerErrorResponse(c, "用户查询失败")
return
}
// 验证密码
if !utils.CheckPasswordHash(req.Password, user.PasswordHash) {
common.BadRequestResponse(c, "密码错误")
return
}
// 检查用户状态
if user.Status != "active" {
common.BadRequestResponse(c, "用户已被禁用")
return
}
// 生成令牌
accessToken, refreshToken, err := middleware.GenerateTokens(user.ID, user.Username, user.Email)
if err != nil {
common.InternalServerErrorResponse(c, "令牌生成失败")
return
}
// 更新用户登录信息
h.userService.UpdateLoginInfo(user.ID, utils.GetClientIP(c))
// 构造响应
nickname := ""
if user.Nickname != nil {
nickname = *user.Nickname
}
avatar := ""
if user.Avatar != nil {
avatar = *user.Avatar
}
userInfo := &UserInfo{
ID: user.ID,
Username: user.Username,
Email: user.Email,
Nickname: nickname,
Avatar: avatar,
Level: "beginner",
Status: user.Status,
}
response := &AuthResponse{
User: userInfo,
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: 7200, // 2小时
}
common.SuccessResponse(c, response)
}
// RefreshToken 刷新访问令牌
func (h *AuthHandler) RefreshToken(c *gin.Context) {
var req RefreshTokenRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 验证刷新令牌
claims, err := middleware.ParseToken(req.RefreshToken)
if err != nil {
common.BadRequestResponse(c, "无效的刷新令牌")
return
}
// 检查令牌类型
if claims.Type != "refresh" {
common.BadRequestResponse(c, "令牌类型错误")
return
}
// 生成新的令牌
accessToken, newRefreshToken, err := middleware.GenerateTokens(claims.UserID, claims.Username, claims.Email)
if err != nil {
common.InternalServerErrorResponse(c, "令牌生成失败")
return
}
response := map[string]interface{}{
"access_token": accessToken,
"refresh_token": newRefreshToken,
"expires_in": 7200,
}
common.SuccessResponse(c, response)
}
// Logout 用户登出
func (h *AuthHandler) Logout(c *gin.Context) {
// 这里可以实现令牌黑名单机制
// 目前简单返回成功
common.SuccessResponse(c, map[string]string{"message": "登出成功"})
}
// GetProfile 获取用户资料
func (h *AuthHandler) GetProfile(c *gin.Context) {
// 获取当前用户ID
userID, exists := utils.GetUserIDFromContext(c)
if !exists {
common.BadRequestResponse(c, "请先登录")
return
}
user, err := h.userService.GetUserByID(userID)
if err != nil {
if businessErr, ok := err.(*common.BusinessError); ok {
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
return
}
common.InternalServerErrorResponse(c, "用户查询失败")
return
}
nickname := ""
if user.Nickname != nil {
nickname = *user.Nickname
}
avatar := ""
if user.Avatar != nil {
avatar = *user.Avatar
}
userInfo := &UserInfo{
ID: user.ID,
Username: user.Username,
Email: user.Email,
Nickname: nickname,
Avatar: avatar,
Level: "beginner",
Status: user.Status,
}
common.SuccessResponse(c, userInfo)
}
// ChangePassword 修改密码
func (h *AuthHandler) ChangePassword(c *gin.Context) {
// 获取当前用户ID
userID, exists := utils.GetUserIDFromContext(c)
if !exists {
common.BadRequestResponse(c, "请先登录")
return
}
var req ChangePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 验证请求参数
if err := h.validator.Struct(&req); err != nil {
common.ValidationErrorResponse(c, err)
return
}
// 验证新密码强度
if !utils.IsStrongPassword(req.NewPassword) {
common.BadRequestResponse(c, "新密码强度不够")
return
}
// 更新密码
err := h.userService.UpdatePassword(userID, req.OldPassword, req.NewPassword)
if err != nil {
if businessErr, ok := err.(*common.BusinessError); ok {
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
return
}
common.InternalServerErrorResponse(c, "密码更新失败")
return
}
common.SuccessResponse(c, map[string]string{"message": "密码修改成功"})
}