diff --git a/docs/admin-openapi.yaml b/docs/admin-openapi.yaml index 9609a66..fef489b 100644 --- a/docs/admin-openapi.yaml +++ b/docs/admin-openapi.yaml @@ -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 diff --git a/internal/model/agent_wallet.go b/internal/model/agent_wallet.go index 0fb6b5a..82e9f74 100644 --- a/internal/model/agent_wallet.go +++ b/internal/model/agent_wallet.go @@ -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"` diff --git a/internal/model/dto/shop_commission_dto.go b/internal/model/dto/shop_commission_dto.go index 7830ccd..a44710f 100644 --- a/internal/model/dto/shop_commission_dto.go +++ b/internal/model/dto/shop_commission_dto.go @@ -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:"流水时间"` } diff --git a/internal/service/order/service.go b/internal/service/order/service.go index 38d9030..218c15f 100644 --- a/internal/service/order/service.go +++ b/internal/service/order/service.go @@ -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 } diff --git a/internal/service/shop_commission/service.go b/internal/service/shop_commission/service.go index ea4bbae..abba4d5 100644 --- a/internal/service/shop_commission/service.go +++ b/internal/service/shop_commission/service.go @@ -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"), }) diff --git a/migrations/000149_add_asset_snapshot_to_agent_wallet_transaction.down.sql b/migrations/000149_add_asset_snapshot_to_agent_wallet_transaction.down.sql new file mode 100644 index 0000000..c845a41 --- /dev/null +++ b/migrations/000149_add_asset_snapshot_to_agent_wallet_transaction.down.sql @@ -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; diff --git a/migrations/000149_add_asset_snapshot_to_agent_wallet_transaction.up.sql b/migrations/000149_add_asset_snapshot_to_agent_wallet_transaction.up.sql new file mode 100644 index 0000000..7c108d9 --- /dev/null +++ b/migrations/000149_add_asset_snapshot_to_agent_wallet_transaction.up.sql @@ -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或虚拟号)';