订单总价和订单明细单价在代购/成本价场景会不一致,已改为同一套价格来源并支持多套餐求和
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m24s

This commit is contained in:
2026-04-23 12:11:48 +08:00
parent 327c72ca74
commit b9fc828567

View File

@@ -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