- 审批核心在实例进入提交失败的同一事务内写 submission_failed 决策投递与终态事件,消费入口单独放行 - 核销(4)、退款(7)、扫码注册(3)、资料资格(4)、线下充值(7)、佣金提现(5)新增“审批提交失败”终态并释放预占/冻结 - 三处恢复入口不再重放提交失败;退款关闭类终态按审批尝试定位退款单(M13) - 核销列表与详情批量返回审批提交状态;筛选、字段说明、状态名称、退款导出加入新状态 - 迁移 000239 放宽五张业务表状态约束与决策投递取值约束(测试库已执行 up/down/up) - OpenSpec Change fix-approval-submission-failure-terminal:18/26,测试部署后端到端验证与存量补数据待完成
45 lines
1.7 KiB
Go
45 lines
1.7 KiB
Go
package approval
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
|
||
"gorm.io/gorm"
|
||
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
)
|
||
|
||
// ConfirmSubmissionFailed 在业务消费事务内锁定审批实例并确认其仍为“提交失败”,返回可对外展示的失败原因。
|
||
// 实例已不是提交失败(例如被恢复重放)时 ok 为 false,业务消费者应不改变业务单。
|
||
// 原因只返回固定白名单文案,禁止透传企微原文或内部错误。
|
||
func ConfirmSubmissionFailed(ctx context.Context, tx *gorm.DB, instanceID uint) (string, bool, error) {
|
||
var row struct {
|
||
Status int `gorm:"column:status"`
|
||
LastError string `gorm:"column:last_error"`
|
||
}
|
||
result := tx.WithContext(ctx).Raw(
|
||
`SELECT ai.status, COALESCE(wc.last_error, '') AS last_error
|
||
FROM tb_approval_instance ai
|
||
LEFT JOIN tb_wecom_approval_context wc ON wc.approval_instance_id = ai.id
|
||
WHERE ai.id = ? FOR UPDATE OF ai`, instanceID).Scan(&row)
|
||
if result.Error != nil {
|
||
return "", false, errors.Wrap(errors.CodeDatabaseError, result.Error, "锁定提交失败审批实例失败")
|
||
}
|
||
if result.RowsAffected == 0 {
|
||
return "", false, errors.New(errors.CodeNotFound, "审批实例不存在")
|
||
}
|
||
if row.Status != constants.ApprovalStatusSubmissionFailed {
|
||
return "", false, nil
|
||
}
|
||
return SubmissionFailureReason(row.LastError), true, nil
|
||
}
|
||
|
||
// SubmissionFailureReason 把企微提交失败原文转换为稳定白名单文案。
|
||
func SubmissionFailureReason(lastError string) string {
|
||
if strings.TrimSpace(lastError) == "" {
|
||
return constants.ApprovalSubmissionFailedStatusName
|
||
}
|
||
return constants.ApprovalSubmissionFailedReason
|
||
}
|