订单相关以及佣金相关修复
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m57s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m57s
This commit is contained in:
@@ -477,6 +477,28 @@ func (s *Service) fetchBuyerSnapshot(ctx context.Context, customerID uint) (phon
|
||||
return phone, nickname
|
||||
}
|
||||
|
||||
func (s *Service) buildPersonalCustomerOperatorSnapshot(ctx context.Context, customerID uint, nickname *string) (*uint, string, string) {
|
||||
if customerID == 0 {
|
||||
return nil, "", ""
|
||||
}
|
||||
|
||||
name := ""
|
||||
if nickname != nil {
|
||||
name = strings.TrimSpace(*nickname)
|
||||
}
|
||||
if name == "" {
|
||||
customer, err := s.personalCustomerStore.GetByID(ctx, customerID)
|
||||
if err != nil {
|
||||
s.logger.Warn("buildPersonalCustomerOperatorSnapshot: 查询个人客户失败", zap.Uint("customer_id", customerID), zap.Error(err))
|
||||
} else if customer != nil {
|
||||
name = strings.TrimSpace(customer.Nickname)
|
||||
}
|
||||
}
|
||||
|
||||
id := customerID
|
||||
return &id, model.OperatorAccountTypePersonalCustomer, name
|
||||
}
|
||||
|
||||
func (s *Service) buildPendingOrder(ctx context.Context, customerID uint, result *purchase_validation.PurchaseValidationResult, sellerCostPrice int64) (*model.Order, error) {
|
||||
orderType := resolveOrderType(result)
|
||||
if orderType == "" {
|
||||
@@ -515,6 +537,7 @@ func (s *Service) buildPendingOrder(ctx context.Context, customerID uint, result
|
||||
}
|
||||
|
||||
order.BuyerPhone, order.BuyerNickname = s.fetchBuyerSnapshot(ctx, customerID)
|
||||
order.OperatorAccountID, order.OperatorAccountType, order.OperatorAccountName = s.buildPersonalCustomerOperatorSnapshot(ctx, customerID, order.BuyerNickname)
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
@@ -78,8 +78,11 @@ func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取订单失败")
|
||||
}
|
||||
|
||||
if order.CommissionStatus == model.CommissionStatusCalculated {
|
||||
s.logger.Warn("订单佣金已计算,跳过", zap.String("order_id", fmt.Sprint(orderID)))
|
||||
if order.CommissionStatus == model.CommissionStatusCompleted || order.CommissionStatus == model.CommissionStatusPendingReview {
|
||||
s.logger.Warn("订单佣金流程已结束,跳过",
|
||||
zap.String("order_id", fmt.Sprint(orderID)),
|
||||
zap.Int("commission_status", order.CommissionStatus),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -88,13 +91,8 @@ func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "计算成本价差佣金失败")
|
||||
}
|
||||
|
||||
for _, record := range costDiffRecords {
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建成本价差佣金记录失败")
|
||||
}
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "佣金入账失败")
|
||||
}
|
||||
if err := s.persistCommissionRecordsInTx(ctx, tx, costDiffRecords); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 代购订单不触发一次性佣金和累计充值更新
|
||||
@@ -110,8 +108,16 @@ func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Model(&model.Order{}).Where("id = ?", orderID).Update("commission_status", model.CommissionStatusCalculated).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新订单佣金状态失败")
|
||||
commissionStatus, commissionResult, err := s.resolveOrderCommissionOutcomeInTx(tx, order.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Model(&model.Order{}).Where("id = ?", orderID).Updates(map[string]any{
|
||||
"commission_status": commissionStatus,
|
||||
"commission_result": commissionResult,
|
||||
}).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新订单佣金结果失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -190,12 +196,7 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.
|
||||
Status: constants.CommissionStatusPendingReview,
|
||||
Remark: fmt.Sprintf("套餐系列[%d]未分配给该代理(shop_id=%d),成本价差链路断裂,请人工核查", *order.SeriesID, currentShop.ID),
|
||||
}
|
||||
if createErr := s.commissionRecordStore.Create(ctx, pendingRecord); createErr != nil {
|
||||
s.logger.Warn("创建待审佣金记录失败",
|
||||
zap.Uint("shop_id", currentShop.ID),
|
||||
zap.Uint("order_id", order.ID),
|
||||
zap.Error(createErr))
|
||||
}
|
||||
records = append(records, pendingRecord)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -284,13 +285,8 @@ func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *g
|
||||
return err
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
|
||||
}
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
|
||||
}
|
||||
if err := s.persistCommissionRecordsInTx(ctx, tx, records); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
card.SetFirstRechargeTriggeredBySeries(seriesID, true)
|
||||
@@ -368,13 +364,8 @@ func (s *Service) triggerOneTimeCommissionForDeviceInTx(ctx context.Context, tx
|
||||
return err
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
|
||||
}
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
|
||||
}
|
||||
if err := s.persistCommissionRecordsInTx(ctx, tx, records); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
device.SetFirstRechargeTriggeredBySeries(seriesID, true)
|
||||
@@ -544,12 +535,7 @@ func (s *Service) calculateChainOneTimeCommission(ctx context.Context, bottomSho
|
||||
Status: constants.CommissionStatusPendingReview,
|
||||
Remark: fmt.Sprintf("套餐系列[%d]未分配给该代理(shop_id=%d),一次性佣金链路断裂,请人工核查", seriesID, parentShopID),
|
||||
}
|
||||
if createErr := s.commissionRecordStore.Create(ctx, pendingRecord); createErr != nil {
|
||||
s.logger.Warn("创建待审佣金记录失败",
|
||||
zap.Uint("shop_id", parentShopID),
|
||||
zap.Uint("order_id", order.ID),
|
||||
zap.Error(createErr))
|
||||
}
|
||||
records = append(records, pendingRecord)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -686,6 +672,48 @@ func (s *Service) creditCommissionInTx(ctx context.Context, tx *gorm.DB, record
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) persistCommissionRecordsInTx(ctx context.Context, tx *gorm.DB, records []*model.CommissionRecord) error {
|
||||
for _, record := range records {
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建佣金记录失败")
|
||||
}
|
||||
if record.Status == constants.CommissionStatusPendingReview {
|
||||
continue
|
||||
}
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "佣金入账失败")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) resolveOrderCommissionOutcomeInTx(tx *gorm.DB, orderID uint) (int, int, error) {
|
||||
var pendingReviewCount int64
|
||||
if err := tx.Model(&model.CommissionRecord{}).
|
||||
Where("order_id = ? AND status = ?", orderID, constants.CommissionStatusPendingReview).
|
||||
Count(&pendingReviewCount).Error; err != nil {
|
||||
return 0, 0, errors.Wrap(errors.CodeDatabaseError, err, "统计待人工处理佣金记录失败")
|
||||
}
|
||||
|
||||
if pendingReviewCount > 0 {
|
||||
return model.CommissionStatusPendingReview, model.CommissionResultChainBroken, nil
|
||||
}
|
||||
|
||||
var releasedCount int64
|
||||
if err := tx.Model(&model.CommissionRecord{}).
|
||||
Where("order_id = ? AND status = ? AND amount > 0", orderID, constants.CommissionStatusReleased).
|
||||
Count(&releasedCount).Error; err != nil {
|
||||
return 0, 0, errors.Wrap(errors.CodeDatabaseError, err, "统计已发放佣金记录失败")
|
||||
}
|
||||
|
||||
if releasedCount > 0 {
|
||||
return model.CommissionStatusCompleted, model.CommissionResultHasAmount, nil
|
||||
}
|
||||
|
||||
return model.CommissionStatusCompleted, model.CommissionResultNoAmount, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreditCommission(ctx context.Context, record *model.CommissionRecord) error {
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
return s.creditCommissionInTx(ctx, tx, record)
|
||||
|
||||
@@ -206,6 +206,7 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde
|
||||
}
|
||||
|
||||
userID := middleware.GetUserIDFromContext(ctx)
|
||||
operatorAccountID, operatorAccountType, operatorAccountName := s.buildAccountOperatorSnapshot(ctx, userID, operatorUserType)
|
||||
|
||||
// 提取资源所属店铺ID
|
||||
var seriesID *uint
|
||||
@@ -261,44 +262,44 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde
|
||||
actualPaidAmount = &actualPaidAmountVal
|
||||
purchaseRole = model.PurchaseRoleSelfPurchase
|
||||
} else {
|
||||
isPlatformOwned := resourceShopID == nil || *resourceShopID == 0
|
||||
isPlatformOwned := resourceShopID == nil || *resourceShopID == 0
|
||||
|
||||
if isPlatformOwned {
|
||||
// ==== 子场景 1.1:平台自营(资源未分配给代理商)====
|
||||
// 平台直接销售给终端用户,无代理商参与,使用零售价,无需查询分配配置
|
||||
orderBuyerType = model.BuyerTypePersonal
|
||||
orderBuyerID = 0
|
||||
paymentMethod = model.PaymentMethodOffline
|
||||
paymentStatus = model.PaymentStatusPaid
|
||||
paidAt = &now
|
||||
isPurchaseOnBehalf = false
|
||||
sellerCostPrice = 0
|
||||
operatorID = nil
|
||||
operatorType = constants.OwnerTypePlatform
|
||||
purchaseRole = model.PurchaseRoleSelfPurchase
|
||||
actualPaidAmount = nil
|
||||
} else {
|
||||
// ==== 子场景 1.2:平台代购(资源属于代理商,平台代为购买)====
|
||||
purchaseBuyerID, buyerCostPriceMap, buyerTotalCost, purchasePaidAt, err := s.resolvePurchaseOnBehalfInfo(ctx, validationResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if isPlatformOwned {
|
||||
// ==== 子场景 1.1:平台自营(资源未分配给代理商)====
|
||||
// 平台直接销售给终端用户,无代理商参与,使用零售价,无需查询分配配置
|
||||
orderBuyerType = model.BuyerTypePersonal
|
||||
orderBuyerID = 0
|
||||
paymentMethod = model.PaymentMethodOffline
|
||||
paymentStatus = model.PaymentStatusPaid
|
||||
paidAt = &now
|
||||
isPurchaseOnBehalf = false
|
||||
sellerCostPrice = 0
|
||||
operatorID = nil
|
||||
operatorType = constants.OwnerTypePlatform
|
||||
purchaseRole = model.PurchaseRoleSelfPurchase
|
||||
actualPaidAmount = nil
|
||||
} else {
|
||||
// ==== 子场景 1.2:平台代购(资源属于代理商,平台代为购买)====
|
||||
purchaseBuyerID, buyerCostPriceMap, buyerTotalCost, purchasePaidAt, err := s.resolvePurchaseOnBehalfInfo(ctx, validationResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderBuyerType = model.BuyerTypeAgent
|
||||
orderBuyerID = purchaseBuyerID
|
||||
itemUnitPriceMap = buyerCostPriceMap
|
||||
totalAmount = buyerTotalCost
|
||||
paymentMethod = model.PaymentMethodOffline
|
||||
paymentStatus = model.PaymentStatusPaid
|
||||
paidAt = purchasePaidAt
|
||||
isPurchaseOnBehalf = true
|
||||
sellerCostPrice = buyerTotalCost
|
||||
|
||||
// 设置操作者信息(平台代购)
|
||||
operatorID = nil
|
||||
operatorType = constants.OwnerTypePlatform
|
||||
purchaseRole = model.PurchaseRolePurchasedByPlatform
|
||||
actualPaidAmount = nil
|
||||
}
|
||||
orderBuyerType = model.BuyerTypeAgent
|
||||
orderBuyerID = purchaseBuyerID
|
||||
itemUnitPriceMap = buyerCostPriceMap
|
||||
totalAmount = buyerTotalCost
|
||||
paymentMethod = model.PaymentMethodOffline
|
||||
paymentStatus = model.PaymentStatusPaid
|
||||
paidAt = purchasePaidAt
|
||||
isPurchaseOnBehalf = true
|
||||
sellerCostPrice = buyerTotalCost
|
||||
|
||||
// 设置操作者信息(平台代购)
|
||||
operatorID = nil
|
||||
operatorType = constants.OwnerTypePlatform
|
||||
purchaseRole = model.PurchaseRolePurchasedByPlatform
|
||||
actualPaidAmount = nil
|
||||
}
|
||||
}
|
||||
|
||||
} else if req.PaymentMethod == model.PaymentMethodWallet {
|
||||
@@ -445,6 +446,9 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde
|
||||
SellerShopID: sellerShopID,
|
||||
SellerCostPrice: sellerCostPrice,
|
||||
IsPurchaseOnBehalf: isPurchaseOnBehalf,
|
||||
OperatorAccountID: operatorAccountID,
|
||||
OperatorAccountType: operatorAccountType,
|
||||
OperatorAccountName: operatorAccountName,
|
||||
OperatorID: operatorID,
|
||||
OperatorType: operatorType,
|
||||
ActualPaidAmount: actualPaidAmount,
|
||||
@@ -475,7 +479,7 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde
|
||||
s.enqueueCommissionCalculation(ctx, order.ID)
|
||||
}
|
||||
s.markOrderCreated(ctx, idempotencyKey, order.ID)
|
||||
return s.buildOrderResponse(order, items), nil
|
||||
return s.buildOrderResponse(ctx, order, items), nil
|
||||
|
||||
} else if req.PaymentMethod == model.PaymentMethodWallet {
|
||||
// 钱包支付:创建订单、扣款、激活套餐(在事务中完成)
|
||||
@@ -489,7 +493,7 @@ func (s *Service) CreateAdminOrder(ctx context.Context, req *dto.CreateAdminOrde
|
||||
return nil, err
|
||||
}
|
||||
s.markOrderCreated(ctx, idempotencyKey, order.ID)
|
||||
return s.buildOrderResponse(order, items), nil
|
||||
return s.buildOrderResponse(ctx, order, items), nil
|
||||
|
||||
} else {
|
||||
// 不应该到这里(DTO 验证已拒绝其他支付方式)
|
||||
@@ -563,6 +567,8 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest
|
||||
}
|
||||
|
||||
userID := middleware.GetUserIDFromContext(ctx)
|
||||
operatorUserType := middleware.GetUserTypeFromContext(ctx)
|
||||
operatorAccountID, operatorAccountType, operatorAccountName := s.buildAccountOperatorSnapshot(ctx, userID, operatorUserType)
|
||||
|
||||
// 提取资源所属店铺ID
|
||||
var resourceShopID *uint
|
||||
@@ -732,6 +738,9 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest
|
||||
SellerShopID: sellerShopID,
|
||||
SellerCostPrice: sellerCostPrice,
|
||||
IsPurchaseOnBehalf: isPurchaseOnBehalf,
|
||||
OperatorAccountID: operatorAccountID,
|
||||
OperatorAccountType: operatorAccountType,
|
||||
OperatorAccountName: operatorAccountName,
|
||||
OperatorID: operatorID,
|
||||
OperatorType: operatorType,
|
||||
ActualPaidAmount: actualPaidAmount,
|
||||
@@ -752,7 +761,7 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest
|
||||
}
|
||||
s.enqueueCommissionCalculation(ctx, order.ID)
|
||||
s.markOrderCreated(ctx, idempotencyKey, order.ID)
|
||||
return s.buildOrderResponse(order, items), nil
|
||||
return s.buildOrderResponse(ctx, order, items), nil
|
||||
|
||||
} else if req.PaymentMethod == model.PaymentMethodWallet {
|
||||
// 钱包支付:创建订单、扣款、激活套餐(在事务中完成)
|
||||
@@ -766,7 +775,7 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest
|
||||
return nil, err
|
||||
}
|
||||
s.markOrderCreated(ctx, idempotencyKey, order.ID)
|
||||
return s.buildOrderResponse(order, items), nil
|
||||
return s.buildOrderResponse(ctx, order, items), nil
|
||||
|
||||
} else {
|
||||
// 其他支付方式:创建待支付订单(H5 端支持 wechat/alipay)
|
||||
@@ -777,7 +786,7 @@ func (s *Service) CreateH5Order(ctx context.Context, req *dto.CreateOrderRequest
|
||||
return nil, err
|
||||
}
|
||||
s.markOrderCreated(ctx, idempotencyKey, order.ID)
|
||||
return s.buildOrderResponse(order, items), nil
|
||||
return s.buildOrderResponse(ctx, order, items), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,6 +918,57 @@ func (s *Service) fetchBuyerSnapshot(ctx context.Context, customerID uint) (phon
|
||||
return phone, nickname
|
||||
}
|
||||
|
||||
func (s *Service) buildAccountOperatorSnapshot(ctx context.Context, userID uint, userType int) (*uint, string, string) {
|
||||
if userID == 0 {
|
||||
return nil, "", ""
|
||||
}
|
||||
|
||||
var account model.Account
|
||||
if err := s.db.WithContext(ctx).Where("id = ?", userID).First(&account).Error; err != nil {
|
||||
s.logger.Warn("buildAccountOperatorSnapshot: 查询账号失败", zap.Uint("user_id", userID), zap.Error(err))
|
||||
id := userID
|
||||
return &id, orderOperatorAccountTypeFromUserType(userType), ""
|
||||
}
|
||||
|
||||
id := account.ID
|
||||
return &id, orderOperatorAccountTypeFromUserType(account.UserType), account.Username
|
||||
}
|
||||
|
||||
func (s *Service) buildPersonalCustomerOperatorSnapshot(ctx context.Context, customerID uint, nickname *string) (*uint, string, string) {
|
||||
if customerID == 0 {
|
||||
return nil, "", ""
|
||||
}
|
||||
|
||||
name := ""
|
||||
if nickname != nil {
|
||||
name = strings.TrimSpace(*nickname)
|
||||
}
|
||||
if name == "" && s.personalCustomerStore != nil {
|
||||
customer, err := s.personalCustomerStore.GetByID(ctx, customerID)
|
||||
if err != nil {
|
||||
s.logger.Warn("buildPersonalCustomerOperatorSnapshot: 查询个人客户失败", zap.Uint("customer_id", customerID), zap.Error(err))
|
||||
} else if customer != nil {
|
||||
name = strings.TrimSpace(customer.Nickname)
|
||||
}
|
||||
}
|
||||
|
||||
id := customerID
|
||||
return &id, model.OperatorAccountTypePersonalCustomer, name
|
||||
}
|
||||
|
||||
func orderOperatorAccountTypeFromUserType(userType int) string {
|
||||
switch userType {
|
||||
case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
|
||||
return model.OperatorAccountTypePlatform
|
||||
case constants.UserTypeAgent:
|
||||
return model.OperatorAccountTypeAgent
|
||||
case constants.UserTypeEnterprise:
|
||||
return model.OperatorAccountTypeEnterprise
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// getCostPrice 查询店铺对套餐的成本价
|
||||
// shopID: 店铺ID
|
||||
// packageID: 套餐ID
|
||||
@@ -1078,9 +1138,8 @@ func (s *Service) createOrderWithWalletPayment(ctx context.Context, order *model
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 事务外:佣金计算(异步)
|
||||
// 仅兼容极少数“平台代购但走钱包事务”的特殊场景,普通钱包订单不在此处入队佣金
|
||||
if order.PurchaseRole == model.PurchaseRolePurchasedByPlatform {
|
||||
// 3. 事务外:所有已支付且适用差价佣金的订单都进入佣金计算
|
||||
if order.CommissionStatus == model.CommissionStatusPending {
|
||||
s.enqueueCommissionCalculation(ctx, order.ID)
|
||||
}
|
||||
|
||||
@@ -1113,7 +1172,7 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.OrderResponse, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.buildOrderResponse(order, items), nil
|
||||
return s.buildOrderResponse(ctx, order, items), nil
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, req *dto.OrderListRequest, buyerType string, buyerID uint) (*dto.OrderListResponse, error) {
|
||||
@@ -1204,7 +1263,7 @@ func (s *Service) List(ctx context.Context, req *dto.OrderListRequest, buyerType
|
||||
|
||||
var list []*dto.OrderResponse
|
||||
for _, o := range orders {
|
||||
list = append(list, s.buildOrderResponse(o, itemsMap[o.ID]))
|
||||
list = append(list, s.buildOrderResponse(ctx, o, itemsMap[o.ID]))
|
||||
}
|
||||
|
||||
return &dto.OrderListResponse{
|
||||
@@ -1478,6 +1537,7 @@ func (s *Service) WalletPay(ctx context.Context, orderID uint, buyerType string,
|
||||
// 根据资源类型选择对应的钱包系统
|
||||
now := time.Now()
|
||||
actualPaidAmount := order.TotalAmount
|
||||
shouldEnqueueCommission := false
|
||||
|
||||
if resourceType == "shop" {
|
||||
// 代理钱包系统(店铺)
|
||||
@@ -1527,6 +1587,7 @@ func (s *Service) WalletPay(ctx context.Context, orderID uint, buyerType string,
|
||||
|
||||
actualPaidAmountSnapshot := actualPaidAmount
|
||||
order.ActualPaidAmount = &actualPaidAmountSnapshot
|
||||
shouldEnqueueCommission = true
|
||||
|
||||
walletResult := tx.Model(&model.AgentWallet{}).
|
||||
Where("id = ? AND balance >= ? AND version = ?", wallet.ID, order.TotalAmount, wallet.Version).
|
||||
@@ -1645,7 +1706,9 @@ func (s *Service) WalletPay(ctx context.Context, orderID uint, buyerType string,
|
||||
return err
|
||||
}
|
||||
|
||||
s.enqueueCommissionCalculation(ctx, orderID)
|
||||
if shouldEnqueueCommission && order.CommissionStatus == model.CommissionStatusPending {
|
||||
s.enqueueCommissionCalculation(ctx, orderID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1659,6 +1722,8 @@ func (s *Service) HandlePaymentCallback(ctx context.Context, orderNo string, pay
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
shouldResumeAfterPayment := false
|
||||
shouldEnqueueCommission := false
|
||||
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Model(&model.Order{}).
|
||||
Where("id = ? AND payment_status = ?", order.ID, model.PaymentStatusPending).
|
||||
@@ -1694,6 +1759,8 @@ func (s *Service) HandlePaymentCallback(ctx context.Context, orderNo string, pay
|
||||
// 回调更新成功后,同步内存对象,确保激活套餐时写入 paid_amount 快照
|
||||
actualPaidAmountSnapshot := actualPaidAmount
|
||||
order.ActualPaidAmount = &actualPaidAmountSnapshot
|
||||
shouldResumeAfterPayment = true
|
||||
shouldEnqueueCommission = true
|
||||
|
||||
return s.activatePackage(ctx, tx, order)
|
||||
})
|
||||
@@ -1702,8 +1769,12 @@ func (s *Service) HandlePaymentCallback(ctx context.Context, orderNo string, pay
|
||||
return err
|
||||
}
|
||||
|
||||
s.tryResumeAfterPayment(ctx, order)
|
||||
s.enqueueCommissionCalculation(ctx, order.ID)
|
||||
if shouldResumeAfterPayment {
|
||||
s.tryResumeAfterPayment(ctx, order)
|
||||
}
|
||||
if shouldEnqueueCommission && order.CommissionStatus == model.CommissionStatusPending {
|
||||
s.enqueueCommissionCalculation(ctx, order.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2169,24 +2240,24 @@ func (s *Service) activateAddonPackage(ctx context.Context, tx *gorm.DB, order *
|
||||
Creator: order.Creator,
|
||||
Updater: order.Creator,
|
||||
},
|
||||
OrderID: order.ID,
|
||||
OrderNo: order.OrderNo,
|
||||
PackageID: pkg.ID,
|
||||
PackageName: pkg.PackageName,
|
||||
UsageType: order.OrderType,
|
||||
DataLimitMB: pkg.RealDataMB,
|
||||
OrderID: order.ID,
|
||||
OrderNo: order.OrderNo,
|
||||
PackageID: pkg.ID,
|
||||
PackageName: pkg.PackageName,
|
||||
UsageType: order.OrderType,
|
||||
DataLimitMB: pkg.RealDataMB,
|
||||
VirtualTotalMBSnapshot: virtualTotalMBSnapshot,
|
||||
DisplayGainRatioSnapshot: displayGainRatioSnapshot,
|
||||
EnableVirtualDataSnapshot: enableVirtualDataSnapshot,
|
||||
Status: status,
|
||||
Priority: priority,
|
||||
MasterUsageID: &mainPackage.ID,
|
||||
ActivatedAt: &activatedAt,
|
||||
ExpiresAt: &expiresAt,
|
||||
DataResetCycle: pkg.DataResetCycle,
|
||||
PaidAmount: order.ActualPaidAmount,
|
||||
PackagePriceConfigStatus: pkg.PriceConfigStatus,
|
||||
PackageIsGift: pkg.IsGift,
|
||||
Status: status,
|
||||
Priority: priority,
|
||||
MasterUsageID: &mainPackage.ID,
|
||||
ActivatedAt: &activatedAt,
|
||||
ExpiresAt: &expiresAt,
|
||||
DataResetCycle: pkg.DataResetCycle,
|
||||
PaidAmount: order.ActualPaidAmount,
|
||||
PackagePriceConfigStatus: pkg.PriceConfigStatus,
|
||||
PackageIsGift: pkg.IsGift,
|
||||
}
|
||||
|
||||
if carrierType == "iot_card" {
|
||||
@@ -2223,7 +2294,7 @@ func (s *Service) enqueueCommissionCalculation(ctx context.Context, orderID uint
|
||||
zap.String("task_type", constants.TaskTypeCommission))
|
||||
}
|
||||
|
||||
func (s *Service) buildOrderResponse(order *model.Order, items []*model.OrderItem) *dto.OrderResponse {
|
||||
func (s *Service) buildOrderResponse(ctx context.Context, order *model.Order, items []*model.OrderItem) *dto.OrderResponse {
|
||||
var itemResponses []*dto.OrderItemResponse
|
||||
for _, item := range items {
|
||||
itemResponses = append(itemResponses, &dto.OrderItemResponse{
|
||||
@@ -2249,14 +2320,8 @@ func (s *Service) buildOrderResponse(order *model.Order, items []*model.OrderIte
|
||||
statusText = "已退款"
|
||||
}
|
||||
|
||||
// 查询操作者名称
|
||||
operatorName := ""
|
||||
if order.OperatorType == "agent" && order.OperatorID != nil {
|
||||
var shop model.Shop
|
||||
if err := s.db.Where("id = ?", *order.OperatorID).First(&shop).Error; err == nil {
|
||||
operatorName = shop.ShopName
|
||||
}
|
||||
}
|
||||
operatorID, operatorType, operatorName := s.resolveOrderOperatorView(ctx, order)
|
||||
sellerShopName := s.resolveSellerShopName(ctx, order.SellerShopID)
|
||||
|
||||
// 生成派生字段
|
||||
isPurchasedByParent := order.PurchaseRole == model.PurchaseRolePurchasedByParent
|
||||
@@ -2288,6 +2353,7 @@ func (s *Service) buildOrderResponse(order *model.Order, items []*model.OrderIte
|
||||
actualPaidAmountSnapshot := order.TotalAmount
|
||||
actualPaidAmount = &actualPaidAmountSnapshot
|
||||
}
|
||||
commissionResult := s.resolveOrderCommissionResult(ctx, order)
|
||||
|
||||
return &dto.OrderResponse{
|
||||
ID: order.ID,
|
||||
@@ -2308,10 +2374,14 @@ func (s *Service) buildOrderResponse(order *model.Order, items []*model.OrderIte
|
||||
IsPurchaseOnBehalf: order.IsPurchaseOnBehalf,
|
||||
CommissionStatus: order.CommissionStatus,
|
||||
CommissionStatusName: constants.GetOrderCommissionStatusName(order.CommissionStatus),
|
||||
CommissionResult: commissionResult,
|
||||
CommissionResultName: constants.GetOrderCommissionResultName(commissionResult),
|
||||
CommissionConfigVersion: order.CommissionConfigVersion,
|
||||
SellerShopID: order.SellerShopID,
|
||||
SellerShopName: sellerShopName,
|
||||
|
||||
OperatorID: order.OperatorID,
|
||||
OperatorType: order.OperatorType,
|
||||
OperatorID: operatorID,
|
||||
OperatorType: operatorType,
|
||||
OperatorName: operatorName,
|
||||
ActualPaidAmount: actualPaidAmount,
|
||||
|
||||
@@ -2331,6 +2401,90 @@ func (s *Service) buildOrderResponse(order *model.Order, items []*model.OrderIte
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) resolveOrderOperatorView(ctx context.Context, order *model.Order) (*uint, string, string) {
|
||||
if order.OperatorAccountID != nil || order.OperatorAccountType != "" || order.OperatorAccountName != "" {
|
||||
return order.OperatorAccountID, order.OperatorAccountType, order.OperatorAccountName
|
||||
}
|
||||
|
||||
if order.Source == constants.OrderSourceClient && order.BuyerType == model.BuyerTypePersonal {
|
||||
operatorID, operatorType, operatorName := s.buildPersonalCustomerOperatorSnapshot(ctx, order.BuyerID, order.BuyerNickname)
|
||||
if operatorID != nil || operatorType != "" || operatorName != "" {
|
||||
return operatorID, operatorType, operatorName
|
||||
}
|
||||
}
|
||||
|
||||
if order.Creator > 0 {
|
||||
operatorID, operatorType, operatorName := s.buildAccountOperatorSnapshot(ctx, order.Creator, 0)
|
||||
if operatorID != nil || operatorType != "" || operatorName != "" {
|
||||
return operatorID, operatorType, operatorName
|
||||
}
|
||||
}
|
||||
|
||||
if order.OperatorType == model.OperatorAccountTypeAgent && order.OperatorID != nil {
|
||||
return nil, model.OperatorAccountTypeAgent, s.resolveLegacyOperatorShopName(ctx, *order.OperatorID)
|
||||
}
|
||||
|
||||
if order.OperatorType == model.OperatorAccountTypePlatform {
|
||||
return nil, model.OperatorAccountTypePlatform, "平台"
|
||||
}
|
||||
|
||||
return nil, "", ""
|
||||
}
|
||||
|
||||
func (s *Service) resolveLegacyOperatorShopName(ctx context.Context, shopID uint) string {
|
||||
if shopID == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var shop model.Shop
|
||||
if err := s.db.WithContext(ctx).Where("id = ?", shopID).First(&shop).Error; err != nil {
|
||||
return ""
|
||||
}
|
||||
return shop.ShopName
|
||||
}
|
||||
|
||||
func (s *Service) resolveSellerShopName(ctx context.Context, sellerShopID *uint) string {
|
||||
if sellerShopID == nil || *sellerShopID == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var shop model.Shop
|
||||
if err := s.db.WithContext(ctx).Where("id = ?", *sellerShopID).First(&shop).Error; err != nil {
|
||||
return ""
|
||||
}
|
||||
return shop.ShopName
|
||||
}
|
||||
|
||||
func (s *Service) resolveOrderCommissionResult(ctx context.Context, order *model.Order) int {
|
||||
if order.CommissionResult != model.CommissionResultUnknown {
|
||||
return order.CommissionResult
|
||||
}
|
||||
|
||||
if order.CommissionStatus == model.CommissionStatusPending {
|
||||
return model.CommissionResultUnknown
|
||||
}
|
||||
|
||||
var pendingReviewCount int64
|
||||
if err := s.db.WithContext(ctx).Model(&model.CommissionRecord{}).
|
||||
Where("order_id = ? AND status = ?", order.ID, constants.CommissionStatusPendingReview).
|
||||
Count(&pendingReviewCount).Error; err == nil && pendingReviewCount > 0 {
|
||||
return model.CommissionResultChainBroken
|
||||
}
|
||||
|
||||
var releasedCount int64
|
||||
if err := s.db.WithContext(ctx).Model(&model.CommissionRecord{}).
|
||||
Where("order_id = ? AND status = ? AND amount > 0", order.ID, constants.CommissionStatusReleased).
|
||||
Count(&releasedCount).Error; err == nil && releasedCount > 0 {
|
||||
return model.CommissionResultHasAmount
|
||||
}
|
||||
|
||||
if order.CommissionStatus == model.CommissionStatusCompleted {
|
||||
return model.CommissionResultNoAmount
|
||||
}
|
||||
|
||||
return model.CommissionResultUnknown
|
||||
}
|
||||
|
||||
// WechatPayJSAPI 发起微信 JSAPI 支付
|
||||
// 通过 order.PaymentConfigID 动态加载支付配置,支持多商户场景
|
||||
func (s *Service) WechatPayJSAPI(ctx context.Context, orderID uint, openID string, buyerType string, buyerID uint) (*dto.WechatPayJSAPIResponse, error) {
|
||||
|
||||
@@ -627,7 +627,7 @@ func (s *Service) CreateWithdrawalRequest(ctx context.Context, shopID uint, req
|
||||
ActualAmount: actualAmount,
|
||||
WithdrawalMethod: req.WithdrawalMethod,
|
||||
AccountInfo: accountInfoJSON,
|
||||
Status: 1, // 待审核
|
||||
Status: constants.WithdrawalStatusPending, // 待审核
|
||||
}
|
||||
withdrawalRequest.Creator = currentUserID
|
||||
withdrawalRequest.Updater = currentUserID
|
||||
|
||||
Reference in New Issue
Block a user