package approval import ( "context" "time" "gorm.io/gorm" approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // DecisionDeliveryStore 持久化标准决策消费事实并管理处理租约。 type DecisionDeliveryStore struct { db *gorm.DB } // NewDecisionDeliveryStore 创建标准决策投递 Store。 func NewDecisionDeliveryStore(db *gorm.DB) *DecisionDeliveryStore { return &DecisionDeliveryStore{db: db} } // Create 在审批状态事务中创建唯一标准决策投递事实。 func (s *DecisionDeliveryStore) Create(ctx context.Context, tx *gorm.DB, event approvalapp.TerminalDecisionEvent) error { if tx == nil { return errors.New(errors.CodeInternalError, "审批决策投递必须使用审批状态事务") } record := model.ApprovalDecisionDelivery{ InstanceID: event.InstanceID, Decision: event.Decision, EventID: event.EventID, Status: constants.ApprovalDecisionDeliveryPending, CreatedAt: event.OccurredAt, UpdatedAt: event.OccurredAt, } if err := tx.WithContext(ctx).Create(&record).Error; err != nil { return errors.Wrap(errors.CodeDatabaseError, err, "创建审批决策投递事实失败") } return nil } // Claim 领取待处理、失败或租约已过期的标准决策。 func (s *DecisionDeliveryStore) Claim( ctx context.Context, eventID string, owner string, now time.Time, duration time.Duration, ) (bool, error) { if s == nil || s.db == nil || eventID == "" || owner == "" || duration <= 0 { return false, errors.New(errors.CodeInternalError, "审批决策处理租约 Store 未完整配置") } result := s.db.WithContext(ctx).Model(&model.ApprovalDecisionDelivery{}). Where("event_id = ? AND (status IN ? OR (status = ? AND lease_expires_at <= ?))", eventID, []int{constants.ApprovalDecisionDeliveryPending, constants.ApprovalDecisionDeliveryFailed}, constants.ApprovalDecisionDeliveryProcessing, now). Updates(map[string]any{ "status": constants.ApprovalDecisionDeliveryProcessing, "lease_owner": owner, "lease_expires_at": now.Add(duration), "updated_at": now, }) if result.Error != nil { return false, errors.Wrap(errors.CodeDatabaseError, result.Error, "领取审批决策处理租约失败") } return result.RowsAffected == 1, nil } // MarkSucceeded 标记当前租约的业务消费者已幂等处理成功。 func (s *DecisionDeliveryStore) MarkSucceeded(ctx context.Context, eventID string, owner string, now time.Time) (bool, error) { if s == nil || s.db == nil { return false, errors.New(errors.CodeInternalError, "审批决策处理租约 Store 未配置") } result := s.db.WithContext(ctx).Model(&model.ApprovalDecisionDelivery{}). Where("event_id = ? AND status = ? AND lease_owner = ?", eventID, constants.ApprovalDecisionDeliveryProcessing, owner). Updates(map[string]any{ "status": constants.ApprovalDecisionDeliverySucceeded, "processed_at": now, "lease_owner": nil, "lease_expires_at": nil, "last_error": "", "updated_at": now, }) if result.Error != nil { return false, errors.Wrap(errors.CodeDatabaseError, result.Error, "完成审批决策业务处理失败") } return result.RowsAffected == 1, nil } // MarkFailed 记录当前租约的安全失败摘要并释放租约等待重试。 func (s *DecisionDeliveryStore) MarkFailed( ctx context.Context, eventID string, owner string, now time.Time, errorSummary string, ) (bool, error) { if s == nil || s.db == nil { return false, errors.New(errors.CodeInternalError, "审批决策处理租约 Store 未配置") } result := s.db.WithContext(ctx).Model(&model.ApprovalDecisionDelivery{}). Where("event_id = ? AND status = ? AND lease_owner = ?", eventID, constants.ApprovalDecisionDeliveryProcessing, owner). Updates(map[string]any{ "status": constants.ApprovalDecisionDeliveryFailed, "retry_count": gorm.Expr("retry_count + 1"), "lease_owner": nil, "lease_expires_at": nil, "last_error": errorSummary, "updated_at": now, }) if result.Error != nil { return false, errors.Wrap(errors.CodeDatabaseError, result.Error, "记录审批决策业务处理失败") } return result.RowsAffected == 1, nil }