785 lines
30 KiB
Go
785 lines
30 KiB
Go
package grpc
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
"hyapp/services/wallet-service/internal/domain/ledger"
|
||
walletservice "hyapp/services/wallet-service/internal/service/wallet"
|
||
)
|
||
|
||
// Server 把 wallet-service 用例层适配为 gRPC 接口。
|
||
type Server struct {
|
||
walletv1.UnimplementedWalletServiceServer
|
||
|
||
svc *walletservice.Service
|
||
}
|
||
|
||
// NewServer 创建 wallet gRPC server。
|
||
func NewServer(svc *walletservice.Service) *Server {
|
||
return &Server{svc: svc}
|
||
}
|
||
|
||
// DebitGift 只负责 protobuf 转换和错误映射,扣费规则在 service/repository。
|
||
func (s *Server) DebitGift(ctx context.Context, req *walletv1.DebitGiftRequest) (*walletv1.DebitGiftResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.DebitGift(ctx, ledger.DebitGiftCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
RoomID: req.GetRoomId(),
|
||
SenderUserID: req.GetSenderUserId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
GiftID: req.GetGiftId(),
|
||
GiftCount: req.GetGiftCount(),
|
||
PriceVersion: req.GetPriceVersion(),
|
||
RegionID: req.GetRegionId(),
|
||
TargetIsHost: req.GetTargetIsHost(),
|
||
// 工资政策区域来自 user-service host profile,不能用房间 visible_region_id 兜底。
|
||
TargetHostRegionID: req.GetTargetHostRegionId(),
|
||
TargetAgencyOwnerUserID: req.GetTargetAgencyOwnerUserId(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.DebitGiftResponse{
|
||
BillingReceiptId: receipt.BillingReceiptID,
|
||
BalanceAfter: receipt.BalanceAfter,
|
||
TransactionId: receipt.TransactionID,
|
||
CoinSpent: receipt.CoinSpent,
|
||
ChargeAssetType: receipt.ChargeAssetType,
|
||
ChargeAmount: receipt.ChargeAmount,
|
||
GiftPointAdded: receipt.GiftPointAdded,
|
||
HeatValue: receipt.HeatValue,
|
||
GiftTypeCode: receipt.GiftTypeCode,
|
||
PriceVersion: receipt.PriceVersion,
|
||
HostPeriodDiamondAdded: receipt.HostPeriodDiamondAdded,
|
||
HostPeriodCycleKey: receipt.HostPeriodCycleKey,
|
||
}, nil
|
||
}
|
||
|
||
// GetBalances 返回用户多资产余额投影。
|
||
func (s *Server) GetBalances(ctx context.Context, req *walletv1.GetBalancesRequest) (*walletv1.GetBalancesResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
balances, err := s.svc.GetBalances(ctx, req.GetUserId(), req.GetAssetTypes())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
response := &walletv1.GetBalancesResponse{Balances: make([]*walletv1.AssetBalance, 0, len(balances))}
|
||
for _, balance := range balances {
|
||
response.Balances = append(response.Balances, &walletv1.AssetBalance{
|
||
AppCode: balance.AppCode,
|
||
AssetType: balance.AssetType,
|
||
AvailableAmount: balance.AvailableAmount,
|
||
FrozenAmount: balance.FrozenAmount,
|
||
Version: balance.Version,
|
||
})
|
||
}
|
||
|
||
return response, nil
|
||
}
|
||
|
||
// GetWalletOverview 返回钱包首页摘要,供我的页和钱包二级页复用。
|
||
func (s *Server) GetWalletOverview(ctx context.Context, req *walletv1.GetWalletOverviewRequest) (*walletv1.GetWalletOverviewResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
overview, err := s.svc.GetWalletOverview(ctx, req.GetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.GetWalletOverviewResponse{
|
||
Balances: balancesToProto(overview.Balances),
|
||
FeatureFlags: walletFeatureFlagsToProto(overview.FeatureFlags),
|
||
}, nil
|
||
}
|
||
|
||
// GetWalletValueSummary 返回我的页钱包卡片最小金币余额。
|
||
func (s *Server) GetWalletValueSummary(ctx context.Context, req *walletv1.GetWalletValueSummaryRequest) (*walletv1.GetWalletValueSummaryResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
summary, err := s.svc.GetWalletValueSummary(ctx, req.GetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.GetWalletValueSummaryResponse{Summary: &walletv1.WalletValueSummary{
|
||
CoinAmount: summary.CoinAmount,
|
||
UpdatedAtMs: summary.UpdatedAtMS,
|
||
}}, nil
|
||
}
|
||
|
||
// GetUserGiftWall 返回当前用户收到礼物的聚合墙;调用方负责限定 user_id 为当前登录用户。
|
||
func (s *Server) GetUserGiftWall(ctx context.Context, req *walletv1.GetUserGiftWallRequest) (*walletv1.GetUserGiftWallResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
wall, err := s.svc.GetUserGiftWall(ctx, req.GetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.GetUserGiftWallResponse{
|
||
Items: make([]*walletv1.GiftWallItem, 0, len(wall.Items)),
|
||
GiftKindCount: wall.GiftKindCount,
|
||
GiftTotalCount: wall.GiftTotalCount,
|
||
TotalValue: wall.TotalValue,
|
||
TotalCoinValue: wall.TotalCoinValue,
|
||
TotalDiamondValue: wall.TotalDiamondValue,
|
||
TotalGiftPoint: wall.TotalGiftPoint,
|
||
TotalHeatValue: wall.TotalHeatValue,
|
||
}
|
||
for _, item := range wall.Items {
|
||
resp.Items = append(resp.Items, giftWallItemToProto(item))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// ListRechargeProducts 返回当前用户区域可用的充值渠道和档位。
|
||
func (s *Server) ListRechargeProducts(ctx context.Context, req *walletv1.ListRechargeProductsRequest) (*walletv1.ListRechargeProductsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
products, channels, err := s.svc.ListRechargeProducts(ctx, req.GetUserId(), req.GetRegionId(), req.GetPlatform())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.ListRechargeProductsResponse{Channels: channels, Products: make([]*walletv1.RechargeProduct, 0, len(products))}
|
||
for _, product := range products {
|
||
resp.Products = append(resp.Products, rechargeProductToProto(product))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// ConfirmGooglePayment 校验 Google Play purchase token 并完成钱包入账。
|
||
func (s *Server) ConfirmGooglePayment(ctx context.Context, req *walletv1.ConfirmGooglePaymentRequest) (*walletv1.ConfirmGooglePaymentResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.ConfirmGooglePayment(ctx, ledger.GooglePaymentCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
RegionID: req.GetRegionId(),
|
||
ProductID: req.GetProductId(),
|
||
ProductCode: req.GetProductCode(),
|
||
PackageName: req.GetPackageName(),
|
||
PurchaseToken: req.GetPurchaseToken(),
|
||
OrderID: req.GetOrderId(),
|
||
PurchaseTimeMS: req.GetPurchaseTimeMs(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.ConfirmGooglePaymentResponse{
|
||
PaymentOrderId: receipt.PaymentOrderID,
|
||
TransactionId: receipt.TransactionID,
|
||
Status: receipt.Status,
|
||
ProductId: receipt.ProductID,
|
||
ProductCode: receipt.ProductCode,
|
||
CoinAmount: receipt.CoinAmount,
|
||
Balance: balanceToProto(receipt.Balance),
|
||
IdempotentReplay: receipt.IdempotentReplay,
|
||
ConsumeState: receipt.ConsumeState,
|
||
}, nil
|
||
}
|
||
|
||
// ListAdminRechargeProducts 返回后台内购配置列表。
|
||
func (s *Server) ListAdminRechargeProducts(ctx context.Context, req *walletv1.ListAdminRechargeProductsRequest) (*walletv1.ListAdminRechargeProductsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
products, total, err := s.svc.ListAdminRechargeProducts(ctx, ledger.ListRechargeProductsQuery{
|
||
AppCode: req.GetAppCode(),
|
||
Status: req.GetStatus(),
|
||
Platform: req.GetPlatform(),
|
||
RegionID: req.GetRegionId(),
|
||
Keyword: req.GetKeyword(),
|
||
Page: req.GetPage(),
|
||
PageSize: req.GetPageSize(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.ListAdminRechargeProductsResponse{Products: make([]*walletv1.RechargeProduct, 0, len(products)), Total: total}
|
||
for _, product := range products {
|
||
resp.Products = append(resp.Products, rechargeProductToProto(product))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// CreateRechargeProduct 创建后台内购商品配置。
|
||
func (s *Server) CreateRechargeProduct(ctx context.Context, req *walletv1.CreateRechargeProductRequest) (*walletv1.RechargeProductResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
product, err := s.svc.CreateRechargeProduct(ctx, ledger.RechargeProductCommand{
|
||
AppCode: req.GetAppCode(),
|
||
AmountMicro: req.GetAmountMicro(),
|
||
CoinAmount: req.GetCoinAmount(),
|
||
ProductName: req.GetProductName(),
|
||
Description: req.GetDescription(),
|
||
Platform: req.GetPlatform(),
|
||
RegionIDs: req.GetRegionIds(),
|
||
Enabled: req.GetEnabled(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.RechargeProductResponse{Product: rechargeProductToProto(product)}, nil
|
||
}
|
||
|
||
// UpdateRechargeProduct 修改后台内购商品配置。
|
||
func (s *Server) UpdateRechargeProduct(ctx context.Context, req *walletv1.UpdateRechargeProductRequest) (*walletv1.RechargeProductResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
product, err := s.svc.UpdateRechargeProduct(ctx, ledger.RechargeProductCommand{
|
||
AppCode: req.GetAppCode(),
|
||
ProductID: req.GetProductId(),
|
||
AmountMicro: req.GetAmountMicro(),
|
||
CoinAmount: req.GetCoinAmount(),
|
||
ProductName: req.GetProductName(),
|
||
Description: req.GetDescription(),
|
||
Platform: req.GetPlatform(),
|
||
RegionIDs: req.GetRegionIds(),
|
||
Enabled: req.GetEnabled(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.RechargeProductResponse{Product: rechargeProductToProto(product)}, nil
|
||
}
|
||
|
||
// DeleteRechargeProduct 删除后台内购商品配置。
|
||
func (s *Server) DeleteRechargeProduct(ctx context.Context, req *walletv1.DeleteRechargeProductRequest) (*walletv1.DeleteRechargeProductResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
if err := s.svc.DeleteRechargeProduct(ctx, req.GetAppCode(), req.GetProductId()); err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.DeleteRechargeProductResponse{Deleted: true}, nil
|
||
}
|
||
|
||
// GetDiamondExchangeConfig 返回钻石兑换配置。
|
||
func (s *Server) GetDiamondExchangeConfig(ctx context.Context, req *walletv1.GetDiamondExchangeConfigRequest) (*walletv1.GetDiamondExchangeConfigResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
rules, err := s.svc.GetDiamondExchangeConfig(ctx, req.GetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.GetDiamondExchangeConfigResponse{Rules: make([]*walletv1.DiamondExchangeRule, 0, len(rules))}
|
||
for _, rule := range rules {
|
||
resp.Rules = append(resp.Rules, &walletv1.DiamondExchangeRule{
|
||
ExchangeType: rule.ExchangeType,
|
||
FromAssetType: rule.FromAssetType,
|
||
ToAssetType: rule.ToAssetType,
|
||
FromAmount: rule.FromAmount,
|
||
ToAmount: rule.ToAmount,
|
||
Enabled: rule.Enabled,
|
||
})
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
func rechargeProductToProto(product ledger.RechargeProduct) *walletv1.RechargeProduct {
|
||
return &walletv1.RechargeProduct{
|
||
AppCode: product.AppCode,
|
||
ProductId: product.ProductID,
|
||
ProductCode: product.ProductCode,
|
||
ProductName: product.ProductName,
|
||
Description: product.Description,
|
||
Platform: product.Platform,
|
||
Channel: product.Channel,
|
||
CurrencyCode: product.CurrencyCode,
|
||
AmountMicro: product.AmountMicro,
|
||
AmountMinor: product.AmountMinor,
|
||
CoinAmount: product.CoinAmount,
|
||
PolicyVersion: product.PolicyVersion,
|
||
Status: product.Status,
|
||
Enabled: product.Enabled,
|
||
SortOrder: product.SortOrder,
|
||
RegionIds: product.RegionIDs,
|
||
ResourceAssetType: product.ResourceAssetType,
|
||
CreatedByUserId: product.CreatedByUserID,
|
||
UpdatedByUserId: product.UpdatedByUserID,
|
||
CreatedAtMs: product.CreatedAtMS,
|
||
UpdatedAtMs: product.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
// ListWalletTransactions 返回当前用户钱包流水。
|
||
func (s *Server) ListWalletTransactions(ctx context.Context, req *walletv1.ListWalletTransactionsRequest) (*walletv1.ListWalletTransactionsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
items, total, err := s.svc.ListWalletTransactions(ctx, ledger.ListWalletTransactionsQuery{
|
||
AppCode: req.GetAppCode(),
|
||
UserID: req.GetUserId(),
|
||
AssetType: req.GetAssetType(),
|
||
Page: req.GetPage(),
|
||
PageSize: req.GetPageSize(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.ListWalletTransactionsResponse{Transactions: make([]*walletv1.WalletTransaction, 0, len(items)), Total: total}
|
||
for _, item := range items {
|
||
resp.Transactions = append(resp.Transactions, &walletv1.WalletTransaction{
|
||
EntryId: item.EntryID,
|
||
TransactionId: item.TransactionID,
|
||
BizType: item.BizType,
|
||
AssetType: item.AssetType,
|
||
AvailableDelta: item.AvailableDelta,
|
||
FrozenDelta: item.FrozenDelta,
|
||
AvailableAfter: item.AvailableAfter,
|
||
FrozenAfter: item.FrozenAfter,
|
||
CounterpartyUserId: item.CounterpartyUserID,
|
||
RoomId: item.RoomID,
|
||
CreatedAtMs: item.CreatedAtMS,
|
||
})
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// ApplyWithdrawal 创建提现申请并冻结美元余额。
|
||
func (s *Server) ApplyWithdrawal(ctx context.Context, req *walletv1.ApplyWithdrawalRequest) (*walletv1.ApplyWithdrawalResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
withdrawal, balance, err := s.svc.ApplyWithdrawal(ctx, ledger.WithdrawalCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
Amount: req.GetAmount(),
|
||
PayoutAccount: req.GetPayoutAccount(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.ApplyWithdrawalResponse{
|
||
Withdrawal: withdrawalToProto(withdrawal),
|
||
Balance: balanceToProto(balance),
|
||
}, nil
|
||
}
|
||
|
||
// ListVipPackages 返回当前 VIP 和可购买包列表。
|
||
func (s *Server) ListVipPackages(ctx context.Context, req *walletv1.ListVipPackagesRequest) (*walletv1.ListVipPackagesResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
current, levels, err := s.svc.ListVipPackages(ctx, req.GetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.ListVipPackagesResponse{CurrentVip: vipToProto(current), Packages: make([]*walletv1.VipLevel, 0, len(levels))}
|
||
for _, level := range levels {
|
||
resp.Packages = append(resp.Packages, vipLevelToProto(level))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// GetMyVip 返回当前登录用户 VIP 状态。
|
||
func (s *Server) GetMyVip(ctx context.Context, req *walletv1.GetMyVipRequest) (*walletv1.GetMyVipResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
vip, err := s.svc.GetMyVip(ctx, req.GetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.GetMyVipResponse{Vip: vipToProto(vip)}, nil
|
||
}
|
||
|
||
// PurchaseVip 购买、续期或升级 VIP。
|
||
func (s *Server) PurchaseVip(ctx context.Context, req *walletv1.PurchaseVipRequest) (*walletv1.PurchaseVipResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.PurchaseVip(ctx, ledger.PurchaseVipCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
Level: req.GetLevel(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.PurchaseVipResponse{
|
||
OrderId: receipt.OrderID,
|
||
TransactionId: receipt.TransactionID,
|
||
Vip: vipToProto(receipt.Vip),
|
||
CoinSpent: receipt.CoinSpent,
|
||
CoinBalanceAfter: receipt.CoinBalanceAfter,
|
||
RewardItems: vipRewardItemsToProto(receipt.RewardItems),
|
||
}, nil
|
||
}
|
||
|
||
// GrantVip 统一处理活动和后台赠送 VIP。
|
||
func (s *Server) GrantVip(ctx context.Context, req *walletv1.GrantVipRequest) (*walletv1.GrantVipResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.GrantVip(ctx, ledger.GrantVipCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Level: req.GetLevel(),
|
||
GrantSource: req.GetGrantSource(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.GrantVipResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
Vip: vipToProto(receipt.Vip),
|
||
RewardItems: vipRewardItemsToProto(receipt.RewardItems),
|
||
ServerTimeMs: time.Now().UnixMilli(),
|
||
}, nil
|
||
}
|
||
|
||
// ListAdminVipLevels 返回后台 VIP 配置。
|
||
func (s *Server) ListAdminVipLevels(ctx context.Context, req *walletv1.ListAdminVipLevelsRequest) (*walletv1.ListAdminVipLevelsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
levels, err := s.svc.ListAdminVipLevels(ctx)
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.ListAdminVipLevelsResponse{Levels: make([]*walletv1.VipLevel, 0, len(levels)), ServerTimeMs: time.Now().UnixMilli()}
|
||
for _, level := range levels {
|
||
resp.Levels = append(resp.Levels, vipLevelToProto(level))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// UpdateAdminVipLevels 保存后台 VIP 配置。
|
||
func (s *Server) UpdateAdminVipLevels(ctx context.Context, req *walletv1.UpdateAdminVipLevelsRequest) (*walletv1.UpdateAdminVipLevelsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
commands := make([]ledger.AdminVipLevelCommand, 0, len(req.GetLevels()))
|
||
for _, item := range req.GetLevels() {
|
||
if item == nil {
|
||
continue
|
||
}
|
||
commands = append(commands, ledger.AdminVipLevelCommand{
|
||
Level: item.GetLevel(),
|
||
Name: item.GetName(),
|
||
Status: item.GetStatus(),
|
||
PriceCoin: item.GetPriceCoin(),
|
||
DurationMS: item.GetDurationMs(),
|
||
RewardResourceGroupID: item.GetRewardResourceGroupId(),
|
||
RequiredRechargeCoinAmount: item.GetRequiredRechargeCoinAmount(),
|
||
SortOrder: item.GetSortOrder(),
|
||
})
|
||
}
|
||
levels, err := s.svc.UpdateAdminVipLevels(ctx, commands, req.GetOperatorUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.UpdateAdminVipLevelsResponse{Levels: make([]*walletv1.VipLevel, 0, len(levels)), ServerTimeMs: time.Now().UnixMilli()}
|
||
for _, level := range levels {
|
||
resp.Levels = append(resp.Levels, vipLevelToProto(level))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// AdminCreditAsset 处理后台手动调账,公网权限和审计入口由 gateway/admin 层控制。
|
||
func (s *Server) AdminCreditAsset(ctx context.Context, req *walletv1.AdminCreditAssetRequest) (*walletv1.AdminCreditAssetResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
balance, transactionID, err := s.svc.AdminCreditAsset(ctx, ledger.AdminCreditAssetCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
AssetType: req.GetAssetType(),
|
||
Amount: req.GetAmount(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
Reason: req.GetReason(),
|
||
EvidenceRef: req.GetEvidenceRef(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.AdminCreditAssetResponse{
|
||
TransactionId: transactionID,
|
||
Balance: &walletv1.AssetBalance{
|
||
AppCode: balance.AppCode,
|
||
AssetType: balance.AssetType,
|
||
AvailableAmount: balance.AvailableAmount,
|
||
FrozenAmount: balance.FrozenAmount,
|
||
Version: balance.Version,
|
||
},
|
||
}, nil
|
||
}
|
||
|
||
func balancesToProto(balances []ledger.AssetBalance) []*walletv1.AssetBalance {
|
||
items := make([]*walletv1.AssetBalance, 0, len(balances))
|
||
for _, balance := range balances {
|
||
items = append(items, balanceToProto(balance))
|
||
}
|
||
return items
|
||
}
|
||
|
||
func balanceToProto(balance ledger.AssetBalance) *walletv1.AssetBalance {
|
||
return &walletv1.AssetBalance{
|
||
AppCode: balance.AppCode,
|
||
AssetType: balance.AssetType,
|
||
AvailableAmount: balance.AvailableAmount,
|
||
FrozenAmount: balance.FrozenAmount,
|
||
Version: balance.Version,
|
||
}
|
||
}
|
||
|
||
func giftWallItemToProto(item ledger.GiftWallItem) *walletv1.GiftWallItem {
|
||
return &walletv1.GiftWallItem{
|
||
GiftId: item.GiftID,
|
||
GiftName: item.GiftName,
|
||
ResourceId: item.ResourceID,
|
||
Resource: giftWallResourceToProto(item.Resource),
|
||
ResourceSnapshotJson: item.ResourceSnapshotJSON,
|
||
GiftTypeCode: item.GiftTypeCode,
|
||
PresentationJson: item.PresentationJSON,
|
||
GiftCount: item.GiftCount,
|
||
TotalValue: item.TotalValue,
|
||
TotalCoinValue: item.TotalCoinValue,
|
||
TotalDiamondValue: item.TotalDiamondValue,
|
||
TotalGiftPoint: item.TotalGiftPoint,
|
||
TotalHeatValue: item.TotalHeatValue,
|
||
ChargeAssetType: item.ChargeAssetType,
|
||
LastPriceVersion: item.LastPriceVersion,
|
||
FirstReceivedAtMs: item.FirstReceivedAtMS,
|
||
LastReceivedAtMs: item.LastReceivedAtMS,
|
||
SortOrder: item.SortOrder,
|
||
}
|
||
}
|
||
|
||
func giftWallResourceToProto(resource ledger.GiftWallResource) *walletv1.Resource {
|
||
if resource.ResourceID == 0 {
|
||
return nil
|
||
}
|
||
return &walletv1.Resource{
|
||
AppCode: resource.AppCode,
|
||
ResourceId: resource.ResourceID,
|
||
ResourceCode: resource.ResourceCode,
|
||
ResourceType: resource.ResourceType,
|
||
Name: resource.Name,
|
||
Status: resource.Status,
|
||
Grantable: resource.Grantable,
|
||
GrantStrategy: resource.GrantStrategy,
|
||
WalletAssetType: resource.WalletAssetType,
|
||
WalletAssetAmount: resource.WalletAssetAmount,
|
||
UsageScopes: resource.UsageScopes,
|
||
AssetUrl: resource.AssetURL,
|
||
PreviewUrl: resource.PreviewURL,
|
||
AnimationUrl: resource.AnimationURL,
|
||
MetadataJson: resource.MetadataJSON,
|
||
SortOrder: resource.SortOrder,
|
||
CreatedAtMs: resource.CreatedAtMS,
|
||
UpdatedAtMs: resource.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
func walletFeatureFlagsToProto(flags ledger.WalletFeatureFlags) *walletv1.WalletFeatureFlags {
|
||
return &walletv1.WalletFeatureFlags{
|
||
RechargeEnabled: flags.RechargeEnabled,
|
||
DiamondExchangeEnabled: flags.DiamondExchangeEnabled,
|
||
WithdrawEnabled: flags.WithdrawEnabled,
|
||
}
|
||
}
|
||
|
||
func withdrawalToProto(withdrawal ledger.WithdrawalRequest) *walletv1.WithdrawalRequest {
|
||
return &walletv1.WithdrawalRequest{
|
||
WithdrawalId: withdrawal.WithdrawalID,
|
||
UserId: withdrawal.UserID,
|
||
AssetType: withdrawal.AssetType,
|
||
Amount: withdrawal.Amount,
|
||
Status: withdrawal.Status,
|
||
PayoutAccount: withdrawal.PayoutAccount,
|
||
Reason: withdrawal.Reason,
|
||
CreatedAtMs: withdrawal.CreatedAtMS,
|
||
UpdatedAtMs: withdrawal.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
func vipToProto(vip ledger.UserVip) *walletv1.UserVip {
|
||
return &walletv1.UserVip{
|
||
UserId: vip.UserID,
|
||
Level: vip.Level,
|
||
Name: vip.Name,
|
||
Active: vip.Active,
|
||
StartedAtMs: vip.StartedAtMS,
|
||
ExpiresAtMs: vip.ExpiresAtMS,
|
||
UpdatedAtMs: vip.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
func vipLevelToProto(level ledger.VipLevel) *walletv1.VipLevel {
|
||
return &walletv1.VipLevel{
|
||
Level: level.Level,
|
||
Name: level.Name,
|
||
Status: level.Status,
|
||
PriceCoin: level.PriceCoin,
|
||
DurationMs: level.DurationMS,
|
||
RewardResourceGroupId: level.RewardResourceGroupID,
|
||
RewardItems: vipRewardItemsToProto(level.RewardItems),
|
||
CanPurchase: level.CanPurchase,
|
||
SortOrder: level.SortOrder,
|
||
RechargeGateRequired: level.RechargeGateRequired,
|
||
RequiredRechargeCoinAmount: level.RequiredRechargeCoinAmount,
|
||
UserRechargeCoinAmount: level.UserRechargeCoinAmount,
|
||
PurchaseLockedReason: level.PurchaseLockedReason,
|
||
CreatedAtMs: level.CreatedAtMS,
|
||
UpdatedAtMs: level.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
func vipRewardItemsToProto(items []ledger.VipRewardItem) []*walletv1.VipRewardItem {
|
||
resp := make([]*walletv1.VipRewardItem, 0, len(items))
|
||
for _, item := range items {
|
||
resp = append(resp, &walletv1.VipRewardItem{
|
||
ResourceId: item.ResourceID,
|
||
ResourceCode: item.ResourceCode,
|
||
ResourceType: item.ResourceType,
|
||
Name: item.Name,
|
||
Quantity: item.Quantity,
|
||
ExpiresAtMs: item.ExpiresAtMS,
|
||
AssetUrl: item.AssetURL,
|
||
PreviewUrl: item.PreviewURL,
|
||
AnimationUrl: item.AnimationURL,
|
||
})
|
||
}
|
||
return resp
|
||
}
|
||
|
||
// CreditTaskReward 处理 activity-service 任务奖励金币入账。
|
||
func (s *Server) CreditTaskReward(ctx context.Context, req *walletv1.CreditTaskRewardRequest) (*walletv1.CreditTaskRewardResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.CreditTaskReward(ctx, ledger.TaskRewardCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Amount: req.GetAmount(),
|
||
TaskType: req.GetTaskType(),
|
||
TaskID: req.GetTaskId(),
|
||
CycleKey: req.GetCycleKey(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.CreditTaskRewardResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
Amount: receipt.Amount,
|
||
GrantedAtMs: receipt.GrantedAtMS,
|
||
Balance: &walletv1.AssetBalance{
|
||
AppCode: receipt.Balance.AppCode,
|
||
AssetType: receipt.Balance.AssetType,
|
||
AvailableAmount: receipt.Balance.AvailableAmount,
|
||
FrozenAmount: receipt.Balance.FrozenAmount,
|
||
Version: receipt.Balance.Version,
|
||
},
|
||
}, nil
|
||
}
|
||
|
||
// CreditLuckyGiftReward 处理 activity-service 幸运礼物抽奖 outbox 的中奖金币入账。
|
||
func (s *Server) CreditLuckyGiftReward(ctx context.Context, req *walletv1.CreditLuckyGiftRewardRequest) (*walletv1.CreditLuckyGiftRewardResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.CreditLuckyGiftReward(ctx, ledger.LuckyGiftRewardCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Amount: req.GetAmount(),
|
||
DrawID: req.GetDrawId(),
|
||
RoomID: req.GetRoomId(),
|
||
VisibleRegionID: req.GetVisibleRegionId(),
|
||
GiftID: req.GetGiftId(),
|
||
PoolID: req.GetPoolId(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.CreditLuckyGiftRewardResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
Amount: receipt.Amount,
|
||
GrantedAtMs: receipt.GrantedAtMS,
|
||
Balance: &walletv1.AssetBalance{
|
||
AppCode: receipt.Balance.AppCode,
|
||
AssetType: receipt.Balance.AssetType,
|
||
AvailableAmount: receipt.Balance.AvailableAmount,
|
||
FrozenAmount: receipt.Balance.FrozenAmount,
|
||
Version: receipt.Balance.Version,
|
||
},
|
||
}, nil
|
||
}
|
||
|
||
// ApplyGameCoinChange 处理 game-service 发起的游戏专用金币改账。
|
||
func (s *Server) ApplyGameCoinChange(ctx context.Context, req *walletv1.ApplyGameCoinChangeRequest) (*walletv1.ApplyGameCoinChangeResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.ApplyGameCoinChange(ctx, ledger.GameCoinChangeCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
PlatformCode: req.GetPlatformCode(),
|
||
GameID: req.GetGameId(),
|
||
ProviderOrderID: req.GetProviderOrderId(),
|
||
ProviderRoundID: req.GetProviderRoundId(),
|
||
OpType: req.GetOpType(),
|
||
CoinAmount: req.GetCoinAmount(),
|
||
RoomID: req.GetRoomId(),
|
||
RequestHash: req.GetRequestHash(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.ApplyGameCoinChangeResponse{
|
||
WalletTransactionId: receipt.TransactionID,
|
||
BalanceAfter: receipt.BalanceAfter,
|
||
IdempotentReplay: receipt.IdempotentReplay,
|
||
}, nil
|
||
}
|
||
|
||
// AdminCreditCoinSellerStock 处理后台币商进货或金币补偿,币商身份由 admin-server 先行校验。
|
||
func (s *Server) AdminCreditCoinSellerStock(ctx context.Context, req *walletv1.AdminCreditCoinSellerStockRequest) (*walletv1.AdminCreditCoinSellerStockResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.AdminCreditCoinSellerStock(ctx, ledger.CoinSellerStockCreditCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
SellerUserID: req.GetSellerUserId(),
|
||
StockType: req.GetStockType(),
|
||
CoinAmount: req.GetCoinAmount(),
|
||
PaidCurrencyCode: req.GetPaidCurrencyCode(),
|
||
PaidAmountMicro: req.GetPaidAmountMicro(),
|
||
PaymentRef: req.GetPaymentRef(),
|
||
EvidenceRef: req.GetEvidenceRef(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.AdminCreditCoinSellerStockResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
SellerUserId: receipt.SellerUserID,
|
||
StockType: receipt.StockType,
|
||
CoinAmount: receipt.CoinAmount,
|
||
PaidCurrencyCode: receipt.PaidCurrencyCode,
|
||
PaidAmountMicro: receipt.PaidAmountMicro,
|
||
CountsAsSellerRecharge: receipt.CountsAsSellerRecharge,
|
||
BalanceAfter: receipt.BalanceAfter,
|
||
CreatedAtMs: receipt.CreatedAtMS,
|
||
}, nil
|
||
}
|
||
|
||
// TransferCoinFromSeller 处理币商转玩家金币,调用方必须先完成币商身份校验。
|
||
func (s *Server) TransferCoinFromSeller(ctx context.Context, req *walletv1.TransferCoinFromSellerRequest) (*walletv1.TransferCoinFromSellerResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.TransferCoinFromSeller(ctx, ledger.CoinSellerTransferCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
SellerUserID: req.GetSellerUserId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
SellerRegionID: req.GetSellerRegionId(),
|
||
TargetRegionID: req.GetTargetRegionId(),
|
||
Amount: req.GetAmount(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.TransferCoinFromSellerResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
SellerBalanceAfter: receipt.SellerBalanceAfter,
|
||
TargetBalanceAfter: receipt.TargetBalanceAfter,
|
||
Amount: receipt.Amount,
|
||
RechargeUsdMinor: receipt.RechargeUSDMinor,
|
||
RechargeCurrencyCode: receipt.RechargeCurrencyCode,
|
||
RechargePolicyId: receipt.RechargePolicyID,
|
||
RechargePolicyVersion: receipt.RechargePolicyVersion,
|
||
RechargePolicyCoinAmount: receipt.RechargePolicyCoinAmount,
|
||
RechargePolicyUsdMinorAmount: receipt.RechargePolicyUSDMinorUnit,
|
||
}, nil
|
||
}
|