diff --git a/internal/service/asset_wallet/service.go b/internal/service/asset_wallet/service.go new file mode 100644 index 0000000..3989bba --- /dev/null +++ b/internal/service/asset_wallet/service.go @@ -0,0 +1,162 @@ +package asset_wallet + +import ( + "context" + + "github.com/break/junhong_cmp_fiber/internal/model/dto" + "github.com/break/junhong_cmp_fiber/internal/store/postgres" + "github.com/break/junhong_cmp_fiber/pkg/errors" + "gorm.io/gorm" +) + +// Service 资产钱包业务服务 +// 负责管理端资产(卡/设备)钱包概况查询和流水列表查询 +type Service struct { + assetWalletStore *postgres.AssetWalletStore + assetWalletTransactionStore *postgres.AssetWalletTransactionStore +} + +// New 创建资产钱包服务实例 +func New(assetWalletStore *postgres.AssetWalletStore, assetWalletTransactionStore *postgres.AssetWalletTransactionStore) *Service { + return &Service{ + assetWalletStore: assetWalletStore, + assetWalletTransactionStore: assetWalletTransactionStore, + } +} + +// GetWallet 查询资产钱包概况 +// assetType 为 card 或 device,映射到 resourceType = iot_card 或 device +func (s *Service) GetWallet(ctx context.Context, assetType string, assetID uint) (*dto.AssetWalletResponse, error) { + resourceType, err := mapAssetTypeToResourceType(assetType) + if err != nil { + return nil, err + } + + wallet, err := s.assetWalletStore.GetByResourceTypeAndID(ctx, resourceType, assetID) + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, errors.New(errors.CodeNotFound, "该资产暂无钱包记录") + } + return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询钱包失败") + } + + statusText := walletStatusText(wallet.Status) + + return &dto.AssetWalletResponse{ + WalletID: wallet.ID, + ResourceType: wallet.ResourceType, + ResourceID: wallet.ResourceID, + Balance: wallet.Balance, + FrozenBalance: wallet.FrozenBalance, + AvailableBalance: wallet.Balance - wallet.FrozenBalance, + Currency: wallet.Currency, + Status: wallet.Status, + StatusText: statusText, + CreatedAt: wallet.CreatedAt, + UpdatedAt: wallet.UpdatedAt, + }, nil +} + +// ListTransactions 查询资产钱包流水列表(分页) +func (s *Service) ListTransactions(ctx context.Context, assetType string, assetID uint, req *dto.AssetWalletTransactionListRequest) (*dto.AssetWalletTransactionListResponse, error) { + resourceType, err := mapAssetTypeToResourceType(assetType) + if err != nil { + return nil, err + } + + page := req.Page + if page < 1 { + page = 1 + } + pageSize := req.PageSize + if pageSize < 1 { + pageSize = 20 + } + if pageSize > 100 { + pageSize = 100 + } + offset := (page - 1) * pageSize + + transactions, err := s.assetWalletTransactionStore.ListByResourceIDWithFilter( + ctx, resourceType, assetID, req.TransactionType, req.StartTime, req.EndTime, offset, pageSize, + ) + if err != nil { + return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询流水列表失败") + } + + total, err := s.assetWalletTransactionStore.CountByResourceIDWithFilter( + ctx, resourceType, assetID, req.TransactionType, req.StartTime, req.EndTime, + ) + if err != nil { + return nil, errors.Wrap(errors.CodeDatabaseError, err, "统计流水数量失败") + } + + list := make([]*dto.AssetWalletTransactionItem, 0, len(transactions)) + for _, tx := range transactions { + list = append(list, &dto.AssetWalletTransactionItem{ + ID: tx.ID, + TransactionType: tx.TransactionType, + TransactionTypeText: transactionTypeText(tx.TransactionType), + Amount: tx.Amount, + BalanceBefore: tx.BalanceBefore, + BalanceAfter: tx.BalanceAfter, + ReferenceType: tx.ReferenceType, + ReferenceNo: tx.ReferenceNo, + Remark: tx.Remark, + CreatedAt: tx.CreatedAt, + }) + } + + totalPages := int(total) / pageSize + if int(total)%pageSize > 0 { + totalPages++ + } + + return &dto.AssetWalletTransactionListResponse{ + List: list, + Total: total, + Page: page, + PageSize: pageSize, + TotalPages: totalPages, + }, nil +} + +// mapAssetTypeToResourceType 将路由参数 assetType(card/device)映射到数据库资源类型 +func mapAssetTypeToResourceType(assetType string) (string, error) { + switch assetType { + case "card": + return "iot_card", nil + case "device": + return "device", nil + default: + return "", errors.New(errors.CodeInvalidParam, "无效的资产类型,仅支持 card 或 device") + } +} + +// walletStatusText 翻译钱包状态文本 +func walletStatusText(status int) string { + switch status { + case 1: + return "正常" + case 2: + return "冻结" + case 3: + return "关闭" + default: + return "未知" + } +} + +// transactionTypeText 翻译交易类型文本 +func transactionTypeText(transactionType string) string { + switch transactionType { + case "recharge": + return "充值" + case "deduct": + return "扣款" + case "refund": + return "退款" + default: + return transactionType + } +}