package projectinstance import ( "context" "fmt" ) // TerminateInput 受控终止输入。 type TerminateInput struct { Reason string `json:"reason"` Confirm bool `json:"confirm"` ExpectedConfigRevision int `json:"expected_config_revision"` } // TerminateResult 终止结果。 type TerminateResult struct { Instance WorkflowInstance `json:"instance"` Reason string `json:"reason"` } // PendingDocSummary 终止预览中的待处理文档摘要。 type PendingDocSummary struct { TaskKey string `json:"task_key"` Status string `json:"status"` StageKey string `json:"stage_key,omitempty"` } // TerminationPreview 受控终止影响预览。 type TerminationPreview struct { InstanceID int64 `json:"instance_id"` PendingDocuments []PendingDocSummary `json:"pending_documents"` InProgressTaskRuns int `json:"in_progress_task_runs"` CanTerminate bool `json:"can_terminate"` Impact string `json:"impact,omitempty"` } func (s *Service) GetTerminationPreview(ctx context.Context, instanceID int64, projectID int64, userID int64, isCreator bool) (*TerminationPreview, error) { if !isCreator { return nil, ErrForbiddenNotCreator } if !s.Available() { return nil, ErrNotAvailable } live, err := s.store.GetLiveInstance(ctx, projectID) if err != nil || live == nil || live.ID != instanceID { return nil, ErrInstanceNotFound } if live.Status == InstanceCompleted || live.Status == "terminated" { return nil, ErrTerminationNotAllowed } detail, err := s.store.GetInstanceByID(ctx, instanceID) if err != nil { return nil, err } pending := make([]PendingDocSummary, 0) for _, st := range detail.Stages { if st.StageKey != detail.ActiveStageKey { continue } for _, dt := range st.DocTasks { if !dt.Enabled || dt.Status == "skipped_disabled" { continue } if dt.Status != "approved" { pending = append(pending, PendingDocSummary{ TaskKey: dt.TaskKey, Status: dt.Status, StageKey: st.StageKey, }) } } } taskRuns, _ := s.store.CountActiveTaskRuns(ctx, projectID) preview := &TerminationPreview{ InstanceID: instanceID, PendingDocuments: pending, InProgressTaskRuns: taskRuns, CanTerminate: true, Impact: fmt.Sprintf( "%d pending documents, %d in-progress task runs will be cancelled", len(pending), taskRuns, ), } return preview, nil } func (s *Service) TerminateInstance(ctx context.Context, instanceID int64, projectID int64, userID int64, isCreator bool, in TerminateInput) (*TerminateResult, error) { if !isCreator { return nil, ErrForbiddenNotCreator } if !in.Confirm { return nil, ErrTerminationNotAllowed } if in.Reason == "" { return nil, ErrValidationFailed } if !s.Available() { return nil, ErrNotAvailable } live, err := s.store.GetLiveInstance(ctx, projectID) if err != nil || live == nil || live.ID != instanceID { return nil, ErrInstanceNotFound } if in.ExpectedConfigRevision > 0 && live.ConfigRevision != in.ExpectedConfigRevision { return nil, ErrInstanceRevisionConflict } inst, err := s.store.TerminateInstance(ctx, instanceID, projectID, userID, in.Reason) if err != nil { return nil, err } return &TerminateResult{Instance: *inst, Reason: in.Reason}, nil }