package resource import ( "strings" "hyapp-admin-server/internal/appctx" "hyapp-admin-server/internal/middleware" walletv1 "hyapp.local/api/proto/wallet/v1" "github.com/gin-gonic/gin" ) const dayMillis int64 = 24 * 60 * 60 * 1000 type resourceRequest struct { ResourceCode string `json:"resourceCode"` ResourceType string `json:"resourceType"` Name string `json:"name"` Status string `json:"status"` Amount int64 `json:"amount"` AssetURL string `json:"assetUrl"` PreviewURL string `json:"previewUrl"` SortOrder int32 `json:"sortOrder"` } type resourceGroupRequest struct { GroupCode string `json:"groupCode"` Name string `json:"name"` Status string `json:"status"` Description string `json:"description"` SortOrder int32 `json:"sortOrder"` Items []resourceGroupItemRequest `json:"items"` } type resourceGroupItemRequest struct { ItemType string `json:"itemType"` ResourceID int64 `json:"resourceId"` AssetType string `json:"assetType"` Amount int64 `json:"amount"` DurationDays int64 `json:"durationDays"` SortOrder int32 `json:"sortOrder"` } type giftRequest struct { GiftID string `json:"giftId"` ResourceID int64 `json:"resourceId"` Status string `json:"status"` Name string `json:"name"` SortOrder int32 `json:"sortOrder"` PresentationJSON string `json:"presentationJson"` PriceVersion string `json:"priceVersion"` CoinPrice int64 `json:"coinPrice"` GiftPointAmount int64 `json:"giftPointAmount"` HeatValue int64 `json:"heatValue"` EffectiveAtMS int64 `json:"effectiveAtMs"` RegionIDs []int64 `json:"regionIds"` } type grantResourceRequest struct { CommandID string `json:"commandId"` TargetUserID int64 `json:"targetUserId"` ResourceID int64 `json:"resourceId"` Quantity int64 `json:"quantity"` DurationMS int64 `json:"durationMs"` Reason string `json:"reason"` } type grantGroupRequest struct { CommandID string `json:"commandId"` TargetUserID int64 `json:"targetUserId"` GroupID int64 `json:"groupId"` Reason string `json:"reason"` } func (r resourceRequest) createProto(c *gin.Context) *walletv1.CreateResourceRequest { resourceType := normalizeResourceType(r.ResourceType) walletAssetType, walletAssetAmount := resourceWalletAsset(resourceType, r.Amount) return &walletv1.CreateResourceRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), ResourceCode: strings.TrimSpace(r.ResourceCode), ResourceType: resourceType, Name: strings.TrimSpace(r.Name), Status: strings.TrimSpace(r.Status), Grantable: true, GrantStrategy: resourceGrantStrategy(resourceType), WalletAssetType: walletAssetType, WalletAssetAmount: walletAssetAmount, UsageScopes: []string{resourceType}, AssetUrl: strings.TrimSpace(r.AssetURL), PreviewUrl: strings.TrimSpace(r.PreviewURL), AnimationUrl: "", MetadataJson: "{}", SortOrder: r.SortOrder, OperatorUserId: actorID(c), } } func (r resourceRequest) updateProto(c *gin.Context, resourceID int64) *walletv1.UpdateResourceRequest { resourceType := normalizeResourceType(r.ResourceType) walletAssetType, walletAssetAmount := resourceWalletAsset(resourceType, r.Amount) return &walletv1.UpdateResourceRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), ResourceId: resourceID, ResourceCode: strings.TrimSpace(r.ResourceCode), ResourceType: resourceType, Name: strings.TrimSpace(r.Name), Status: strings.TrimSpace(r.Status), Grantable: true, GrantStrategy: resourceGrantStrategy(resourceType), WalletAssetType: walletAssetType, WalletAssetAmount: walletAssetAmount, UsageScopes: []string{resourceType}, AssetUrl: strings.TrimSpace(r.AssetURL), PreviewUrl: strings.TrimSpace(r.PreviewURL), AnimationUrl: "", MetadataJson: "{}", SortOrder: r.SortOrder, OperatorUserId: actorID(c), } } func (r resourceGroupRequest) createProto(c *gin.Context) *walletv1.CreateResourceGroupRequest { return &walletv1.CreateResourceGroupRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GroupCode: strings.TrimSpace(r.GroupCode), Name: strings.TrimSpace(r.Name), Status: strings.TrimSpace(r.Status), Description: strings.TrimSpace(r.Description), SortOrder: r.SortOrder, Items: groupItemInputs(r.Items), OperatorUserId: actorID(c), } } func (r resourceGroupRequest) updateProto(c *gin.Context, groupID int64) *walletv1.UpdateResourceGroupRequest { return &walletv1.UpdateResourceGroupRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GroupId: groupID, GroupCode: strings.TrimSpace(r.GroupCode), Name: strings.TrimSpace(r.Name), Status: strings.TrimSpace(r.Status), Description: strings.TrimSpace(r.Description), SortOrder: r.SortOrder, Items: groupItemInputs(r.Items), OperatorUserId: actorID(c), } } func (r giftRequest) createProto(c *gin.Context) *walletv1.CreateGiftConfigRequest { return &walletv1.CreateGiftConfigRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GiftId: strings.TrimSpace(r.GiftID), ResourceId: r.ResourceID, Status: strings.TrimSpace(r.Status), Name: strings.TrimSpace(r.Name), SortOrder: r.SortOrder, PresentationJson: strings.TrimSpace(r.PresentationJSON), PriceVersion: strings.TrimSpace(r.PriceVersion), CoinPrice: r.CoinPrice, GiftPointAmount: r.GiftPointAmount, HeatValue: r.HeatValue, EffectiveAtMs: r.EffectiveAtMS, OperatorUserId: actorID(c), RegionIds: r.RegionIDs, } } func (r giftRequest) updateProto(c *gin.Context, giftID string) *walletv1.UpdateGiftConfigRequest { return &walletv1.UpdateGiftConfigRequest{ RequestId: middleware.CurrentRequestID(c), AppCode: appctx.FromContext(c.Request.Context()), GiftId: strings.TrimSpace(giftID), ResourceId: r.ResourceID, Status: strings.TrimSpace(r.Status), Name: strings.TrimSpace(r.Name), SortOrder: r.SortOrder, PresentationJson: strings.TrimSpace(r.PresentationJSON), PriceVersion: strings.TrimSpace(r.PriceVersion), CoinPrice: r.CoinPrice, GiftPointAmount: r.GiftPointAmount, HeatValue: r.HeatValue, EffectiveAtMs: r.EffectiveAtMS, OperatorUserId: actorID(c), RegionIds: r.RegionIDs, } } func groupItemInputs(items []resourceGroupItemRequest) []*walletv1.ResourceGroupItemInput { out := make([]*walletv1.ResourceGroupItemInput, 0, len(items)) for _, item := range items { itemType := strings.ToLower(strings.TrimSpace(item.ItemType)) if itemType == "wallet_asset" { out = append(out, &walletv1.ResourceGroupItemInput{ ItemType: itemType, WalletAssetType: strings.TrimSpace(item.AssetType), WalletAssetAmount: item.Amount, Quantity: 1, SortOrder: item.SortOrder, }) continue } out = append(out, &walletv1.ResourceGroupItemInput{ ItemType: "resource", ResourceId: item.ResourceID, Quantity: 1, DurationMs: item.DurationDays * dayMillis, SortOrder: item.SortOrder, }) } return out } func normalizeResourceType(value string) string { return strings.ToLower(strings.TrimSpace(value)) } func resourceWalletAsset(resourceType string, amount int64) (string, int64) { if resourceType == "coin" { return "COIN", amount } return "", 0 } func resourceGrantStrategy(resourceType string) string { switch resourceType { case "coin": return "wallet_credit" case "avatar_frame", "vehicle", "chat_bubble": return "extend_expiry" case "badge": return "set_active_flag" default: return "increase_quantity" } }