351 lines
12 KiB
Go

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(),
}
}