86 lines
3.0 KiB
Go
86 lines
3.0 KiB
Go
package approval
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
)
|
|
|
|
// DecisionProcessingStore 管理标准决策交给业务消费者时的处理租约。
|
|
type DecisionProcessingStore interface {
|
|
Claim(ctx context.Context, eventID string, owner string, now time.Time, duration time.Duration) (bool, error)
|
|
MarkSucceeded(ctx context.Context, eventID string, owner string, now time.Time) (bool, error)
|
|
MarkFailed(ctx context.Context, eventID string, owner string, now time.Time, errorSummary string) (bool, error)
|
|
}
|
|
|
|
// BusinessDecisionHandler 消费渠道无关标准决策。
|
|
// 实现必须以审批实例 ID 和决策作为业务幂等键,并且不得依赖任何渠道 SDK、DTO 或状态码。
|
|
type BusinessDecisionHandler interface {
|
|
Handle(ctx context.Context, event TerminalDecisionEvent) error
|
|
}
|
|
|
|
// DecisionDispatcher 使用处理租约把标准决策交给对应业务消费者。
|
|
type DecisionDispatcher struct {
|
|
store DecisionProcessingStore
|
|
handlers map[string]BusinessDecisionHandler
|
|
owner string
|
|
logger *zap.Logger
|
|
now func() time.Time
|
|
}
|
|
|
|
// NewDecisionDispatcher 创建通用审批标准决策分发器。
|
|
func NewDecisionDispatcher(
|
|
store DecisionProcessingStore,
|
|
handlers map[string]BusinessDecisionHandler,
|
|
owner string,
|
|
logger *zap.Logger,
|
|
now func() time.Time,
|
|
) *DecisionDispatcher {
|
|
if logger == nil {
|
|
logger = zap.NewNop()
|
|
}
|
|
if now == nil {
|
|
now = time.Now
|
|
}
|
|
return &DecisionDispatcher{store: store, handlers: handlers, owner: owner, logger: logger, now: now}
|
|
}
|
|
|
|
// Consume 幂等消费一条标准决策;重复投递或其他有效租约正在处理时正常结束。
|
|
func (d *DecisionDispatcher) Consume(ctx context.Context, event TerminalDecisionEvent) error {
|
|
if d == nil || d.store == nil || d.owner == "" || event.EventID == "" || event.InstanceID == 0 || event.BusinessType == "" {
|
|
return errors.New(errors.CodeInternalError, "通用审批决策分发器未完整配置")
|
|
}
|
|
handler := d.handlers[event.BusinessType]
|
|
if handler == nil {
|
|
return errors.New(errors.CodeServiceUnavailable, "审批业务消费者尚未注册")
|
|
}
|
|
now := d.now().UTC()
|
|
claimed, err := d.store.Claim(ctx, event.EventID, d.owner, now, constants.ApprovalDecisionDeliveryLeaseDuration)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !claimed {
|
|
return nil
|
|
}
|
|
if err := handler.Handle(ctx, event); err != nil {
|
|
if _, markErr := d.store.MarkFailed(ctx, event.EventID, d.owner, d.now().UTC(), "业务消费者处理失败"); markErr != nil {
|
|
d.logger.Error("审批标准决策失败状态保存失败",
|
|
zap.String("event_id", event.EventID), zap.Uint("approval_instance_id", event.InstanceID),
|
|
zap.String("business_type", event.BusinessType), zap.Error(markErr))
|
|
}
|
|
return err
|
|
}
|
|
marked, err := d.store.MarkSucceeded(ctx, event.EventID, d.owner, d.now().UTC())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !marked {
|
|
return errors.New(errors.CodeConflict, "审批标准决策处理租约已失效")
|
|
}
|
|
return nil
|
|
}
|