Files
DevFlow/src/stores/project/workflow.ts
绝尘 f574562a8d 修复: CR-18 decision 死逻辑(CR-11⑪引入)+CR-19 action 注释
CR-18 workflow.ts:67-69 selectType 三元两分支返回值相同(死逻辑)删,直接 decisions[0] ?? '';CR-19 project.ts listener action 当前未消费,保留为未来 delete 本地移除等差异化刷新预留+注释明确;CR-20 stopDataChangedListener try/catch 经核查与 workflow.ts:108 stopEventListener 写法一致(走查误判,不修)。cron 巡检捕获另一会话走查第2轮 CR-18/19/20,vue-tsc 0
2026-06-15 06:17:43 +08:00

128 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { workflowApi } from '../../api'
import { state, _eventUnlisten, setEventUnlisten } from './state'
/**
* 工作流 + 审批子 store(共享全局 state)。
*
* 越层 invoke(approve_human_approval / cancel_workflow_node) 已下沉到
* src/api/workflow.ts,本子 store 不再直接 invoke IPC。
*/
export function createWorkflowStore() {
async function runWorkflow(name: string, dag: unknown, config?: Record<string, unknown>) {
return await workflowApi.run(name, dag, config)
}
async function loadWorkflowExecutions() {
try {
state.workflowExecutions = await workflowApi.listExecutions()
} catch (e: any) {
state.error = e?.toString() ?? '加载工作流记录失败'
}
}
async function startEventListener() {
if (_eventUnlisten) return _eventUnlisten
const unlisten = await workflowApi.onEvent((payload) => {
try {
// 固时间戳(FR-C1):formattedEvents computed 重算时用事件发生时刻,非当前时刻
state.liveEvents.push({ ...payload, _ts: Date.now() })
// 限长(FR-R3):防 liveEvents 无限增长撑爆内存,保留最近 200 条
if (state.liveEvents.length > 200) {
state.liveEvents.splice(0, state.liveEvents.length - 200)
}
// 处理人工审批请求
// WorkflowEvent serde tag=type rename_all=snake_case → type='human_approval_request',字段扁平(无 data 包装)(B-03b-R7)
if (payload.event?.type === 'human_approval_request') {
state.pendingApproval = payload.event as unknown as typeof state.pendingApproval
}
} catch (e) {
console.error('处理工作流事件失败:', e, payload)
}
})
setEventUnlisten(unlisten)
return unlisten
}
function clearLiveEvents() {
state.liveEvents = []
}
/**
* 发送审批响应。
*
* 统一以 decisions 数组表达选择,按 pendingApproval.select_type 判定单/多选:
* - 单选(select_type='single' 或缺省):取 decisions[0] 作为后端 decision 单值;
* - 多选(select_type='multiple'):decisions 透传全量。
*
* CR-11⑪:签名保持 approveHumanApproval(decisions: string[], comment?)。
* F-260615-01: decisions/select_type 透传,与后端 IPC 签名对齐。
*/
async function approveHumanApproval(
decisions: string[],
comment?: string,
) {
if (!state.pendingApproval) return
const selectType = state.pendingApproval.select_type ?? 'single'
// CR-260615-18:删死逻辑三元(原 selectType 多/单分支返回值相同)
// 单选取首项(后端 decision 单值);多选 decision 取首项占位(后端按 decisions 走)
const decision = decisions[0] ?? ''
try {
// R-PD-5: 透传 options 闭环后端校验(options 非空时 decision 必须 ∈ options,否则 IPC 报错)。
// options 来自已收到的 HumanApprovalRequest 事件 payload(IPC 无法访问节点 config)。
// B-260615-34:snake_case 由 api 层对齐后端 workflow.rs:211
await workflowApi.approveHumanApproval({
execution_id: state.pendingApproval.execution_id,
node_id: state.pendingApproval.node_id,
decision,
comment,
options: state.pendingApproval.options ?? [],
decisions,
select_type: selectType,
})
// 清除待审批状态
state.pendingApproval = null
} catch (error) {
console.error('审批失败:', error)
}
}
/** 取消当前待审批节点(调 cancel_workflow_node IPC 置节点 Cancelled */
async function cancelHumanApproval() {
if (!state.pendingApproval) return
try {
await workflowApi.cancelHumanApproval({
execution_id: state.pendingApproval.execution_id,
node_id: state.pendingApproval.node_id,
})
// 清除待审批状态
state.pendingApproval = null
} catch (error) {
console.error('取消审批失败:', error)
}
}
function stopEventListener() {
if (_eventUnlisten) {
try {
_eventUnlisten()
} catch (e) {
console.error('停止事件监听失败:', e)
}
setEventUnlisten(null)
}
}
return {
runWorkflow,
loadWorkflowExecutions,
startEventListener,
stopEventListener,
clearLiveEvents,
approveHumanApproval,
cancelHumanApproval,
}
}