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 }