package userauthapi import ( "context" "net/http" "strings" "time" gatewaymiddleware "github.com/LoveLosita/smartflow/backend/gateway/middleware" "github.com/LoveLosita/smartflow/backend/gateway/shared/respond" contracts "github.com/LoveLosita/smartflow/backend/shared/contracts/userauth" "github.com/LoveLosita/smartflow/backend/shared/ports" "github.com/gin-gonic/gin" ) type UserHandler struct { client ports.UserCommandClient captcha *GeeTestService } // NewUserHandler 只接收 user/auth 客户端与验证码服务,不再直接依赖本地 user service。 func NewUserHandler(client ports.UserCommandClient, captcha *GeeTestService) *UserHandler { return &UserHandler{ client: client, captcha: captcha, } } func (api *UserHandler) CaptchaRegister(c *gin.Context) { captchaCtx, cancel := context.WithTimeout(c.Request.Context(), 3*time.Second) defer cancel() registerData, err := api.captcha.Register(captchaCtx, c.ClientIP()) if err != nil { respond.DealWithError(c, err) return } c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, registerData)) } func (api *UserHandler) UserRegister(c *gin.Context) { var req registerRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, respond.WrongParamType) return } // 1. 先用独立超时完成极验二次校验,避免第三方接口抖动侵入内部 RPC 超时预算。 // 2. 只有验证码通过后才继续调 user/auth 注册服务,防止无效流量进入内部链路。 // 3. 内部 RPC 仍保留原先 2 秒超时边界,不改变现有 user/auth 服务 SLA。 captchaCtx, cancelCaptcha := context.WithTimeout(c.Request.Context(), 3*time.Second) defer cancelCaptcha() if err := api.captcha.Verify(captchaCtx, req.captchaPayload(), c.ClientIP()); err != nil { respond.DealWithError(c, err) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) defer cancel() retUser, err := api.client.Register(ctx, req.toContract()) if err != nil { respond.DealWithError(c, err) return } c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, retUser)) } func (api *UserHandler) UserLogin(c *gin.Context) { var req loginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, respond.WrongParamType) return } captchaCtx, cancelCaptcha := context.WithTimeout(c.Request.Context(), 3*time.Second) defer cancelCaptcha() if err := api.captcha.Verify(captchaCtx, req.captchaPayload(), c.ClientIP()); err != nil { respond.DealWithError(c, err) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) defer cancel() tokens, err := api.client.Login(ctx, req.toContract()) if err != nil { respond.DealWithError(c, err) return } c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, tokens)) } func (api *UserHandler) RefreshTokenHandler(c *gin.Context) { var req contracts.RefreshTokenRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, respond.WrongParamType) return } if strings.TrimSpace(req.RefreshToken) == "" { c.JSON(http.StatusBadRequest, respond.MissingParam) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) defer cancel() tokens, err := api.client.RefreshToken(ctx, req) if err != nil { respond.DealWithError(c, err) return } c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, tokens)) } func (api *UserHandler) UserLogout(c *gin.Context) { token := gatewaymiddleware.ExtractTokenFromAuthorization(c.GetHeader("Authorization")) if token == "" { c.JSON(http.StatusUnauthorized, respond.MissingToken) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) defer cancel() if err := api.client.Logout(ctx, token); err != nil { respond.DealWithError(c, err) return } c.JSON(http.StatusOK, respond.Ok) }