package projectinstance import ( "fmt" "chat-agent-system/internal/workflowdef" ) func validatePlan(plan WorkflowPlan, version *workflowdef.Version) workflowdef.ValidationResult { issues := make([]workflowdef.ValidationIssue, 0) if version == nil { return workflowdef.ValidationResult{ Valid: false, Issues: []workflowdef.ValidationIssue{{ Code: "WORKFLOW_VERSION_NOT_FOUND", Message: "工作流版本不存在或未发布", }}, } } if version.Status != workflowdef.StatusPublished { issues = append(issues, workflowdef.ValidationIssue{ Code: "WORKFLOW_NOT_PUBLISHED", Message: "只能绑定已发布的工作流版本", }) } if !workflowdef.IsOfficialWorkflowKey(plan.WorkflowDefinitionKey) { issues = append(issues, workflowdef.ValidationIssue{ Code: "WORKFLOW_NOT_SELECTABLE", Field: "workflow_definition_key", Message: "仅允许选择正式三工作流", }) } if plan.WorkflowDefinitionKey != version.WorkflowKey { issues = append(issues, workflowdef.ValidationIssue{ Code: "WORKFLOW_KEY_MISMATCH", Field: "workflow_definition_key", Message: fmt.Sprintf("规划 key %s 与版本 %s 不一致", plan.WorkflowDefinitionKey, version.WorkflowKey), }) } if plan.WorkflowVersion != version.Version { issues = append(issues, workflowdef.ValidationIssue{ Code: "WORKFLOW_VERSION_MISMATCH", Field: "workflow_version", Message: fmt.Sprintf("规划版本 %d 与已发布版本 %d 不一致", plan.WorkflowVersion, version.Version), }) } stageDefMap := make(map[string]workflowdef.StageDef, len(version.Definition.Stages)) for _, st := range version.Definition.Stages { stageDefMap[st.Key] = st } seenStage := map[string]bool{} for _, st := range plan.Stages { if seenStage[st.StageKey] { issues = append(issues, workflowdef.ValidationIssue{ Code: "DUPLICATE_STAGE", Field: st.StageKey, Message: "阶段 key 重复", }) continue } seenStage[st.StageKey] = true def, ok := stageDefMap[st.StageKey] if !ok { issues = append(issues, workflowdef.ValidationIssue{ Code: "UNKNOWN_STAGE", Field: st.StageKey, Message: "阶段不在工作流定义中", }) continue } if !def.Optional && !st.Enabled { issues = append(issues, workflowdef.ValidationIssue{ Code: "REQUIRED_STAGE_DISABLED", Field: st.StageKey, Message: "必需阶段不能关闭", }) } taskDefMap := make(map[string]workflowdef.DocTaskDef, len(def.DocTasks)) for _, dt := range def.DocTasks { taskDefMap[dt.Key] = dt } seenTask := map[string]bool{} for _, dt := range st.DocTasks { if seenTask[dt.TaskKey] { issues = append(issues, workflowdef.ValidationIssue{ Code: "DUPLICATE_DOC_TASK", Field: dt.TaskKey, Message: "文档任务 key 重复", }) continue } seenTask[dt.TaskKey] = true td, ok := taskDefMap[dt.TaskKey] if !ok { issues = append(issues, workflowdef.ValidationIssue{ Code: "UNKNOWN_DOC_TASK", Field: dt.TaskKey, Message: "文档任务不在阶段定义中", }) continue } if !dt.Enabled && !td.IsCreatorConfigurable() { issues = append(issues, workflowdef.ValidationIssue{ Code: "REQUIRED_DOC_DISABLED", Field: dt.TaskKey, Message: "该文档任务不可由创建者关闭", }) } if dt.Enabled { issues = append(issues, validateDuties(dt.Duties, dt.TaskKey)...) } } } enabledStages := 0 for _, st := range plan.Stages { if st.Enabled { enabledStages++ } } if enabledStages == 0 { issues = append(issues, workflowdef.ValidationIssue{ Code: "NO_ENABLED_STAGE", Message: "至少启用一个阶段", }) } return workflowdef.ValidationResult{Valid: len(issues) == 0, Issues: issues} } func validateActivationPlan(plan WorkflowPlan, version *workflowdef.Version) workflowdef.ValidationResult { base := validatePlan(plan, version) if !base.Valid { return base } if countEnabledDocTasks(plan) == 0 { base.Valid = false base.Issues = append(base.Issues, workflowdef.ValidationIssue{ Code: "EMPTY_WORKFLOW", Message: "至少启用一个文档任务才能激活工作流", }) } return base } func countEnabledDocTasks(plan WorkflowPlan) int { n := 0 for _, st := range plan.Stages { if !st.Enabled { continue } for _, dt := range st.DocTasks { if dt.Enabled { n++ } } } return n } func validateDuties(d DocDuty, taskKey string) []workflowdef.ValidationIssue { issues := make([]workflowdef.ValidationIssue, 0) if d.AuthorUserID <= 0 { issues = append(issues, workflowdef.ValidationIssue{ Code: "DUTY_AUTHOR_REQUIRED", Field: taskKey + ".author", Message: "启用的文档任务必须指定作者", }) } if d.ReviewerUserID <= 0 { issues = append(issues, workflowdef.ValidationIssue{ Code: "DUTY_REVIEWER_REQUIRED", Field: taskKey + ".reviewer", Message: "启用的文档任务必须指定审核人", }) } if d.ApproverUserID <= 0 { issues = append(issues, workflowdef.ValidationIssue{ Code: "DUTY_APPROVER_REQUIRED", Field: taskKey + ".approver", Message: "启用的文档任务必须指定批准人", }) } ids := []int64{d.AuthorUserID, d.ReviewerUserID, d.ApproverUserID} for i := 0; i < len(ids); i++ { if ids[i] <= 0 { continue } for j := i + 1; j < len(ids); j++ { if ids[j] > 0 && ids[i] == ids[j] { issues = append(issues, workflowdef.ValidationIssue{ Code: "SEPARATION_OF_DUTY_VIOLATION", Field: taskKey, Message: "作者、审核人、批准人必须分离", }) return issues } } } return issues } func buildDefaultPlan(version *workflowdef.Version) WorkflowPlan { plan := WorkflowPlan{ WorkflowDefinitionKey: version.WorkflowKey, WorkflowVersion: version.Version, WorkflowVersionID: version.ID, Stages: make([]StagePlan, 0, len(version.Definition.Stages)), } for _, st := range version.Definition.Stages { sp := StagePlan{ StageKey: st.Key, StageName: st.Name, Enabled: !st.Optional, DocTasks: make([]DocTaskPlan, 0, len(st.DocTasks)), } for _, dt := range st.DocTasks { sp.DocTasks = append(sp.DocTasks, DocTaskPlan{ TaskKey: dt.Key, TaskTitle: dt.Title, Enabled: dt.DefaultEnabled, }) } plan.Stages = append(plan.Stages, sp) } return plan } // applyDefinitionLabels 使用已发布定义覆盖展示名称,避免客户端把自定义文本写入冻结快照。 func applyDefinitionLabels(plan *WorkflowPlan, version *workflowdef.Version) { if plan == nil || version == nil { return } stageDefs := make(map[string]workflowdef.StageDef, len(version.Definition.Stages)) for _, stage := range version.Definition.Stages { stageDefs[stage.Key] = stage } for stageIdx := range plan.Stages { stage := &plan.Stages[stageIdx] stageDef, ok := stageDefs[stage.StageKey] if !ok { continue } stage.StageName = stageDef.Name taskDefs := make(map[string]workflowdef.DocTaskDef, len(stageDef.DocTasks)) for _, task := range stageDef.DocTasks { taskDefs[task.Key] = task } for taskIdx := range stage.DocTasks { task := &stage.DocTasks[taskIdx] if taskDef, exists := taskDefs[task.TaskKey]; exists { task.TaskTitle = taskDef.Title } } } } func previewSummary(plan WorkflowPlan, version *workflowdef.Version) PreviewSummary { sum := PreviewSummary{ WorkflowVersion: plan.WorkflowVersion, } if version != nil { sum.WorkflowName = version.WorkflowName if sum.WorkflowName == "" { sum.WorkflowName = version.WorkflowKey } } for _, st := range plan.Stages { if !st.Enabled { continue } sum.EnabledStages++ if sum.FirstStageKey == "" { sum.FirstStageKey = st.StageKey if version != nil { for _, sd := range version.Definition.Stages { if sd.Key == st.StageKey { sum.FirstStageName = sd.Name break } } } } for _, dt := range st.DocTasks { if !dt.Enabled { continue } sum.EnabledDocTasks++ if dt.Duties.AuthorUserID > 0 { sum.AssignedDuties++ } if dt.Duties.ReviewerUserID > 0 { sum.AssignedDuties++ } if dt.Duties.ApproverUserID > 0 { sum.AssignedDuties++ } } } return sum }