package wallet import ( "context" "strings" "time" "hyapp/pkg/appcode" "hyapp/pkg/xerr" "hyapp/services/wallet-service/internal/domain/ledger" ) // ListPointWithdrawalCoinSellers 返回服务端过滤后的币商白名单;区域和国家都来自可信用户资料,客户端不能自报服务范围。 func (s *Service) ListPointWithdrawalCoinSellers(ctx context.Context, appCode string, regionID int64, countryCode string, includeDisabled bool) ([]ledger.PointWithdrawalCoinSellerConfig, error) { if strings.TrimSpace(appCode) == "" || regionID < 0 { return nil, xerr.New(xerr.InvalidArgument, "app_code is required") } if s.repository == nil { return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured") } appCode = appcode.Normalize(appCode) return s.repository.ListPointWithdrawalCoinSellers(appcode.WithContext(ctx, appCode), appCode, regionID, strings.ToUpper(strings.TrimSpace(countryCode)), includeDisabled) } // TransferPointToCoinSeller 验证客户端允许提交的最小字段;兑换比例由 repository 事务内锁定配置解析。 func (s *Service) TransferPointToCoinSeller(ctx context.Context, command ledger.PointToCoinSellerCommand) (ledger.PointToCoinSellerReceipt, error) { command.SourceAssetType = strings.ToUpper(strings.TrimSpace(command.SourceAssetType)) if command.SourceAssetType == "" { command.SourceAssetType = ledger.AssetPoint } validAmount := command.PointAmount > 0 if command.SourceAssetType == ledger.AssetPointDiamond { validAmount = command.GrossUSDMinor > 0 } if strings.TrimSpace(command.AppCode) == "" || strings.TrimSpace(command.CommandID) == "" || command.SourceUserID <= 0 || command.SellerUserID <= 0 || !validAmount || command.RegionID <= 0 || (command.SourceAssetType != ledger.AssetPoint && command.SourceAssetType != ledger.AssetPointDiamond) { return ledger.PointToCoinSellerReceipt{}, xerr.New(xerr.InvalidArgument, "point coin seller transfer command is incomplete") } command.AppCode = appcode.Normalize(command.AppCode) command.CommandID = strings.TrimSpace(command.CommandID) command.SourceCountryCode = strings.ToUpper(strings.TrimSpace(command.SourceCountryCode)) command.Reason = strings.TrimSpace(command.Reason) if len(command.CommandID) > 128 || len(command.SourceCountryCode) > 16 || len(command.Reason) > 512 { return ledger.PointToCoinSellerReceipt{}, xerr.New(xerr.InvalidArgument, "point coin seller transfer text fields are too long") } if s.repository == nil { return ledger.PointToCoinSellerReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured") } return s.repository.TransferPointToCoinSeller(appcode.WithContext(ctx, command.AppCode), command) } // ExchangePointToCoin 校验入参;目标 COIN 数量由 repository 事务内的 wallet 政策与统一工资兑币比例决定。 func (s *Service) ExchangePointToCoin(ctx context.Context, command ledger.PointToCoinCommand) (ledger.PointToCoinReceipt, error) { command.SourceAssetType = strings.ToUpper(strings.TrimSpace(command.SourceAssetType)) if command.SourceAssetType == "" { command.SourceAssetType = ledger.AssetPoint } if strings.TrimSpace(command.AppCode) == "" || strings.TrimSpace(command.CommandID) == "" || command.UserID <= 0 || command.PointAmount <= 0 || command.RegionID < 0 || (command.SourceAssetType != ledger.AssetPoint && command.SourceAssetType != ledger.AssetPointDiamond) { return ledger.PointToCoinReceipt{}, xerr.New(xerr.InvalidArgument, "point to coin exchange command is incomplete") } command.AppCode = appcode.Normalize(command.AppCode) command.CommandID = strings.TrimSpace(command.CommandID) if len(command.CommandID) > 128 { return ledger.PointToCoinReceipt{}, xerr.New(xerr.InvalidArgument, "point to coin exchange command id is too long") } if command.NowMS <= 0 { command.NowMS = time.Now().UTC().UnixMilli() } if s.repository == nil { return ledger.PointToCoinReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured") } return s.repository.ExchangePointToCoin(appcode.WithContext(ctx, command.AppCode), command) } // GetPointWithdrawalConfig 读取已发布 wallet 政策快照;nowMS 由服务端补齐,避免客户端选择过期配置。 func (s *Service) GetPointWithdrawalConfig(ctx context.Context, appCode string, regionID int64, nowMS int64) (ledger.PointWithdrawalRuntimeConfig, error) { return s.GetPointWithdrawalConfigForAsset(ctx, appCode, regionID, nowMS, ledger.AssetPoint) } func (s *Service) GetPointWithdrawalConfigForAsset(ctx context.Context, appCode string, regionID int64, nowMS int64, assetType string) (ledger.PointWithdrawalRuntimeConfig, error) { return s.getPointWithdrawalConfig(ctx, appCode, 0, regionID, nowMS, assetType) } // GetPointWithdrawalConfigForUser 为钱包 overview 追加实时日期/次数能力;动作执行仍在账变事务内二次校验。 func (s *Service) GetPointWithdrawalConfigForUser(ctx context.Context, appCode string, userID int64, regionID int64, nowMS int64, assetType string) (ledger.PointWithdrawalRuntimeConfig, error) { if userID <= 0 { return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.InvalidArgument, "point withdrawal config user is invalid") } return s.getPointWithdrawalConfig(ctx, appCode, userID, regionID, nowMS, assetType) } func (s *Service) getPointWithdrawalConfig(ctx context.Context, appCode string, userID int64, regionID int64, nowMS int64, assetType string) (ledger.PointWithdrawalRuntimeConfig, error) { if strings.TrimSpace(appCode) == "" || regionID < 0 { return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.InvalidArgument, "point withdrawal config query is invalid") } if s.repository == nil { return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured") } if nowMS <= 0 { nowMS = time.Now().UnixMilli() } appCode = appcode.Normalize(appCode) assetType = strings.ToUpper(strings.TrimSpace(assetType)) if assetType == "" { assetType = ledger.AssetPoint } if assetType != ledger.AssetPoint && assetType != ledger.AssetPointDiamond { return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.InvalidArgument, "point asset_type is invalid") } if userID > 0 { return s.repository.GetPointWithdrawalConfigForUser(appcode.WithContext(ctx, appCode), appCode, userID, regionID, nowMS, assetType) } return s.repository.GetPointWithdrawalConfigForAsset(appcode.WithContext(ctx, appCode), appCode, regionID, nowMS, assetType) }