forked from ChaiHao/BPOS-MVP
三面拆分⑫:网关去节点化 —— 分支即条件出边,汇合即多入边异或
模型(与 Mastra .branch 同构,条件挂在步与步之间): - routing.selectEdges:多出边一律异或,不分节点种类 —— 条件命中优先(按声明序), 无条件边作默认兜底,恰取一条;单出边直通保留(历史并行网关的遗留通道) - closure:含网关即 gateway-deprecated(历史版本仅可读、不可激活); 「隐式合并」禁令删除(多入边=异或汇合,上游互斥无歧义); 新规:多出边节点的无条件边至多一条,超出报 multi-default-edge - contracts:CLOSURE_VIOLATION_KINDS 去 gateway-unbalanced/join-ambiguous-source, 进 multi-default-edge/gateway-deprecated(下游零引用,api-report 随行) 种子(按用户要求:只留一条流一个版本,图/节点/边全部对账): - MVP_PACKAGE 重写:10 节点 14 边零网关 —— G1/G2 展开为 A1 的三条出边 (选型/直接寻源/默认直接定价)+ A2 的两条,G2J/G3 整颗删除, A5 审批点的两个去向就是自己的两条 verdict 条件出边(驳回边与 reject 声明字字对齐) - 手绘图:顶车道 y30 补画 A1→A3「直接寻源」—— 画布保存只认画出来的边, 不画这条语义边会在首次存盘被静默丢掉(新增图边对账测试卡死这类漏画); 「供应商底价表」标注钉进 y190 车道空档,不再被卡点压住 - 开发库外科手术:清运行数据 + 删 v1–v4,重种为唯一生效版 v1(闭合过闸) UI 与文案同步: - 元素盘/追加盘不再有网关件(画布仍认得历史图);图例删「判断/并行网关」「菱形=网关」 - 新建流程补激活一跳(保存不生效模型下,不激活「建出来就能起单」就是谎言) - 总览四种编排要素、「画图不检查,激活才查完整」贯穿 blueprint/memory/process 文案 测试:closure 43 通过(gateway-deprecated + multi-default-edge 两用例), seed-mvp-flow 8 通过(含图边对账),端到端八例真库全绿(网关从不建实例,断言原样存活); 五闸 typecheck/lint/depcruise/api:check/test 全过(退出码验)。 债:M5-4 AND 并行待「并行组」声明(Mastra .parallel);M5-5 网关运行时兼容三处, 含网关在途流清零后随 schema 一并摘除。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -32,8 +32,8 @@ export function BlueprintHome({ nav }: HomeProps) {
|
||||
const packages = pkgs.data?.packages ?? [];
|
||||
|
||||
/*
|
||||
* 没有「草稿」也没有「未闭合」可数了 —— 严格检查 + 保存即生效:不闭合的包**存不进来**。
|
||||
* 硬摆这两个计数就是摆两个恒为 0 的假指标。改数真能说话的:每条流有几版、最新是第几版。
|
||||
* 「未闭合」不值得单摆一个计数 —— 画图不检查,半成品版本本来就该存在;
|
||||
* 激活才查完整,异常在包详情的看板里逐条看。这里改数真能说话的:每条流有几版。
|
||||
*/
|
||||
const flowCount = new Set(packages.map((p) => p.id)).size;
|
||||
const activities = acts.data?.activities ?? [];
|
||||
@@ -73,7 +73,7 @@ export function BlueprintHome({ nav }: HomeProps) {
|
||||
</span>
|
||||
<span className="g-chain-v">{flowCount}</span>
|
||||
<span className="g-chain-h">
|
||||
企业以哪些流转运转 —— 共 {packages.length} 个版本,保存即生效
|
||||
企业以哪些流转运转 —— 共 {packages.length} 个版本,恰一个生效版
|
||||
</span>
|
||||
</button>
|
||||
<span className="g-chain-arr">
|
||||
|
||||
@@ -66,9 +66,9 @@ export function CockpitHome({ nav }: HomeProps) {
|
||||
sub="失败走向未消化,待人工处置"
|
||||
/>
|
||||
{/*
|
||||
* 「未闭合草稿」这张卡撤了 —— 严格检查 + 保存即生效之后,不闭合的包**存不进来**,
|
||||
* 这个数结构上恒为 0。摆一个永远是 0 的卡位就是在占地方装样子。
|
||||
* 天然的第三张是「死路」(网关一条边都判不中而卡住的流,已有 route-dead-end 事件),
|
||||
* 「未闭合草稿」这张卡撤了 —— 未闭合是**定义面**的常态(画图不检查,激活才查),
|
||||
* 归流程包详情的异常看板管,不是运营指标。
|
||||
* 天然的第三张是「死路」(条件出边一条都判不中而卡住的流,已有 route-dead-end 事件),
|
||||
* 但那需要一条按事件类型聚合的查询路由 —— 没有之前不摆(同死信卡的规矩)。
|
||||
*/}
|
||||
<Kpi ico="activity" tone="brand" label="在途任务" value={running} sub="尚未落终态的任务" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* feature `gov-process` 唯一出口(编码规范 §9.1)。
|
||||
* 流程包编排画布(diagram-js 画布件平移):连线即映射、画图即校验、不闭合不得发布
|
||||
* 流程包编排画布(diagram-js 画布件平移):连线即映射;画图不检查,激活才查完整
|
||||
*/
|
||||
export { WorkflowsHome, PkgPage } from './internal/panel.tsx';
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 图下是节点属性,再往下才是声明式的契约细节(调用接口 · 边映射 · 触发器)。
|
||||
*
|
||||
* 版本是**运行时开关**,不是发布流程:
|
||||
* · 保存 = 严格检查 + 存为新版 + 即刻生效(不闭合存不进来)
|
||||
* · 画图不检查:保存 = 存为新版,**不生效**;激活才跑闭合校验(异常清零方可)
|
||||
* · 一条流恰一个生效版本;可把生效切回旧版,也可全部停用让整条流下线
|
||||
* · 在途工作流按启动时钉住的版本跑完 —— 启停对它们零影响
|
||||
*/
|
||||
@@ -95,13 +95,15 @@ function OverviewHome() {
|
||||
label="边闭合率"
|
||||
value={totalEdges === 0 ? 100 : Math.round((closedEdges / totalEdges) * 100)}
|
||||
unit="%"
|
||||
sub="不闭合存不进来"
|
||||
sub="不闭合不得激活"
|
||||
/>
|
||||
<Kpi ico="boxes" label="编排要素" value="五种" sub="活动 · 审批 · 网关 · 触发器 · 流程包" />
|
||||
{/* 网关已废除:分支 = 条件出边,不再是一种要素 */}
|
||||
<Kpi ico="boxes" label="编排要素" value="四种" sub="活动 · 审批 · 触发器 · 流程包" />
|
||||
</div>
|
||||
<Note>
|
||||
保存即生效:严格检查过了才存得进来,存下来即刻对<b>新起的</b>工作流生效;
|
||||
在途的按启动时版本跑完。要回退,去流程包里把生效切回旧版本 —— 一条流一次只有一个生效版本。
|
||||
画图不检查,激活才查完整:随手画的都存得下(存出来不生效),异常清零后激活,
|
||||
即刻对<b>新起的</b>工作流生效;在途的按启动时版本跑完。要回退,把生效切回旧版本 ——
|
||||
一条流一次只有一个生效版本。
|
||||
</Note>
|
||||
</QueryGate>
|
||||
);
|
||||
@@ -111,7 +113,7 @@ function OverviewHome() {
|
||||
|
||||
/**
|
||||
* 新建的流程 = 一张**最小可生效**的图:开始 → 结束。
|
||||
* 空图也要过闭合校验(不闭合存不进来),这张最小图天然闭合 —— 建出来就能起单
|
||||
* 激活要过闭合校验,这张最小图天然闭合,建完即激活 —— 建出来就能起单
|
||||
* (起了直接走完,没业务意义,但机制是通的);进详情画上活动再存,即成下一版。
|
||||
*/
|
||||
function minimalFlow(id: string, name: string): ProcessPackage {
|
||||
@@ -146,10 +148,20 @@ function FlowShelf({ nav }: { nav: Nav }) {
|
||||
const idTaken = flows.some((f) => f.id === nfId);
|
||||
|
||||
const create = useMutation({
|
||||
mutationFn: () =>
|
||||
call<{ pkg: ProcessPackage; report: ClosureReport }>(saveProcessRoute, {
|
||||
/*
|
||||
* 保存不生效(存出来一律 retired)—— 新建要兑现「建出来就能起单」,
|
||||
* 得跟一记激活。最小图天然闭合,激活闸必过;真有 412 就让它抛,onError 如实报。
|
||||
*/
|
||||
mutationFn: async () => {
|
||||
const out = await call<{ pkg: ProcessPackage; report: ClosureReport }>(saveProcessRoute, {
|
||||
body: { pkg: minimalFlow(nfId, nfName.trim()) },
|
||||
}),
|
||||
});
|
||||
await call<{ packages: ProcessPackage[] }>(setProcessStateRoute, {
|
||||
params: { id: out.pkg.id, version: String(out.pkg.version) },
|
||||
body: { state: 'active' },
|
||||
});
|
||||
return out;
|
||||
},
|
||||
onSuccess: (out) => {
|
||||
void queryClient.invalidateQueries({ queryKey: ['gov', 'process'] });
|
||||
setCreating(false);
|
||||
@@ -441,7 +453,7 @@ function FlowLab({
|
||||
});
|
||||
|
||||
/**
|
||||
* 新建版本 = **以当前看的版本为底克隆一版**(服务端定版号并即刻生效)。
|
||||
* 新建版本 = **以当前看的版本为底克隆一版**(服务端定版号,**不生效**)。
|
||||
* 与「激活」分工明确:激活只是把生效指针切过去,不产生新版本;
|
||||
* 新建版本才长一版 —— 把旧定义复活成最新版、或在现状上另起一版接着画,都走这儿。
|
||||
*/
|
||||
@@ -682,7 +694,7 @@ function FlowLab({
|
||||
<FlowFigureBlock
|
||||
key={`${pkg.id}@${pkg.version}`}
|
||||
title={`${pkg.name} v${pkg.version}`}
|
||||
oneLiner={`蓝 = 标准活动 · 绿 = 案例活动 · 琥珀 = 审批点 · 菱形 = 网关 · 红 = 卡点`}
|
||||
oneLiner={`蓝 = 标准活动 · 绿 = 案例活动 · 琥珀 = 审批点 · 红 = 卡点`}
|
||||
graph={graph}
|
||||
drilledIds={nodes.filter((n) => n.kind === 'activity').map((n) => n.id)}
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user