package order import ( "context" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/model/dto" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // orderHasOnlinePaymentInfo 判断订单是否为 C 端在线购买且已支付或已退款,只有这类订单返回原始支付信息。 func orderHasOnlinePaymentInfo(order *model.Order) bool { if order == nil || order.BuyerType != model.BuyerTypePersonal { return false } if order.PaymentMethod != model.PaymentMethodWechat && order.PaymentMethod != model.PaymentMethodAlipay { return false } return order.PaymentStatus == model.PaymentStatusPaid || order.PaymentStatus == model.PaymentStatusRefunded } // orderIsOnlinePurchase 判断订单是否为个人客户微信或支付宝订单,详情支付单列表按此范围返回。 func orderIsOnlinePurchase(order *model.Order) bool { if order == nil || order.BuyerType != model.BuyerTypePersonal { return false } return order.PaymentMethod == model.PaymentMethodWechat || order.PaymentMethod == model.PaymentMethodAlipay } // fillOrderPaymentInfo 批量为订单响应填充交易流水号、支付渠道与商户名称;一次查询支付单、一次查询支付配置。 func (s *Service) fillOrderPaymentInfo(ctx context.Context, orders []*model.Order, responses []*dto.OrderResponse) error { orderIDs := make([]uint, 0, len(orders)) for _, order := range orders { if orderHasOnlinePaymentInfo(order) { orderIDs = append(orderIDs, order.ID) } } if len(orderIDs) == 0 { return nil } var payments []*model.Payment // 带 order_type 过滤:tb_payment 同时存充值类支付,业务单 ID 空间与套餐订单不同。 if err := s.db.WithContext(ctx). Where("order_id IN ? AND order_type = ? AND status = ?", orderIDs, model.PaymentOrderTypePackage, constants.PaymentRecordStatusPaid). Order("order_id ASC, paid_at DESC NULLS LAST, id DESC"). Find(&payments).Error; err != nil { return errors.Wrap(errors.CodeDatabaseError, err, "查询订单支付记录失败") } channels, err := s.resolvePaymentChannels(ctx, payments) if err != nil { return err } paidByOrder := make(map[uint]*model.Payment, len(payments)) for _, payment := range payments { if _, exists := paidByOrder[payment.OrderID]; !exists { paidByOrder[payment.OrderID] = payment } } for index, order := range orders { payment, ok := paidByOrder[order.ID] if !ok || !orderHasOnlinePaymentInfo(order) { continue } responses[index].ThirdPartyTradeNo = payment.ThirdPartyTradeNo responses[index].PaymentChannel = paymentChannel(payment, channels) responses[index].PaymentMerchantName = payment.MerchantNameSnapshot } return nil } // listOrderPayments 返回订单详情的全部支付单(任何状态),按创建时间倒序;不适用的订单返回空数组。 func (s *Service) listOrderPayments(ctx context.Context, order *model.Order) ([]*dto.OrderPaymentResponse, error) { result := make([]*dto.OrderPaymentResponse, 0) if !orderIsOnlinePurchase(order) { return result, nil } var payments []*model.Payment if err := s.db.WithContext(ctx). Where("order_id = ? AND order_type = ?", order.ID, model.PaymentOrderTypePackage). Order("created_at DESC, id DESC"). Find(&payments).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询订单支付单失败") } channels, err := s.resolvePaymentChannels(ctx, payments) if err != nil { return nil, err } for _, payment := range payments { result = append(result, &dto.OrderPaymentResponse{ PaymentNo: payment.PaymentNo, PaymentMethod: payment.PaymentMethod, PaymentChannel: paymentChannel(payment, channels), PaymentMerchantName: payment.MerchantNameSnapshot, ThirdPartyTradeNo: payment.ThirdPartyTradeNo, Amount: payment.Amount, Status: payment.Status, StatusName: constants.GetPaymentRecordStatusName(payment.Status), CreatedAt: payment.CreatedAt, PaidAt: payment.PaidAt, ExpireAt: payment.ExpireAt, }) } return result, nil } // resolvePaymentChannels 为缺少渠道快照的历史支付单批量读取原支付配置的渠道类型(含已删除配置)。 func (s *Service) resolvePaymentChannels(ctx context.Context, payments []*model.Payment) (map[uint]string, error) { configIDs := make([]uint, 0) for _, payment := range payments { if payment.MerchantProviderTypeSnapshot == "" && payment.PaymentConfigID != nil { configIDs = append(configIDs, *payment.PaymentConfigID) } } channels := make(map[uint]string, len(configIDs)) if len(configIDs) == 0 { return channels, nil } var configs []model.WechatConfig if err := s.db.WithContext(ctx).Unscoped().Select("id", "provider_type"). Where("id IN ?", configIDs).Find(&configs).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史支付配置失败") } for _, config := range configs { channels[config.ID] = config.ProviderType } return channels, nil } // paymentChannel 取支付单渠道类型快照,没有快照时按原支付配置推断,推断不出为空串。 func paymentChannel(payment *model.Payment, channels map[uint]string) string { if payment.MerchantProviderTypeSnapshot != "" { return payment.MerchantProviderTypeSnapshot } if payment.PaymentConfigID == nil { return "" } return channels[*payment.PaymentConfigID] }