fix: 修复C端订单差价佣金计算错误及自动购包订单系列ID缺失问题
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m11s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m11s
- C端普通订单(createPackageOrder)创建时未设置 SellerCostPrice, 导致底层店铺得到整个零售价作为差价佣金,上级代理链无法获得任何佣金。 修复:在 createPackageOrder 中查询卖家成本价并写入订单字段。 - purchase_validation 新增 GetCostPrice 方法,供 C 端订单查询代理渠道成本价。 - auto_purchase 自动购包订单未设置 SeriesID 和 SellerCostPrice, 导致差价佣金计算被整体跳过。修复:在 ProcessTask 中从卡/设备获取 SeriesID,从套餐分配记录获取 SellerCostPrice,传入 buildOrderAndItems。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -284,7 +284,19 @@ func (s *Service) createPackageOrder(
|
||||
redisKey string,
|
||||
created *bool,
|
||||
) (*dto.ClientCreateOrderResponse, error) {
|
||||
order, err := s.buildPendingOrder(customerID, validationResult)
|
||||
// 查询卖家成本价,用于差价佣金链式计算的起点
|
||||
// 平台直销(sellerShopID == 0)时成本价为 0,CalculateCostDiffCommission 会直接跳过
|
||||
var sellerCostPrice int64
|
||||
sellerShopID := resolveSellerShopID(validationResult)
|
||||
if sellerShopID > 0 && len(validationResult.Packages) > 0 {
|
||||
costPrice, err := s.purchaseValidationService.GetCostPrice(ctx, validationResult.Packages[0], sellerShopID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "查询卖家成本价失败")
|
||||
}
|
||||
sellerCostPrice = costPrice
|
||||
}
|
||||
|
||||
order, err := s.buildPendingOrder(customerID, validationResult, sellerCostPrice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -389,7 +401,7 @@ func (s *Service) createForceRechargeOrder(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) buildPendingOrder(customerID uint, result *purchase_validation.PurchaseValidationResult) (*model.Order, error) {
|
||||
func (s *Service) buildPendingOrder(customerID uint, result *purchase_validation.PurchaseValidationResult, sellerCostPrice int64) (*model.Order, error) {
|
||||
orderType := resolveOrderType(result)
|
||||
if orderType == "" {
|
||||
return nil, errors.New(errors.CodeInvalidParam)
|
||||
@@ -413,6 +425,7 @@ func (s *Service) buildPendingOrder(customerID uint, result *purchase_validation
|
||||
Source: constants.OrderSourceClient,
|
||||
Generation: resolveGeneration(result),
|
||||
ExpiresAt: &expiresAt,
|
||||
SellerCostPrice: sellerCostPrice,
|
||||
}
|
||||
|
||||
if result.Card != nil {
|
||||
|
||||
@@ -190,6 +190,19 @@ func (s *Service) GetPurchasePrice(ctx context.Context, pkg *model.Package, sell
|
||||
return pkg.SuggestedRetailPrice, nil
|
||||
}
|
||||
|
||||
// GetCostPrice 获取代理渠道的套餐成本价
|
||||
// sellerShopID == 0 表示平台自营,返回 0(平台直销无成本价概念)
|
||||
func (s *Service) GetCostPrice(ctx context.Context, pkg *model.Package, sellerShopID uint) (int64, error) {
|
||||
if sellerShopID == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
allocation, err := s.packageAllocationStore.GetByShopAndPackageForSystem(ctx, sellerShopID, pkg.ID)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(errors.CodeInternalError, err, "查询套餐分配记录失败")
|
||||
}
|
||||
return allocation.CostPrice, nil
|
||||
}
|
||||
|
||||
// ValidateAdminOfflineCardPurchase 后台 offline 订单专用卡验证
|
||||
// 绕过代理 Allocation 上架检查,仅验证套餐全局状态
|
||||
func (s *Service) ValidateAdminOfflineCardPurchase(ctx context.Context, cardID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
|
||||
|
||||
@@ -24,15 +24,18 @@ type AutoPurchasePayload struct {
|
||||
|
||||
// AutoPurchaseHandler 充值后自动购包任务处理器
|
||||
type AutoPurchaseHandler struct {
|
||||
db *gorm.DB
|
||||
orderStore *postgres.OrderStore
|
||||
rechargeRecordStore *postgres.AssetRechargeStore
|
||||
walletStore *postgres.AssetWalletStore
|
||||
walletTransactionStore *postgres.AssetWalletTransactionStore
|
||||
packageUsageStore *postgres.PackageUsageStore
|
||||
redis *redis.Client
|
||||
asynqClient *asynq.Client // 用于事务提交成功后触发佣金计算任务
|
||||
logger *zap.Logger
|
||||
db *gorm.DB
|
||||
orderStore *postgres.OrderStore
|
||||
rechargeRecordStore *postgres.AssetRechargeStore
|
||||
walletStore *postgres.AssetWalletStore
|
||||
walletTransactionStore *postgres.AssetWalletTransactionStore
|
||||
packageUsageStore *postgres.PackageUsageStore
|
||||
shopPackageAllocationStore *postgres.ShopPackageAllocationStore // 用于查询卖家成本价
|
||||
iotCardStore *postgres.IotCardStore // 用于获取卡的系列ID
|
||||
deviceStore *postgres.DeviceStore // 用于获取设备的系列ID
|
||||
redis *redis.Client
|
||||
asynqClient *asynq.Client // 用于事务提交成功后触发佣金计算任务
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAutoPurchaseHandler 创建充值后自动购包处理器
|
||||
@@ -64,15 +67,18 @@ func NewAutoPurchaseHandler(
|
||||
}
|
||||
|
||||
return &AutoPurchaseHandler{
|
||||
db: db,
|
||||
orderStore: orderStore,
|
||||
rechargeRecordStore: rechargeRecordStore,
|
||||
walletStore: walletStore,
|
||||
walletTransactionStore: walletTransactionStore,
|
||||
packageUsageStore: packageUsageStore,
|
||||
redis: redisClient,
|
||||
asynqClient: asynqClient,
|
||||
logger: logger,
|
||||
db: db,
|
||||
orderStore: orderStore,
|
||||
rechargeRecordStore: rechargeRecordStore,
|
||||
walletStore: walletStore,
|
||||
walletTransactionStore: walletTransactionStore,
|
||||
packageUsageStore: packageUsageStore,
|
||||
shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(db),
|
||||
iotCardStore: postgres.NewIotCardStore(db, redisClient),
|
||||
deviceStore: postgres.NewDeviceStore(db, redisClient),
|
||||
redis: redisClient,
|
||||
asynqClient: asynqClient,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +130,43 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
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 {
|
||||
seriesID = card.SeriesID
|
||||
} else {
|
||||
h.logger.Warn("自动购包获取卡系列ID失败",
|
||||
zap.Uint("card_id", *rechargeRecord.LinkedCarrierID),
|
||||
zap.Error(cardErr))
|
||||
}
|
||||
} else if rechargeRecord.LinkedCarrierType == "device" || rechargeRecord.LinkedCarrierType == constants.AssetWalletResourceTypeDevice {
|
||||
if device, deviceErr := h.deviceStore.GetByID(ctx, *rechargeRecord.LinkedCarrierID); deviceErr == nil {
|
||||
seriesID = device.SeriesID
|
||||
} else {
|
||||
h.logger.Warn("自动购包获取设备系列ID失败",
|
||||
zap.Uint("device_id", *rechargeRecord.LinkedCarrierID),
|
||||
zap.Error(deviceErr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取卖家成本价,用于差价佣金链式计算的起点
|
||||
// 平台直销(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 allocErr == nil {
|
||||
sellerCostPrice = allocation.CostPrice
|
||||
} else {
|
||||
h.logger.Warn("自动购包获取卖家成本价失败,差价佣金将计算为0",
|
||||
zap.Uint("shop_id", rechargeRecord.ShopIDTag),
|
||||
zap.Uint("package_id", packages[0].ID),
|
||||
zap.Error(allocErr))
|
||||
}
|
||||
}
|
||||
|
||||
var createdOrderID uint
|
||||
if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
wallet, walletErr := h.walletStore.GetByID(ctx, rechargeRecord.AssetWalletID)
|
||||
@@ -142,7 +185,7 @@ func (h *AutoPurchaseHandler) ProcessTask(ctx context.Context, task *asynq.Task)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
order, orderItems, buildErr := h.buildOrderAndItems(rechargeRecord, packages, totalAmount, now)
|
||||
order, orderItems, buildErr := h.buildOrderAndItems(rechargeRecord, packages, totalAmount, seriesID, sellerCostPrice, now)
|
||||
if buildErr != nil {
|
||||
return buildErr
|
||||
}
|
||||
@@ -290,6 +333,8 @@ func (h *AutoPurchaseHandler) buildOrderAndItems(
|
||||
rechargeRecord *model.AssetRechargeRecord,
|
||||
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)
|
||||
@@ -302,6 +347,12 @@ func (h *AutoPurchaseHandler) buildOrderAndItems(
|
||||
generation = 1
|
||||
}
|
||||
|
||||
var sellerShopID *uint
|
||||
if rechargeRecord.ShopIDTag > 0 {
|
||||
shopID := rechargeRecord.ShopIDTag
|
||||
sellerShopID = &shopID
|
||||
}
|
||||
|
||||
paidAmount := totalAmount
|
||||
order := &model.Order{
|
||||
BaseModel: model.BaseModel{
|
||||
@@ -323,7 +374,9 @@ func (h *AutoPurchaseHandler) buildOrderAndItems(
|
||||
Source: constants.OrderSourceClient,
|
||||
Generation: generation,
|
||||
ActualPaidAmount: &paidAmount,
|
||||
SellerShopID: &rechargeRecord.ShopIDTag,
|
||||
SellerShopID: sellerShopID,
|
||||
SeriesID: seriesID,
|
||||
SellerCostPrice: sellerCostPrice,
|
||||
}
|
||||
|
||||
items := make([]*model.OrderItem, 0, len(packages))
|
||||
|
||||
Reference in New Issue
Block a user