1366 lines
55 KiB
Go
1366 lines
55 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(),
|
||
SenderRegionID: req.GetSenderRegionId(),
|
||
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 debitGiftResponseFromReceipt(receipt), nil
|
||
}
|
||
|
||
// BatchDebitGift 只负责批量目标 protobuf 转换;同事务扣费和幂等由 wallet service/repository 保证。
|
||
func (s *Server) BatchDebitGift(ctx context.Context, req *walletv1.BatchDebitGiftRequest) (*walletv1.BatchDebitGiftResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
targets := make([]ledger.DebitGiftTargetCommand, 0, len(req.GetTargets()))
|
||
for _, target := range req.GetTargets() {
|
||
targets = append(targets, ledger.DebitGiftTargetCommand{
|
||
CommandID: target.GetCommandId(),
|
||
TargetUserID: target.GetTargetUserId(),
|
||
TargetIsHost: target.GetTargetIsHost(),
|
||
TargetHostRegionID: target.GetTargetHostRegionId(),
|
||
TargetAgencyOwnerUserID: target.GetTargetAgencyOwnerUserId(),
|
||
})
|
||
}
|
||
receipt, err := s.svc.BatchDebitGift(ctx, ledger.BatchDebitGiftCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
RoomID: req.GetRoomId(),
|
||
SenderUserID: req.GetSenderUserId(),
|
||
GiftID: req.GetGiftId(),
|
||
GiftCount: req.GetGiftCount(),
|
||
PriceVersion: req.GetPriceVersion(),
|
||
RegionID: req.GetRegionId(),
|
||
SenderRegionID: req.GetSenderRegionId(),
|
||
Targets: targets,
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
response := &walletv1.BatchDebitGiftResponse{
|
||
Aggregate: debitGiftResponseFromReceipt(receipt.Aggregate),
|
||
Receipts: make([]*walletv1.BatchDebitGiftReceipt, 0, len(receipt.Targets)),
|
||
}
|
||
for _, target := range receipt.Targets {
|
||
response.Receipts = append(response.Receipts, &walletv1.BatchDebitGiftReceipt{
|
||
TargetUserId: target.TargetUserID,
|
||
CommandId: target.CommandID,
|
||
Billing: debitGiftResponseFromReceipt(target.Receipt),
|
||
})
|
||
}
|
||
return response, nil
|
||
}
|
||
|
||
// DebitRobotGift 扣机器人专用金币;该入口只供内部机器人房间编排链路调用。
|
||
func (s *Server) DebitRobotGift(ctx context.Context, req *walletv1.DebitRobotGiftRequest) (*walletv1.DebitGiftResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.DebitRobotGift(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(),
|
||
SenderRegionID: req.GetSenderRegionId(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return debitGiftResponseFromReceipt(receipt), nil
|
||
}
|
||
|
||
func debitGiftResponseFromReceipt(receipt ledger.Receipt) *walletv1.DebitGiftResponse {
|
||
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,
|
||
CpRelationType: receipt.CPRelationType,
|
||
GiftName: receipt.GiftName,
|
||
GiftIconUrl: receipt.GiftIconURL,
|
||
GiftAnimationUrl: receipt.GiftAnimationURL,
|
||
GiftEffectTypes: receipt.GiftEffectTypes,
|
||
PriceVersion: receipt.PriceVersion,
|
||
HostPeriodDiamondAdded: receipt.HostPeriodDiamondAdded,
|
||
HostPeriodCycleKey: receipt.HostPeriodCycleKey,
|
||
}
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
// GetActiveHostSalaryPolicy 读取当前生效的主播工资政策,供 gateway 按当前主播区域展示平台规则。
|
||
func (s *Server) GetActiveHostSalaryPolicy(ctx context.Context, req *walletv1.GetActiveHostSalaryPolicyRequest) (*walletv1.GetActiveHostSalaryPolicyResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
policy, found, err := s.svc.GetActiveHostSalaryPolicy(ctx, req.GetAppCode(), req.GetRegionId(), req.GetSettlementMode(), req.GetSettlementTriggerMode(), req.GetNowMs())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
response := &walletv1.GetActiveHostSalaryPolicyResponse{Found: found}
|
||
if found {
|
||
response.Policy = hostSalaryPolicyToProto(policy)
|
||
}
|
||
return response, nil
|
||
}
|
||
|
||
// GetHostSalaryProgress 读取主播当前工资周期钻石累计,gateway 用它计算工资政策等级进度。
|
||
func (s *Server) GetHostSalaryProgress(ctx context.Context, req *walletv1.GetHostSalaryProgressRequest) (*walletv1.GetHostSalaryProgressResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
progress, err := s.svc.GetHostSalaryProgress(ctx, req.GetAppCode(), req.GetHostUserId(), req.GetCycleKey(), req.GetNowMs())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.GetHostSalaryProgressResponse{Progress: hostSalaryProgressToProto(progress)}, nil
|
||
}
|
||
|
||
func hostSalaryProgressToProto(progress ledger.HostSalaryProgress) *walletv1.HostSalaryProgress {
|
||
return &walletv1.HostSalaryProgress{
|
||
HostUserId: progress.HostUserID,
|
||
CycleKey: progress.CycleKey,
|
||
RegionId: progress.RegionID,
|
||
AgencyOwnerUserId: progress.AgencyOwnerUserID,
|
||
TotalDiamonds: progress.TotalDiamonds,
|
||
GiftDiamondTotal: progress.GiftDiamondTotal,
|
||
UpdatedAtMs: progress.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
func hostSalaryPolicyToProto(policy ledger.HostSalaryPolicy) *walletv1.HostSalaryPolicy {
|
||
levels := make([]*walletv1.HostSalaryPolicyLevel, 0, len(policy.Levels))
|
||
for _, level := range policy.Levels {
|
||
levels = append(levels, hostSalaryPolicyLevelToProto(level))
|
||
}
|
||
return &walletv1.HostSalaryPolicy{
|
||
PolicyId: policy.PolicyID,
|
||
Name: policy.Name,
|
||
RegionId: policy.RegionID,
|
||
Status: policy.Status,
|
||
SettlementMode: policy.SettlementMode,
|
||
SettlementTriggerMode: policy.SettlementTriggerMode,
|
||
GiftCoinToDiamondRatio: policy.GiftCoinToDiamondRatio,
|
||
ResidualDiamondToUsdRate: policy.ResidualDiamondToUSDRate,
|
||
EffectiveFromMs: policy.EffectiveFromMs,
|
||
EffectiveToMs: policy.EffectiveToMs,
|
||
Levels: levels,
|
||
}
|
||
}
|
||
|
||
func hostSalaryPolicyLevelToProto(level ledger.HostSalaryPolicyLevel) *walletv1.HostSalaryPolicyLevel {
|
||
return &walletv1.HostSalaryPolicyLevel{
|
||
LevelNo: int32(level.LevelNo),
|
||
RequiredDiamonds: level.RequiredDiamonds,
|
||
HostSalaryUsdMinor: level.HostSalaryUSDMinor,
|
||
HostCoinReward: level.HostCoinReward,
|
||
AgencySalaryUsdMinor: level.AgencySalaryUSDMinor,
|
||
Status: level.Status,
|
||
SortOrder: int32(level.SortOrder),
|
||
}
|
||
}
|
||
|
||
// 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(), req.GetAudienceType())
|
||
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(),
|
||
AudienceType: req.GetAudienceType(),
|
||
})
|
||
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(),
|
||
AudienceType: req.GetAudienceType(),
|
||
})
|
||
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(),
|
||
AudienceType: req.GetAudienceType(),
|
||
})
|
||
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
|
||
}
|
||
|
||
// ListThirdPartyPaymentChannels 返回三方支付渠道、国家和方式树,后台用它直接渲染二级展开列表。
|
||
func (s *Server) ListThirdPartyPaymentChannels(ctx context.Context, req *walletv1.ListThirdPartyPaymentChannelsRequest) (*walletv1.ListThirdPartyPaymentChannelsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
channels, err := s.svc.ListThirdPartyPaymentChannels(ctx, ledger.ListThirdPartyPaymentChannelsQuery{
|
||
AppCode: req.GetAppCode(),
|
||
ProviderCode: req.GetProviderCode(),
|
||
Status: req.GetStatus(),
|
||
IncludeDisabledMethods: req.GetIncludeDisabledMethods(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.ListThirdPartyPaymentChannelsResponse{Channels: make([]*walletv1.ThirdPartyPaymentChannel, 0, len(channels))}
|
||
for _, channel := range channels {
|
||
resp.Channels = append(resp.Channels, thirdPartyPaymentChannelToProto(channel))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// SetThirdPartyPaymentMethodStatus 开关某个国家下的具体支付方式;渠道本身不被连带关闭。
|
||
func (s *Server) SetThirdPartyPaymentMethodStatus(ctx context.Context, req *walletv1.SetThirdPartyPaymentMethodStatusRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
method, err := s.svc.SetThirdPartyPaymentMethodStatus(ctx, ledger.ThirdPartyPaymentMethodStatusCommand{
|
||
AppCode: req.GetAppCode(),
|
||
MethodID: req.GetMethodId(),
|
||
Enabled: req.GetEnabled(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.ThirdPartyPaymentMethodResponse{Method: thirdPartyPaymentMethodToProto(method)}, nil
|
||
}
|
||
|
||
// UpdateThirdPartyPaymentRate 保存 USD 到国家本币汇率;H5 只展示已配置正汇率的国家方式。
|
||
func (s *Server) UpdateThirdPartyPaymentRate(ctx context.Context, req *walletv1.UpdateThirdPartyPaymentRateRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
method, err := s.svc.UpdateThirdPartyPaymentRate(ctx, ledger.ThirdPartyPaymentRateCommand{
|
||
AppCode: req.GetAppCode(),
|
||
MethodID: req.GetMethodId(),
|
||
USDToCurrencyRate: req.GetUsdToCurrencyRate(),
|
||
OperatorUserID: req.GetOperatorUserId(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.ThirdPartyPaymentMethodResponse{Method: thirdPartyPaymentMethodToProto(method)}, nil
|
||
}
|
||
|
||
// ListH5RechargeOptions 返回 H5 当前账号的商品和支付方式,普通用户和币商由 audience_type 分流。
|
||
func (s *Server) ListH5RechargeOptions(ctx context.Context, req *walletv1.H5RechargeOptionsRequest) (*walletv1.H5RechargeOptionsResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
options, err := s.svc.ListH5RechargeOptions(ctx, ledger.H5RechargeOptionsQuery{
|
||
AppCode: req.GetAppCode(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
TargetRegionID: req.GetTargetRegionId(),
|
||
TargetCountryCode: req.GetTargetCountryCode(),
|
||
AudienceType: req.GetAudienceType(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
resp := &walletv1.H5RechargeOptionsResponse{
|
||
Products: make([]*walletv1.RechargeProduct, 0, len(options.Products)),
|
||
PaymentMethods: make([]*walletv1.ThirdPartyPaymentMethod, 0, len(options.PaymentMethods)),
|
||
UsdtTrc20Enabled: options.USDTTRC20Enabled,
|
||
UsdtTrc20Address: options.USDTTRC20Address,
|
||
}
|
||
for _, product := range options.Products {
|
||
resp.Products = append(resp.Products, rechargeProductToProto(product))
|
||
}
|
||
for _, method := range options.PaymentMethods {
|
||
resp.PaymentMethods = append(resp.PaymentMethods, thirdPartyPaymentMethodToProto(method))
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// CreateH5RechargeOrder 创建外部充值订单;MiFaPay 只返回 pay_url,USDT 只返回共享收款地址。
|
||
func (s *Server) CreateH5RechargeOrder(ctx context.Context, req *walletv1.CreateH5RechargeOrderRequest) (*walletv1.H5RechargeOrderResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
order, err := s.svc.CreateH5RechargeOrder(ctx, ledger.CreateExternalRechargeOrderCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
TargetRegionID: req.GetTargetRegionId(),
|
||
TargetCountryCode: req.GetTargetCountryCode(),
|
||
AudienceType: req.GetAudienceType(),
|
||
ProductID: req.GetProductId(),
|
||
ProviderCode: req.GetProviderCode(),
|
||
PaymentMethodID: req.GetPaymentMethodId(),
|
||
ReturnURL: req.GetReturnUrl(),
|
||
NotifyURL: req.GetNotifyUrl(),
|
||
ClientIP: req.GetClientIp(),
|
||
Language: req.GetLanguage(),
|
||
PayerName: req.GetPayerName(),
|
||
PayerAccount: req.GetPayerAccount(),
|
||
PayerEmail: req.GetPayerEmail(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
|
||
}
|
||
|
||
// SubmitH5RechargeTx 接收 USDT tx_hash;链上校验和幂等入账仍在 wallet-service 事务里完成。
|
||
func (s *Server) SubmitH5RechargeTx(ctx context.Context, req *walletv1.SubmitH5RechargeTxRequest) (*walletv1.H5RechargeOrderResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
order, err := s.svc.SubmitH5RechargeTx(ctx, ledger.SubmitExternalRechargeTxCommand{
|
||
AppCode: req.GetAppCode(),
|
||
OrderID: req.GetOrderId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
TxHash: req.GetTxHash(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
|
||
}
|
||
|
||
// GetH5RechargeOrder 给 H5 轮询订单状态;target_user_id 用于避免无 token 场景越权看单。
|
||
func (s *Server) GetH5RechargeOrder(ctx context.Context, req *walletv1.GetH5RechargeOrderRequest) (*walletv1.H5RechargeOrderResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
order, err := s.svc.GetH5RechargeOrder(ctx, req.GetAppCode(), req.GetOrderId(), req.GetTargetUserId())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
|
||
}
|
||
|
||
// HandleMifapayNotify 验签并处理 MiFaPay 回调;成功或重复成功由 HTTP 层按 response_text 返回大写 SUCCESS。
|
||
func (s *Server) HandleMifapayNotify(ctx context.Context, req *walletv1.HandleMifapayNotifyRequest) (*walletv1.HandleMifapayNotifyResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
order, accepted, err := s.svc.HandleMifapayNotify(ctx, req.GetAppCode(), ledger.MifaPayNotification{
|
||
MerAccount: req.GetMerAccount(),
|
||
Data: req.GetData(),
|
||
Sign: req.GetSign(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
responseText := "FAIL"
|
||
if accepted {
|
||
responseText = "SUCCESS"
|
||
}
|
||
return &walletv1.HandleMifapayNotifyResponse{Accepted: accepted, ResponseText: responseText, OrderId: order.OrderID}, 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,
|
||
AudienceType: product.AudienceType,
|
||
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,
|
||
}
|
||
}
|
||
|
||
func thirdPartyPaymentChannelToProto(channel ledger.ThirdPartyPaymentChannel) *walletv1.ThirdPartyPaymentChannel {
|
||
resp := &walletv1.ThirdPartyPaymentChannel{
|
||
AppCode: channel.AppCode,
|
||
ProviderCode: channel.ProviderCode,
|
||
ProviderName: channel.ProviderName,
|
||
Status: channel.Status,
|
||
SortOrder: channel.SortOrder,
|
||
Methods: make([]*walletv1.ThirdPartyPaymentMethod, 0, len(channel.Methods)),
|
||
}
|
||
for _, method := range channel.Methods {
|
||
resp.Methods = append(resp.Methods, thirdPartyPaymentMethodToProto(method))
|
||
}
|
||
return resp
|
||
}
|
||
|
||
func thirdPartyPaymentMethodToProto(method ledger.ThirdPartyPaymentMethod) *walletv1.ThirdPartyPaymentMethod {
|
||
return &walletv1.ThirdPartyPaymentMethod{
|
||
MethodId: method.MethodID,
|
||
AppCode: method.AppCode,
|
||
ProviderCode: method.ProviderCode,
|
||
ProviderName: method.ProviderName,
|
||
CountryCode: method.CountryCode,
|
||
CountryName: method.CountryName,
|
||
CurrencyCode: method.CurrencyCode,
|
||
PayWay: method.PayWay,
|
||
PayType: method.PayType,
|
||
MethodName: method.MethodName,
|
||
LogoUrl: method.LogoURL,
|
||
Status: method.Status,
|
||
UsdToCurrencyRate: method.USDToCurrencyRate,
|
||
SortOrder: method.SortOrder,
|
||
CreatedAtMs: method.CreatedAtMS,
|
||
UpdatedAtMs: method.UpdatedAtMS,
|
||
}
|
||
}
|
||
|
||
func externalRechargeOrderToProto(order ledger.ExternalRechargeOrder) *walletv1.ExternalRechargeOrder {
|
||
return &walletv1.ExternalRechargeOrder{
|
||
OrderId: order.OrderID,
|
||
AppCode: order.AppCode,
|
||
TargetUserId: order.TargetUserID,
|
||
TargetRegionId: order.TargetRegionID,
|
||
TargetCountryCode: order.TargetCountryCode,
|
||
AudienceType: order.AudienceType,
|
||
ProductId: order.ProductID,
|
||
ProductName: order.ProductName,
|
||
CoinAmount: order.CoinAmount,
|
||
UsdMinorAmount: order.USDMinorAmount,
|
||
ProviderCode: order.ProviderCode,
|
||
PaymentMethodId: order.PaymentMethodID,
|
||
CountryCode: order.CountryCode,
|
||
CurrencyCode: order.CurrencyCode,
|
||
ProviderAmountMinor: order.ProviderAmountMinor,
|
||
PayWay: order.PayWay,
|
||
PayType: order.PayType,
|
||
PayUrl: order.PayURL,
|
||
ProviderOrderId: order.ProviderOrderID,
|
||
TxHash: order.TxHash,
|
||
ReceiveAddress: order.ReceiveAddress,
|
||
Status: order.Status,
|
||
FailureReason: order.FailureReason,
|
||
TransactionId: order.TransactionID,
|
||
CreatedAtMs: order.CreatedAtMS,
|
||
UpdatedAtMs: order.UpdatedAtMS,
|
||
IdempotentReplay: order.IdempotentReplay,
|
||
}
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
// DebitCPBreakupFee 扣除解除 CP/兄弟/姐妹关系费用;关系状态确认由 user-service 负责。
|
||
func (s *Server) DebitCPBreakupFee(ctx context.Context, req *walletv1.DebitCPBreakupFeeRequest) (*walletv1.DebitCPBreakupFeeResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.DebitCPBreakupFee(ctx, ledger.CPBreakupFeeCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
RelationshipID: req.GetRelationshipId(),
|
||
RelationType: req.GetRelationType(),
|
||
Amount: req.GetAmount(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.DebitCPBreakupFeeResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
CoinSpent: receipt.CoinSpent,
|
||
CoinBalanceAfter: receipt.CoinBalanceAfter,
|
||
Balance: balanceToProto(receipt.Balance),
|
||
}, nil
|
||
}
|
||
|
||
// DebitWheelDraw 扣除转盘抽奖金币;中奖结果不在 wallet 内生成,避免钱包反向拥有活动规则。
|
||
func (s *Server) DebitWheelDraw(ctx context.Context, req *walletv1.DebitWheelDrawRequest) (*walletv1.DebitWheelDrawResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.DebitWheelDraw(ctx, ledger.WheelDrawDebitCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
WheelID: req.GetWheelId(),
|
||
DrawCount: req.GetDrawCount(),
|
||
Amount: req.GetAmount(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.DebitWheelDrawResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
CoinSpent: receipt.CoinSpent,
|
||
CoinBalanceAfter: receipt.CoinBalanceAfter,
|
||
Balance: balanceToProto(receipt.Balance),
|
||
}, 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,
|
||
}
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
// CreditWheelReward 处理 activity-service 转盘金币奖品 outbox 的入账。
|
||
func (s *Server) CreditWheelReward(ctx context.Context, req *walletv1.CreditWheelRewardRequest) (*walletv1.CreditWheelRewardResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.CreditWheelReward(ctx, ledger.WheelRewardCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Amount: req.GetAmount(),
|
||
DrawID: req.GetDrawId(),
|
||
WheelID: req.GetWheelId(),
|
||
SelectedTierID: req.GetSelectedTierId(),
|
||
VisibleRegionID: req.GetVisibleRegionId(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.CreditWheelRewardResponse{
|
||
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
|
||
}
|
||
|
||
// CreditRoomTurnoverReward 处理 activity-service 每周房间流水奖励金币入账。
|
||
func (s *Server) CreditRoomTurnoverReward(ctx context.Context, req *walletv1.CreditRoomTurnoverRewardRequest) (*walletv1.CreditRoomTurnoverRewardResponse, error) {
|
||
// gRPC 层不解释活动规则,只把 activity-service 的结算命令转换为钱包领域命令。
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.CreditRoomTurnoverReward(ctx, ledger.RoomTurnoverRewardCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Amount: req.GetAmount(),
|
||
SettlementID: req.GetSettlementId(),
|
||
RoomID: req.GetRoomId(),
|
||
PeriodStartMS: req.GetPeriodStartMs(),
|
||
PeriodEndMS: req.GetPeriodEndMs(),
|
||
CoinSpent: req.GetCoinSpent(),
|
||
TierID: req.GetTierId(),
|
||
TierCode: req.GetTierCode(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.CreditRoomTurnoverRewardResponse{
|
||
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
|
||
}
|
||
|
||
// CreditInviteActivityReward 处理 activity-service 邀请活动奖励金币入账。
|
||
func (s *Server) CreditInviteActivityReward(ctx context.Context, req *walletv1.CreditInviteActivityRewardRequest) (*walletv1.CreditInviteActivityRewardResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.CreditInviteActivityReward(ctx, ledger.InviteActivityRewardCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Amount: req.GetAmount(),
|
||
ClaimID: req.GetClaimId(),
|
||
RewardType: req.GetRewardType(),
|
||
TierID: req.GetTierId(),
|
||
TierCode: req.GetTierCode(),
|
||
CycleKey: req.GetCycleKey(),
|
||
ReachedValue: req.GetReachedValue(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.CreditInviteActivityRewardResponse{
|
||
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
|
||
}
|
||
|
||
// CreditAgencyOpeningReward 处理 activity-service 代理开业流水档位奖励金币入账。
|
||
func (s *Server) CreditAgencyOpeningReward(ctx context.Context, req *walletv1.CreditAgencyOpeningRewardRequest) (*walletv1.CreditAgencyOpeningRewardResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.CreditAgencyOpeningReward(ctx, ledger.AgencyOpeningRewardCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
TargetUserID: req.GetTargetUserId(),
|
||
Amount: req.GetAmount(),
|
||
ApplicationID: req.GetApplicationId(),
|
||
CycleID: req.GetCycleId(),
|
||
AgencyID: req.GetAgencyId(),
|
||
RankNo: req.GetRankNo(),
|
||
ScoreCoins: req.GetScoreCoins(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
|
||
return &walletv1.CreditAgencyOpeningRewardResponse{
|
||
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(),
|
||
SellerCountryID: req.GetSellerCountryId(),
|
||
SellerRegionID: req.GetSellerRegionId(),
|
||
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(),
|
||
TargetCountryID: req.GetTargetCountryId(),
|
||
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
|
||
}
|
||
|
||
// ListCoinSellerSalaryExchangeRateTiers 返回工资转币商使用的区域比例区间,调用方用它做预计到账展示。
|
||
func (s *Server) ListCoinSellerSalaryExchangeRateTiers(ctx context.Context, req *walletv1.ListCoinSellerSalaryExchangeRateTiersRequest) (*walletv1.ListCoinSellerSalaryExchangeRateTiersResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
tiers, err := s.svc.ListCoinSellerSalaryExchangeRateTiers(ctx, req.GetAppCode(), req.GetRegionId(), req.GetIncludeDisabled())
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
response := &walletv1.ListCoinSellerSalaryExchangeRateTiersResponse{Tiers: make([]*walletv1.CoinSellerSalaryExchangeRateTier, 0, len(tiers))}
|
||
for _, tier := range tiers {
|
||
response.Tiers = append(response.Tiers, &walletv1.CoinSellerSalaryExchangeRateTier{
|
||
RegionId: tier.RegionID,
|
||
MinUsdMinor: tier.MinUSDMinor,
|
||
MaxUsdMinor: tier.MaxUSDMinor,
|
||
CoinPerUsd: tier.CoinPerUSD,
|
||
Status: tier.Status,
|
||
SortOrder: int32(tier.SortOrder),
|
||
UpdatedAtMs: tier.UpdatedAtMS,
|
||
})
|
||
}
|
||
return response, nil
|
||
}
|
||
|
||
// ExchangeSalaryToCoin 处理用户工资兑换普通金币,身份资产已经由 gateway 校验并注入。
|
||
func (s *Server) ExchangeSalaryToCoin(ctx context.Context, req *walletv1.ExchangeSalaryToCoinRequest) (*walletv1.ExchangeSalaryToCoinResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.ExchangeSalaryToCoin(ctx, ledger.SalaryExchangeCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
UserID: req.GetUserId(),
|
||
SalaryAssetType: req.GetSalaryAssetType(),
|
||
SalaryUSDMinor: req.GetSalaryUsdMinor(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.ExchangeSalaryToCoinResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
SalaryBalanceAfter: receipt.SalaryBalanceAfter,
|
||
CoinBalanceAfter: receipt.CoinBalanceAfter,
|
||
SalaryUsdMinor: receipt.SalaryUSDMinor,
|
||
CoinAmount: receipt.CoinAmount,
|
||
CoinPerUsd: receipt.CoinPerUSD,
|
||
}, nil
|
||
}
|
||
|
||
// TransferSalaryToCoinSeller 处理用户工资转同区域币商专用金币库存。
|
||
func (s *Server) TransferSalaryToCoinSeller(ctx context.Context, req *walletv1.TransferSalaryToCoinSellerRequest) (*walletv1.TransferSalaryToCoinSellerResponse, error) {
|
||
ctx = appcode.WithContext(ctx, req.GetAppCode())
|
||
receipt, err := s.svc.TransferSalaryToCoinSeller(ctx, ledger.SalaryTransferToCoinSellerCommand{
|
||
AppCode: req.GetAppCode(),
|
||
CommandID: req.GetCommandId(),
|
||
SourceUserID: req.GetSourceUserId(),
|
||
SellerUserID: req.GetSellerUserId(),
|
||
SalaryAssetType: req.GetSalaryAssetType(),
|
||
SalaryUSDMinor: req.GetSalaryUsdMinor(),
|
||
RegionID: req.GetRegionId(),
|
||
Reason: req.GetReason(),
|
||
})
|
||
if err != nil {
|
||
return nil, xerr.ToGRPCError(err)
|
||
}
|
||
return &walletv1.TransferSalaryToCoinSellerResponse{
|
||
TransactionId: receipt.TransactionID,
|
||
SourceSalaryBalanceAfter: receipt.SourceSalaryBalanceAfter,
|
||
SellerBalanceAfter: receipt.SellerBalanceAfter,
|
||
SalaryUsdMinor: receipt.SalaryUSDMinor,
|
||
CoinAmount: receipt.CoinAmount,
|
||
CoinPerUsd: receipt.CoinPerUSD,
|
||
RateMinUsdMinor: receipt.RateMinUSDMinor,
|
||
RateMaxUsdMinor: receipt.RateMaxUSDMinor,
|
||
}, nil
|
||
}
|