2026-04-16 15:25:09 +08:00

205 lines
5.3 KiB
Go

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": "success2"})
})
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())
}