380 lines
15 KiB
Go
380 lines
15 KiB
Go
package payment
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||
)
|
||
|
||
type rechargeBillDTO struct {
|
||
AppCode string `json:"appCode"`
|
||
TransactionID string `json:"transactionId"`
|
||
CommandID string `json:"commandId"`
|
||
RechargeType string `json:"rechargeType"`
|
||
Status string `json:"status"`
|
||
ExternalRef string `json:"externalRef"`
|
||
UserID int64 `json:"userId,string"`
|
||
SellerUserID int64 `json:"sellerUserId,string"`
|
||
SellerRegionID int64 `json:"sellerRegionId"`
|
||
TargetRegionID int64 `json:"targetRegionId"`
|
||
PolicyID int64 `json:"policyId"`
|
||
PolicyVersion string `json:"policyVersion"`
|
||
CurrencyCode string `json:"currencyCode"`
|
||
CoinAmount int64 `json:"coinAmount"`
|
||
USDMinorAmount int64 `json:"usdMinorAmount"`
|
||
ExchangeCoinAmount int64 `json:"exchangeCoinAmount"`
|
||
ExchangeUSDMinorAmount int64 `json:"exchangeUsdMinorAmount"`
|
||
ProviderAmountMinor int64 `json:"providerAmountMinor,omitempty"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
ProviderCode string `json:"providerCode"`
|
||
UserPaidCurrencyCode string `json:"userPaidCurrencyCode"`
|
||
UserPaidAmountMicro int64 `json:"userPaidAmountMicro"`
|
||
ProviderFeeMicro int64 `json:"providerFeeMicro"`
|
||
ProviderTaxMicro int64 `json:"providerTaxMicro"`
|
||
ProviderNetMicro int64 `json:"providerNetMicro"`
|
||
PaidSyncedAtMS int64 `json:"paidSyncedAtMs"`
|
||
User rechargeBillUserDTO `json:"user"`
|
||
Seller rechargeBillUserDTO `json:"seller"`
|
||
}
|
||
|
||
type rechargeBillUserDTO struct {
|
||
UserID string `json:"userId"`
|
||
DisplayUserID string `json:"displayUserId"`
|
||
Username string `json:"username"`
|
||
Avatar string `json:"avatar"`
|
||
CountryCode string `json:"countryCode"`
|
||
CountryName string `json:"countryName"`
|
||
CountryDisplayName string `json:"countryDisplayName"`
|
||
}
|
||
|
||
type rechargeProductDTO struct {
|
||
AppCode string `json:"appCode"`
|
||
ProductID int64 `json:"productId"`
|
||
ProductCode string `json:"productCode"`
|
||
ProductName string `json:"productName"`
|
||
Description string `json:"description"`
|
||
AudienceType string `json:"audienceType"`
|
||
Platform string `json:"platform"`
|
||
Channel string `json:"channel"`
|
||
CurrencyCode string `json:"currencyCode"`
|
||
AmountUSDT string `json:"amountUsdt"`
|
||
AmountUSDTMicro int64 `json:"amountUsdtMicro"`
|
||
AmountMicro int64 `json:"amountMicro"`
|
||
AmountMinor int64 `json:"amountMinor"`
|
||
CoinAmount int64 `json:"coinAmount"`
|
||
PolicyVersion string `json:"policyVersion"`
|
||
RegionIDs []int64 `json:"regionIds"`
|
||
ResourceAssetType string `json:"resourceAssetType"`
|
||
Status string `json:"status"`
|
||
Enabled bool `json:"enabled"`
|
||
AppVisible bool `json:"appVisible"`
|
||
SortOrder int32 `json:"sortOrder"`
|
||
CreatedByUserID int64 `json:"createdByUserId"`
|
||
UpdatedByUserID int64 `json:"updatedByUserId"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
}
|
||
|
||
type thirdPartyPaymentChannelDTO struct {
|
||
AppCode string `json:"appCode"`
|
||
ProviderCode string `json:"providerCode"`
|
||
ProviderName string `json:"providerName"`
|
||
Status string `json:"status"`
|
||
SortOrder int32 `json:"sortOrder"`
|
||
Methods []thirdPartyPaymentMethodDTO `json:"methods"`
|
||
}
|
||
|
||
type thirdPartyPaymentMethodDTO struct {
|
||
MethodID int64 `json:"methodId"`
|
||
AppCode string `json:"appCode"`
|
||
ProviderCode string `json:"providerCode"`
|
||
ProviderName string `json:"providerName"`
|
||
CountryCode string `json:"countryCode"`
|
||
CountryName string `json:"countryName"`
|
||
CurrencyCode string `json:"currencyCode"`
|
||
PayWay string `json:"payWay"`
|
||
PayType string `json:"payType"`
|
||
MethodName string `json:"methodName"`
|
||
LogoURL string `json:"logoUrl"`
|
||
Status string `json:"status"`
|
||
USDToCurrencyRate string `json:"usdToCurrencyRate"`
|
||
SortOrder int32 `json:"sortOrder"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
}
|
||
|
||
type thirdPartyPaymentMethodSyncDTO struct {
|
||
ProviderCode string `json:"providerCode"`
|
||
ScannedCountryCount int32 `json:"scannedCountryCount"`
|
||
FetchedCountryCount int32 `json:"fetchedCountryCount"`
|
||
CreatedCount int32 `json:"createdCount"`
|
||
UpdatedCount int32 `json:"updatedCount"`
|
||
SkippedCount int32 `json:"skippedCount"`
|
||
FailedCountryCount int `json:"failedCountryCount"`
|
||
Issues []thirdPartyPaymentMethodIssueDTO `json:"issues"`
|
||
}
|
||
|
||
type thirdPartyPaymentMethodIssueDTO struct {
|
||
CountryCode string `json:"countryCode"`
|
||
CurrencyCode string `json:"currencyCode"`
|
||
Code string `json:"code"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
type temporaryPaymentLinkDTO struct {
|
||
OrderID string `json:"orderId"`
|
||
AppCode string `json:"appCode"`
|
||
RegionID int64 `json:"regionId"`
|
||
OwnerUserID uint `json:"ownerUserId,omitempty"`
|
||
OwnerUsername string `json:"ownerUsername,omitempty"`
|
||
OwnerName string `json:"ownerName,omitempty"`
|
||
CommandID string `json:"commandId,omitempty"`
|
||
AudienceType string `json:"audienceType"`
|
||
ProductName string `json:"productName"`
|
||
CoinAmount int64 `json:"coinAmount"`
|
||
USDMinorAmount int64 `json:"usdMinorAmount"`
|
||
ProviderCode string `json:"providerCode"`
|
||
PaymentMethodID int64 `json:"paymentMethodId"`
|
||
CountryCode string `json:"countryCode"`
|
||
CurrencyCode string `json:"currencyCode"`
|
||
ProviderAmountMinor int64 `json:"providerAmountMinor"`
|
||
PayWay string `json:"payWay"`
|
||
PayType string `json:"payType"`
|
||
PayURL string `json:"payUrl"`
|
||
ProviderOrderID string `json:"providerOrderId"`
|
||
TxHash string `json:"txHash"`
|
||
Status string `json:"status"`
|
||
FailureReason string `json:"failureReason"`
|
||
TransactionID string `json:"transactionId"`
|
||
CreatedAtMS int64 `json:"createdAtMs"`
|
||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||
}
|
||
|
||
func rechargeBillFromProto(item *walletv1.RechargeBill) rechargeBillDTO {
|
||
if item == nil {
|
||
return rechargeBillDTO{}
|
||
}
|
||
return rechargeBillDTO{
|
||
AppCode: item.GetAppCode(),
|
||
TransactionID: item.GetTransactionId(),
|
||
CommandID: item.GetCommandId(),
|
||
RechargeType: item.GetRechargeType(),
|
||
Status: item.GetStatus(),
|
||
ExternalRef: item.GetExternalRef(),
|
||
UserID: item.GetUserId(),
|
||
SellerUserID: item.GetSellerUserId(),
|
||
SellerRegionID: item.GetSellerRegionId(),
|
||
TargetRegionID: item.GetTargetRegionId(),
|
||
PolicyID: item.GetPolicyId(),
|
||
PolicyVersion: item.GetPolicyVersion(),
|
||
CurrencyCode: item.GetCurrencyCode(),
|
||
CoinAmount: item.GetCoinAmount(),
|
||
USDMinorAmount: item.GetUsdMinorAmount(),
|
||
ExchangeCoinAmount: item.GetExchangeCoinAmount(),
|
||
ExchangeUSDMinorAmount: item.GetExchangeUsdMinorAmount(),
|
||
ProviderAmountMinor: item.GetProviderAmountMinor(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
ProviderCode: item.GetProviderCode(),
|
||
UserPaidCurrencyCode: item.GetUserPaidCurrencyCode(),
|
||
UserPaidAmountMicro: item.GetUserPaidAmountMicro(),
|
||
ProviderFeeMicro: item.GetProviderFeeMicro(),
|
||
ProviderTaxMicro: item.GetProviderTaxMicro(),
|
||
ProviderNetMicro: item.GetProviderNetMicro(),
|
||
PaidSyncedAtMS: item.GetPaidSyncedAtMs(),
|
||
}
|
||
}
|
||
|
||
// collectRechargeBillUserIDs 把付款用户和币商用户放进同一个去重列表;后续只查一次 users 表即可覆盖两列展示。
|
||
func collectRechargeBillUserIDs(items []rechargeBillDTO) []int64 {
|
||
seen := make(map[int64]struct{}, len(items)*2)
|
||
userIDs := make([]int64, 0, len(items)*2)
|
||
for _, item := range items {
|
||
for _, userID := range []int64{item.UserID, item.SellerUserID} {
|
||
if userID <= 0 {
|
||
continue
|
||
}
|
||
if _, ok := seen[userID]; ok {
|
||
continue
|
||
}
|
||
seen[userID] = struct{}{}
|
||
userIDs = append(userIDs, userID)
|
||
}
|
||
}
|
||
return userIDs
|
||
}
|
||
|
||
// rechargeBillUserOrFallback 保证用户资料缺失时仍返回长 ID,列表和导出不会因为历史用户资料缺失而显示空白。
|
||
func rechargeBillUserOrFallback(profile rechargeBillUserDTO, userID int64) rechargeBillUserDTO {
|
||
if userID <= 0 {
|
||
return rechargeBillUserDTO{}
|
||
}
|
||
if profile.UserID != "" {
|
||
return profile
|
||
}
|
||
return rechargeBillUserDTO{UserID: strconv.FormatInt(userID, 10)}
|
||
}
|
||
|
||
// int64Args 将用户 ID 列表转换为 database/sql 可展开的参数切片,避免为 IN 条件拼接原始数值。
|
||
func int64Args(values []int64) []any {
|
||
args := make([]any, 0, len(values))
|
||
for _, value := range values {
|
||
args = append(args, value)
|
||
}
|
||
return args
|
||
}
|
||
|
||
// placeholders 只生成固定数量的问号占位符;实际用户 ID 仍通过参数绑定传入,避免 SQL 注入。
|
||
func placeholders(count int) string {
|
||
if count <= 0 {
|
||
return ""
|
||
}
|
||
items := make([]string, count)
|
||
for index := range items {
|
||
items[index] = "?"
|
||
}
|
||
return strings.Join(items, ",")
|
||
}
|
||
|
||
func rechargeProductFromProto(item *walletv1.RechargeProduct) rechargeProductDTO {
|
||
if item == nil {
|
||
return rechargeProductDTO{}
|
||
}
|
||
return rechargeProductDTO{
|
||
AppCode: item.GetAppCode(),
|
||
ProductID: item.GetProductId(),
|
||
ProductCode: item.GetProductCode(),
|
||
ProductName: item.GetProductName(),
|
||
Description: item.GetDescription(),
|
||
AudienceType: item.GetAudienceType(),
|
||
Platform: item.GetPlatform(),
|
||
Channel: item.GetChannel(),
|
||
CurrencyCode: item.GetCurrencyCode(),
|
||
AmountUSDT: formatUSDTMicro(item.GetAmountMicro()),
|
||
AmountUSDTMicro: item.GetAmountMicro(),
|
||
AmountMicro: item.GetAmountMicro(),
|
||
AmountMinor: item.GetAmountMinor(),
|
||
CoinAmount: item.GetCoinAmount(),
|
||
PolicyVersion: item.GetPolicyVersion(),
|
||
RegionIDs: append([]int64(nil), item.GetRegionIds()...),
|
||
ResourceAssetType: item.GetResourceAssetType(),
|
||
Status: item.GetStatus(),
|
||
Enabled: item.GetEnabled(),
|
||
AppVisible: item.GetAppVisible(),
|
||
SortOrder: item.GetSortOrder(),
|
||
CreatedByUserID: item.GetCreatedByUserId(),
|
||
UpdatedByUserID: item.GetUpdatedByUserId(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
}
|
||
}
|
||
|
||
func thirdPartyPaymentChannelFromProto(item *walletv1.ThirdPartyPaymentChannel) thirdPartyPaymentChannelDTO {
|
||
if item == nil {
|
||
return thirdPartyPaymentChannelDTO{}
|
||
}
|
||
dto := thirdPartyPaymentChannelDTO{
|
||
AppCode: item.GetAppCode(),
|
||
ProviderCode: item.GetProviderCode(),
|
||
ProviderName: item.GetProviderName(),
|
||
Status: item.GetStatus(),
|
||
SortOrder: item.GetSortOrder(),
|
||
Methods: make([]thirdPartyPaymentMethodDTO, 0, len(item.GetMethods())),
|
||
}
|
||
for _, method := range item.GetMethods() {
|
||
dto.Methods = append(dto.Methods, thirdPartyPaymentMethodFromProto(method))
|
||
}
|
||
return dto
|
||
}
|
||
|
||
func thirdPartyPaymentMethodFromProto(item *walletv1.ThirdPartyPaymentMethod) thirdPartyPaymentMethodDTO {
|
||
if item == nil {
|
||
return thirdPartyPaymentMethodDTO{}
|
||
}
|
||
return thirdPartyPaymentMethodDTO{
|
||
MethodID: item.GetMethodId(),
|
||
AppCode: item.GetAppCode(),
|
||
ProviderCode: item.GetProviderCode(),
|
||
ProviderName: item.GetProviderName(),
|
||
CountryCode: item.GetCountryCode(),
|
||
CountryName: item.GetCountryName(),
|
||
CurrencyCode: item.GetCurrencyCode(),
|
||
PayWay: item.GetPayWay(),
|
||
PayType: item.GetPayType(),
|
||
MethodName: item.GetMethodName(),
|
||
LogoURL: item.GetLogoUrl(),
|
||
Status: item.GetStatus(),
|
||
USDToCurrencyRate: item.GetUsdToCurrencyRate(),
|
||
SortOrder: item.GetSortOrder(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
}
|
||
}
|
||
|
||
func thirdPartyPaymentMethodSyncFromProto(item *walletv1.SyncThirdPartyPaymentMethodsResponse) thirdPartyPaymentMethodSyncDTO {
|
||
if item == nil {
|
||
return thirdPartyPaymentMethodSyncDTO{}
|
||
}
|
||
dto := thirdPartyPaymentMethodSyncDTO{
|
||
ProviderCode: item.GetProviderCode(),
|
||
ScannedCountryCount: item.GetScannedCountryCount(),
|
||
FetchedCountryCount: item.GetFetchedCountryCount(),
|
||
CreatedCount: item.GetCreatedCount(),
|
||
UpdatedCount: item.GetUpdatedCount(),
|
||
SkippedCount: item.GetSkippedCount(),
|
||
FailedCountryCount: len(item.GetIssues()),
|
||
Issues: make([]thirdPartyPaymentMethodIssueDTO, 0, len(item.GetIssues())),
|
||
}
|
||
for _, issue := range item.GetIssues() {
|
||
dto.Issues = append(dto.Issues, thirdPartyPaymentMethodIssueDTO{
|
||
CountryCode: issue.GetCountryCode(),
|
||
CurrencyCode: issue.GetCurrencyCode(),
|
||
Code: issue.GetCode(),
|
||
Message: issue.GetMessage(),
|
||
})
|
||
}
|
||
return dto
|
||
}
|
||
|
||
func temporaryPaymentLinkFromProto(item *walletv1.ExternalRechargeOrder) temporaryPaymentLinkDTO {
|
||
if item == nil {
|
||
return temporaryPaymentLinkDTO{}
|
||
}
|
||
return temporaryPaymentLinkDTO{
|
||
OrderID: item.GetOrderId(),
|
||
AppCode: item.GetAppCode(),
|
||
AudienceType: item.GetAudienceType(),
|
||
ProductName: item.GetProductName(),
|
||
CoinAmount: item.GetCoinAmount(),
|
||
USDMinorAmount: item.GetUsdMinorAmount(),
|
||
ProviderCode: item.GetProviderCode(),
|
||
PaymentMethodID: item.GetPaymentMethodId(),
|
||
CountryCode: item.GetCountryCode(),
|
||
CurrencyCode: item.GetCurrencyCode(),
|
||
ProviderAmountMinor: item.GetProviderAmountMinor(),
|
||
PayWay: item.GetPayWay(),
|
||
PayType: item.GetPayType(),
|
||
PayURL: item.GetPayUrl(),
|
||
ProviderOrderID: item.GetProviderOrderId(),
|
||
TxHash: item.GetTxHash(),
|
||
Status: item.GetStatus(),
|
||
FailureReason: item.GetFailureReason(),
|
||
TransactionID: item.GetTransactionId(),
|
||
CreatedAtMS: item.GetCreatedAtMs(),
|
||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||
}
|
||
}
|
||
|
||
func formatUSDTMicro(amount int64) string {
|
||
if amount <= 0 {
|
||
return "0"
|
||
}
|
||
whole := amount / usdtMicroUnit
|
||
fraction := amount % usdtMicroUnit
|
||
if fraction == 0 {
|
||
return strconv.FormatInt(whole, 10)
|
||
}
|
||
return fmt.Sprintf("%d.%s", whole, strings.TrimRight(fmt.Sprintf("%06d", fraction), "0"))
|
||
}
|