Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 修复B-2:27个分页DTO字段名统一(list→items, page_size→size,删除total_pages) - 修复B-1:角色接口统一返回DTO(id小写,消除GORM大写字段名) - 修复C-1:套餐详情接口(GET /packages/:id)补充代理佣金字段 - 修复C-2:批量分配已存在记录由500改为静默跳过 - 修复C-3:创建系列授权响应在事务提交后构建,packages数组不再为空 - 新增C端开发测试登录接口(POST /api/c/v1/auth/dev-login,仅logging.development=true时生效) - 新增Hurl测试:card-price-verification-flow.hurl(导入卡→分配→C端价格验证) - 新增测试Excel数据文件和dev.env测试变量
158 lines
4.6 KiB
Go
158 lines
4.6 KiB
Go
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 {
|
||
// 暂无钱包记录属于正常业务状态(企业卡/未充值资产),返回 nil 而非错误
|
||
return nil, nil
|
||
}
|
||
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,
|
||
})
|
||
}
|
||
|
||
return &dto.AssetWalletTransactionListResponse{
|
||
List: list,
|
||
Total: total,
|
||
Page: page,
|
||
PageSize: pageSize,
|
||
}, 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
|
||
}
|
||
}
|