2026-06-12 12:08:01 +08:00

228 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,string"`
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) {
// 列表筛选沿用通用 ListOptionsstatus 过滤结算状态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(),
}
}