package router import ( "strings" "affiliate_dash/internal/handler" "affiliate_dash/internal/middleware" "affiliate_dash/internal/model" "affiliate_dash/internal/pkg/jwt" "affiliate_dash/internal/service" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type Handlers struct { Auth *handler.AuthHandler Dashboard *handler.DashboardHandler Delivery *handler.DeliveryHandler User *handler.UserHandler Open *handler.OpenV1Handler SourceOpen *handler.OpenHandler Merchant *handler.MerchantHandler Recharge *handler.RechargeHandler JWT *jwt.Manager Tenant *service.TenantService OpenDB *gorm.DB SecretCodec *service.SecretCodec OpenAPIKey string OpenAPISecret string OpenSignSkew int64 OpenAPIDebug bool UploadDir string // CORSAllowedOrigins 跨域来源白名单;为空时使用本地开发默认来源。 CORSAllowedOrigins []string } // defaultCORSOrigins 未配置 CORS_ALLOWED_ORIGINS 时的本地开发默认来源。 var defaultCORSOrigins = []string{ "http://localhost:15173", "http://127.0.0.1:15173", "http://localhost:18080", "http://127.0.0.1:18080", } // corsOrigins 返回生效的 CORS 来源白名单,并拒绝通配符 "*" // (与 AllowCredentials 组合会让浏览器把凭证请求放行给任意站点)。 func corsOrigins(h *Handlers) []string { var origins []string for _, o := range h.CORSAllowedOrigins { o = strings.TrimSpace(o) if o == "" || o == "*" { continue } origins = append(origins, o) } if len(origins) == 0 { return defaultCORSOrigins } return origins } // requestLogger 访问日志中间件:跳过 /health 健康检查(每 30 秒一次,避免刷屏)。 func requestLogger() gin.HandlerFunc { inner := gin.Logger() return func(c *gin.Context) { if c.Request.URL.Path == "/health" { c.Next() return } inner(c) } } func Setup(h *Handlers) *gin.Engine { r := gin.New() r.Use(requestLogger(), gin.Recovery()) r.Use(cors.New(cors.Config{ AllowOrigins: corsOrigins(h), AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Merchant-ID", "X-App-Key", "X-Api-Key", "X-Timestamp", "X-Nonce", "X-Sign", "X-Request-ID"}, ExposeHeaders: []string{"Content-Length", "X-Request-ID"}, AllowCredentials: true, })) r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) // 上传的凭证等静态文件(公开访问,文件名不可枚举) if h.UploadDir != "" { r.Static("/uploads", h.UploadDir) } api := r.Group("/api") { api.POST("/auth/login", h.Auth.Login) // 用户侧公开发货页接口:商户把发货链接给用户,用户只填写游戏 UID。 delivery := api.Group("/delivery/v1") { delivery.GET("/orders/:order_no", h.Delivery.GetOrder) delivery.POST("/orders/:order_no/bind", h.Delivery.Bind) delivery.POST("/orders/:order_no/submit", h.Delivery.Submit) } // 源头侧接口:上游发货平台查询订单并回传发货结果。 sourceOpen := api.Group("/open/v1") sourceOpen.Use(middleware.SourceOpenAuth(middleware.SourceOpenAuthConfig{ DB: h.OpenDB, APIKey: h.OpenAPIKey, APISecret: h.OpenAPISecret, SkewSeconds: h.OpenSignSkew, Debug: h.OpenAPIDebug, })) { sourceOpen.GET("/orders/:order_no", h.SourceOpen.QueryOrder) sourceOpen.POST("/orders/ship-notify", h.SourceOpen.ShipNotify) } // 商户侧接口:每个商户独立 API 客户端 + HMAC 签名。 clientOpen := api.Group("/client/v1") clientOpen.Use(middleware.OpenAuth(middleware.OpenAuthConfig{ DB: h.OpenDB, Codec: h.SecretCodec, SkewSeconds: h.OpenSignSkew, Debug: h.OpenAPIDebug, })) { clientOpen.GET("/products", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureProducts), middleware.RequireAPIScope("products:read"), h.Open.ListProducts) clientOpen.POST("/orders", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:write"), h.Open.CreateOrder) clientOpen.GET("/orders/:order_no", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:read", "shipping:read"), h.Open.QueryOrder) clientOpen.POST("/orders/:order_no/cancel", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:write"), h.Open.CancelOrder) clientOpen.GET("/orders/:order_no/delivery-link", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:read", "shipping:read"), h.Open.GetDeliveryLink) clientOpen.GET("/orders/:order_no/delivery", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:read", "shipping:read"), h.Open.GetDeliveryOrder) clientOpen.POST("/orders/:order_no/delivery/bind", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:write"), h.Open.BindDeliveryOrder) clientOpen.GET("/orders/:order_no/delivery/bind-result", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:read", "shipping:read"), h.Open.GetDeliveryBindResult) clientOpen.POST("/orders/:order_no/delivery/submit", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireAPIScope("orders:write"), h.Open.SubmitDeliveryOrder) clientOpen.GET("/wallet", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireAPIScope("wallet:read"), h.Open.GetWallet) } auth := api.Group("") auth.Use(middleware.Auth(h.JWT)) auth.Use(middleware.Tenant(h.Tenant)) { auth.GET("/auth/profile", h.Auth.Profile) auth.PUT("/auth/password", h.Auth.ChangePassword) auth.GET("/dashboard", h.Dashboard.Dashboard) // 商户后台:商户就是平台下游客户,成员只代表该商户内部员工。 merchant := auth.Group("/merchant") { merchant.GET("", h.Merchant.Current) merchant.GET("/products", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureProducts), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionProductsManage), h.Merchant.ListProducts) merchant.PATCH("/products/:id/status", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureProducts), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionProductsManage), h.Merchant.UpdateProductStatus) merchant.GET("/orders", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.ListOrders) merchant.GET("/orders/products", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.ListManualOrderProducts) merchant.POST("/orders/manual", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.CreateManualOrder) merchant.POST("/orders/test", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.CreateTestOrder) merchant.GET("/orders/:order_no/delivery-link", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.GetDeliveryLink) merchant.POST("/orders/:order_no/delivery-link/revoke", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.RevokeDeliveryLink) merchant.POST("/orders/:order_no/delivery-link/restore", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureOrders), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionOrdersManage), h.Merchant.RestoreDeliveryLink) merchant.GET("/wallet", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionWalletView), h.Merchant.GetWallet) merchant.GET("/wallet/ledger", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionWalletLedger), h.Merchant.ListWalletLedger) merchant.POST("/recharge/applications", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.CreateApplication) merchant.GET("/recharge/applications", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionWalletView), h.Recharge.ListApplications) merchant.GET("/recharge/alert-config", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.GetAlertConfig) merchant.PUT("/recharge/alert-config", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.SaveAlertConfig) merchant.GET("/alert-channels", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.ListAlertChannels) merchant.POST("/alert-channels", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.CreateAlertChannel) merchant.PUT("/alert-channels/:id", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.UpdateAlertChannel) merchant.DELETE("/alert-channels/:id", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.DeleteAlertChannel) merchant.POST("/alert-channels/:id/test", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionRechargeManage), h.Recharge.TestAlertChannel) merchant.GET("/api-clients", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionAPIManage), h.Merchant.ListAPIClients) merchant.POST("/api-clients", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionAPIManage), h.Merchant.CreateAPIClient) merchant.PATCH("/api-clients/:id/status", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionAPIManage), h.Merchant.UpdateAPIClientStatus) merchant.DELETE("/api-clients/:id", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionAPIManage), h.Merchant.DeleteAPIClient) merchant.GET("/callbacks", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionCallbacksManage), h.Merchant.ListCallbacks) merchant.POST("/callbacks", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantPermissions(h.Tenant, model.PermissionCallbacksManage), h.Merchant.CreateCallback) merchant.GET("/members", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.ListMembers) merchant.POST("/members", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.AddCurrentMerchantMember) merchant.PATCH("/members/:id", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.UpdateCurrentMerchantMember) merchant.DELETE("/members/:id", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.RemoveCurrentMerchantMember) merchant.GET("/roles", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.ListRoles) merchant.POST("/roles", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.CreateRole) merchant.PATCH("/roles/:id", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.UpdateRole) } // 用户 / 发货记录 / 平台商户(仅管理员) admin := auth.Group("") admin.Use(middleware.RequireRole(model.RoleAdmin)) { admin.GET("/users", h.User.List) admin.POST("/users", h.User.Create) admin.PATCH("/users/:id/status", h.User.UpdateStatus) admin.DELETE("/users/:id", h.User.Delete) admin.GET("/platform/merchants", h.Merchant.ListPlatformMerchants) admin.POST("/platform/merchants", h.Merchant.CreateMerchant) admin.PATCH("/platform/merchants/:id", h.Merchant.UpdateMerchantSettings) admin.POST("/platform/merchants/:id/members", h.Merchant.AddPlatformMerchantMember) admin.GET("/platform/product-catalog", h.Merchant.ListProductCatalog) admin.GET("/platform/merchants/:id/products", h.Merchant.ListMerchantProductsByAdmin) admin.PATCH("/platform/merchants/:id/products/:pid", h.Merchant.AdminUpdateMerchantProduct) admin.POST("/platform/merchants/:id/products/assign", h.Merchant.AssignProducts) admin.GET("/platform/merchants/:id/wallet", h.Merchant.AdminGetWallet) admin.GET("/platform/merchants/:id/wallet/ledger", h.Merchant.AdminListWalletLedger) admin.POST("/platform/merchants/:id/wallet/adjust", h.Merchant.AdminAdjustWallet) admin.GET("/platform/recharge/applications", h.Recharge.AdminListApplications) admin.POST("/platform/recharge/applications/:id/review", h.Recharge.AdminReviewApplication) } auth.POST("/upload", h.Recharge.Upload) } } return r }