feat: 新增 AssetWallet Service,实现资产钱包业务逻辑
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
162
internal/service/asset_wallet/service.go
Normal file
162
internal/service/asset_wallet/service.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user