钱包流水新增资产信息
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m59s

This commit is contained in:
Break
2026-05-29 10:52:12 +08:00
parent 34a7cf0f3c
commit 1ee554daec
7 changed files with 88 additions and 5 deletions

View File

@@ -5740,6 +5740,16 @@ components:
amount:
description: 变动金额(分,正数为入账,负数为扣款)
type: integer
asset_id:
description: 资产ID仅套餐扣款流水有值
minimum: 0
type: integer
asset_identifier:
description: 资产标识快照ICCID、IMEI或虚拟号仅套餐扣款流水有值
type: string
asset_type:
description: 资产类型iot_card:物联网卡, device:设备,仅套餐扣款流水有值)
type: string
balance_after:
description: 变动后余额(分)
type: integer

View File

@@ -50,6 +50,9 @@ type AgentWalletTransaction struct {
ReferenceType *string `gorm:"column:reference_type;type:varchar(50);comment:关联业务类型(order | commission | withdrawal | topup)" json:"reference_type,omitempty"`
ReferenceID *uint `gorm:"column:reference_id;comment:关联业务ID" json:"reference_id,omitempty"`
RelatedShopID *uint `gorm:"column:related_shop_id;comment:关联店铺ID代购时记录下级店铺" json:"related_shop_id,omitempty"`
AssetType string `gorm:"column:asset_type;type:varchar(20);not null;default:'';comment:资产类型(iot_card-物联网卡 | device-设备)" json:"asset_type,omitempty"`
AssetID uint `gorm:"column:asset_id;not null;default:0;comment:资产ID快照(卡ID或设备ID)" json:"asset_id,omitempty"`
AssetIdentifier string `gorm:"column:asset_identifier;type:varchar(100);not null;default:'';comment:资产标识快照(ICCID、IMEI或虚拟号)" json:"asset_identifier,omitempty"`
Remark *string `gorm:"column:remark;type:text;comment:备注" json:"remark,omitempty"`
Metadata *string `gorm:"column:metadata;type:jsonb;comment:扩展信息(如手续费、支付方式等)" json:"metadata,omitempty"`
Creator uint `gorm:"column:creator;not null;comment:创建人ID" json:"creator"`

View File

@@ -60,6 +60,9 @@ type MainWalletTransactionItem struct {
Amount int64 `json:"amount" description:"变动金额(分,正数为入账,负数为扣款)"`
BalanceBefore int64 `json:"balance_before" description:"变动前余额(分)"`
BalanceAfter int64 `json:"balance_after" description:"变动后余额(分)"`
AssetType string `json:"asset_type" description:"资产类型iot_card:物联网卡, device:设备,仅套餐扣款流水有值)"`
AssetID uint `json:"asset_id" description:"资产ID仅套餐扣款流水有值"`
AssetIdentifier string `json:"asset_identifier" description:"资产标识快照ICCID、IMEI或虚拟号仅套餐扣款流水有值"`
Remark string `json:"remark" description:"备注(可为空)"`
CreatedAt string `json:"created_at" description:"流水时间"`
}

View File

@@ -728,6 +728,7 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest
BuyerID: orderBuyerID,
IotCardID: req.IotCardID,
DeviceID: req.DeviceID,
AssetIdentifier: buildOrderAssetIdentifier(validationResult),
TotalAmount: totalAmount,
PaymentMethod: paymentMethod,
PaymentStatus: paymentStatus,
@@ -988,11 +989,11 @@ func (s *Service) getCostPrice(ctx context.Context, shopID uint, packageID uint)
// ctx: 上下文
// tx: 事务对象
// wallet: 扣款前的钱包快照
// orderID: 订单ID
// order: 订单快照
// amount: 扣款金额(正数)
// purchaseRole: 订单角色
// relatedShopID: 关联店铺ID代购场景填充下级店铺ID
func (s *Service) createWalletTransaction(ctx context.Context, tx *gorm.DB, wallet *model.AgentWallet, orderID uint, amount int64, purchaseRole string, relatedShopID *uint) error {
func (s *Service) createWalletTransaction(ctx context.Context, tx *gorm.DB, wallet *model.AgentWallet, order *model.Order, amount int64, purchaseRole string, relatedShopID *uint) error {
var subtype *string
remark := "购买套餐"
if purchaseRole == "" {
@@ -1021,6 +1022,7 @@ func (s *Service) createWalletTransaction(ctx context.Context, tx *gorm.DB, wall
}
userID := middleware.GetUserIDFromContext(ctx)
assetType, assetID, assetIdentifier := buildAgentWalletTransactionAssetSnapshot(order)
// 创建钱包流水记录
transaction := &model.AgentWalletTransaction{
@@ -1034,8 +1036,11 @@ func (s *Service) createWalletTransaction(ctx context.Context, tx *gorm.DB, wall
BalanceAfter: wallet.Balance - amount,
Status: constants.TransactionStatusSuccess,
ReferenceType: strPtr(constants.ReferenceTypeOrder),
ReferenceID: &orderID,
ReferenceID: &order.ID,
RelatedShopID: relatedShopID,
AssetType: assetType,
AssetID: assetID,
AssetIdentifier: assetIdentifier,
Remark: &remark,
Creator: userID,
ShopIDTag: wallet.ShopIDTag,
@@ -1049,6 +1054,51 @@ func (s *Service) createWalletTransaction(ctx context.Context, tx *gorm.DB, wall
return nil
}
// buildAgentWalletTransactionAssetSnapshot 从订单中生成代理钱包扣款流水的资产快照。
func buildAgentWalletTransactionAssetSnapshot(order *model.Order) (string, uint, string) {
if order == nil {
return "", 0, ""
}
switch order.OrderType {
case model.OrderTypeSingleCard:
if order.IotCardID == nil {
return constants.AssetTypeIotCard, 0, order.AssetIdentifier
}
return constants.AssetTypeIotCard, *order.IotCardID, order.AssetIdentifier
case model.OrderTypeDevice:
if order.DeviceID == nil {
return constants.AssetTypeDevice, 0, order.AssetIdentifier
}
return constants.AssetTypeDevice, *order.DeviceID, order.AssetIdentifier
default:
return "", 0, order.AssetIdentifier
}
}
// buildOrderAssetIdentifier 生成订单资产标识快照。
// 后台按标识下单时保留请求标识按ID下单时用卡ICCID或设备IMEI/虚拟号兜底。
func buildOrderAssetIdentifier(result *purchase_validation.PurchaseValidationResult) string {
if result == nil {
return ""
}
if result.Card != nil {
return result.Card.ICCID
}
if result.Device != nil {
return firstNonEmpty(result.Device.IMEI, result.Device.VirtualNo)
}
return ""
}
func firstNonEmpty(values ...string) string {
for _, value := range values {
if value != "" {
return value
}
}
return ""
}
// strPtr 字符串指针辅助函数
func strPtr(s string) *string {
return &s
@@ -1113,7 +1163,7 @@ func (s *Service) createOrderWithWalletPayment(ctx context.Context, order *model
if order.PurchaseRole == model.PurchaseRolePurchaseForSubordinate {
relatedShopID = &buyerShopID
}
if err := s.createWalletTransaction(ctx, tx, wallet, order.ID, actualAmount, order.PurchaseRole, relatedShopID); err != nil {
if err := s.createWalletTransaction(ctx, tx, wallet, order, actualAmount, order.PurchaseRole, relatedShopID); err != nil {
return err
}
@@ -1593,7 +1643,7 @@ func (s *Service) WalletPay(ctx context.Context, orderID uint, buyerType string,
return errors.New(errors.CodeInsufficientBalance, "余额不足或并发冲突")
}
if err := s.createWalletTransaction(ctx, tx, wallet, order.ID, order.TotalAmount, order.PurchaseRole, nil); err != nil {
if err := s.createWalletTransaction(ctx, tx, wallet, order, order.TotalAmount, order.PurchaseRole, nil); err != nil {
return err
}

View File

@@ -739,6 +739,9 @@ func (s *Service) ListMainWalletTransactions(ctx context.Context, shopID uint, r
Amount: t.Amount,
BalanceBefore: t.BalanceBefore,
BalanceAfter: t.BalanceAfter,
AssetType: t.AssetType,
AssetID: t.AssetID,
AssetIdentifier: t.AssetIdentifier,
Remark: remark,
CreatedAt: t.CreatedAt.Format("2006-01-02 15:04:05"),
})

View File

@@ -0,0 +1,4 @@
ALTER TABLE tb_agent_wallet_transaction
DROP COLUMN IF EXISTS asset_identifier,
DROP COLUMN IF EXISTS asset_id,
DROP COLUMN IF EXISTS asset_type;

View File

@@ -0,0 +1,10 @@
-- 为代理钱包流水新增消费资产快照字段
-- 仅新产生的套餐扣款流水在消费发生时写入,存量历史流水不回填。
ALTER TABLE tb_agent_wallet_transaction
ADD COLUMN IF NOT EXISTS asset_type VARCHAR(20) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS asset_id BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS asset_identifier VARCHAR(100) NOT NULL DEFAULT '';
COMMENT ON COLUMN tb_agent_wallet_transaction.asset_type IS '消费资产类型快照iot_card-物联网卡 | device-设备';
COMMENT ON COLUMN tb_agent_wallet_transaction.asset_id IS '消费资产ID快照卡ID或设备ID';
COMMENT ON COLUMN tb_agent_wallet_transaction.asset_identifier IS '消费资产标识快照ICCID、IMEI或虚拟号';