2026-06-08 14:12:23 +08:00

700 lines
17 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 resource
import (
"strings"
"unicode/utf8"
"hyapp/services/wallet-service/internal/domain/ledger"
)
const (
TypeAvatarFrame = "avatar_frame"
TypeProfileCard = "profile_card"
TypeCoin = "coin"
TypeVehicle = "vehicle"
TypeChatBubble = "chat_bubble"
TypeBadge = "badge"
TypeFloatingScreen = "floating_screen"
TypeGift = "gift"
TypeMicSeatIcon = "mic_seat_icon"
TypeMicSeatAnimation = "mic_seat_animation"
TypeEmojiPack = "emoji_pack"
StatusActive = "active"
StatusDisabled = "disabled"
GrantStrategyWalletCredit = "wallet_credit"
GrantStrategyNewEntitlement = "new_entitlement"
GrantStrategyExtendExpiry = "extend_expiry"
GrantStrategyIncreaseQuantity = "increase_quantity"
GrantStrategySetActiveFlag = "set_active_flag"
GrantSubjectResource = "resource"
GrantSubjectGroup = "resource_group"
GrantSourceAdmin = "admin"
GrantSourceManagerCenter = "manager_center"
GrantSourceGrowthLevel = "growth_level"
GrantSourceAchievement = "achievement"
GrantSourceResourceShop = "resource_shop"
GrantStatusDone = "succeeded"
ResultWalletCredit = "wallet_credit"
ResultEntitlement = "entitlement"
GroupItemTypeResource = "resource"
GroupItemTypeWalletAsset = "wallet_asset"
GiftTypeNormal = "normal"
GiftTypeCP = "cp"
GiftTypeLucky = "lucky"
GiftTypeSuperLucky = "super_lucky"
GiftTypeExclusive = "exclusive"
GiftTypeNoble = "noble"
GiftTypeFlag = "flag"
GiftTypeActivity = "activity"
GiftTypeMagic = "magic"
GiftTypeCustom = "custom"
GiftTypeTabKeyNormal = "Gift"
GiftTypeTabKeyCP = "CP"
GiftTypeTabKeyLucky = "Lucky"
GiftTypeTabKeySuperLucky = "Super Lucky"
GiftTypeTabKeyExclusive = "Exclusive"
GiftTypeTabKeyNoble = "Noble"
GiftTypeTabKeyFlag = "Flag"
GiftTypeTabKeyActivity = "Activity"
GiftTypeTabKeyMagic = "Magic"
GiftTypeTabKeyCustom = "Custom"
GiftEffectAnimation = "animation"
GiftEffectMusic = "music"
GiftEffectGlobalBroadcast = "global_broadcast"
PriceTypeCoin = "coin"
PriceTypeFree = "free"
ShopDurationOneDay int32 = 1
ShopDurationThreeDays int32 = 3
ShopDurationSevenDays int32 = 7
)
type Resource struct {
AppCode string
ResourceID int64
ResourceCode string
ResourceType string
Name string
Status string
Grantable bool
GrantStrategy string
WalletAssetType string
WalletAssetAmount int64
PriceType string
CoinPrice int64
// GiftPointAmount 是历史字段GIFT_POINT 已下线,新资源和礼物配置只保留真实 COIN 价格。
GiftPointAmount int64
UsageScopes []string
AssetURL string
PreviewURL string
AnimationURL string
MetadataJSON string
SortOrder int32
CreatedByUserID int64
UpdatedByUserID int64
CreatedAtMS int64
UpdatedAtMS int64
ManagerGrantEnabled bool
}
type ResourceGroupItem struct {
GroupItemID int64
GroupID int64
ItemType string
ResourceID int64
Resource Resource
WalletAssetType string
WalletAssetAmount int64
Quantity int64
DurationMS int64
SortOrder int32
CreatedAtMS int64
UpdatedAtMS int64
}
type ResourceGroup struct {
AppCode string
GroupID int64
GroupCode string
Name string
Status string
Description string
SortOrder int32
Items []ResourceGroupItem
CreatedByUserID int64
UpdatedByUserID int64
CreatedAtMS int64
UpdatedAtMS int64
}
type GiftConfig struct {
AppCode string
GiftID string
ResourceID int64
Resource Resource
Status string
Name string
SortOrder int32
PresentationJSON string
PriceVersion string
CoinPrice int64
// GiftPointAmount 是历史字段;送礼结算不再读取,新配置固定为 0。
GiftPointAmount int64
HeatValue int64
GiftTypeCode string
// CPRelationType 只在 gift_type_code=cp 的礼物上使用,值为 cp、brother 或 sister。
CPRelationType string
ChargeAssetType string
EffectiveFromMS int64
EffectiveToMS int64
EffectTypes []string
CreatedByUserID int64
UpdatedByUserID int64
CreatedAtMS int64
UpdatedAtMS int64
RegionIDs []int64
}
type GiftTypeConfig struct {
AppCode string
TypeCode string
Name string
TabKey string
Status string
SortOrder int32
CreatedByUserID int64
UpdatedByUserID int64
CreatedAtMS int64
UpdatedAtMS int64
}
type ResourceShopItem struct {
AppCode string
ShopItemID int64
ResourceID int64
Resource Resource
Status string
DurationDays int32
PriceType string
CoinPrice int64
EffectiveFromMS int64
EffectiveToMS int64
SortOrder int32
CreatedByUserID int64
UpdatedByUserID int64
CreatedAtMS int64
UpdatedAtMS int64
}
type UserResourceEntitlement struct {
AppCode string
EntitlementID string
UserID int64
ResourceID int64
Resource Resource
Status string
Quantity int64
RemainingQuantity int64
EffectiveAtMS int64
ExpiresAtMS int64
SourceGrantID string
CreatedAtMS int64
UpdatedAtMS int64
Equipped bool
}
type ResourceGrantItem struct {
GrantItemID int64
GrantID string
ResourceID int64
ResourceSnapshotJSON string
Quantity int64
DurationMS int64
ResultType string
WalletTransactionID string
EntitlementID string
CreatedAtMS int64
}
type ResourceGrant struct {
AppCode string
GrantID string
CommandID string
TargetUserID int64
GrantSource string
GrantSubjectType string
GrantSubjectID string
Status string
Reason string
OperatorUserID int64
Items []ResourceGrantItem
CreatedAtMS int64
UpdatedAtMS int64
}
type ListResourcesQuery struct {
AppCode string
ResourceType string
Status string
Keyword string
Page int32
PageSize int32
ActiveOnly bool
ManagerGrantOnly bool
}
type ResourceCommand struct {
AppCode string
ResourceID int64
ResourceCode string
ResourceType string
Name string
Status string
Grantable bool
GrantStrategy string
WalletAssetType string
WalletAssetAmount int64
PriceType string
CoinPrice int64
// GiftPointAmount 是历史字段;保存资源时固定为 0保留字段只兼容旧表结构。
GiftPointAmount int64
UsageScopes []string
AssetURL string
PreviewURL string
AnimationURL string
MetadataJSON string
SortOrder int32
OperatorUserID int64
ManagerGrantEnabled bool
}
type StatusCommand struct {
AppCode string
ID int64
StringID string
Status string
OperatorUserID int64
}
type ResourceGroupItemInput struct {
ItemType string
ResourceID int64
WalletAssetType string
WalletAssetAmount int64
Quantity int64
DurationMS int64
SortOrder int32
}
type ListResourceGroupsQuery struct {
AppCode string
Status string
Keyword string
Page int32
PageSize int32
ActiveOnly bool
}
type ResourceGroupCommand struct {
AppCode string
GroupID int64
GroupCode string
Name string
Status string
Description string
SortOrder int32
Items []ResourceGroupItemInput
OperatorUserID int64
}
type ListGiftConfigsQuery struct {
AppCode string
Status string
Keyword string
GiftTypeCode string
Page int32
PageSize int32
ActiveOnly bool
RegionID int64
FilterRegion bool
}
type ListGiftTypeConfigsQuery struct {
AppCode string
Status string
ActiveOnly bool
}
type GiftConfigCommand struct {
AppCode string
GiftID string
ResourceID int64
Status string
Name string
SortOrder int32
PresentationJSON string
PriceVersion string
CoinPrice int64
// GiftPointAmount 是历史字段;保存礼物价格时固定为 0业务计算只读 CoinPrice。
GiftPointAmount int64
HeatValue int64
EffectiveAtMS int64
GiftTypeCode string
// CPRelationType 会写入 presentation_json避免新增 gift_configs 表字段。
CPRelationType string
ChargeAssetType string
EffectiveFromMS int64
EffectiveToMS int64
EffectTypes []string
OperatorUserID int64
RegionIDs []int64
}
type GiftTypeConfigCommand struct {
AppCode string
TypeCode string
Name string
TabKey string
Status string
SortOrder int32
OperatorUserID int64
}
type GrantResourceCommand struct {
AppCode string
CommandID string
TargetUserID int64
ResourceID int64
Quantity int64
DurationMS int64
Reason string
OperatorUserID int64
GrantSource string
}
type GrantResourceGroupCommand struct {
AppCode string
CommandID string
TargetUserID int64
GroupID int64
Reason string
OperatorUserID int64
GrantSource string
}
type ListUserResourcesQuery struct {
AppCode string
UserID int64
ResourceType string
ActiveOnly bool
}
type EquipUserResourceCommand struct {
AppCode string
UserID int64
ResourceID int64
EntitlementID string
}
type UnequipUserResourceCommand struct {
AppCode string
UserID int64
ResourceType string
}
type UnequipUserResourceResult struct {
ResourceType string
Unequipped bool
UpdatedAtMS int64
}
type BatchGetUserEquippedResourcesQuery struct {
AppCode string
UserIDs []int64
ResourceTypes []string
}
type UserEquippedResources struct {
UserID int64
Resources []UserResourceEntitlement
}
type ListResourceGrantsQuery struct {
AppCode string
TargetUserID int64
Status string
Page int32
PageSize int32
}
type ResourceShopItemInput struct {
ShopItemID int64
ResourceID int64
Status string
DurationDays int32
EffectiveFromMS int64
EffectiveToMS int64
SortOrder int32
}
type ListResourceShopItemsQuery struct {
AppCode string
ResourceType string
Status string
Keyword string
Page int32
PageSize int32
ActiveOnly bool
}
type ResourceShopItemsCommand struct {
AppCode string
Items []ResourceShopItemInput
OperatorUserID int64
}
type ResourceShopItemStatusCommand struct {
AppCode string
ShopItemID int64
Status string
OperatorUserID int64
}
type ResourceShopPurchaseCommand struct {
AppCode string
CommandID string
UserID int64
ShopItemID int64
}
type ResourceShopPurchaseReceipt struct {
OrderID string
TransactionID string
ShopItem ResourceShopItem
Resource UserResourceEntitlement
Balance ledger.AssetBalance
CoinSpent int64
GrantID string
}
// BadgeGrantOutbox is a wallet resource grant event relayed to activity-service badge display projection.
type BadgeGrantOutbox struct {
AppCode string
EventID string
EventType string
CommandID string
UserID int64
GrantID string
GrantSource string
PayloadJSON string
Status string
AttemptCount int32
FailureReason string
CreatedAtMS int64
UpdatedAtMS int64
Items []BadgeGrantItem
}
// BadgeGrantItem is the badge subset of a resource grant item.
type BadgeGrantItem struct {
ResourceID int64 `json:"resource_id"`
ResourceCode string `json:"resource_code"`
ResourceType string `json:"resource_type"`
Name string `json:"name"`
EntitlementID string `json:"entitlement_id"`
ResourceSnapshotJSON string `json:"resource_snapshot_json"`
MetadataJSON string `json:"metadata_json"`
}
func NormalizeStatus(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
return StatusActive
}
return value
}
func NormalizeGrantSource(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
return GrantSourceAdmin
}
return value
}
func ValidResourceType(value string) bool {
switch strings.ToLower(strings.TrimSpace(value)) {
case TypeAvatarFrame, TypeProfileCard, TypeCoin, TypeVehicle, TypeChatBubble, TypeBadge, TypeFloatingScreen, TypeGift, TypeMicSeatIcon, TypeMicSeatAnimation, TypeEmojiPack:
return true
default:
return false
}
}
func ResourceTypeSellableInShop(value string) bool {
switch NormalizeResourceType(value) {
case TypeAvatarFrame, TypeProfileCard, TypeVehicle, TypeChatBubble, TypeBadge, TypeMicSeatIcon, TypeMicSeatAnimation:
return true
default:
return false
}
}
func ValidShopDurationDays(value int32) bool {
switch value {
case ShopDurationOneDay, ShopDurationThreeDays, ShopDurationSevenDays:
return true
default:
return false
}
}
func ValidStatus(value string) bool {
switch NormalizeStatus(value) {
case StatusActive, StatusDisabled:
return true
default:
return false
}
}
func ValidGrantStrategy(value string) bool {
switch strings.ToLower(strings.TrimSpace(value)) {
case GrantStrategyWalletCredit, GrantStrategyNewEntitlement, GrantStrategyExtendExpiry, GrantStrategyIncreaseQuantity, GrantStrategySetActiveFlag:
return true
default:
return false
}
}
func NormalizeGroupItemType(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
return GroupItemTypeResource
}
return value
}
func ValidGroupItemType(value string) bool {
switch NormalizeGroupItemType(value) {
case GroupItemTypeResource, GroupItemTypeWalletAsset:
return true
default:
return false
}
}
func NormalizeResourceType(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}
func NormalizeGrantStrategy(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}
func NormalizeWalletAssetType(value string) string {
return strings.ToUpper(strings.TrimSpace(value))
}
func NormalizePriceType(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}
func ValidPriceType(value string) bool {
switch NormalizePriceType(value) {
case PriceTypeCoin, PriceTypeFree:
return true
default:
return false
}
}
func NormalizeGiftTypeCode(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
value = strings.ReplaceAll(value, "-", "_")
if value == "" {
return GiftTypeNormal
}
return value
}
func ValidGiftTypeCode(value string) bool {
value = NormalizeGiftTypeCode(value)
if value == "" || len(value) > 32 {
return false
}
for index, char := range value {
if index == 0 && (char < 'a' || char > 'z') {
return false
}
if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == '_' {
continue
}
return false
}
return true
}
func NormalizeGiftTypeTabKey(value string) string {
return strings.TrimSpace(value)
}
func ValidGiftTypeTabKey(value string) bool {
value = NormalizeGiftTypeTabKey(value)
return value != "" && utf8.RuneCountInString(value) <= 32
}
func DefaultGiftTypeConfigs(appCode string) []GiftTypeConfig {
appCode = strings.TrimSpace(appCode)
defaults := []GiftTypeConfig{
{TypeCode: GiftTypeNormal, Name: "普通礼物", TabKey: GiftTypeTabKeyNormal, Status: StatusActive, SortOrder: 10},
{TypeCode: GiftTypeCP, Name: "CP礼物", TabKey: GiftTypeTabKeyCP, Status: StatusActive, SortOrder: 20},
{TypeCode: GiftTypeLucky, Name: "幸运礼物", TabKey: GiftTypeTabKeyLucky, Status: StatusActive, SortOrder: 30},
{TypeCode: GiftTypeSuperLucky, Name: "超级幸运礼物", TabKey: GiftTypeTabKeySuperLucky, Status: StatusActive, SortOrder: 40},
{TypeCode: GiftTypeExclusive, Name: "专属礼物", TabKey: GiftTypeTabKeyExclusive, Status: StatusActive, SortOrder: 50},
{TypeCode: GiftTypeNoble, Name: "贵族礼物", TabKey: GiftTypeTabKeyNoble, Status: StatusActive, SortOrder: 60},
{TypeCode: GiftTypeFlag, Name: "国旗礼物", TabKey: GiftTypeTabKeyFlag, Status: StatusActive, SortOrder: 70},
{TypeCode: GiftTypeActivity, Name: "活动礼物", TabKey: GiftTypeTabKeyActivity, Status: StatusActive, SortOrder: 80},
{TypeCode: GiftTypeMagic, Name: "魔法礼物", TabKey: GiftTypeTabKeyMagic, Status: StatusActive, SortOrder: 90},
{TypeCode: GiftTypeCustom, Name: "定制礼物", TabKey: GiftTypeTabKeyCustom, Status: StatusActive, SortOrder: 100},
}
for index := range defaults {
defaults[index].AppCode = appCode
}
return defaults
}
func NormalizeGiftEffectType(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}
func ValidGiftEffectType(value string) bool {
switch NormalizeGiftEffectType(value) {
case GiftEffectAnimation, GiftEffectMusic, GiftEffectGlobalBroadcast:
return true
default:
return false
}
}
func IsWalletCredit(resource Resource) bool {
return NormalizeGrantStrategy(resource.GrantStrategy) == GrantStrategyWalletCredit
}
func IsCoinResource(resource Resource) bool {
return NormalizeResourceType(resource.ResourceType) == TypeCoin
}
func WalletAssetForCoin() string {
return ledger.AssetCoin
}