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