Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕 - 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败 - 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影 - 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序 - 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列 - 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名 - 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
101 lines
3.9 KiB
Go
101 lines
3.9 KiB
Go
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.BusinessType, 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, result.IntegrationID); err != nil {
|
||
return err
|
||
}
|
||
case submissionOutcomeUnknown:
|
||
if err := c.repository.MarkUnknown(ctx, event.InstanceID, result.Message, result.IntegrationID); 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, result.IntegrationID); err != nil {
|
||
return err
|
||
}
|
||
default:
|
||
return errors.New(errors.CodeInternalError, "企业微信审批提交结果无效")
|
||
}
|
||
return submitErr
|
||
}
|
||
|
||
var _ outbox.EventConsumer = (*ApprovalSubmissionConsumer)(nil)
|