重构充值订单模块:用 tb_recharge_order + tb_payment 替换 tb_asset_recharge_record
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m32s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m32s
- 新增 RechargeOrder 和 Payment 模型及对应 Store - 新增 ClientRechargeOrderHandler 提供充值订单列表/详情接口 - 修改 client_wallet.go 使用新表读写充值数据 - 修改 callback/payment.go 将 CRCH 订单路由到 rechargeOrderService - 修改 client_order/service.go 的强充流程使用新表 - 修改 auto_purchase.go 从 tb_recharge_order 读取 linked_package_ids - 修改 order/service.go 的 WalletPay 使用 tb_payment 记录 - 修改 wechat_config_store.go 从 tb_recharge_order 统计待支付充值数 - 移除 AssetRechargeStore 和 AssetRechargeRecord 的注册引用 - 修复文档生成器缺失 ClientRechargeOrder handler - 状态枚举改为 0-based: Pending=0, Paid=1, Closed=2, Refunded=3
This commit is contained in:
@@ -19,14 +19,15 @@ import (
|
||||
|
||||
// AutoPurchasePayload 充值后自动购包任务载荷
|
||||
type AutoPurchasePayload struct {
|
||||
RechargeRecordID uint `json:"recharge_record_id"`
|
||||
RechargeOrderID uint `json:"recharge_order_id"`
|
||||
}
|
||||
|
||||
// AutoPurchaseHandler 充值后自动购包任务处理器
|
||||
type AutoPurchaseHandler struct {
|
||||
db *gorm.DB
|
||||
orderStore *postgres.OrderStore
|
||||
rechargeRecordStore *postgres.AssetRechargeStore
|
||||
rechargeOrderStore *postgres.RechargeOrderStore
|
||||
paymentStore *postgres.PaymentStore
|
||||
walletStore *postgres.AssetWalletStore
|
||||
walletTransactionStore *postgres.AssetWalletTransactionStore
|
||||
packageUsageStore *postgres.PackageUsageStore
|
||||
@@ -42,7 +43,8 @@ type AutoPurchaseHandler struct {
|
||||
func NewAutoPurchaseHandler(
|
||||
db *gorm.DB,
|
||||
orderStore *postgres.OrderStore,
|
||||
rechargeRecordStore *postgres.AssetRechargeStore,
|
||||
rechargeOrderStore *postgres.RechargeOrderStore,
|
||||
paymentStore *postgres.PaymentStore,
|
||||
walletStore *postgres.AssetWalletStore,
|
||||
walletTransactionStore *postgres.AssetWalletTransactionStore,
|
||||
packageUsageStore *postgres.PackageUsageStore,
|
||||
@@ -53,8 +55,11 @@ func NewAutoPurchaseHandler(
|
||||
if orderStore == nil {
|
||||
orderStore = postgres.NewOrderStore(db, redisClient)
|
||||
}
|
||||
if rechargeRecordStore == nil {
|
||||
rechargeRecordStore = postgres.NewAssetRechargeStore(db, redisClient)
|
||||
if rechargeOrderStore == nil {
|
||||
rechargeOrderStore = postgres.NewRechargeOrderStore(db, redisClient)
|
||||
}
|
||||
if paymentStore == nil {
|
||||
paymentStore = postgres.NewPaymentStore(db, redisClient)
|
||||
}
|
||||
if walletStore == nil {
|
||||
walletStore = postgres.NewAssetWalletStore(db, redisClient)
|
||||
@@ -69,7 +74,8 @@ func NewAutoPurchaseHandler(
|
||||
return &AutoPurchaseHandler{
|
||||
db: db,
|
||||
orderStore: orderStore,
|
||||
rechargeRecordStore: rechargeRecordStore,
|
||||
rechargeOrderStore: rechargeOrderStore,
|
||||
paymentStore: paymentStore,
|
||||
walletStore: walletStore,
|
||||
walletTransactionStore: walletTransactionStore,
|
||||
packageUsageStore: packageUsageStore,
|
||||
@@ -89,64 +95,64 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
h.logger.Error("解析自动购包任务载荷失败", zap.Error(err))
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
if payload.RechargeRecordID == 0 {
|
||||
h.logger.Error("自动购包任务载荷无效", zap.Uint("recharge_record_id", payload.RechargeRecordID))
|
||||
if payload.RechargeOrderID == 0 {
|
||||
h.logger.Error("自动购包任务载荷无效", zap.Uint("recharge_order_id", payload.RechargeOrderID))
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
|
||||
rechargeRecord, err := h.rechargeRecordStore.GetByID(ctx, payload.RechargeRecordID)
|
||||
rechargeOrder, err := h.rechargeOrderStore.GetByID(ctx, payload.RechargeOrderID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
h.logger.Warn("充值记录不存在,跳过自动购包", zap.Uint("recharge_record_id", payload.RechargeRecordID))
|
||||
h.logger.Warn("充值订单不存在,跳过自动购包", zap.Uint("recharge_order_id", payload.RechargeOrderID))
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
h.logger.Error("查询充值记录失败", zap.Uint("recharge_record_id", payload.RechargeRecordID), zap.Error(err))
|
||||
h.logger.Error("查询充值订单失败", zap.Uint("recharge_order_id", payload.RechargeOrderID), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
if rechargeRecord.AutoPurchaseStatus == constants.AutoPurchaseStatusSuccess {
|
||||
if rechargeOrder.AutoPurchaseStatus == constants.AutoPurchaseStatusSuccess {
|
||||
return nil
|
||||
}
|
||||
if rechargeRecord.AutoPurchaseStatus == constants.AutoPurchaseStatusFailed {
|
||||
if rechargeOrder.AutoPurchaseStatus == constants.AutoPurchaseStatusFailed {
|
||||
return nil
|
||||
}
|
||||
|
||||
packageIDs, err := parseLinkedPackageIDs(rechargeRecord.LinkedPackageIDs)
|
||||
packageIDs, err := parseLinkedPackageIDs(rechargeOrder.LinkedPackageIDs)
|
||||
if err != nil {
|
||||
h.logger.Error("解析关联套餐ID失败", zap.Uint("recharge_record_id", rechargeRecord.ID), zap.Error(err))
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeRecord.ID)
|
||||
h.logger.Error("解析关联套餐ID失败", zap.Uint("recharge_order_id", rechargeOrder.ID), zap.Error(err))
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeOrder.ID)
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
if len(packageIDs) == 0 {
|
||||
h.logger.Error("关联套餐ID为空,无法自动购包", zap.Uint("recharge_record_id", rechargeRecord.ID))
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeRecord.ID)
|
||||
h.logger.Error("关联套餐ID为空,无法自动购包", zap.Uint("recharge_order_id", rechargeOrder.ID))
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeOrder.ID)
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
|
||||
packages, totalAmount, err := h.loadPackages(ctx, packageIDs)
|
||||
if err != nil {
|
||||
h.logger.Error("加载关联套餐失败", zap.Uint("recharge_record_id", rechargeRecord.ID), zap.Error(err))
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeRecord.ID)
|
||||
h.logger.Error("加载关联套餐失败", zap.Uint("recharge_order_id", rechargeOrder.ID), zap.Error(err))
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeOrder.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取资产系列ID,用于差价佣金和一次性佣金计算
|
||||
var seriesID *uint
|
||||
if rechargeRecord.LinkedCarrierID != nil && *rechargeRecord.LinkedCarrierID > 0 {
|
||||
if rechargeRecord.LinkedCarrierType == "card" || rechargeRecord.LinkedCarrierType == constants.AssetWalletResourceTypeIotCard {
|
||||
if card, cardErr := h.iotCardStore.GetByID(ctx, *rechargeRecord.LinkedCarrierID); cardErr == nil {
|
||||
if rechargeOrder.LinkedCarrierID != nil && *rechargeOrder.LinkedCarrierID > 0 {
|
||||
if rechargeOrder.LinkedCarrierType == "card" || rechargeOrder.LinkedCarrierType == constants.AssetWalletResourceTypeIotCard {
|
||||
if card, cardErr := h.iotCardStore.GetByID(ctx, *rechargeOrder.LinkedCarrierID); cardErr == nil {
|
||||
seriesID = card.SeriesID
|
||||
} else {
|
||||
h.logger.Warn("自动购包获取卡系列ID失败",
|
||||
zap.Uint("card_id", *rechargeRecord.LinkedCarrierID),
|
||||
zap.Uint("card_id", *rechargeOrder.LinkedCarrierID),
|
||||
zap.Error(cardErr))
|
||||
}
|
||||
} else if rechargeRecord.LinkedCarrierType == "device" || rechargeRecord.LinkedCarrierType == constants.AssetWalletResourceTypeDevice {
|
||||
if device, deviceErr := h.deviceStore.GetByID(ctx, *rechargeRecord.LinkedCarrierID); deviceErr == nil {
|
||||
} else if rechargeOrder.LinkedCarrierType == "device" || rechargeOrder.LinkedCarrierType == constants.AssetWalletResourceTypeDevice {
|
||||
if device, deviceErr := h.deviceStore.GetByID(ctx, *rechargeOrder.LinkedCarrierID); deviceErr == nil {
|
||||
seriesID = device.SeriesID
|
||||
} else {
|
||||
h.logger.Warn("自动购包获取设备系列ID失败",
|
||||
zap.Uint("device_id", *rechargeRecord.LinkedCarrierID),
|
||||
zap.Uint("device_id", *rechargeOrder.LinkedCarrierID),
|
||||
zap.Error(deviceErr))
|
||||
}
|
||||
}
|
||||
@@ -155,13 +161,13 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
// 获取卖家成本价,用于差价佣金链式计算的起点
|
||||
// 平台直销(ShopIDTag == 0)时成本价为 0,差价佣金计算会直接跳过
|
||||
var sellerCostPrice int64
|
||||
if rechargeRecord.ShopIDTag > 0 && len(packages) > 0 {
|
||||
allocation, allocErr := h.shopPackageAllocationStore.GetByShopAndPackageForSystem(ctx, rechargeRecord.ShopIDTag, packages[0].ID)
|
||||
if rechargeOrder.ShopIDTag > 0 && len(packages) > 0 {
|
||||
allocation, allocErr := h.shopPackageAllocationStore.GetByShopAndPackageForSystem(ctx, rechargeOrder.ShopIDTag, packages[0].ID)
|
||||
if allocErr == nil {
|
||||
sellerCostPrice = allocation.CostPrice
|
||||
} else {
|
||||
h.logger.Warn("自动购包获取卖家成本价失败,差价佣金将计算为0",
|
||||
zap.Uint("shop_id", rechargeRecord.ShopIDTag),
|
||||
zap.Uint("shop_id", rechargeOrder.ShopIDTag),
|
||||
zap.Uint("package_id", packages[0].ID),
|
||||
zap.Error(allocErr))
|
||||
}
|
||||
@@ -169,7 +175,7 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
|
||||
var createdOrderID uint
|
||||
if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
wallet, walletErr := h.walletStore.GetByID(ctx, rechargeRecord.AssetWalletID)
|
||||
wallet, walletErr := h.walletStore.GetByID(ctx, rechargeOrder.AssetWalletID)
|
||||
if walletErr != nil {
|
||||
if walletErr == gorm.ErrRecordNotFound {
|
||||
return errors.New("资产钱包不存在")
|
||||
@@ -185,7 +191,7 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
order, orderItems, buildErr := h.buildOrderAndItems(rechargeRecord, packages, totalAmount, seriesID, sellerCostPrice, now)
|
||||
order, orderItems, buildErr := h.buildOrderAndItems(rechargeOrder, packages, totalAmount, seriesID, sellerCostPrice, now)
|
||||
if buildErr != nil {
|
||||
return buildErr
|
||||
}
|
||||
@@ -202,12 +208,24 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
return err
|
||||
}
|
||||
|
||||
payment := &model.Payment{
|
||||
PaymentNo: order.OrderNo,
|
||||
OrderID: order.ID,
|
||||
OrderType: model.PaymentOrderTypePackage,
|
||||
PaymentMethod: model.PaymentByWallet,
|
||||
Amount: totalAmount,
|
||||
Status: model.PaymentRecordStatusPaid,
|
||||
}
|
||||
if err = tx.Create(payment).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
refType := constants.ReferenceTypeOrder
|
||||
walletTx := &model.AssetWalletTransaction{
|
||||
AssetWalletID: wallet.ID,
|
||||
ResourceType: wallet.ResourceType,
|
||||
ResourceID: wallet.ResourceID,
|
||||
UserID: rechargeRecord.UserID,
|
||||
UserID: rechargeOrder.UserID,
|
||||
TransactionType: constants.AssetTransactionTypeDeduct,
|
||||
Amount: -totalAmount,
|
||||
BalanceBefore: wallet.Balance,
|
||||
@@ -215,7 +233,7 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
Status: constants.TransactionStatusSuccess,
|
||||
ReferenceType: &refType,
|
||||
ReferenceNo: &order.OrderNo,
|
||||
Creator: rechargeRecord.UserID,
|
||||
Creator: rechargeOrder.UserID,
|
||||
ShopIDTag: wallet.ShopIDTag,
|
||||
EnterpriseIDTag: wallet.EnterpriseIDTag,
|
||||
}
|
||||
@@ -227,8 +245,8 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Model(&model.AssetRechargeRecord{}).
|
||||
Where("id = ?", rechargeRecord.ID).
|
||||
if err = tx.Model(&model.RechargeOrder{}).
|
||||
Where("id = ?", rechargeOrder.ID).
|
||||
Update("auto_purchase_status", constants.AutoPurchaseStatusSuccess).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -236,10 +254,10 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
return nil
|
||||
}); err != nil {
|
||||
h.logger.Error("自动购包任务执行失败",
|
||||
zap.Uint("recharge_record_id", rechargeRecord.ID),
|
||||
zap.Uint("recharge_record_id", rechargeOrder.ID),
|
||||
zap.Error(err),
|
||||
)
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeRecord.ID)
|
||||
h.markAutoPurchaseFailedIfFinalRetry(ctx, rechargeOrder.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -263,13 +281,13 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
}
|
||||
}
|
||||
|
||||
h.logger.Info("自动购包任务执行成功", zap.Uint("recharge_record_id", rechargeRecord.ID))
|
||||
h.logger.Info("自动购包任务执行成功", zap.Uint("recharge_record_id", rechargeOrder.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewAutoPurchaseTask 创建充值后自动购包任务
|
||||
func NewAutoPurchaseTask(rechargeRecordID uint) (*asynq.Task, error) {
|
||||
payloadBytes, err := sonic.Marshal(AutoPurchasePayload{RechargeRecordID: rechargeRecordID})
|
||||
func NewAutoPurchaseTask(rechargeOrderID uint) (*asynq.Task, error) {
|
||||
payloadBytes, err := sonic.Marshal(AutoPurchasePayload{RechargeOrderID: rechargeOrderID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -281,7 +299,7 @@ func NewAutoPurchaseTask(rechargeRecordID uint) (*asynq.Task, error) {
|
||||
), nil
|
||||
}
|
||||
|
||||
func (h *AutoPurchaseHandler) markAutoPurchaseFailedIfFinalRetry(ctx context.Context, rechargeRecordID uint) {
|
||||
func (h *AutoPurchaseHandler) markAutoPurchaseFailedIfFinalRetry(ctx context.Context, rechargeOrderID uint) {
|
||||
retryCount, ok := asynq.GetRetryCount(ctx)
|
||||
if !ok {
|
||||
return
|
||||
@@ -295,17 +313,17 @@ func (h *AutoPurchaseHandler) markAutoPurchaseFailedIfFinalRetry(ctx context.Con
|
||||
}
|
||||
|
||||
if err := h.db.WithContext(ctx).
|
||||
Model(&model.AssetRechargeRecord{}).
|
||||
Where("id = ?", rechargeRecordID).
|
||||
Model(&model.RechargeOrder{}).
|
||||
Where("id = ?", rechargeOrderID).
|
||||
Update("auto_purchase_status", constants.AutoPurchaseStatusFailed).Error; err != nil {
|
||||
h.logger.Error("更新自动购包失败状态失败",
|
||||
zap.Uint("recharge_record_id", rechargeRecordID),
|
||||
zap.Uint("recharge_record_id", rechargeOrderID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Warn("自动购包达到最大重试次数,已标记失败", zap.Uint("recharge_record_id", rechargeRecordID))
|
||||
h.logger.Warn("自动购包达到最大重试次数,已标记失败", zap.Uint("recharge_record_id", rechargeOrderID))
|
||||
}
|
||||
|
||||
func (h *AutoPurchaseHandler) loadPackages(ctx context.Context, packageIDs []uint) ([]*model.Package, int64, error) {
|
||||
@@ -330,39 +348,39 @@ func (h *AutoPurchaseHandler) loadPackages(ctx context.Context, packageIDs []uin
|
||||
}
|
||||
|
||||
func (h *AutoPurchaseHandler) buildOrderAndItems(
|
||||
rechargeRecord *model.AssetRechargeRecord,
|
||||
rechargeOrder *model.RechargeOrder,
|
||||
packages []*model.Package,
|
||||
totalAmount int64,
|
||||
seriesID *uint,
|
||||
sellerCostPrice int64,
|
||||
now time.Time,
|
||||
) (*model.Order, []*model.OrderItem, error) {
|
||||
orderType, iotCardID, deviceID, err := parseLinkedCarrier(rechargeRecord.LinkedOrderType, rechargeRecord.LinkedCarrierType, rechargeRecord.LinkedCarrierID)
|
||||
orderType, iotCardID, deviceID, err := parseLinkedCarrier(rechargeOrder.LinkedOrderType, rechargeOrder.LinkedCarrierType, rechargeOrder.LinkedCarrierID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
generation := rechargeRecord.Generation
|
||||
generation := rechargeOrder.Generation
|
||||
if generation <= 0 {
|
||||
generation = 1
|
||||
}
|
||||
|
||||
var sellerShopID *uint
|
||||
if rechargeRecord.ShopIDTag > 0 {
|
||||
shopID := rechargeRecord.ShopIDTag
|
||||
if rechargeOrder.ShopIDTag > 0 {
|
||||
shopID := rechargeOrder.ShopIDTag
|
||||
sellerShopID = &shopID
|
||||
}
|
||||
|
||||
paidAmount := totalAmount
|
||||
order := &model.Order{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: rechargeRecord.UserID,
|
||||
Updater: rechargeRecord.UserID,
|
||||
Creator: rechargeOrder.UserID,
|
||||
Updater: rechargeOrder.UserID,
|
||||
},
|
||||
OrderNo: h.orderStore.GenerateOrderNo(),
|
||||
OrderType: orderType,
|
||||
BuyerType: model.BuyerTypePersonal,
|
||||
BuyerID: rechargeRecord.UserID,
|
||||
BuyerID: rechargeOrder.UserID,
|
||||
IotCardID: iotCardID,
|
||||
DeviceID: deviceID,
|
||||
TotalAmount: totalAmount,
|
||||
@@ -383,8 +401,8 @@ func (h *AutoPurchaseHandler) buildOrderAndItems(
|
||||
for _, pkg := range packages {
|
||||
items = append(items, &model.OrderItem{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: rechargeRecord.UserID,
|
||||
Updater: rechargeRecord.UserID,
|
||||
Creator: rechargeOrder.UserID,
|
||||
Updater: rechargeOrder.UserID,
|
||||
},
|
||||
PackageID: pkg.ID,
|
||||
PackageName: pkg.PackageName,
|
||||
|
||||
Reference in New Issue
Block a user