package projectinstance import ( "context" "chat-agent-system/internal/workflowdef" ) // ActiveWorkflowScope 返回项目当前激活工作流的键与文档任务键集合。 // // 语义(供意图填槽与文档正文读取收窄作用域): // - 从未激活(无 live 实例)→ workflowKey 为空、taskKeys 为 nil → 调用方不得过滤(legacy / planning) // - live 系实例 → workflowKey 取自 projects.active_workflow_key(回落实例快照定义键), // taskKeys 收集全部启用文档任务,并补齐旧键/新键双向别名 // - completed / terminated 不属于激活:active_workflow_key 已在终止时清空,视为未激活 // - 存储错误原样上抛(fail-closed) func (s *Service) ActiveWorkflowScope(ctx context.Context, projectID int64) (workflowKey string, taskKeys map[string]bool, err error) { if s == nil || s.store == nil || projectID <= 0 { return "", nil, nil } _, activeKey, _, _, err := s.store.GetProjectMeta(ctx, projectID) if err != nil { return "", nil, err } if activeKey == "" { // 未激活:不过滤,保持现状行为。 return "", nil, nil } live, err := s.store.GetLiveInstance(ctx, projectID) if err != nil { return "", nil, err } if live == nil { // 有激活键但无 live 快照:仅返回键供话术,任务集合交由调用方按空处理(不过滤)。 return activeKey, nil, nil } if live.ConfigSnapshot.WorkflowDefinitionKey != "" { workflowKey = live.ConfigSnapshot.WorkflowDefinitionKey } else { workflowKey = activeKey } taskKeys = make(map[string]bool) for _, st := range live.ConfigSnapshot.Stages { if !st.Enabled { continue } for _, dt := range st.DocTasks { if !dt.Enabled || dt.TaskKey == "" { continue } taskKeys[dt.TaskKey] = true // 补齐旧键/新键双向别名,冻结实例与发布清单互指。 if canonical := workflowdef.CanonicalTaskKey(dt.TaskKey); canonical != dt.TaskKey { taskKeys[canonical] = true } if legacy := workflowdef.ImplementationTaskKey(dt.TaskKey); legacy != dt.TaskKey { taskKeys[legacy] = true } } } return workflowKey, taskKeys, nil } // TaskKeyInScope 判断任务键是否落在激活工作流任务集合内。 // taskKeys 为空(未激活 / 无快照)时一律放行,保持现状。 func TaskKeyInScope(taskKeys map[string]bool, taskKey string) bool { if len(taskKeys) == 0 { return true } return mapHasTaskKey(taskKeys, taskKey) }