package roomturnoverreward import ( "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"` Tiers []tierDTO `json:"tiers"` } type configDTO struct { AppCode string `json:"app_code"` Enabled bool `json:"enabled"` Tiers []tierDTO `json:"tiers"` UpdatedByAdminID int64 `json:"updated_by_admin_id"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } type tierDTO struct { TierID int64 `json:"tier_id"` TierCode string `json:"tier_code"` TierName string `json:"tier_name"` ThresholdCoinSpent int64 `json:"threshold_coin_spent"` RewardCoinAmount int64 `json:"reward_coin_amount"` Status string `json:"status"` SortOrder int32 `json:"sort_order"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } type settlementDTO struct { SettlementID string `json:"settlement_id"` RoomID string `json:"room_id"` OwnerUserID int64 `json:"owner_user_id"` PeriodStartMS int64 `json:"period_start_ms"` PeriodEndMS int64 `json:"period_end_ms"` CoinSpent int64 `json:"coin_spent"` TierID int64 `json:"tier_id"` TierCode string `json:"tier_code"` ThresholdCoinSpent int64 `json:"threshold_coin_spent"` RewardCoinAmount int64 `json:"reward_coin_amount"` Status string `json:"status"` WalletCommandID string `json:"wallet_command_id"` WalletTransactionID string `json:"wallet_transaction_id"` FailureReason string `json:"failure_reason"` SettledAtMS int64 `json:"settled_at_ms"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } func (h *Handler) GetConfig(c *gin.Context) { // 后台服务只透传到 activity-service 获取当前 App 配置;活动规则 owner 不在 admin-server 内部复制。 resp, err := h.activity.GetRoomTurnoverRewardConfig(c.Request.Context(), &activityv1.GetRoomTurnoverRewardConfigRequest{Meta: h.meta(c)}) if err != nil { response.ServerError(c, "获取房间流水奖励配置失败") 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, "房间流水奖励配置参数不正确") return } tiers := make([]*activityv1.RoomTurnoverRewardTier, 0, len(req.Tiers)) for _, tier := range req.Tiers { // 后台只做输入清洗和操作者透传;阈值递增、奖励金额和启用状态等业务校验统一由 activity-service 执行。 tiers = append(tiers, &activityv1.RoomTurnoverRewardTier{ TierId: tier.TierID, TierCode: strings.TrimSpace(tier.TierCode), TierName: strings.TrimSpace(tier.TierName), ThresholdCoinSpent: tier.ThresholdCoinSpent, RewardCoinAmount: tier.RewardCoinAmount, Status: strings.TrimSpace(tier.Status), SortOrder: tier.SortOrder, }) } resp, err := h.activity.UpdateRoomTurnoverRewardConfig(c.Request.Context(), &activityv1.UpdateRoomTurnoverRewardConfigRequest{ Meta: h.meta(c), Enabled: req.Enabled, Tiers: tiers, OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := configFromProto(resp.GetConfig()) // 配置修改必须留下后台审计资源 ID,后续排查结算金额变化时可以回到具体 App 配置记录。 shared.OperationLogWithResourceID(c, h.audit, "update-room-turnover-reward", "room_turnover_reward_configs", item.AppCode, "success", "") response.OK(c, item) } func (h *Handler) ListSettlements(c *gin.Context) { // 列表筛选沿用通用 ListOptions:status 过滤结算状态,keyword 用作 room_id 精确查询,避免后台接口引入模糊扫表。 options := shared.ListOptions(c) resp, err := h.activity.ListRoomTurnoverRewardSettlements(c.Request.Context(), &activityv1.ListRoomTurnoverRewardSettlementsRequest{ Meta: h.meta(c), Status: strings.TrimSpace(options.Status), RoomId: strings.TrimSpace(options.Keyword), Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取房间流水奖励结算记录失败") return } items := make([]settlementDTO, 0, len(resp.GetSettlements())) for _, settlement := range resp.GetSettlements() { items = append(items, settlementFromProto(settlement)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) RetrySettlement(c *gin.Context) { settlementID := strings.TrimSpace(c.Param("settlement_id")) if settlementID == "" { response.BadRequest(c, "结算记录 ID 不正确") return } // retry 只把指定 settlement 交还 activity-service;是否重新取房主、是否调用钱包和是否幂等都由活动服务闭环处理。 resp, err := h.activity.RetryRoomTurnoverRewardSettlement(c.Request.Context(), &activityv1.RetryRoomTurnoverRewardSettlementRequest{ Meta: h.meta(c), SettlementId: settlementID, OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := settlementFromProto(resp.GetSettlement()) shared.OperationLogWithResourceID(c, h.audit, "retry-room-turnover-reward", "room_turnover_reward_settlements", settlementID, "success", "") response.OK(c, item) } func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { return &activityv1.RequestMeta{ // 使用后台请求 ID 串起 admin-server、activity-service 和 wallet-service 日志,AppCode 来自当前后台上下文。 RequestId: middleware.CurrentRequestID(c), Caller: "admin-server", AppCode: appctx.FromContext(c.Request.Context()), SentAtMs: time.Now().UnixMilli(), } } func configFromProto(config *activityv1.RoomTurnoverRewardConfig) configDTO { if config == nil { return configDTO{Tiers: []tierDTO{}} } tiers := make([]tierDTO, 0, len(config.GetTiers())) for _, tier := range config.GetTiers() { tiers = append(tiers, tierFromProto(tier)) } return configDTO{ AppCode: config.GetAppCode(), Enabled: config.GetEnabled(), Tiers: tiers, UpdatedByAdminID: config.GetUpdatedByAdminId(), CreatedAtMS: config.GetCreatedAtMs(), UpdatedAtMS: config.GetUpdatedAtMs(), } } func tierFromProto(tier *activityv1.RoomTurnoverRewardTier) tierDTO { if tier == nil { return tierDTO{} } return tierDTO{ TierID: tier.GetTierId(), TierCode: tier.GetTierCode(), TierName: tier.GetTierName(), ThresholdCoinSpent: tier.GetThresholdCoinSpent(), RewardCoinAmount: tier.GetRewardCoinAmount(), Status: tier.GetStatus(), SortOrder: tier.GetSortOrder(), CreatedAtMS: tier.GetCreatedAtMs(), UpdatedAtMS: tier.GetUpdatedAtMs(), } } func settlementFromProto(settlement *activityv1.RoomTurnoverRewardSettlement) settlementDTO { if settlement == nil { return settlementDTO{} } return settlementDTO{ SettlementID: settlement.GetSettlementId(), RoomID: settlement.GetRoomId(), OwnerUserID: settlement.GetOwnerUserId(), PeriodStartMS: settlement.GetPeriodStartMs(), PeriodEndMS: settlement.GetPeriodEndMs(), CoinSpent: settlement.GetCoinSpent(), TierID: settlement.GetTierId(), TierCode: settlement.GetTierCode(), ThresholdCoinSpent: settlement.GetThresholdCoinSpent(), RewardCoinAmount: settlement.GetRewardCoinAmount(), Status: settlement.GetStatus(), WalletCommandID: settlement.GetWalletCommandId(), WalletTransactionID: settlement.GetWalletTransactionId(), FailureReason: settlement.GetFailureReason(), SettledAtMS: settlement.GetSettledAtMs(), CreatedAtMS: settlement.GetCreatedAtMs(), UpdatedAtMS: settlement.GetUpdatedAtMs(), } }