diff --git a/internal/service/client_order/service.go b/internal/service/client_order/service.go index f64a44b..cc1974d 100644 --- a/internal/service/client_order/service.go +++ b/internal/service/client_order/service.go @@ -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 { diff --git a/internal/service/purchase_validation/service.go b/internal/service/purchase_validation/service.go index 989629e..e89fe3a 100644 --- a/internal/service/purchase_validation/service.go +++ b/internal/service/purchase_validation/service.go @@ -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) { diff --git a/internal/task/auto_purchase.go b/internal/task/auto_purchase.go index 1012abc..c6370be 100644 --- a/internal/task/auto_purchase.go +++ b/internal/task/auto_purchase.go @@ -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))