Files
junhong_cmp_fiber/internal/infrastructure/approval/decision_delivery.go
2026-07-24 16:07:18 +08:00

105 lines
4.2 KiB
Go

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
}