fix(退款): 修复创建退款申请插入审批尝试记录失败
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m23s

创建路径把审批尝试记录在事务外预构造后原样插入,缺失只能在事务内生成的
attempt_no 与 package_usage_snapshot,触发 NOT NULL 与 CHECK 约束,
接口统一返回 2002 数据库错误(5xx 脱敏掩盖了具体消息)。

- 尝试记录收敛为事务内唯一构造点 buildAttempt,CreateCommand 与
  ResubmitCommand 只传按冻结商户派生的渠道退款请求号
- TriggerHistorical 补上缺失的尝试记录插入,此前 attempt.ID 恒为 0,
  必然以「关联已变化」冲突收场
- 按「首次接入企业微信审批的实例」语义回写
  tb_refund_request.approval_instance_id,恢复本地人工终审的 IS NULL
  防重保护与退款导出投影
This commit is contained in:
2026-09-17 15:49:04 +08:00
parent aab56a6998
commit 70e6b186df
3 changed files with 66 additions and 40 deletions

View File

@@ -254,20 +254,12 @@ func validateFrozenRefundAmount(amount, frozenActualReceivedAmount int64) error
return nil
}
// buildAttemptFromDecision 依据方式判定结果构造一条不可变审批尝试记录
func buildAttemptFromDecision(decision *refundMethodDecision, refund *model.RefundRequest, config *model.WechatConfig, now time.Time) *model.RefundRequestAttempt {
attempt := &model.RefundRequestAttempt{
RefundID: refund.ID,
Method: refund.Method,
RefundAmount: refund.RequestedRefundAmount,
FrozenActualReceivedAmount: decision.FrozenActualReceivedAmount,
RefundReason: refund.RefundReason,
CustomerAccountInfo: refund.CustomerAccountInfo,
CustomerVoucherKeys: refund.RefundVoucherKey,
SubmittedByAccountID: refund.Creator,
// buildChannelRefundRequestNo 仅在原路退款方式下按冻结商户派生本次尝试的渠道退款请求号
// 请求号由收口审批申请用例冻结到不可变尝试记录上,同一尝试的渠道重试复用它作为幂等标识,
// 重提生成新值;非原路方式返回空串。
func buildChannelRefundRequestNo(method string, config *model.WechatConfig) string {
if method != constants.RefundMethodOriginalRoute {
return ""
}
if refund.Method == constants.RefundMethodOriginalRoute {
attempt.ChannelRefundRequestNo = requestChannelRefundNo(config, now)
}
return attempt
return requestChannelRefundNo(config, time.Now())
}

View File

@@ -200,9 +200,9 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateRefundRequest) (*dt
if s.refundApprovalCreation == nil {
return nil, errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")
}
attempt := buildAttemptFromDecision(decision, refund, decision.ChannelConfig, time.Now())
result, err := s.refundApprovalCreation.Execute(ctx, refundapprovalapp.CreateCommand{
Refund: refund, Order: order, SubmitterAccountID: userID, Attempt: attempt,
Refund: refund, Order: order, SubmitterAccountID: userID,
ChannelRefundRequestNo: buildChannelRefundRequestNo(refund.Method, decision.ChannelConfig),
})
if err != nil {
failedRefund := *refund
@@ -852,12 +852,12 @@ func (s *Service) Resubmit(ctx context.Context, id uint, req *dto.ResubmitRefund
updated.CustomerAccountInfo = customerAccountInfo
updated.Creator = userID
attempt := buildAttemptFromDecision(decision, &updated, decision.ChannelConfig, time.Now())
attemptChannelRefundRequestNo := buildChannelRefundRequestNo(method, decision.ChannelConfig)
if s.refundApprovalCreation == nil {
return errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")
}
if _, err := s.refundApprovalCreation.Resubmit(ctx, id, refundapprovalapp.ResubmitCommand{
Refund: &updated, Attempt: attempt,
Refund: &updated, ChannelRefundRequestNo: attemptChannelRefundRequestNo,
}); err != nil {
s.recordRefundFailure(ctx, constants.AuditActionRefundResubmitted, "重新提交退款申请失败", refund, order, err)
return err