Files
junhong_cmp_fiber/internal/query/employeecollection/application_submission.go
邹进 6298a9061f fix: 第1批 M8、M13、M14 审批提交失败终态与退款关闭类终态定位
- 审批核心在实例进入提交失败的同一事务内写 submission_failed 决策投递与终态事件,消费入口单独放行
- 核销(4)、退款(7)、扫码注册(3)、资料资格(4)、线下充值(7)、佣金提现(5)新增“审批提交失败”终态并释放预占/冻结
- 三处恢复入口不再重放提交失败;退款关闭类终态按审批尝试定位退款单(M13)
- 核销列表与详情批量返回审批提交状态;筛选、字段说明、状态名称、退款导出加入新状态
- 迁移 000239 放宽五张业务表状态约束与决策投递取值约束(测试库已执行 up/down/up)
- OpenSpec Change fix-approval-submission-failure-terminal:18/26,测试部署后端到端验证与存量补数据待完成
2026-10-12 00:03:46 +09:00

72 lines
3.0 KiB
Go

package employeecollection
import (
"context"
"time"
approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
"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"
)
// applicationSubmissionRow 是核销申请最新审批实例的提交事实。
type applicationSubmissionRow struct {
InstanceID uint `gorm:"column:instance_id"`
InstanceStatus int `gorm:"column:instance_status"`
SubmissionStatus int `gorm:"column:submission_status"`
LastError string `gorm:"column:last_error"`
LastRecoveryAt *time.Time `gorm:"column:last_recovery_at"`
}
// fillApplicationSubmission 按最新审批实例一次批量读取企微审批上下文,填充提交状态、脱敏摘要、可恢复与最近恢复时间。
// 无审批实例或无企微上下文的申请保持零值。
func (q *ApplicationQuery) fillApplicationSubmission(ctx context.Context, responses []*dto.EmployeeCollectionApplicationResponse) error {
instanceIDs := make([]uint, 0, len(responses))
for _, response := range responses {
if response != nil && response.LatestApprovalInstanceID != 0 {
instanceIDs = append(instanceIDs, response.LatestApprovalInstanceID)
}
}
if len(instanceIDs) == 0 {
return nil
}
var rows []applicationSubmissionRow
if err := q.db.WithContext(ctx).Raw(
`SELECT ai.id AS instance_id, ai.status AS instance_status,
COALESCE(wc.submission_status, 0) AS submission_status,
COALESCE(wc.last_error, '') AS last_error, wc.last_recovery_at
FROM tb_approval_instance ai
LEFT JOIN tb_wecom_approval_context wc ON wc.approval_instance_id = ai.id
WHERE ai.id IN ?`, instanceIDs).Scan(&rows).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "批量查询核销申请审批提交状态失败")
}
byInstance := make(map[uint]applicationSubmissionRow, len(rows))
for _, row := range rows {
byInstance[row.InstanceID] = row
}
for _, response := range responses {
if response == nil {
continue
}
row, ok := byInstance[response.LatestApprovalInstanceID]
if !ok {
continue
}
response.ApprovalSubmissionStatus = row.SubmissionStatus
response.ApprovalSubmissionName = constants.GetWeComSubmissionStatusName(row.SubmissionStatus)
if row.SubmissionStatus == constants.WeComSubmissionStatusFailed {
response.ApprovalFailureSummary = approvalapp.SubmissionFailureReason(row.LastError)
}
// 提交失败为终态不可恢复;只有审批中的申请且实例仍在提交中、审批中或结果未知时可恢复。
response.Recoverable = response.Status == constants.EmployeeCollectionApplicationStatusPending &&
(row.InstanceStatus == constants.ApprovalStatusSubmitting ||
row.InstanceStatus == constants.ApprovalStatusPending ||
row.InstanceStatus == constants.ApprovalStatusSubmissionUnknown)
if row.LastRecoveryAt != nil {
response.LastRecoveryAt = row.LastRecoveryAt.UTC().Format(time.RFC3339)
}
}
return nil
}