Files
backend/internal/handler/auth_handler.go
lan a111872b32
Some checks failed
Build / build (push) Successful in 2m23s
Build / build-docker (push) Failing after 1m37s
feat(auth): upgrade casbin to v3 and enhance connection pool configurations
- Upgrade casbin from v2 to v3 across go.mod and pkg/auth/casbin.go
- Add slide captcha verification to registration flow (CheckVerified, ConsumeVerified)
- Add DB wrapper with connection pool statistics and health checks
- Add Redis connection pool optimizations with stats and health monitoring
- Add new config options: ConnMaxLifetime, HealthCheckInterval, EnableRetryOnError
- Optimize slow query threshold from 200ms to 100ms
- Add ping with retry mechanism for database and Redis connections
2026-02-25 19:00:50 +08:00

185 lines
6.0 KiB
Go
Raw 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 handler
import (
"carrotskin/internal/container"
"carrotskin/internal/service"
"carrotskin/internal/types"
"carrotskin/pkg/email"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// AuthHandler 认证处理器(依赖注入版本)
type AuthHandler struct {
container *container.Container
logger *zap.Logger
}
// NewAuthHandler 创建AuthHandler实例
func NewAuthHandler(c *container.Container) *AuthHandler {
return &AuthHandler{
container: c,
logger: c.Logger,
}
}
// Register 用户注册
// @Summary 用户注册
// @Description 注册新用户账号
// @Tags auth
// @Accept json
// @Produce json
// @Param request body types.RegisterRequest true "注册信息"
// @Success 200 {object} model.Response{data=types.LoginResponse} "注册成功"
// @Failure 400 {object} model.ErrorResponse "请求参数错误"
// @Router /api/v1/auth/register [post]
func (h *AuthHandler) Register(c *gin.Context) {
var req types.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
RespondBadRequest(c, "请求参数错误", err)
return
}
// 验证滑动验证码(检查是否已验证)
if ok, err := h.container.CaptchaService.CheckVerified(c.Request.Context(), req.CaptchaID); err != nil || !ok {
h.logger.Warn("滑动验证码验证失败", zap.String("captcha_id", req.CaptchaID), zap.Error(err))
RespondBadRequest(c, "滑动验证码验证失败", nil)
return
}
// 使用 defer 确保验证码在函数返回前被消耗(不管成功还是失败)
defer func() {
if err := h.container.CaptchaService.ConsumeVerified(c.Request.Context(), req.CaptchaID); err != nil {
h.logger.Warn("消耗验证码失败", zap.String("captcha_id", req.CaptchaID), zap.Error(err))
}
}()
// 验证邮箱验证码
if err := h.container.VerificationService.VerifyCode(c.Request.Context(), req.Email, req.VerificationCode, service.VerificationTypeRegister); err != nil {
h.logger.Warn("邮箱验证码验证失败", zap.String("email", req.Email), zap.Error(err))
RespondBadRequest(c, err.Error(), nil)
return
}
// 注册用户
user, token, err := h.container.UserService.Register(c.Request.Context(), req.Username, req.Password, req.Email, req.Avatar)
if err != nil {
h.logger.Error("用户注册失败", zap.Error(err))
RespondBadRequest(c, err.Error(), nil)
return
}
RespondSuccess(c, &types.LoginResponse{
Token: token,
UserInfo: UserToUserInfo(user),
})
}
// Login 用户登录
// @Summary 用户登录
// @Description 用户登录获取JWT Token支持用户名或邮箱登录
// @Tags auth
// @Accept json
// @Produce json
// @Param request body types.LoginRequest true "登录信息username字段支持用户名或邮箱"
// @Success 200 {object} model.Response{data=types.LoginResponse} "登录成功"
// @Failure 400 {object} model.ErrorResponse "请求参数错误"
// @Failure 401 {object} model.ErrorResponse "登录失败"
// @Router /api/v1/auth/login [post]
func (h *AuthHandler) Login(c *gin.Context) {
var req types.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
RespondBadRequest(c, "请求参数错误", err)
return
}
ipAddress := c.ClientIP()
userAgent := c.GetHeader("User-Agent")
user, token, err := h.container.UserService.Login(c.Request.Context(), req.Username, req.Password, ipAddress, userAgent)
if err != nil {
h.logger.Warn("用户登录失败",
zap.String("username_or_email", req.Username),
zap.String("ip", ipAddress),
zap.Error(err),
)
RespondUnauthorized(c, err.Error())
return
}
RespondSuccess(c, &types.LoginResponse{
Token: token,
UserInfo: UserToUserInfo(user),
})
}
// SendVerificationCode 发送验证码
// @Summary 发送验证码
// @Description 发送邮箱验证码(注册/重置密码/更换邮箱)
// @Tags auth
// @Accept json
// @Produce json
// @Param request body types.SendVerificationCodeRequest true "发送验证码请求"
// @Success 200 {object} model.Response{data=map[string]string} "发送成功"
// @Failure 400 {object} model.ErrorResponse "请求参数错误"
// @Router /api/v1/auth/send-code [post]
func (h *AuthHandler) SendVerificationCode(c *gin.Context) {
var req types.SendVerificationCodeRequest
if err := c.ShouldBindJSON(&req); err != nil {
RespondBadRequest(c, "请求参数错误", err)
return
}
if err := h.container.VerificationService.SendCode(c.Request.Context(), req.Email, req.Type); err != nil {
h.logger.Error("发送验证码失败",
zap.String("email", req.Email),
zap.String("type", req.Type),
zap.Error(err),
)
RespondBadRequest(c, err.Error(), nil)
return
}
RespondSuccess(c, gin.H{"message": "验证码已发送,请查收邮件"})
}
// ResetPassword 重置密码
// @Summary 重置密码
// @Description 通过邮箱验证码重置密码
// @Tags auth
// @Accept json
// @Produce json
// @Param request body types.ResetPasswordRequest true "重置密码请求"
// @Success 200 {object} model.Response{data=map[string]string} "重置成功"
// @Failure 400 {object} model.ErrorResponse "请求参数错误"
// @Router /api/v1/auth/reset-password [post]
func (h *AuthHandler) ResetPassword(c *gin.Context) {
var req types.ResetPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
RespondBadRequest(c, "请求参数错误", err)
return
}
// 验证验证码
if err := h.container.VerificationService.VerifyCode(c.Request.Context(), req.Email, req.VerificationCode, service.VerificationTypeResetPassword); err != nil {
h.logger.Warn("验证码验证失败", zap.String("email", req.Email), zap.Error(err))
RespondBadRequest(c, err.Error(), nil)
return
}
// 重置密码
if err := h.container.UserService.ResetPassword(c.Request.Context(), req.Email, req.NewPassword); err != nil {
h.logger.Error("重置密码失败", zap.String("email", req.Email), zap.Error(err))
RespondServerError(c, err.Error(), nil)
return
}
RespondSuccess(c, gin.H{"message": "密码重置成功"})
}
// getEmailService 获取邮件服务(暂时使用全局方式,后续可改为依赖注入)
func (h *AuthHandler) getEmailService() (*email.Service, error) {
return email.GetService()
}