/** * package api * 作用:认证相关API处理器 */ package api import ( "xk-websocket-v2/internal/model" "xk-websocket-v2/internal/service" "xk-websocket-v2/internal/utils" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) // isProdEnv 判断是否为生产环境(app.env 为 production/prod)。 // 用于决定是否把验证码等敏感信息返回给客户端:仅非生产环境返回,便于本地调试。 func isProdEnv() bool { env := viper.GetString("app.env") return env == "production" || env == "prod" } /** * LoginHandler * 功能:用户登录 * 路径:POST /api/login */ func LoginHandler(c *gin.Context) { var req model.LoginReq if err := c.ShouldBindJSON(&req); err != nil { utils.BadRequest(c, "参数错误: "+err.Error()) return } // 获取客户端IP ip := utils.GetClientIP(c) // 调用认证服务登录 user, err := service.AuthSvc.Login(req.Account, req.Password) if err != nil { // 记录登录失败日志 service.LoginLogSvc.LogLogin(req.Account, "0", ip, false) utils.Unauthorized(c, err.Error()) return } // 生成Token token, err := utils.GenerateToken(user.ID) if err != nil { // 记录登录失败日志 service.LoginLogSvc.LogLogin(req.Account, "0", ip, false) utils.InternalError(c, "生成Token失败") return } // 清除密码字段 user.Password = "" // 记录登录成功日志 service.LoginLogSvc.LogLogin(req.Account, user.ID, ip, true) utils.SuccessWithData(c, model.LoginResponse{ Token: token, User: *user, }, "登录成功") } /** * RegisterHandler * 功能:用户注册 * 路径:POST /api/register */ func RegisterHandler(c *gin.Context) { var req model.RegisterReq if err := c.ShouldBindJSON(&req); err != nil { utils.BadRequest(c, "参数错误: "+err.Error()) return } // 如果提供了验证码,验证验证码(只校验不消费——注册还可能失败) codeTarget, codeType := "", "" if req.Code != "" { // 根据邮箱或手机号确定类型 codeType = "email" if len(req.Phone) > 0 { codeType = "sms" } codeTarget = req.Email if codeType == "sms" { codeTarget = req.Phone } valid, err := service.AuthSvc.VerifyCode(codeTarget, req.Code, codeType) if err != nil || !valid { utils.BadRequest(c, "验证码无效或已过期") return } } // 调用认证服务注册 user, err := service.AuthSvc.Register(&req) if err != nil { // 注册失败时验证码未被消费,用户可用同一验证码修正参数后重试 utils.BadRequest(c, err.Error()) return } // 生成Token token, err := utils.GenerateToken(user.ID) if err != nil { utils.InternalError(c, "生成Token失败") return } // 全流程成功(注册+Token)后才消费验证码,防止同码重放注册; // 中途任何失败都保留验证码,把"可重试"的窗口留到最后一刻 if req.Code != "" { service.AuthSvc.ConsumeCode(codeTarget, req.Code, codeType) } utils.SuccessWithData(c, model.RegisterResponse{ Token: token, User: *user, }, "注册成功") } /** * LogoutHandler * 功能:用户登出(可选,前端清除token) * 路径:POST /api/logout */ func LogoutHandler(c *gin.Context) { // 登出主要是前端清除token,后端可以记录日志或清除session utils.Success(c, "登出成功") } /** * CheckTokenHandler * 功能:检查Token有效性 * 路径:GET /api/check-token */ func CheckTokenHandler(c *gin.Context) { token := c.GetHeader("Authorization") if token == "" { utils.Unauthorized(c, "缺少Token") return } // 移除 "Bearer " 前缀(如果存在) if len(token) > 7 && token[:7] == "Bearer " { token = token[7:] } userID, err := utils.ValidateToken(token) if err != nil { utils.Unauthorized(c, "Token无效或已过期") return } utils.SuccessWithData(c, gin.H{ "status": "valid", "user_id": userID, }, "Token有效") } /** * SendEmailCodeHandler * 功能:发送邮箱验证码 * 路径:POST /api/send-email-code */ func SendEmailCodeHandler(c *gin.Context) { var req model.SendCodeReq if err := c.ShouldBindJSON(&req); err != nil { utils.BadRequest(c, "参数错误: "+err.Error()) return } if req.Type != "email" { utils.BadRequest(c, "类型必须是email") return } code, err := service.AuthSvc.SendEmailCode(req.Target) if err != nil { utils.InternalError(c, err.Error()) return } // 生产环境不返回验证码,避免直接绕过邮件校验;仅非生产环境返回以便调试 data := gin.H{} if !isProdEnv() { data["code"] = code } utils.SuccessWithData(c, data, "验证码已发送") } /** * SendSmsCodeHandler * 功能:发送短信验证码 * 路径:POST /api/send-sms-code */ func SendSmsCodeHandler(c *gin.Context) { var req model.SendCodeReq if err := c.ShouldBindJSON(&req); err != nil { utils.BadRequest(c, "参数错误: "+err.Error()) return } if req.Type != "sms" { utils.BadRequest(c, "类型必须是sms") return } code, err := service.AuthSvc.SendSmsCode(req.Target) if err != nil { utils.InternalError(c, err.Error()) return } // 生产环境不返回验证码,避免直接绕过短信校验;仅非生产环境返回以便调试 data := gin.H{} if !isProdEnv() { data["code"] = code } utils.SuccessWithData(c, data, "验证码已发送") }