package http import ( "chatapp3-golang/internal/config" "chatapp3-golang/internal/integration" "chatapp3-golang/internal/repo" "chatapp3-golang/internal/service" "context" "errors" "net/http" "strings" "time" "github.com/gin-gonic/gin" ) const authUserContextKey = "auth_user" func NewRouter( cfg config.Config, repository *repo.Repository, javaClient *integration.Client, inviteService *service.InviteService, baishunService *service.BaishunService, ) *gin.Engine { router := gin.New() router.Use(gin.Logger(), gin.Recovery()) router.GET("/health", func(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), cfg.Timeout) defer cancel() if err := repository.Ping(ctx); err != nil { c.JSON(http.StatusServiceUnavailable, gin.H{ "code": "dependency_unavailable", "message": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{"code": "ok", "message": "success"}) }) router.GET("/public/h5/invite-campaign/landing", func(c *gin.Context) { resp, err := inviteService.GetLanding(c.Request.Context(), c.Query("code")) if err != nil { writeError(c, err) return } writeOK(c, resp) }) internal := router.Group("/internal/invite-campaign") internal.POST("/recharge-success", func(c *gin.Context) { var req service.RechargeSuccessRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := inviteService.HandleRechargeSuccess(c.Request.Context(), req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) authenticated := router.Group("/app/h5/invite-campaign") authenticated.Use(authMiddleware(javaClient)) authenticated.GET("/home", func(c *gin.Context) { user := mustAuthUser(c) resp, err := inviteService.GetHome(c.Request.Context(), user) if err != nil { writeError(c, err) return } writeOK(c, resp) }) authenticated.POST("/bind-code", func(c *gin.Context) { user := mustAuthUser(c) var req service.BindCodeRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := inviteService.BindCode(c.Request.Context(), user, resolveClientIP(c), req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) authenticated.POST("/tasks/claim", func(c *gin.Context) { user := mustAuthUser(c) var req service.TaskClaimRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := inviteService.ClaimTask(c.Request.Context(), user, req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) registerBaishunRoutes(router, cfg, javaClient, baishunService) return router } func authMiddleware(javaClient *integration.Client) gin.HandlerFunc { return func(c *gin.Context) { authorization := strings.TrimSpace(c.GetHeader("Authorization")) if authorization == "" { writeError(c, service.NewAppError(http.StatusUnauthorized, "missing_authorization", "authorization is required")) c.Abort() return } token := trimBearer(authorization) ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second) defer cancel() credential, err := javaClient.AuthenticateToken(ctx, token) if err != nil { writeError(c, service.NewAppError(http.StatusUnauthorized, "invalid_token", err.Error())) c.Abort() return } c.Set(authUserContextKey, service.AuthUser{ UserID: int64(credential.UserID), SysOrigin: credential.SysOrigin, Token: token, Authorization: authorization, }) c.Next() } } func mustAuthUser(c *gin.Context) service.AuthUser { value, exists := c.Get(authUserContextKey) if !exists { return service.AuthUser{} } user, _ := value.(service.AuthUser) return user } func writeOK(c *gin.Context, data any) { c.JSON(http.StatusOK, gin.H{ "status": true, "errorCode": 0, "errorMsg": "success", "body": data, "code": "success", "message": "success", "data": data, "time": time.Now().UnixMilli(), }) } func writeError(c *gin.Context, err error) { var appErr *service.AppError if errors.As(err, &appErr) { c.JSON(appErr.Status, gin.H{ "status": false, "errorCode": appErr.Status, "errorCodeName": appErr.Code, "errorMsg": appErr.Message, "body": nil, "code": appErr.Code, "message": appErr.Message, "data": nil, "time": time.Now().UnixMilli(), }) return } c.JSON(http.StatusInternalServerError, gin.H{ "status": false, "errorCode": http.StatusInternalServerError, "errorCodeName": "internal_error", "errorMsg": err.Error(), "body": nil, "code": "internal_error", "message": err.Error(), "data": nil, "time": time.Now().UnixMilli(), }) } func trimBearer(authorization string) string { if strings.HasPrefix(strings.ToLower(authorization), "bearer ") { return strings.TrimSpace(authorization[7:]) } return authorization } func resolveClientIP(c *gin.Context) string { if forwarded := strings.TrimSpace(c.GetHeader("X-Forwarded-For")); forwarded != "" { parts := strings.Split(forwarded, ",") if len(parts) > 0 { return strings.TrimSpace(parts[0]) } } return strings.TrimSpace(c.ClientIP()) }