七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
This commit is contained in:
159
internal/infrastructure/wecom/approval_detail_task.go
Normal file
159
internal/infrastructure/wecom/approval_detail_task.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package wecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/integrationlog"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
)
|
||||
|
||||
// ApprovalDetailTaskHandler 拉取企微审批详情并翻译为现有标准决策。
|
||||
type ApprovalDetailTaskHandler struct {
|
||||
details *ApprovalDetailClient
|
||||
contexts *ApprovalContextRepository
|
||||
decisions *approvalapp.SyncDecisionService
|
||||
integration TokenIntegrationLog
|
||||
}
|
||||
|
||||
// NewApprovalDetailTaskHandler 创建企业微信审批详情同步任务 Handler。
|
||||
func NewApprovalDetailTaskHandler(details *ApprovalDetailClient, contexts *ApprovalContextRepository, decisions *approvalapp.SyncDecisionService, integration TokenIntegrationLog) *ApprovalDetailTaskHandler {
|
||||
return &ApprovalDetailTaskHandler{details: details, contexts: contexts, decisions: decisions, integration: integration}
|
||||
}
|
||||
|
||||
// Handle 处理回调或后续轮询提交的详情同步任务。
|
||||
func (h *ApprovalDetailTaskHandler) Handle(ctx context.Context, task *asynq.Task) error {
|
||||
if h == nil || h.details == nil || h.contexts == nil || h.decisions == nil || h.integration == nil {
|
||||
return errors.New(errors.CodeServiceUnavailable, "企业微信审批详情同步任务未配置")
|
||||
}
|
||||
var payload ApprovalDetailSyncTask
|
||||
if err := sonic.Unmarshal(task.Payload(), &payload); err != nil || payload.ApplicationID == 0 || strings.TrimSpace(payload.SPNo) == "" {
|
||||
return errors.New(errors.CodeInvalidParam, "企业微信审批详情同步任务载荷无效")
|
||||
}
|
||||
if !validApprovalSyncSource(payload.Source) {
|
||||
return errors.New(errors.CodeInvalidParam, "企业微信审批详情同步来源无效")
|
||||
}
|
||||
detail, err := h.details.Get(ctx, payload.ApplicationID, payload.SPNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
record, err := h.contexts.FindBySPNo(ctx, payload.ApplicationID, payload.SPNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if record == nil {
|
||||
record, err = h.recoverUnknownCallback(ctx, payload, detail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if record == nil {
|
||||
return h.completeIgnoredCallback(ctx, payload)
|
||||
}
|
||||
}
|
||||
if err := h.contexts.SaveLatestDetail(ctx, record.Instance.ID, detail.SPStatus, detail.Snapshot); err != nil {
|
||||
return err
|
||||
}
|
||||
decisions := weComDecisions(detail.SPStatus)
|
||||
for _, decision := range decisions {
|
||||
if _, err := h.decisions.Execute(ctx, approvalapp.SyncDecisionCommand{
|
||||
InstanceID: record.Instance.ID, Decision: decision, DecisionSnapshot: detail.Snapshot, Source: payload.Source,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(payload.IntegrationID) != "" {
|
||||
_, err = h.integration.Complete(ctx, payload.IntegrationID, integrationlog.Completion{
|
||||
Result: constants.IntegrationResultCompleted, ProviderCode: "processed",
|
||||
ProviderMessage: "企业微信审批回调已完成权威详情同步",
|
||||
ResponseSummary: map[string]any{"sp_no": payload.SPNo, "sp_status": detail.SPStatus, "decisions": decisions},
|
||||
DurationMS: 0, StateChanged: len(decisions) > 0,
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *ApprovalDetailTaskHandler) recoverUnknownCallback(ctx context.Context, payload ApprovalDetailSyncTask, detail ApprovalDetail) (*ApprovalSubmissionRecord, error) {
|
||||
if payload.Source != constants.ApprovalSyncSourceCallback {
|
||||
return nil, nil
|
||||
}
|
||||
fingerprint := approvalDetailFingerprint(detail.Snapshot)
|
||||
candidate, err := h.contexts.FindUniqueUnknownByFingerprint(
|
||||
ctx, payload.ApplicationID, fingerprint.TemplateID, fingerprint.CreatorUserID, fingerprint.SubmittedAt,
|
||||
)
|
||||
if err != nil || candidate == nil {
|
||||
return nil, err
|
||||
}
|
||||
recovered, err := h.contexts.RecoverSubmitted(ctx, candidate.InstanceID, payload.SPNo)
|
||||
if err != nil || !recovered {
|
||||
return nil, err
|
||||
}
|
||||
return h.contexts.Get(ctx, candidate.InstanceID)
|
||||
}
|
||||
|
||||
func (h *ApprovalDetailTaskHandler) completeIgnoredCallback(ctx context.Context, payload ApprovalDetailSyncTask) error {
|
||||
if strings.TrimSpace(payload.IntegrationID) == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := h.integration.Complete(ctx, payload.IntegrationID, integrationlog.Completion{
|
||||
Result: constants.IntegrationResultIgnored, ProviderCode: "unrelated_approval",
|
||||
ProviderMessage: "企业微信审批单未关联本系统业务,已忽略",
|
||||
ResponseSummary: map[string]any{"sp_no": payload.SPNo, "ignored": true},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
type approvalFingerprint struct {
|
||||
TemplateID string
|
||||
CreatorUserID string
|
||||
SubmittedAt time.Time
|
||||
}
|
||||
|
||||
func approvalDetailFingerprint(snapshot []byte) approvalFingerprint {
|
||||
var value struct {
|
||||
TemplateID string `json:"template_id"`
|
||||
ApplyTime int64 `json:"apply_time"`
|
||||
Applyer struct {
|
||||
UserID string `json:"userid"`
|
||||
} `json:"applyer"`
|
||||
}
|
||||
if sonic.Unmarshal(snapshot, &value) != nil || value.ApplyTime <= 0 {
|
||||
return approvalFingerprint{}
|
||||
}
|
||||
return approvalFingerprint{
|
||||
TemplateID: strings.TrimSpace(value.TemplateID), CreatorUserID: strings.TrimSpace(value.Applyer.UserID),
|
||||
SubmittedAt: time.Unix(value.ApplyTime, 0).UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
func validApprovalSyncSource(source string) bool {
|
||||
switch source {
|
||||
case constants.ApprovalSyncSourceCallback, constants.ApprovalSyncSourcePolling, constants.ApprovalSyncSourceManual:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// weComDecisions 只翻译现有审批核心支持的企微标准终态;通过后撤销按领域允许顺序补齐两个事实。
|
||||
func weComDecisions(status int) []string {
|
||||
switch status {
|
||||
case 2:
|
||||
return []string{constants.ApprovalDecisionApproved}
|
||||
case 3:
|
||||
return []string{constants.ApprovalDecisionRejected}
|
||||
case 4:
|
||||
return []string{constants.ApprovalDecisionCancelled}
|
||||
case 6:
|
||||
return []string{constants.ApprovalDecisionApproved, constants.ApprovalDecisionRevokedAfterApproved}
|
||||
case 7:
|
||||
return []string{constants.ApprovalDecisionDeleted}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user