Files
junhong_cmp_fiber/internal/service/asset_wallet/service.go

163 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 将路由参数 assetTypecard/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
}
}