Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕 - 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败 - 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影 - 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序 - 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列 - 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名 - 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
72 lines
3.1 KiB
Go
72 lines
3.1 KiB
Go
package refund
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
|
||
"go.uber.org/zap"
|
||
|
||
refundapprovalapp "github.com/break/junhong_cmp_fiber/internal/application/refundapproval"
|
||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||
)
|
||
|
||
// refundAssetType 把订单类型映射为退款资产类型:card 单卡、device 设备。
|
||
// 与退款列表、详情响应的资产类型口径一致;空值或未知订单类型返回空串。
|
||
func refundAssetType(orderType string) string {
|
||
switch orderType {
|
||
case model.OrderTypeSingleCard:
|
||
return "card"
|
||
case model.OrderTypeDevice:
|
||
return "device"
|
||
default:
|
||
return ""
|
||
}
|
||
}
|
||
|
||
// loadRefundMaterial 按既有展示口径解析本次提交的企业微信审批材料事实。
|
||
//
|
||
// 三个维度各自独立解析且全部尽力而为:
|
||
// 1. 资产类型沿用退款申请的下单快照推导(refundAssetType,与详情响应同一规则);
|
||
// 2. 设备类型与型号取退款请求关联设备,无关联设备或设备不可读时留空;
|
||
// 3. 套餐已用量与总量复用退款展示解析规则(loadRefundPackageUsages,冻结套餐记录 → 订单主套餐 →
|
||
// 订单任一套餐)与真流量口径,解析不到时为 0;
|
||
// 4. 原支付渠道交易流水号取创建申请时冻结的原成功支付事实,线下订单为空。
|
||
//
|
||
// 审批材料是审批判断依据而非金额与状态判定输入,因此解析失败 MUST NOT 阻断审批提交:
|
||
// 任一项不可解析时以空值或零值提交,并记录日志供排查。
|
||
func (s *Service) loadRefundMaterial(ctx context.Context, refund *model.RefundRequest, payment *model.Payment) refundapprovalapp.RefundMaterial {
|
||
material := refundapprovalapp.RefundMaterial{
|
||
AssetType: refundAssetType(refund.OrderType),
|
||
OriginalChannelTradeNo: strings.TrimSpace(refund.OriginalChannelTradeNo),
|
||
}
|
||
if payment != nil && strings.TrimSpace(payment.ThirdPartyTradeNo) != "" {
|
||
material.OriginalChannelTradeNo = strings.TrimSpace(payment.ThirdPartyTradeNo)
|
||
}
|
||
if refund.DeviceID != nil && *refund.DeviceID > 0 {
|
||
var device model.Device
|
||
if err := s.db.WithContext(ctx).Select("id", "device_type", "device_model").First(&device, *refund.DeviceID).Error; err != nil {
|
||
s.logMaterialFailure(ctx, refund.ID, "device", err)
|
||
} else {
|
||
material.DeviceType = strings.TrimSpace(device.DeviceType)
|
||
material.DeviceModel = strings.TrimSpace(device.DeviceModel)
|
||
}
|
||
}
|
||
usages, err := s.loadRefundPackageUsages(ctx, []*model.RefundRequest{refund})
|
||
if err != nil {
|
||
s.logMaterialFailure(ctx, refund.ID, "package_usage", err)
|
||
} else if usage, exists := usages[refund.ID]; exists {
|
||
material.PackageUsedMB = usage.UsedMB
|
||
material.PackageTotalMB = usage.TotalMB
|
||
}
|
||
return material
|
||
}
|
||
|
||
// logMaterialFailure 记录审批材料解析失败:材料按空值或零值降级提交,因此只记录不返回错误。
|
||
func (s *Service) logMaterialFailure(ctx context.Context, refundID uint, dimension string, err error) {
|
||
if s.logger == nil {
|
||
return
|
||
}
|
||
s.logger.Warn("退款审批材料解析失败,按空值提交",
|
||
zap.Uint("refund_id", refundID), zap.String("dimension", dimension), zap.Error(err))
|
||
}
|