package wecom import ( "context" stdErrors "errors" "github.com/bytedance/sonic" "github.com/hibiken/asynq" approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval" "github.com/break/junhong_cmp_fiber/internal/infrastructure/messaging/outbox" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // ApprovalSubmissionConsumer 消费通用审批提交 Outbox,并保证结果未知时不盲目重提。 type ApprovalSubmissionConsumer struct { repository *ApprovalContextRepository client *ApprovalSubmissionClient attachments approvalAttachmentPort } // NewApprovalSubmissionConsumer 创建企业微信审批提交消费者。 func NewApprovalSubmissionConsumer(repository *ApprovalContextRepository, client *ApprovalSubmissionClient, attachments approvalAttachmentPort) *ApprovalSubmissionConsumer { return &ApprovalSubmissionConsumer{repository: repository, client: client, attachments: attachments} } // Consume 校验通用事件并提交一次企业微信审批申请。 func (c *ApprovalSubmissionConsumer) Consume(ctx context.Context, envelope outbox.DeliveryEnvelope) error { if c == nil || c.repository == nil || c.client == nil { return errors.New(errors.CodeServiceUnavailable, "企业微信审批提交消费者未配置") } if envelope.PayloadVersion != constants.ApprovalSubmissionPayloadVersionV1 { return stdErrors.Join(asynq.SkipRetry, errors.New(errors.CodeInvalidParam, "不支持的审批提交事件版本")) } var event approvalapp.SubmissionRequestedEvent if err := sonic.Unmarshal(envelope.Payload, &event); err != nil { return stdErrors.Join(asynq.SkipRetry, errors.New(errors.CodeInvalidParam, "审批提交事件载荷无效")) } if event.InstanceID == 0 || event.Provider != constants.IntegrationProviderWeCom { return stdErrors.Join(asynq.SkipRetry, errors.New(errors.CodeInvalidParam, "审批提交事件渠道或实例无效")) } record, claimed, err := c.repository.ClaimSubmission(ctx, event.InstanceID) if err != nil { return err } if !claimed { return nil } form, err := buildApprovalForm( ctx, record.Context.ApplicationID, event.InstanceID, record.Context.ControlMapping, record.Context.TemplateSnapshot, record.Instance.RequestSnapshot, c.attachments, ) if err != nil { var attachmentErr *attachmentPreparationError if stdErrors.As(err, &attachmentErr) { if releaseErr := c.repository.ReleaseForRetry(ctx, event.InstanceID, "审批附件准备失败,等待重试"); releaseErr != nil { return releaseErr } return err } if markErr := c.repository.MarkFailed(ctx, event.InstanceID, err.Error()); markErr != nil { return markErr } return nil } result, submitErr := c.client.Submit(ctx, ApprovalSubmitRequest{ InstanceID: event.InstanceID, ApplicationID: record.Context.ApplicationID, TemplateID: record.Context.TemplateID, CreatorUserID: record.Context.CreatorUserID, CorrelationID: event.CorrelationID, Contents: form.Contents, }) switch result.Outcome { case submissionOutcomeSuccess: if err := c.repository.MarkSubmitted(ctx, event.InstanceID, result.SPNo); err != nil { return err } case submissionOutcomeUnknown: if err := c.repository.MarkUnknown(ctx, event.InstanceID, result.Message); err != nil { return err } case submissionOutcomeFailed: if result.SafeToRetry { if err := c.repository.ReleaseForRetry(ctx, event.InstanceID, result.Message); err != nil { return err } if submitErr != nil { return submitErr } return errors.New(errors.CodeServiceUnavailable, result.Message) } if err := c.repository.MarkFailed(ctx, event.InstanceID, result.Message); err != nil { return err } default: return errors.New(errors.CodeInternalError, "企业微信审批提交结果无效") } return submitErr } var _ outbox.EventConsumer = (*ApprovalSubmissionConsumer)(nil)