Files
junhong_cmp_fiber/internal/service/order/payment_info.go
邹进 25c081fe60
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8h37m36s
fix: 第1批 M2、M3、M16、M23 导入与展示类缺陷
- M2 批量换绑表头改为“平台登录账号”,只认手机号,细分格式、不存在、多匹配、非平台、已禁用失败原因
- M3 后台订单列表与详情返回 C 端在线购买的交易流水号、支付渠道、商户名称,详情新增全部支付单 payments
- M16 预警规则不再返回 real_data_mb,预警列表/详情新增 threshold_mb,导出末列追加“阈值流量(MB)”
- M23 设备导入 IMEI 必填,空 IMEI 行记为失败
- OpenSpec Change fix-august-iteration-follow-up-gaps:13/18,导入任务、导出任务的 Worker 端到端验证与前端/运营通知待部署后完成
2026-10-11 23:49:29 +09:00

140 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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]
}