fix: normalize Yumi gift challenge rewards
This commit is contained in:
parent
489aa40a50
commit
5cc47b90f6
@ -4,12 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"chatapp3-golang/internal/integration"
|
||||||
"chatapp3-golang/internal/model"
|
"chatapp3-golang/internal/model"
|
||||||
"chatapp3-golang/internal/utils"
|
"chatapp3-golang/internal/utils"
|
||||||
|
|
||||||
@ -607,8 +610,9 @@ func (s *Service) buildRewardSnapshots(ctx context.Context, activity model.YumiG
|
|||||||
})
|
})
|
||||||
for index, item := range detail.RewardConfigList {
|
for index, item := range detail.RewardConfigList {
|
||||||
rewardConfigID := int64(item.ID)
|
rewardConfigID := int64(item.ID)
|
||||||
if rewardConfigID <= 0 || strings.TrimSpace(item.Type) == "" || int64(item.Quantity) <= 0 {
|
normalizedItem, normalizeErr := normalizeFrozenRewardItem(groupID, index, item)
|
||||||
return nil, NewAppError(http.StatusBadRequest, "invalid_reward_item", fmt.Sprintf("reward group %d contains an invalid item", groupID))
|
if normalizeErr != nil {
|
||||||
|
return nil, normalizeErr
|
||||||
}
|
}
|
||||||
key := fmt.Sprintf("%d:%d", groupID, rewardConfigID)
|
key := fmt.Sprintf("%d:%d", groupID, rewardConfigID)
|
||||||
if _, exists := seen[key]; exists {
|
if _, exists := seen[key]; exists {
|
||||||
@ -626,8 +630,8 @@ func (s *Service) buildRewardSnapshots(ctx context.Context, activity model.YumiG
|
|||||||
displayName := truncate(firstNonBlank(item.Name, item.BadgeName, item.Remark), 500)
|
displayName := truncate(firstNonBlank(item.Name, item.BadgeName, item.Remark), 500)
|
||||||
rows = append(rows, model.YumiGiftChallengeRewardSnapshot{
|
rows = append(rows, model.YumiGiftChallengeRewardSnapshot{
|
||||||
ID: id, ActivityID: activity.ID, ResourceGroupID: groupID, RewardConfigID: rewardConfigID,
|
ID: id, ActivityID: activity.ID, ResourceGroupID: groupID, RewardConfigID: rewardConfigID,
|
||||||
RewardType: strings.ToUpper(strings.TrimSpace(item.Type)), DetailType: strings.TrimSpace(item.DetailType),
|
RewardType: normalizedItem.rewardType, DetailType: normalizedItem.detailType,
|
||||||
Content: strings.TrimSpace(item.Content), Quantity: int64(item.Quantity), SortOrder: sortOrder,
|
Content: normalizedItem.content, Quantity: normalizedItem.quantity, SortOrder: sortOrder,
|
||||||
Remark: strings.TrimSpace(item.Remark), Cover: strings.TrimSpace(item.Cover),
|
Remark: strings.TrimSpace(item.Remark), Cover: strings.TrimSpace(item.Cover),
|
||||||
SourceURL: strings.TrimSpace(item.SourceURL), DisplayName: displayName, CreateTime: now,
|
SourceURL: strings.TrimSpace(item.SourceURL), DisplayName: displayName, CreateTime: now,
|
||||||
})
|
})
|
||||||
@ -639,6 +643,71 @@ func (s *Service) buildRewardSnapshots(ctx context.Context, activity model.YumiG
|
|||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type frozenRewardItem struct {
|
||||||
|
rewardType string
|
||||||
|
detailType string
|
||||||
|
content string
|
||||||
|
quantity int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeFrozenRewardItem(groupID int64, index int, item integration.RewardGroupItem) (frozenRewardItem, error) {
|
||||||
|
result := frozenRewardItem{
|
||||||
|
rewardType: strings.ToUpper(strings.TrimSpace(item.Type)),
|
||||||
|
detailType: strings.TrimSpace(item.DetailType),
|
||||||
|
content: strings.TrimSpace(item.Content),
|
||||||
|
quantity: int64(item.Quantity),
|
||||||
|
}
|
||||||
|
itemLabel := fmt.Sprintf("reward group %d item %d (id=%d, type=%s, quantity=%d)",
|
||||||
|
groupID, index+1, int64(item.ID), result.rewardType, result.quantity)
|
||||||
|
if int64(item.ID) <= 0 {
|
||||||
|
return frozenRewardItem{}, NewAppError(http.StatusBadRequest, "invalid_reward_item", itemLabel+" has an invalid id")
|
||||||
|
}
|
||||||
|
if result.rewardType == "" {
|
||||||
|
return frozenRewardItem{}, NewAppError(http.StatusBadRequest, "invalid_reward_item", itemLabel+" has an empty type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 奖励组详情为了展示会把原始 PROPS 改写成 AVATAR_FRAME/RIDE 等子类型,
|
||||||
|
// 但冻结发奖接口只按基础类型分流;这里还原 PROPS,同时保留子类型用于展示和通知。
|
||||||
|
switch result.rewardType {
|
||||||
|
case "AVATAR_FRAME", "RIDE", "NOBLE_VIP", "THEME", "LAYOUT", "CHAT_BUBBLE",
|
||||||
|
"FLOAT_PICTURE", "DATA_CARD", "VIP_EFFECT_IMAGE", "RED_PACKET":
|
||||||
|
if result.detailType == "" || strings.EqualFold(result.detailType, "PROPS") {
|
||||||
|
result.detailType = result.rewardType
|
||||||
|
}
|
||||||
|
result.rewardType = "PROPS"
|
||||||
|
case "ROOM_BADGE", "HONOR_ACTIVITY":
|
||||||
|
if result.detailType == "" || strings.EqualFold(result.detailType, "BADGE") {
|
||||||
|
result.detailType = result.rewardType
|
||||||
|
}
|
||||||
|
result.rewardType = "BADGE"
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.quantity < 0 || result.quantity > math.MaxInt32 {
|
||||||
|
return frozenRewardItem{}, NewAppError(http.StatusBadRequest, "invalid_reward_item", itemLabel+" has an out-of-range quantity")
|
||||||
|
}
|
||||||
|
// 所有当前可发奖类型的 content 都是正整数资源 ID 或整数币值。启用时先拒绝空值、
|
||||||
|
// 非数字和零值,避免活动完成后才在 Java 发奖链路留下 UNKNOWN 记录。
|
||||||
|
contentNumber, contentOK := new(big.Int).SetString(result.content, 10)
|
||||||
|
if !contentOK || contentNumber.Sign() <= 0 {
|
||||||
|
return frozenRewardItem{}, NewAppError(http.StatusBadRequest, "invalid_reward_item", itemLabel+" has invalid content")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch result.rewardType {
|
||||||
|
case "GOLD", "DIAMOND":
|
||||||
|
// 币值由 content 承载,旧奖励组按契约把 quantity 保存为 0;不能把币值复制到
|
||||||
|
// quantity,否则既破坏冻结事实,也会与 Java 实际按 content 发币的逻辑不一致。
|
||||||
|
case "BADGE":
|
||||||
|
// quantity=0 是 Java 的永久徽章语义,正数才表示临时徽章天数。
|
||||||
|
case "PROPS", "GIFT", "EMOJI", "FRAGMENTS", "CUSTOMIZE", "PROP_COUPON":
|
||||||
|
if result.quantity == 0 {
|
||||||
|
return frozenRewardItem{}, NewAppError(http.StatusBadRequest, "invalid_reward_item", itemLabel+" requires a positive quantity")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return frozenRewardItem{}, NewAppError(http.StatusBadRequest, "unsupported_reward_item", itemLabel+" is not supported by the reward delivery service")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func firstNonBlank(values ...string) string {
|
func firstNonBlank(values ...string) string {
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
if value = strings.TrimSpace(value); value != "" {
|
if value = strings.TrimSpace(value); value != "" {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user