package http import ( "net/http" "strings" userv1 "hyapp.local/api/proto/user/v1" walletv1 "hyapp.local/api/proto/wallet/v1" "hyapp/pkg/appcode" "hyapp/services/gateway-service/internal/auth" ) type rechargeProductData struct { ProductID int64 `json:"product_id"` ProductCode string `json:"product_code"` Channel string `json:"channel"` CurrencyCode string `json:"currency_code"` CoinAmount int64 `json:"coin_amount"` AmountMinor int64 `json:"amount_minor"` PolicyVersion string `json:"policy_version"` Enabled bool `json:"enabled"` SortOrder int32 `json:"sort_order"` } type diamondExchangeRuleData struct { ExchangeType string `json:"exchange_type"` FromAssetType string `json:"from_asset_type"` ToAssetType string `json:"to_asset_type"` FromAmount int64 `json:"from_amount"` ToAmount int64 `json:"to_amount"` Enabled bool `json:"enabled"` } type walletOverviewData struct { Balances []assetBalanceData `json:"balances"` FeatureFlags walletFeatureFlagsData `json:"feature_flags"` } type walletFeatureFlagsData struct { RechargeEnabled bool `json:"recharge_enabled"` DiamondExchangeEnabled bool `json:"diamond_exchange_enabled"` WithdrawEnabled bool `json:"withdraw_enabled"` } type walletTransactionData struct { EntryID int64 `json:"entry_id"` TransactionID string `json:"transaction_id"` BizType string `json:"biz_type"` AssetType string `json:"asset_type"` AvailableDelta int64 `json:"available_delta"` FrozenDelta int64 `json:"frozen_delta"` AvailableAfter int64 `json:"available_after"` FrozenAfter int64 `json:"frozen_after"` CounterpartyUserID int64 `json:"counterparty_user_id"` RoomID string `json:"room_id"` CreatedAtMS int64 `json:"created_at_ms"` } type withdrawalApplyRequestBody struct { CommandID string `json:"command_id"` CommandIDAlt string `json:"commandId"` Amount int64 `json:"amount"` PayoutAccount string `json:"payout_account"` PayoutAlt string `json:"payoutAccount"` Reason string `json:"reason"` } type withdrawalData struct { WithdrawalID string `json:"withdrawal_id"` UserID int64 `json:"user_id"` AssetType string `json:"asset_type"` Amount int64 `json:"amount"` Status string `json:"status"` PayoutAccount string `json:"payout_account"` Reason string `json:"reason"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } type vipPurchaseRequestBody struct { CommandID string `json:"command_id"` CommandIDAlt string `json:"commandId"` Level int32 `json:"level"` } type vipPackageData struct { Level int32 `json:"level"` Name string `json:"name"` Status string `json:"status"` PriceCoin int64 `json:"price_coin"` DurationMS int64 `json:"duration_ms"` RewardResourceGroupID int64 `json:"reward_resource_group_id"` RewardItems []vipRewardItemData `json:"reward_items"` CanPurchase bool `json:"can_purchase"` SortOrder int32 `json:"sort_order"` } type vipRewardItemData struct { ResourceID int64 `json:"resource_id"` ResourceCode string `json:"resource_code"` ResourceType string `json:"resource_type"` Name string `json:"name"` Quantity int64 `json:"quantity"` ExpiresAtMS int64 `json:"expires_at_ms"` } // getWalletOverview 返回钱包二级页摘要;我的页首屏使用更轻的 WalletValueSummary。 func (h *Handler) getWalletOverview(writer http.ResponseWriter, request *http.Request) { if h.walletClient == nil { writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error") return } resp, err := h.walletClient.GetWalletOverview(request.Context(), &walletv1.GetWalletOverviewRequest{ RequestId: requestIDFromContext(request.Context()), AppCode: appcode.FromContext(request.Context()), UserId: auth.UserIDFromContext(request.Context()), }) if err != nil { writeRPCError(writer, request, err) return } writeOK(writer, request, walletOverviewFromProto(resp)) } // listRechargeProducts 返回用户当前区域可用的充值档位。 func (h *Handler) listRechargeProducts(writer http.ResponseWriter, request *http.Request) { if h.walletClient == nil || h.userProfileClient == nil { writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error") return } userID := auth.UserIDFromContext(request.Context()) profileResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{ Meta: authRequestMeta(request, ""), UserId: userID, }) if err != nil { writeRPCError(writer, request, err) return } resp, err := h.walletClient.ListRechargeProducts(request.Context(), &walletv1.ListRechargeProductsRequest{ RequestId: requestIDFromContext(request.Context()), AppCode: appcode.FromContext(request.Context()), UserId: userID, RegionId: profileResp.GetUser().GetRegionId(), }) if err != nil { writeRPCError(writer, request, err) return } products := make([]rechargeProductData, 0, len(resp.GetProducts())) for _, product := range resp.GetProducts() { products = append(products, rechargeProductFromProto(product)) } writeOK(writer, request, map[string]any{"channels": resp.GetChannels(), "products": products}) } // getDiamondExchangeConfig 返回钻石兑换配置。 func (h *Handler) getDiamondExchangeConfig(writer http.ResponseWriter, request *http.Request) { if h.walletClient == nil { writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error") return } resp, err := h.walletClient.GetDiamondExchangeConfig(request.Context(), &walletv1.GetDiamondExchangeConfigRequest{ RequestId: requestIDFromContext(request.Context()), AppCode: appcode.FromContext(request.Context()), UserId: auth.UserIDFromContext(request.Context()), }) if err != nil { writeRPCError(writer, request, err) return } rules := make([]diamondExchangeRuleData, 0, len(resp.GetRules())) for _, rule := range resp.GetRules() { rules = append(rules, diamondExchangeRuleFromProto(rule)) } writeOK(writer, request, map[string]any{"rules": rules}) } // listWalletTransactions 返回当前用户钱包流水分页。 func (h *Handler) listWalletTransactions(writer http.ResponseWriter, request *http.Request) { if h.walletClient == nil { writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error") return } page, ok := parsePositiveInt32Query(request, "page", 1) if !ok { writeError(writer, request, http.StatusBadRequest, codeInvalidArgument, "invalid argument") return } pageSize, ok := parsePositiveInt32Query(request, "page_size", 20) if !ok { writeError(writer, request, http.StatusBadRequest, codeInvalidArgument, "invalid argument") return } resp, err := h.walletClient.ListWalletTransactions(request.Context(), &walletv1.ListWalletTransactionsRequest{ RequestId: requestIDFromContext(request.Context()), AppCode: appcode.FromContext(request.Context()), UserId: auth.UserIDFromContext(request.Context()), AssetType: strings.TrimSpace(request.URL.Query().Get("asset_type")), Page: page, PageSize: pageSize, }) if err != nil { writeRPCError(writer, request, err) return } items := make([]walletTransactionData, 0, len(resp.GetTransactions())) for _, item := range resp.GetTransactions() { items = append(items, walletTransactionFromProto(item)) } writeOK(writer, request, map[string]any{"items": items, "total": resp.GetTotal(), "page": page, "page_size": pageSize}) } // applyWithdrawal 创建待人工审核的提现申请。 func (h *Handler) applyWithdrawal(writer http.ResponseWriter, request *http.Request) { if h.walletClient == nil { writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error") return } var body withdrawalApplyRequestBody if !decode(writer, request, &body) { return } commandID := strings.TrimSpace(body.CommandID) if commandID == "" { commandID = strings.TrimSpace(body.CommandIDAlt) } payoutAccount := strings.TrimSpace(body.PayoutAccount) if payoutAccount == "" { payoutAccount = strings.TrimSpace(body.PayoutAlt) } if commandID == "" || body.Amount <= 0 || payoutAccount == "" { writeError(writer, request, http.StatusBadRequest, codeInvalidArgument, "invalid argument") return } resp, err := h.walletClient.ApplyWithdrawal(request.Context(), &walletv1.ApplyWithdrawalRequest{ CommandId: commandID, AppCode: appcode.FromContext(request.Context()), UserId: auth.UserIDFromContext(request.Context()), Amount: body.Amount, PayoutAccount: payoutAccount, Reason: strings.TrimSpace(body.Reason), }) if err != nil { writeRPCError(writer, request, err) return } writeOK(writer, request, map[string]any{ "withdrawal": withdrawalFromProto(resp.GetWithdrawal()), "balance": balanceFromProto(resp.GetBalance()), }) } func rechargeProductFromProto(product *walletv1.RechargeProduct) rechargeProductData { if product == nil { return rechargeProductData{} } return rechargeProductData{ ProductID: product.GetProductId(), ProductCode: product.GetProductCode(), Channel: product.GetChannel(), CurrencyCode: product.GetCurrencyCode(), CoinAmount: product.GetCoinAmount(), AmountMinor: product.GetAmountMinor(), PolicyVersion: product.GetPolicyVersion(), Enabled: product.GetEnabled(), SortOrder: product.GetSortOrder(), } } func diamondExchangeRuleFromProto(rule *walletv1.DiamondExchangeRule) diamondExchangeRuleData { if rule == nil { return diamondExchangeRuleData{} } return diamondExchangeRuleData{ ExchangeType: rule.GetExchangeType(), FromAssetType: rule.GetFromAssetType(), ToAssetType: rule.GetToAssetType(), FromAmount: rule.GetFromAmount(), ToAmount: rule.GetToAmount(), Enabled: rule.GetEnabled(), } } func walletOverviewFromProto(resp *walletv1.GetWalletOverviewResponse) walletOverviewData { if resp == nil { return walletOverviewData{} } balances := make([]assetBalanceData, 0, len(resp.GetBalances())) for _, balance := range resp.GetBalances() { balances = append(balances, balanceFromProto(balance)) } flags := resp.GetFeatureFlags() return walletOverviewData{ Balances: balances, FeatureFlags: walletFeatureFlagsData{ RechargeEnabled: flags.GetRechargeEnabled(), DiamondExchangeEnabled: flags.GetDiamondExchangeEnabled(), WithdrawEnabled: flags.GetWithdrawEnabled(), }, } } func walletTransactionFromProto(item *walletv1.WalletTransaction) walletTransactionData { if item == nil { return walletTransactionData{} } return walletTransactionData{ EntryID: item.GetEntryId(), TransactionID: item.GetTransactionId(), BizType: item.GetBizType(), AssetType: item.GetAssetType(), AvailableDelta: item.GetAvailableDelta(), FrozenDelta: item.GetFrozenDelta(), AvailableAfter: item.GetAvailableAfter(), FrozenAfter: item.GetFrozenAfter(), CounterpartyUserID: item.GetCounterpartyUserId(), RoomID: item.GetRoomId(), CreatedAtMS: item.GetCreatedAtMs(), } } func withdrawalFromProto(item *walletv1.WithdrawalRequest) withdrawalData { if item == nil { return withdrawalData{} } return withdrawalData{ WithdrawalID: item.GetWithdrawalId(), UserID: item.GetUserId(), AssetType: item.GetAssetType(), Amount: item.GetAmount(), Status: item.GetStatus(), PayoutAccount: item.GetPayoutAccount(), Reason: item.GetReason(), CreatedAtMS: item.GetCreatedAtMs(), UpdatedAtMS: item.GetUpdatedAtMs(), } } func balanceFromProto(balance *walletv1.AssetBalance) assetBalanceData { if balance == nil { return assetBalanceData{} } return assetBalanceData{ AssetType: balance.GetAssetType(), AvailableAmount: balance.GetAvailableAmount(), FrozenAmount: balance.GetFrozenAmount(), Version: balance.GetVersion(), } }