补齐 DocumentAccessDecider HTTP 409、projectcontext 私有对话排除统计,并更新整改 gap matrix 与测试报告。
114 lines
3.3 KiB
Go
114 lines
3.3 KiB
Go
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
|
|
}
|