From b9fc828567ef46d74eb84b16e22ae8dba02e9ceb Mon Sep 17 00:00:00 2001 From: huang Date: Thu, 23 Apr 2026 12:11:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=80=BB=E4=BB=B7=E5=92=8C?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=98=8E=E7=BB=86=E5=8D=95=E4=BB=B7=E5=9C=A8?= =?UTF-8?q?=E4=BB=A3=E8=B4=AD/=E6=88=90=E6=9C=AC=E4=BB=B7=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E4=BC=9A=E4=B8=8D=E4=B8=80=E8=87=B4=EF=BC=8C=E5=B7=B2?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=90=8C=E4=B8=80=E5=A5=97=E4=BB=B7=E6=A0=BC?= =?UTF-8?q?=E6=9D=A5=E6=BA=90=E5=B9=B6=E6=94=AF=E6=8C=81=E5=A4=9A=E5=A5=97?= =?UTF-8?q?=E9=A4=90=E6=B1=82=E5=92=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/order/service.go | 169 +++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 51 deletions(-) diff --git a/internal/service/order/service.go b/internal/service/order/service.go index 2b9281a..0983c63 100644 --- a/internal/service/order/service.go +++ b/internal/service/order/service.go @@ -207,10 +207,20 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde seriesID = validationResult.Device.SeriesID } + // 先按零售价构建明细价格快照,后续在代购/成本价场景覆盖 + sellerShopIDValue := uint(0) + if resourceShopID != nil { + sellerShopIDValue = *resourceShopID + } + itemUnitPriceMap, retailTotalAmount, err := s.buildRetailPriceMap(ctx, validationResult.Packages, sellerShopIDValue) + if err != nil { + return nil, err + } + // 初始化订单字段 orderBuyerType := buyerType orderBuyerID := buyerID - totalAmount := validationResult.TotalPrice + totalAmount := retailTotalAmount paymentMethod := req.PaymentMethod paymentStatus := model.PaymentStatusPaid var paidAt *time.Time @@ -244,18 +254,19 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde actualPaidAmount = nil } else { // ==== 子场景 1.2:平台代购(资源属于代理商,平台代为购买)==== - purchaseBuyerID, buyerCostPrice, purchasePaidAt, err := s.resolvePurchaseOnBehalfInfo(ctx, validationResult) + purchaseBuyerID, buyerCostPriceMap, buyerTotalCost, purchasePaidAt, err := s.resolvePurchaseOnBehalfInfo(ctx, validationResult) if err != nil { return nil, err } orderBuyerType = model.BuyerTypeAgent orderBuyerID = purchaseBuyerID - totalAmount = buyerCostPrice + itemUnitPriceMap = buyerCostPriceMap + totalAmount = buyerTotalCost paymentMethod = model.PaymentMethodOffline paymentStatus = model.PaymentStatusPaid paidAt = purchasePaidAt isPurchaseOnBehalf = true - sellerCostPrice = buyerCostPrice + sellerCostPrice = buyerTotalCost // 设置操作者信息(平台代购) operatorID = nil @@ -277,22 +288,17 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde return nil, errors.New(errors.CodeInternalError, "资源店铺ID为空") } - // 获取第一个套餐ID用于查询成本价 - if len(validationResult.Packages) == 0 { - return nil, errors.New(errors.CodeInternalError, "套餐列表为空") - } - firstPackageID := validationResult.Packages[0].ID - if *resourceShopID == operatorShopID { // ==== 子场景 2.1:代理自购 ==== - costPrice, err := s.getCostPrice(ctx, operatorShopID, firstPackageID) + operatorCostPriceMap, operatorTotalCost, err := s.buildCostPriceMap(ctx, validationResult.Packages, operatorShopID) if err != nil { return nil, err } orderBuyerType = model.BuyerTypeAgent orderBuyerID = operatorShopID - totalAmount = costPrice + itemUnitPriceMap = operatorCostPriceMap + totalAmount = operatorTotalCost paymentMethod = model.PaymentMethodWallet paymentStatus = model.PaymentStatusPaid paidAt = &now @@ -300,28 +306,29 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde operatorID = &operatorShopID operatorType = "agent" - actualPaidAmountVal := costPrice + actualPaidAmountVal := operatorTotalCost actualPaidAmount = &actualPaidAmountVal purchaseRole = model.PurchaseRoleSelfPurchase - sellerCostPrice = costPrice + sellerCostPrice = operatorTotalCost } else { // ==== 子场景 2.2:代理代购(给下级购买)==== // 获取买家成本价 - buyerCostPrice, err := s.getCostPrice(ctx, *resourceShopID, firstPackageID) + buyerCostPriceMap, buyerTotalCost, err := s.buildCostPriceMap(ctx, validationResult.Packages, *resourceShopID) if err != nil { return nil, err } // 获取操作者成本价 - operatorCostPrice, err := s.getCostPrice(ctx, operatorShopID, firstPackageID) + _, operatorTotalCost, err := s.buildCostPriceMap(ctx, validationResult.Packages, operatorShopID) if err != nil { return nil, err } orderBuyerType = model.BuyerTypeAgent orderBuyerID = *resourceShopID - totalAmount = buyerCostPrice + itemUnitPriceMap = buyerCostPriceMap + totalAmount = buyerTotalCost paymentMethod = model.PaymentMethodWallet paymentStatus = model.PaymentStatusPaid paidAt = &now @@ -329,10 +336,11 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde operatorID = &operatorShopID operatorType = "agent" - actualPaidAmount = &operatorCostPrice + actualPaidAmountVal := operatorTotalCost + actualPaidAmount = &actualPaidAmountVal purchaseRole = model.PurchaseRolePurchaseForSubordinate // 代理代购下级时,佣金差额基准应是操作方(代理自己)的成本价,而非下级的成本价 - sellerCostPrice = operatorCostPrice + sellerCostPrice = operatorTotalCost } } else { // 兜底检查:后台不支持其他支付方式(DTO 验证已拒绝,此为防御性编程) @@ -407,7 +415,7 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde order.BuyerPhone, order.BuyerNickname = s.fetchBuyerSnapshot(ctx, orderBuyerID) } - items := s.buildOrderItems(userID, validationResult.Packages) + items := s.buildOrderItems(userID, validationResult.Packages, itemUnitPriceMap) idempotencyKey := buildOrderIdempotencyKey(buyerType, buyerID, orderType, carrierType, carrierID, req.PackageIDs) @@ -502,10 +510,20 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest seriesID = validationResult.Device.SeriesID } + // 先按零售价构建明细价格快照,后续在代购/成本价场景覆盖 + sellerShopIDValue := uint(0) + if resourceShopID != nil { + sellerShopIDValue = *resourceShopID + } + itemUnitPriceMap, retailTotalAmount, err := s.buildRetailPriceMap(ctx, validationResult.Packages, sellerShopIDValue) + if err != nil { + return nil, err + } + // 初始化订单字段 orderBuyerType := buyerType orderBuyerID := buyerID - totalAmount := validationResult.TotalPrice + totalAmount := retailTotalAmount paymentMethod := req.PaymentMethod paymentStatus := model.PaymentStatusPending var paidAt *time.Time @@ -522,18 +540,19 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest // 场景判断:offline(平台代购)、wallet(代理钱包支付)、其他(待支付) if req.PaymentMethod == model.PaymentMethodOffline { // ==== 场景 1:平台代购(offline)==== - purchaseBuyerID, buyerCostPrice, purchasePaidAt, err := s.resolvePurchaseOnBehalfInfo(ctx, validationResult) + purchaseBuyerID, buyerCostPriceMap, buyerTotalCost, purchasePaidAt, err := s.resolvePurchaseOnBehalfInfo(ctx, validationResult) if err != nil { return nil, err } orderBuyerType = model.BuyerTypeAgent orderBuyerID = purchaseBuyerID - totalAmount = buyerCostPrice + itemUnitPriceMap = buyerCostPriceMap + totalAmount = buyerTotalCost paymentMethod = model.PaymentMethodOffline paymentStatus = model.PaymentStatusPaid paidAt = purchasePaidAt isPurchaseOnBehalf = true - sellerCostPrice = buyerCostPrice + sellerCostPrice = buyerTotalCost // 设置操作者信息(平台代购) operatorID = nil @@ -554,22 +573,17 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest return nil, errors.New(errors.CodeInternalError, "资源店铺ID为空") } - // 获取第一个套餐ID用于查询成本价 - if len(validationResult.Packages) == 0 { - return nil, errors.New(errors.CodeInternalError, "套餐列表为空") - } - firstPackageID := validationResult.Packages[0].ID - if *resourceShopID == operatorShopID { // ==== 子场景 2.1:代理自购 ==== - costPrice, err := s.getCostPrice(ctx, operatorShopID, firstPackageID) + operatorCostPriceMap, operatorTotalCost, err := s.buildCostPriceMap(ctx, validationResult.Packages, operatorShopID) if err != nil { return nil, err } orderBuyerType = model.BuyerTypeAgent orderBuyerID = operatorShopID - totalAmount = costPrice + itemUnitPriceMap = operatorCostPriceMap + totalAmount = operatorTotalCost paymentMethod = model.PaymentMethodWallet paymentStatus = model.PaymentStatusPaid paidAt = &now @@ -577,28 +591,29 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest operatorID = &operatorShopID operatorType = "agent" - actualPaidAmountVal := costPrice + actualPaidAmountVal := operatorTotalCost actualPaidAmount = &actualPaidAmountVal purchaseRole = model.PurchaseRoleSelfPurchase - sellerCostPrice = costPrice + sellerCostPrice = operatorTotalCost } else { // ==== 子场景 2.2:代理代购(给下级购买)==== // 获取买家成本价 - buyerCostPrice, err := s.getCostPrice(ctx, *resourceShopID, firstPackageID) + buyerCostPriceMap, buyerTotalCost, err := s.buildCostPriceMap(ctx, validationResult.Packages, *resourceShopID) if err != nil { return nil, err } // 获取操作者成本价 - operatorCostPrice, err := s.getCostPrice(ctx, operatorShopID, firstPackageID) + _, operatorTotalCost, err := s.buildCostPriceMap(ctx, validationResult.Packages, operatorShopID) if err != nil { return nil, err } orderBuyerType = model.BuyerTypeAgent orderBuyerID = *resourceShopID - totalAmount = buyerCostPrice + itemUnitPriceMap = buyerCostPriceMap + totalAmount = buyerTotalCost paymentMethod = model.PaymentMethodWallet paymentStatus = model.PaymentStatusPaid paidAt = &now @@ -606,10 +621,11 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest operatorID = &operatorShopID operatorType = "agent" - actualPaidAmount = &operatorCostPrice + actualPaidAmountVal := operatorTotalCost + actualPaidAmount = &actualPaidAmountVal purchaseRole = model.PurchaseRolePurchaseForSubordinate // 代理代购下级时,佣金差额基准应是操作方(代理自己)的成本价,而非下级的成本价 - sellerCostPrice = operatorCostPrice + sellerCostPrice = operatorTotalCost } } @@ -659,7 +675,7 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest PaymentConfigID: h5PaymentConfigID, } - items := s.buildOrderItems(userID, validationResult.Packages) + items := s.buildOrderItems(userID, validationResult.Packages, itemUnitPriceMap) idempotencyKey := buildOrderIdempotencyKey(buyerType, buyerID, req.OrderType, carrierType, carrierID, req.PackageIDs) @@ -700,7 +716,7 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest } } -func (s *Service) resolvePurchaseOnBehalfInfo(ctx context.Context, result *purchase_validation.PurchaseValidationResult) (uint, int64, *time.Time, error) { +func (s *Service) resolvePurchaseOnBehalfInfo(ctx context.Context, result *purchase_validation.PurchaseValidationResult) (uint, map[uint]int64, int64, *time.Time, error) { var resourceShopID *uint var seriesID *uint @@ -713,30 +729,38 @@ func (s *Service) resolvePurchaseOnBehalfInfo(ctx context.Context, result *purch } if resourceShopID == nil || *resourceShopID == 0 { - return 0, 0, nil, errors.New(errors.CodeInvalidParam, "资源未分配给代理商,无法代购") + return 0, nil, 0, nil, errors.New(errors.CodeInvalidParam, "资源未分配给代理商,无法代购") } if seriesID == nil || *seriesID == 0 { - return 0, 0, nil, errors.New(errors.CodeInvalidParam, "资源未关联套餐系列") + return 0, nil, 0, nil, errors.New(errors.CodeInvalidParam, "资源未关联套餐系列") } if len(result.Packages) == 0 { - return 0, 0, nil, errors.New(errors.CodeInvalidParam, "订单中没有套餐") + return 0, nil, 0, nil, errors.New(errors.CodeInvalidParam, "订单中没有套餐") } - firstPackageID := result.Packages[0].ID - buyerAllocation, err := s.shopPackageAllocationStore.GetByShopAndPackage(ctx, *resourceShopID, firstPackageID) + buyerCostPriceMap, buyerTotalCost, err := s.buildCostPriceMap(ctx, result.Packages, *resourceShopID) if err != nil { - return 0, 0, nil, errors.New(errors.CodeInvalidParam, "买家没有该套餐的分配配置") + return 0, nil, 0, nil, err } now := time.Now() - return *resourceShopID, buyerAllocation.CostPrice, &now, nil + return *resourceShopID, buyerCostPriceMap, buyerTotalCost, &now, nil } -func (s *Service) buildOrderItems(operatorID uint, packages []*model.Package) []*model.OrderItem { +func (s *Service) buildOrderItems(operatorID uint, packages []*model.Package, unitPriceMap map[uint]int64) []*model.OrderItem { items := make([]*model.OrderItem, 0, len(packages)) for _, pkg := range packages { + if pkg == nil { + continue + } + + unitPrice := pkg.SuggestedRetailPrice + if price, ok := unitPriceMap[pkg.ID]; ok { + unitPrice = price + } + item := &model.OrderItem{ BaseModel: model.BaseModel{ Creator: operatorID, @@ -746,8 +770,8 @@ func (s *Service) buildOrderItems(operatorID uint, packages []*model.Package) [] PackageName: pkg.PackageName, PackageType: &pkg.PackageType, Quantity: 1, - UnitPrice: pkg.SuggestedRetailPrice, - Amount: pkg.SuggestedRetailPrice, + UnitPrice: unitPrice, + Amount: unitPrice, } items = append(items, item) } @@ -755,6 +779,49 @@ func (s *Service) buildOrderItems(operatorID uint, packages []*model.Package) [] return items } +// buildRetailPriceMap 构建订单明细零售价映射和总价(分) +// sellerShopID > 0 时取代理分配零售价,=0 时取套餐建议零售价 +func (s *Service) buildRetailPriceMap(ctx context.Context, packages []*model.Package, sellerShopID uint) (map[uint]int64, int64, error) { + priceMap := make(map[uint]int64, len(packages)) + var total int64 + + for _, pkg := range packages { + if pkg == nil { + continue + } + + price, err := s.purchaseValidationService.GetPurchasePrice(ctx, pkg, sellerShopID) + if err != nil { + return nil, 0, err + } + priceMap[pkg.ID] = price + total += price + } + + return priceMap, total, nil +} + +// buildCostPriceMap 构建订单明细成本价映射和总价(分) +func (s *Service) buildCostPriceMap(ctx context.Context, packages []*model.Package, shopID uint) (map[uint]int64, int64, error) { + priceMap := make(map[uint]int64, len(packages)) + var total int64 + + for _, pkg := range packages { + if pkg == nil { + continue + } + + costPrice, err := s.getCostPrice(ctx, shopID, pkg.ID) + if err != nil { + return nil, 0, err + } + priceMap[pkg.ID] = costPrice + total += costPrice + } + + return priceMap, total, nil +} + func (s *Service) fetchBuyerSnapshot(ctx context.Context, customerID uint) (phone, nickname *string) { if customerID == 0 { return nil, nil