351 lines
12 KiB
Go
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(),
|
|
}
|
|
}
|