完善退款审批材料与恢复流程
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m35s

This commit is contained in:
2026-09-20 17:59:52 +08:00
parent 4a6cec2730
commit b063617153
29 changed files with 1119 additions and 156 deletions

View File

@@ -5,6 +5,7 @@ import (
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/hibiken/asynq"
"github.com/break/junhong_cmp_fiber/pkg/constants"
@@ -25,12 +26,21 @@ func NewApprovalRecoveryTaskHandler(contexts *ApprovalContextRepository, infos *
return &ApprovalRecoveryTaskHandler{contexts: contexts, infos: infos, queue: queueClient, now: time.Now}
}
// Handle 扫描未终态和结果未知记录;结果未知只查询关联,绝不重新调用 applyevent
func (h *ApprovalRecoveryTaskHandler) Handle(ctx context.Context, _ *asynq.Task) error {
// Handle 支持全局扫描任务与按实例人工任务,二者共享同一 unknown 确认算法
func (h *ApprovalRecoveryTaskHandler) Handle(ctx context.Context, task *asynq.Task) error {
if h == nil || h.contexts == nil || h.infos == nil || h.queue == nil {
return errors.New(errors.CodeServiceUnavailable, "企业微信审批主动恢复任务未配置")
}
var payload struct {
InstanceID uint `json:"instance_id"`
}
if task != nil && len(task.Payload()) > 0 {
_ = sonic.Unmarshal(task.Payload(), &payload)
}
now := h.now().UTC()
if payload.InstanceID > 0 {
return h.recoverUnknownInstance(ctx, payload.InstanceID, now)
}
if err := h.contexts.PromoteStaleSendingToUnknown(ctx, now.Add(-constants.WeComApprovalSendingLease)); err != nil {
return err
}
@@ -53,48 +63,58 @@ func (h *ApprovalRecoveryTaskHandler) enqueuePendingSync(ctx context.Context, no
return nil
}
func (h *ApprovalRecoveryTaskHandler) recoverUnknownInstance(ctx context.Context, instanceID uint, now time.Time) error {
record, err := h.contexts.GetRecoveryRecord(ctx, instanceID)
if err != nil || record == nil {
return err
}
claimed, err := h.contexts.ClaimUnknownRecovery(ctx, instanceID, now.Add(-constants.WeComApprovalPollingInterval))
if err != nil || !claimed {
return err
}
return h.recoverUnknownRecord(ctx, *record, now)
}
func (h *ApprovalRecoveryTaskHandler) recoverUnknownRecord(ctx context.Context, record ApprovalRecoveryRecord, now time.Time) error {
spNo, submissionIntegrationID, err := h.contexts.FindSuccessfulSubmissionSPNo(ctx, record.InstanceID)
if err != nil {
return err
}
integrationIDs := []string{submissionIntegrationID}
if spNo == "" {
var ids []string
spNo, ids, err = h.findUniqueSPNo(ctx, record, now)
if err != nil {
return err
}
integrationIDs = append(integrationIDs, ids...)
}
if spNo == "" {
return nil
}
recovered, err := h.contexts.RecoverSubmitted(ctx, record.InstanceID, spNo, integrationIDs, constants.AuditActorScheduledJob, constants.ApprovalAuditActorRecoveryJob, constants.AuditSourceScheduler)
if err != nil || !recovered {
return err
}
return h.enqueueDetailSync(ctx, record.ApplicationID, spNo)
}
func (h *ApprovalRecoveryTaskHandler) recoverUnknown(ctx context.Context, now time.Time) error {
cutoff := now.Add(-constants.WeComApprovalPollingInterval)
records, err := h.contexts.ListUnknownRecovery(ctx, cutoff, constants.WeComApprovalUnknownRecoveryBatchSize)
records, err := h.contexts.ListUnknownRecovery(ctx, now.Add(-constants.WeComApprovalPollingInterval), constants.WeComApprovalUnknownRecoveryBatchSize)
if err != nil {
return err
}
for _, record := range records {
claimed, err := h.contexts.ClaimUnknownRecovery(ctx, record.InstanceID, cutoff)
claimed, err := h.contexts.ClaimUnknownRecovery(ctx, record.InstanceID, now.Add(-constants.WeComApprovalPollingInterval))
if err != nil {
return err
}
if !claimed {
continue
}
spNo, submissionIntegrationID, err := h.contexts.FindSuccessfulSubmissionSPNo(ctx, record.InstanceID)
if err != nil {
if err := h.recoverUnknownRecord(ctx, record, now); err != nil {
return err
}
integrationIDs := []string{submissionIntegrationID}
if spNo == "" {
var recoveryIntegrationIDs []string
spNo, recoveryIntegrationIDs, err = h.findUniqueSPNo(ctx, record, now)
if err != nil {
return err
}
integrationIDs = append(integrationIDs, recoveryIntegrationIDs...)
}
if spNo == "" {
continue
}
recovered, err := h.contexts.RecoverSubmitted(
ctx, record.InstanceID, spNo, integrationIDs,
constants.AuditActorScheduledJob, constants.ApprovalAuditActorRecoveryJob, constants.AuditSourceScheduler,
)
if err != nil {
return err
}
if recovered {
if err := h.enqueueDetailSync(ctx, record.ApplicationID, spNo); err != nil {
return err
}
}
}
return nil
}