From d57419c99e1617a05458215717d14ae31780f95a Mon Sep 17 00:00:00 2001 From: zhx Date: Thu, 25 Jun 2026 13:48:50 +0800 Subject: [PATCH] =?UTF-8?q?cp=E5=92=8C=E5=85=85=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/admin/cmd/server/main.go | 2 + .../internal/modules/cprelation/handler.go | 5 + .../internal/modules/cpweeklyrank/handler.go | 192 ++++++++++++++++++ .../internal/modules/cpweeklyrank/routes.go | 16 ++ .../internal/modules/payment/handler_test.go | 4 +- server/admin/internal/repository/seed.go | 20 +- server/admin/internal/router/router.go | 3 + .../062_cp_weekly_rank_navigation.sql | 57 ++++++ .../http/walletapi/h5_recharge_handler.go | 3 +- .../wallet/external_recharge_temporary.go | 6 +- .../external_recharge_order_repository.go | 10 +- 11 files changed, 301 insertions(+), 17 deletions(-) create mode 100644 server/admin/internal/modules/cpweeklyrank/handler.go create mode 100644 server/admin/internal/modules/cpweeklyrank/routes.go create mode 100644 server/admin/migrations/062_cp_weekly_rank_navigation.sql diff --git a/server/admin/cmd/server/main.go b/server/admin/cmd/server/main.go index 0aab45d0..0c95f0ca 100644 --- a/server/admin/cmd/server/main.go +++ b/server/admin/cmd/server/main.go @@ -35,6 +35,7 @@ import ( coinledgermodule "hyapp-admin-server/internal/modules/coinledger" countryregionmodule "hyapp-admin-server/internal/modules/countryregion" cprelationmodule "hyapp-admin-server/internal/modules/cprelation" + cpweeklyrankmodule "hyapp-admin-server/internal/modules/cpweeklyrank" cumulativerechargerewardmodule "hyapp-admin-server/internal/modules/cumulativerechargereward" dailytaskmodule "hyapp-admin-server/internal/modules/dailytask" dashboardmodule "hyapp-admin-server/internal/modules/dashboard" @@ -264,6 +265,7 @@ func main() { CoinLedger: coinledgermodule.New(userDB, walletDB, sqlDB, walletclient.NewGRPC(walletConn), auditHandler), CountryRegion: countryregionmodule.New(userclient.NewGRPC(userConn), userDB, cfg, auditHandler), CPRelation: cprelationmodule.NewWithActivity(userDB, activityclient.NewGRPC(activityConn), auditHandler), + CPWeeklyRank: cpweeklyrankmodule.New(activityclient.NewGRPC(activityConn), auditHandler), CumulativeRecharge: cumulativerechargerewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler), DailyTask: dailytaskmodule.New(activityclient.NewGRPC(activityConn), auditHandler), Dashboard: dashboardmodule.New(store, cfg, userclient.NewGRPC(userConn)), diff --git a/server/admin/internal/modules/cprelation/handler.go b/server/admin/internal/modules/cprelation/handler.go index 6cb72bb9..f3050e55 100644 --- a/server/admin/internal/modules/cprelation/handler.go +++ b/server/admin/internal/modules/cprelation/handler.go @@ -68,6 +68,11 @@ func (h *Handler) UpdateConfig(c *gin.Context) { return } if req.WeeklyRank != nil { + // CP 周榜已经拆成独立活动入口;旧接口保留兼容字段,但写入必须同时具备周榜活动权限,避免只拿到关系配置权限的后台用户绕过新菜单。 + if !middleware.HasPermission(c, "cp-weekly-rank:update") { + response.Forbidden(c, "没有操作权限") + return + } req.WeeklyRank.UpdatedByAdminID = int64(middleware.CurrentUserID(c)) } config, err := h.service.UpdateConfig(c.Request.Context(), appctx.FromContext(c.Request.Context()), req) diff --git a/server/admin/internal/modules/cpweeklyrank/handler.go b/server/admin/internal/modules/cpweeklyrank/handler.go new file mode 100644 index 00000000..09e56b5a --- /dev/null +++ b/server/admin/internal/modules/cpweeklyrank/handler.go @@ -0,0 +1,192 @@ +package cpweeklyrank + +import ( + "strconv" + "strings" + "time" + + "hyapp-admin-server/internal/appctx" + "hyapp-admin-server/internal/integration/activityclient" + "hyapp-admin-server/internal/middleware" + "hyapp-admin-server/internal/modules/shared" + "hyapp-admin-server/internal/response" + activityv1 "hyapp.local/api/proto/activity/v1" + + "github.com/gin-gonic/gin" +) + +type Handler struct { + activity activityclient.Client + audit shared.OperationLogger +} + +func New(activity activityclient.Client, audit shared.OperationLogger) *Handler { + return &Handler{activity: activity, audit: audit} +} + +type configRequest struct { + Enabled bool `json:"enabled"` + TopCount int32 `json:"top_count"` + Rewards []rewardDTO `json:"rewards"` +} + +type configDTO struct { + AppCode string `json:"app_code"` + ActivityCode string `json:"activity_code"` + Enabled bool `json:"enabled"` + RelationType string `json:"relation_type"` + TopCount int32 `json:"top_count"` + Rewards []rewardDTO `json:"rewards"` + UpdatedByAdminID int64 `json:"updated_by_admin_id,string"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type rewardDTO struct { + RankNo int32 `json:"rank_no"` + ResourceGroupID int64 `json:"resource_group_id"` +} + +type settlementDTO struct { + SettlementID string `json:"settlement_id"` + PeriodStartMS int64 `json:"period_start_ms"` + PeriodEndMS int64 `json:"period_end_ms"` + RankNo int32 `json:"rank_no"` + RelationshipID string `json:"relationship_id"` + UserID int64 `json:"user_id,string"` + Score int64 `json:"score"` + ResourceGroupID int64 `json:"resource_group_id"` + WalletCommandID string `json:"wallet_command_id"` + WalletGrantID string `json:"wallet_grant_id"` + Status string `json:"status"` + FailureReason string `json:"failure_reason"` + AttemptCount int32 `json:"attempt_count"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +func (h *Handler) GetConfig(c *gin.Context) { + resp, err := h.activity.GetCPWeeklyRankConfig(c.Request.Context(), &activityv1.GetCPWeeklyRankConfigRequest{Meta: h.meta(c)}) + if err != nil { + response.ServerError(c, "获取CP排行活动配置失败") + return + } + response.OK(c, configFromProto(resp.GetConfig())) +} + +func (h *Handler) UpdateConfig(c *gin.Context) { + var req configRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "CP排行活动配置参数不正确") + return + } + resp, err := h.activity.UpdateCPWeeklyRankConfig(c.Request.Context(), &activityv1.UpdateCPWeeklyRankConfigRequest{ + Meta: h.meta(c), + Config: req.toProto(), + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := configFromProto(resp.GetConfig()) + shared.OperationLogWithResourceID(c, h.audit, "update-cp-weekly-rank-config", "cp_weekly_rank_configs", item.AppCode, "success", "") + response.OK(c, item) +} + +func (h *Handler) ListSettlements(c *gin.Context) { + periodStartMS := parseInt64Query(c, "period_start_ms") + periodEndMS := parseInt64Query(c, "period_end_ms") + resp, err := h.activity.ListCPWeeklyRankSettlements(c.Request.Context(), &activityv1.ListCPWeeklyRankSettlementsRequest{ + Meta: h.meta(c), + PeriodStartMs: periodStartMS, + PeriodEndMs: periodEndMS, + }) + if err != nil { + response.ServerError(c, "获取CP排行活动发奖记录失败") + return + } + items := make([]settlementDTO, 0, len(resp.GetSettlements())) + for _, settlement := range resp.GetSettlements() { + items = append(items, settlementFromProto(settlement)) + } + response.OK(c, gin.H{"items": items, "total": len(items), "period_start_ms": periodStartMS, "period_end_ms": periodEndMS}) +} + +func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + RequestId: middleware.CurrentRequestID(c), + Caller: "admin-server", + AppCode: appctx.FromContext(c.Request.Context()), + SentAtMs: time.Now().UTC().UnixMilli(), + } +} + +func (r configRequest) toProto() *activityv1.CPWeeklyRankConfig { + topCount := r.TopCount + if topCount <= 0 { + topCount = 3 + } + rewards := make([]*activityv1.CPWeeklyRankReward, 0, len(r.Rewards)) + for _, reward := range r.Rewards { + // activity-service 会按 top_count 归一化和校验;admin-server 只做传输层适配, + // 保留原 rank/resource_group 组合,避免两个服务出现不同的配置裁决口径。 + rewards = append(rewards, &activityv1.CPWeeklyRankReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID}) + } + return &activityv1.CPWeeklyRankConfig{ + ActivityCode: "cp_weekly_rank", + Enabled: r.Enabled, + RelationType: "cp", + TopCount: topCount, + Rewards: rewards, + } +} + +func configFromProto(config *activityv1.CPWeeklyRankConfig) configDTO { + if config == nil { + return configDTO{ActivityCode: "cp_weekly_rank", Enabled: true, RelationType: "cp", TopCount: 3, Rewards: []rewardDTO{}} + } + rewards := make([]rewardDTO, 0, len(config.GetRewards())) + for _, reward := range config.GetRewards() { + rewards = append(rewards, rewardDTO{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()}) + } + return configDTO{ + AppCode: config.GetAppCode(), + ActivityCode: config.GetActivityCode(), + Enabled: config.GetEnabled(), + RelationType: config.GetRelationType(), + TopCount: config.GetTopCount(), + Rewards: rewards, + UpdatedByAdminID: config.GetUpdatedByAdminId(), + CreatedAtMS: config.GetCreatedAtMs(), + UpdatedAtMS: config.GetUpdatedAtMs(), + } +} + +func settlementFromProto(settlement *activityv1.CPWeeklyRankSettlement) settlementDTO { + if settlement == nil { + return settlementDTO{} + } + return settlementDTO{ + SettlementID: settlement.GetSettlementId(), + PeriodStartMS: settlement.GetPeriodStartMs(), + PeriodEndMS: settlement.GetPeriodEndMs(), + RankNo: settlement.GetRankNo(), + RelationshipID: settlement.GetRelationshipId(), + UserID: settlement.GetUserId(), + Score: settlement.GetScore(), + ResourceGroupID: settlement.GetResourceGroupId(), + WalletCommandID: settlement.GetWalletCommandId(), + WalletGrantID: settlement.GetWalletGrantId(), + Status: settlement.GetStatus(), + FailureReason: settlement.GetFailureReason(), + AttemptCount: settlement.GetAttemptCount(), + CreatedAtMS: settlement.GetCreatedAtMs(), + UpdatedAtMS: settlement.GetUpdatedAtMs(), + } +} + +func parseInt64Query(c *gin.Context, key string) int64 { + value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64) + return value +} diff --git a/server/admin/internal/modules/cpweeklyrank/routes.go b/server/admin/internal/modules/cpweeklyrank/routes.go new file mode 100644 index 00000000..d991a0ce --- /dev/null +++ b/server/admin/internal/modules/cpweeklyrank/routes.go @@ -0,0 +1,16 @@ +package cpweeklyrank + +import ( + "hyapp-admin-server/internal/middleware" + + "github.com/gin-gonic/gin" +) + +func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { + if h == nil { + return + } + protected.GET("/admin/activity/cp-weekly-rank/config", middleware.RequirePermission("cp-weekly-rank:view"), h.GetConfig) + protected.PUT("/admin/activity/cp-weekly-rank/config", middleware.RequirePermission("cp-weekly-rank:update"), h.UpdateConfig) + protected.GET("/admin/activity/cp-weekly-rank/settlements", middleware.RequirePermission("cp-weekly-rank:view"), h.ListSettlements) +} diff --git a/server/admin/internal/modules/payment/handler_test.go b/server/admin/internal/modules/payment/handler_test.go index 210b4e31..173c221c 100644 --- a/server/admin/internal/modules/payment/handler_test.go +++ b/server/admin/internal/modules/payment/handler_test.go @@ -97,8 +97,8 @@ func TestListTemporaryPaymentLinksForwardsFilters(t *testing.T) { OrderId: "tmp_1001", AppCode: "lalu", AudienceType: "temporary", - ProductName: "Temporary recharge 1000 coins", - CoinAmount: 1000, + ProductName: "Temporary payment USD 1.99", + CoinAmount: 0, UsdMinorAmount: 199, ProviderCode: "mifapay", PaymentMethodId: 810, diff --git a/server/admin/internal/repository/seed.go b/server/admin/internal/repository/seed.go index 87281b64..2ec025c2 100644 --- a/server/admin/internal/repository/seed.go +++ b/server/admin/internal/repository/seed.go @@ -143,6 +143,8 @@ var defaultPermissions = []model.Permission{ {Name: "红包配置更新", Code: "red-packet:update", Kind: "button"}, {Name: "CP 配置查看", Code: "cp-config:view", Kind: "menu"}, {Name: "CP 配置更新", Code: "cp-config:update", Kind: "button"}, + {Name: "CP 排行活动查看", Code: "cp-weekly-rank:view", Kind: "menu"}, + {Name: "CP 排行活动更新", Code: "cp-weekly-rank:update", Kind: "button"}, {Name: "VIP 配置查看", Code: "vip-config:view", Kind: "menu"}, {Name: "VIP 配置更新", Code: "vip-config:update", Kind: "button"}, {Name: "VIP 赠送", Code: "vip-config:grant", Kind: "button"}, @@ -305,10 +307,11 @@ func (s *Store) seedMenus() error { {ParentID: &activityID, Title: "用户榜单", Code: "user-leaderboard", Path: "/activities/user-leaderboards", Icon: "leaderboard", PermissionCode: "user-leaderboard:view", Sort: 74, Visible: true}, {ParentID: &activityID, Title: "红包配置", Code: "red-packet", Path: "/activities/red-packets", Icon: "redeem", PermissionCode: "red-packet:view", Sort: 75, Visible: true}, {ParentID: &activityID, Title: "CP配置", Code: "cp-config", Path: "/activities/cp-config", Icon: "favorite", PermissionCode: "cp-config:view", Sort: 76, Visible: true}, - {ParentID: &activityID, Title: "VIP配置", Code: "vip-config", Path: "/activities/vip-config", Icon: "workspace_premium", PermissionCode: "vip-config:view", Sort: 77, Visible: true}, - {ParentID: &activityID, Title: "周星配置", Code: "weekly-star", Path: "/activities/weekly-star", Icon: "star", PermissionCode: "weekly-star:view", Sort: 78, Visible: true}, - {ParentID: &activityID, Title: "代理开业活动", Code: "agency-opening", Path: "/activities/agency-opening", Icon: "workspace_premium", PermissionCode: "agency-opening:view", Sort: 79, Visible: true}, - {ParentID: &activityID, Title: "转盘抽奖", Code: "wheel", Path: "/activities/wheel", Icon: "casino", PermissionCode: "wheel:view", Sort: 80, Visible: true}, + {ParentID: &activityID, Title: "CP排行活动", Code: "cp-weekly-rank", Path: "/activities/cp-weekly-rank", Icon: "leaderboard", PermissionCode: "cp-weekly-rank:view", Sort: 77, Visible: true}, + {ParentID: &activityID, Title: "VIP配置", Code: "vip-config", Path: "/activities/vip-config", Icon: "workspace_premium", PermissionCode: "vip-config:view", Sort: 78, Visible: true}, + {ParentID: &activityID, Title: "周星配置", Code: "weekly-star", Path: "/activities/weekly-star", Icon: "star", PermissionCode: "weekly-star:view", Sort: 79, Visible: true}, + {ParentID: &activityID, Title: "代理开业活动", Code: "agency-opening", Path: "/activities/agency-opening", Icon: "workspace_premium", PermissionCode: "agency-opening:view", Sort: 80, Visible: true}, + {ParentID: &activityID, Title: "转盘抽奖", Code: "wheel", Path: "/activities/wheel", Icon: "casino", PermissionCode: "wheel:view", Sort: 81, Visible: true}, {ParentID: &gameID, Title: "游戏列表", Code: "game-list", Path: "/games", Icon: "sports_esports", PermissionCode: "game:view", Sort: 70, Visible: true}, {ParentID: &gameID, Title: "自研游戏", Code: "self-games", Path: "/games/self-games", Icon: "settings", PermissionCode: "game:view", Sort: 80, Visible: true}, {ParentID: &gameID, Title: "全站机器人", Code: "game-robots", Path: "/games/robots", Icon: "team", PermissionCode: "game:view", Sort: 90, Visible: true}, @@ -558,7 +561,7 @@ func defaultRolePermissionCodes(code string) []string { "seven-day-checkin:view", "seven-day-checkin:update", "room-rocket:view", "room-rocket:update", "red-packet:view", "red-packet:update", - "cp-config:view", "cp-config:update", + "cp-config:view", "cp-config:update", "cp-weekly-rank:view", "cp-weekly-rank:update", "vip-config:view", "vip-config:update", "vip-config:grant", "weekly-star:view", "weekly-star:create", "weekly-star:update", "weekly-star:settle", "agency-opening:view", "agency-opening:create", "agency-opening:update", @@ -567,7 +570,7 @@ func defaultRolePermissionCodes(code string) []string { "upload:create", } case "auditor": - return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "pretty-id:view", "risk-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "room-whitelist:view", "room-robot:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "full-server-notice:view", "lucky-gift:view", "wheel:view", "payment-bill:view", "payment-third-party:view", "payment-temporary-link:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "vip-config:view", "weekly-star:view", "agency-opening:view", "role:view", "permission:view", "job:view"} + return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "pretty-id:view", "risk-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "room-whitelist:view", "room-robot:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "full-server-notice:view", "lucky-gift:view", "wheel:view", "payment-bill:view", "payment-third-party:view", "payment-temporary-link:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "cp-weekly-rank:view", "vip-config:view", "weekly-star:view", "agency-opening:view", "role:view", "permission:view", "job:view"} case "readonly": return []string{ "overview:view", @@ -618,6 +621,7 @@ func defaultRolePermissionCodes(code string) []string { "room-rocket:view", "red-packet:view", "cp-config:view", + "cp-weekly-rank:view", "vip-config:view", "weekly-star:view", "agency-opening:view", @@ -663,13 +667,13 @@ func defaultRolePermissionMigrationCodes(code string) []string { "seven-day-checkin:view", "seven-day-checkin:update", "room-rocket:view", "room-rocket:update", "red-packet:view", "red-packet:update", - "cp-config:view", "cp-config:update", + "cp-config:view", "cp-config:update", "cp-weekly-rank:view", "cp-weekly-rank:update", "vip-config:view", "vip-config:update", "vip-config:grant", "weekly-star:view", "weekly-star:create", "weekly-star:update", "weekly-star:settle", "agency-opening:view", "agency-opening:create", "agency-opening:update", } case "auditor", "readonly": - return []string{"level-config:view", "pretty-id:view", "risk-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "room-whitelist:view", "room-robot:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-seller:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "full-server-notice:view", "lucky-gift:view", "wheel:view", "payment-bill:view", "payment-third-party:view", "payment-temporary-link:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "vip-config:view", "weekly-star:view", "agency-opening:view"} + return []string{"level-config:view", "pretty-id:view", "risk-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "room-whitelist:view", "room-robot:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-seller:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "full-server-notice:view", "lucky-gift:view", "wheel:view", "payment-bill:view", "payment-third-party:view", "payment-temporary-link:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "cp-weekly-rank:view", "vip-config:view", "weekly-star:view", "agency-opening:view"} default: return nil } diff --git a/server/admin/internal/router/router.go b/server/admin/internal/router/router.go index e076554c..90451f66 100644 --- a/server/admin/internal/router/router.go +++ b/server/admin/internal/router/router.go @@ -14,6 +14,7 @@ import ( "hyapp-admin-server/internal/modules/coinledger" "hyapp-admin-server/internal/modules/countryregion" "hyapp-admin-server/internal/modules/cprelation" + "hyapp-admin-server/internal/modules/cpweeklyrank" "hyapp-admin-server/internal/modules/cumulativerechargereward" "hyapp-admin-server/internal/modules/dailytask" "hyapp-admin-server/internal/modules/dashboard" @@ -69,6 +70,7 @@ type Handlers struct { CoinLedger *coinledger.Handler CountryRegion *countryregion.Handler CPRelation *cprelation.Handler + CPWeeklyRank *cpweeklyrank.Handler CumulativeRecharge *cumulativerechargereward.Handler DailyTask *dailytask.Handler Dashboard *dashboard.Handler @@ -134,6 +136,7 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine { appconfig.RegisterRoutes(protected, h.AppConfig) countryregion.RegisterRoutes(protected, h.CountryRegion) cprelation.RegisterRoutes(protected, h.CPRelation) + cpweeklyrank.RegisterRoutes(protected, h.CPWeeklyRank) cumulativerechargereward.RegisterRoutes(protected, h.CumulativeRecharge) dailytask.RegisterRoutes(protected, h.DailyTask) firstrechargereward.RegisterRoutes(protected, h.FirstRechargeReward) diff --git a/server/admin/migrations/062_cp_weekly_rank_navigation.sql b/server/admin/migrations/062_cp_weekly_rank_navigation.sql new file mode 100644 index 00000000..df667e9d --- /dev/null +++ b/server/admin/migrations/062_cp_weekly_rank_navigation.sql @@ -0,0 +1,57 @@ +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CP排行活动对应 H5 的 cp-weekly-rank 活动读接口;后台只维护启停、Top 奖励资源组和查看发奖事实。 +SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES + ('CP 排行活动查看', 'cp-weekly-rank:view', 'menu', '', @now_ms, @now_ms), + ('CP 排行活动更新', 'cp-weekly-rank:update', 'button', '', @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + kind = VALUES(kind), + description = VALUES(description), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) +SELECT parent.id, 'CP排行活动', 'cp-weekly-rank', '/activities/cp-weekly-rank', 'leaderboard', 'cp-weekly-rank:view', 77, TRUE, @now_ms, @now_ms +FROM admin_menus parent +WHERE parent.code = 'activities' +ON DUPLICATE KEY UPDATE + parent_id = VALUES(parent_id), + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +UPDATE admin_menus +SET sort = 78, updated_at_ms = @now_ms +WHERE code = 'vip-config'; + +UPDATE admin_menus +SET sort = 79, updated_at_ms = @now_ms +WHERE code = 'weekly-star'; + +UPDATE admin_menus +SET sort = 80, updated_at_ms = @now_ms +WHERE code = 'agency-opening'; + +UPDATE admin_menus +SET sort = 81, updated_at_ms = @now_ms +WHERE code = 'wheel'; + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code IN ('platform-admin', 'ops-admin') + AND admin_permission.code IN ('cp-weekly-rank:view', 'cp-weekly-rank:update'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code IN ('auditor', 'readonly') + AND admin_permission.code = 'cp-weekly-rank:view'; diff --git a/services/gateway-service/internal/transport/http/walletapi/h5_recharge_handler.go b/services/gateway-service/internal/transport/http/walletapi/h5_recharge_handler.go index f7fd1b17..d15a1786 100644 --- a/services/gateway-service/internal/transport/http/walletapi/h5_recharge_handler.go +++ b/services/gateway-service/internal/transport/http/walletapi/h5_recharge_handler.go @@ -286,7 +286,8 @@ func (h *Handler) createTemporaryPayLink(writer http.ResponseWriter, request *ht usdMinorAmount := temporaryUSDMinorAmount(body) paymentMethodID := firstPositiveInt64(body.PaymentMethodID, body.PaymentMethodAlt) providerCode := normalizeH5PaymentProvider(firstNonEmptyString(body.ProviderCode, body.ProviderCodeAlt), paymentMethodID) - if coinAmount <= 0 || usdMinorAmount <= 0 || paymentMethodID <= 0 || providerCode == "" { + // 临时链接只代表一笔收款,不绑定用户和发币动作;coin_amount 仅保留协议兼容,页面不再提交正数。 + if body.CoinAmount < 0 || body.CoinAmountAlt < 0 || usdMinorAmount <= 0 || paymentMethodID <= 0 || providerCode == "" { httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument") return } diff --git a/services/wallet-service/internal/service/wallet/external_recharge_temporary.go b/services/wallet-service/internal/service/wallet/external_recharge_temporary.go index e3108f4a..5a351396 100644 --- a/services/wallet-service/internal/service/wallet/external_recharge_temporary.go +++ b/services/wallet-service/internal/service/wallet/external_recharge_temporary.go @@ -185,9 +185,13 @@ func normalizeCreateTemporaryExternalRechargeOrderCommand(command ledger.CreateT } func validateCreateTemporaryExternalRechargeOrderCommand(command ledger.CreateTemporaryExternalRechargeOrderCommand) error { - if command.CommandID == "" || command.CoinAmount <= 0 || command.USDMinorAmount <= 0 || command.PaymentMethodID <= 0 { + if command.CommandID == "" || command.USDMinorAmount <= 0 || command.PaymentMethodID <= 0 { return xerr.New(xerr.InvalidArgument, "temporary recharge command is incomplete") } + // 临时支付链接只记录三方收款事实,不会触发用户金币入账;负数没有业务含义,0 用于表达“不发币”。 + if command.CoinAmount < 0 { + return xerr.New(xerr.InvalidArgument, "temporary recharge coin amount is invalid") + } if command.ProviderCode == "" { return xerr.New(xerr.InvalidArgument, "temporary recharge provider is invalid") } diff --git a/services/wallet-service/internal/storage/mysql/external_recharge_order_repository.go b/services/wallet-service/internal/storage/mysql/external_recharge_order_repository.go index 786f86a2..4597a786 100644 --- a/services/wallet-service/internal/storage/mysql/external_recharge_order_repository.go +++ b/services/wallet-service/internal/storage/mysql/external_recharge_order_repository.go @@ -552,7 +552,7 @@ func temporaryExternalRechargeOrderFromCommand(command ledger.CreateTemporaryExt AudienceType: ledger.RechargeAudienceTemporary, ProductID: 0, ProductCode: "temporary", - ProductName: temporaryExternalRechargeProductName(command.CoinAmount), + ProductName: temporaryExternalRechargeProductName(command.USDMinorAmount), CoinAmount: command.CoinAmount, USDMinorAmount: command.USDMinorAmount, ProviderCode: ledger.NormalizePaymentProvider(command.ProviderCode), @@ -568,11 +568,11 @@ func temporaryExternalRechargeOrderFromCommand(command ledger.CreateTemporaryExt } } -func temporaryExternalRechargeProductName(coinAmount int64) string { - if coinAmount <= 0 { - return "Temporary recharge" +func temporaryExternalRechargeProductName(usdMinorAmount int64) string { + if usdMinorAmount <= 0 { + return "Temporary payment" } - return fmt.Sprintf("Temporary recharge %d coins", coinAmount) + return fmt.Sprintf("Temporary payment USD %d.%02d", usdMinorAmount/100, usdMinorAmount%100) } func externalRechargeOrderID(appCode string, commandID string) string {