主办先挂起,收齐意见再拍板:jeeflow 的动态会签

一、主办为什么先把自己挂起

一个事项走到主办节点,主办看完材料,发现自己一个人拍不了板:需要财务看预算、法务看条款,可能还要问人事。

名单不是流程设计时能定死的。得到这一刻,主办才知道该拉谁;少拉了财务,还得再拉一轮;每条意见都要能独立出证,不能被下一轮覆盖。

这不是会签节点。会签池在进节点之前就定死了,谁同意、谁反对、几个人算过,都是静态的。也不是后加签。后加签是主办已经办结、把同一手交给下一位;动态会签是主办还没拍板,先把意见收进来。

它的名字叫动态会签(征询)。这篇只回答三件事:主办为什么挂起;收齐前为什么连驳回都按不了;撤回和多轮怎么处理。

二、先和后加签分开

后加签和动态会签共用同一个协议键 tf_addSignOperators,但行为由节点属性分流,解析期互斥:

维度 后加签 动态会签
主办任务 办结,原人退出 挂起,不办结
新任务 同节点新一轮接力 按人建成员行收意见
节点推进 接力链没人再点时流出 收齐意见、主办再办结才流出
拍板权 随 token 前移 始终在主办
成员角色 后继办理人 出意见的人,无表决
什么时候用 我办完,把这一手交出去 我不能独自拍板,先收意见

一句话:后加签是主办把这一手交出去;动态会签是主办把意见收进来,自己拍板。

三、发起:主办不办结,按人建成员行

主办在同意时带上成员名单,必要时再带一个发起原因:

text 复制代码
POST /wf/processTask/execute
{
  "operator": "<当前用户 id>",
  "processTaskId": "<主办任务 id>",
  "submitType": 1,
  "tf_approvalComment": "先收集财务、法务意见",
  "tf_addSignOperators": ["<吴昊 id>", "<赵敏 id>"],
  "tf_addSignReason": "需要财务、法务专业意见"
}

引擎做完这件事,数据上是四件:

  1. 主办行仍然是 DOING(10),不办结;
  2. 按人建成员行:task_parent_id 指向主办行,节点同名同节点,参与者就是那名成员;
  3. 每条成员行盖轮次戳 addSignRound_{节点名}(这里就是节点 id,例子里的 consult)和发起原因 tf_addSignReason;
  4. 主办行账本追加一条 tf_addSignHistory:{round:1, fromActor:<主办 id>, toActors:[<成员 id>...], time, operator}。

响应里会带 data.dynamicCountersign={round,total,submitted};集成层可以据此提示"已发起会签"。这次真跑的 vben5 演示前端提交后仍提示"提交成功",提示文案归集成层。

成员行是普通任务行,不是会签池;一人一行,一条意见,不新增表。意见的物理隔离由"一行一条"保证:每个人的意见落在自己那一行的变量里,finish 一次写死,没有原地改意见的通道。

成员行可以转办、委托、抄送,但不能带拉人参数------成员不能再拉人,只有主办能把人拉进本轮征询。多选时一次建多行,轮次上限 addSignMaxRounds 由后加签和动态会签共用。

四、收齐前,连驳回都按不了

主办再提交时,引擎先看主办行下面还有没有 DOING 的成员行:

  • 有:报错,零写入。对外文案是 节点[{节点名}]动态会签意见未收齐(已提交 {m}/{n}),不能办理;
  • 没有:放行,主办按普通办理档办结,节点流出。

关键点是:闸门挡全部 submitType,包括驳回。未收齐时,同意(1)、不同意(2)、退回上一步(3)、退回发起人(6)、跳转(4)全部不放行;唯一出口是撤回动态会签。

这里的"唯一出口"指办结/离开节点 。审批意见框、附件、发起控件还在------发起或补人不是办结动作,走带人 execute;但想把主办这一手办结、让节点往下走,只能先撤回。未收齐时主办自己的拒绝、退回也会被同一道闸门挡住,走不到清场。

前端把这件事做成"整排置灰":主办视角的五个办理动作全灰,只剩一条警告、一个进度和"撤回动态会签";审批意见框、附件、发起控件还在,它们不是办结动作。进度是 已提交 m/n;1/2 时五个动作按钮全灰,只剩撤回。置灰是体验,服务端闸门是权威------绕过前端直接调接口,同样过不去。

收齐判据只有一条:主办行下没有 DOING 成员行。废弃行不进分母;撤回清掉未答行之后,闸门自然恢复。

流程图里也不会把主办和成员画成三个节点:还是同一个「专业意见征询」节点,节点里竖排主办和成员。进行时主办是蓝点、已交的成员绿勾、未交的灰点;收齐后节点变绿,终审才变当前节点。

五、撤回与多轮:只废未答行,已答意见保留

撤回是置灰态的唯一恢复出口,不是"暂停"。

  • 废弃本主办行下全部未答成员行;
  • 已提交的意见行原样保留,审计凭证不动;
  • 账本追加撤回条目;
  • 闸门自然恢复,轮次不消耗。

撤回是独立 action,不在 submitType 枚举里:

text 复制代码
POST /wf/processTask/revokeDynamicCountersign
{
  "operator": "<主办 id>",
  "processTaskId": "<主办任务 id>",
  "reason": "成员名单有变化"
}

主办行的 processTask/detail 里,ext.dynamicCountersign 给出 {active,round,total,submitted,actors};前端的进度和置灰就是读它。

我这次真跑撤回的一单:主办拉了吴昊、赵敏,吴昊交了一条意见,赵敏还没交;撤回后清场的是赵敏,吴昊那条已交意见保留。库里读回来:吴昊行 state=20(FINISHED,已交意见还在),赵敏行 state=99(废弃,未答被清场),主办行仍 10(DOING)。这个结果和"只废未答行"的契约一致。

撤回后再拉一轮,不是再点一次撤回,而是主办再提一次带 tf_addSignOperators 的 execute:round+1,闸门重新关闭,跨轮意见互不覆盖。每一轮的发起原因、成员、意见都留在各自的账本/任务行里,读侧按轮分组。

未收齐时,主办自己的拒绝、退回会被同一道闸门挡住,走不到清场;真正可达的是实例撤回(以及流程定义允许的其他离开本节点路径)。这些路径离开本节点时,pending 成员行一并废弃,不留孤儿待办。

图里成员意见行显示姓名;上面的加签账本列存的是用户 id,两个要分开看。征询原因、轮次、每条意见都在主办那一行下面按轮展开。

六、怎么配,前端怎么接

节点上和后加签有关的两个键仍然只有两个:

键 说明
dynamicCountersign bool,不写键=false;开启后该节点主办可在运行期拉人收意见。
addSignMaxRounds int ≥ 0,不写键=0=不限;和后加签共用。

解析期会拦下四类:

  • 非法值:解析期报错,不静默缺省;
  • 会签节点配 dynamicCountersign:报错,会签池推进和征询挂起是两种语义;
  • 首任务节点(申请节点)配 dynamicCountersign:报错,申请节点走发起/重提语义;
  • 后加签和动态会签双开:报错,两者共用 tf_addSignOperators,双开等于行为歧义。

设计器把它做成三选一的"办理协同":无、后加签(接力)、动态会签(征询)。选动态会签写 dynamicCountersign、删 allowAddSign;选无则把这一组键一起清掉。

这次真跑的 vben5 演示前端里,主办看到的是"发起动态会签"和"会签人员";公开 npm 包的 jeeflow-ui 这一版仍写"加签给下一位",文案归集成层。发起动作仍然是同意(1)的附属效果,不新增 submitType。

发起原因 tf_addSignReason 是协议选填;演示前端这一版没有单独放原因输入框,原因由集成层/接口传入,审批记录会显示出来。这是前端形态,不是引擎缺字段。

前端还有两件事:

  • 未收齐时,执行弹窗整排动作置灰,只留进度和"撤回动态会签";
  • 审批记录按轮分组:主办那一行下面列征询原因、成员意见、轮次;成员意见一行一条,不聚合。

服务端闸门和前端置灰是双保险:前端负责让人看懂"为什么点不动",服务端负责让绕过前端的人也过不去。

七、边界:v1 不做的

  • 成员不递归拉人:只有主办能把人拉进本轮征询;成员行携带拉人参数会报错。
  • 没有表决语义:成员提交类型只作记录,无否决、比例、一票否决;投票应该用会签节点,不是这里。
  • 只做并行征询:不做串行逐个征询。
  • 表单快照另案:本轮账本已有 round、reason、成员和意见,快照存储形态还没拍。
  • 多进程锁不在承诺内:单进程部署按实例串行;多进程部署的跨进程锁仍要仓储侧提供,本特性不宣称已支持。
  • 离开节点时的清场:实例撤回/上游退回等真正离开本节点的路径会把 pending 成员行一并废弃,不留孤儿待办;未收齐时主办自己的拒绝、退回被闸门挡住,走不到清场。

八、什么时候选它

  • 选动态会签:主办不能独自拍板,需要先收意见、自己再决定;人员运行期才定。
  • 选后加签:主办已经表过态,只需要把同一手交给下一位专业复核。
  • 选会签节点:所有人在流出前都要表态,或者需要比例、一票否决------这是静态会签的语义,不是动态会签。
  • 选转办:我不办,换人办。
  • 选委托:建单时就把代理人并进名册,不摘原人。

一句话:动态会签是"收意见",不是"投票";拍板权始终在主办手里。

八种语言的引擎按同一份契约实现,参考实现是 Java。我这次真跑的是 boot3 演示栈(Java 参考实现 + vben5 前端),实例、任务行、账本都在上面;其余七种语言这次只对照了规范和共享测试流程,没有逐个做 UI 走查。

落地清单

  1. 确认你的引擎代次带 dynamicCountersign 和 addSignMaxRounds,设计器有"办理协同"三选一。
  2. 只在普通审批节点上开;会签节点、申请节点解析期就会拒。
  3. 需要限制轮次就设 addSignMaxRounds;0 是不限。
  4. 前端未收齐时置灰全部办理动作,只留"撤回动态会签";服务端闸门不要省。
  5. 上线前跑一遍自己的样本:主办拉两人 → 一人交意见看 1/2 置灰 → 撤回看已答意见是否保留 → 再拉一轮 → 两人交齐 → 主办拍板,看节点是否在收齐后才流出。

参考资料

jeeflow 文档站 规范 04「引擎操作」------动态会签(征询模式) jeeflow-doc.mldong.com/spec/04-eng...

jeeflow 文档站 规范 02「流程定义」------办理协同开关(§4.3 allowAddSign / §4.4 dynamicCountersign) jeeflow-doc.mldong.com/spec/02-flo...

jeeflow 文档站 规范 06「统一门面」------tf_addSignOperators / tf_addSignReason / 投影 jeeflow-doc.mldong.com/spec/06-fac...

jeeflow 文档站 更新日志(八引擎各自代次) jeeflow-doc.mldong.com/changelog/

jeeflow Java 参考实现 github.com/mldong/jeef...

八语言演示站(在线可试) jeeflow-demo.mldong.com/

相关推荐
CQU_JIAKE8 小时前
11.7[高体】
java·后端·spring
Csvn9 小时前
认识 Nginx
后端·nginx
红尘散仙9 小时前
谁说终端只能敲键盘?我用 Rust 给小说阅读器加上了鼠标
前端·后端·rust
特立独行的猫A9 小时前
我给女儿写了个打字练习:一边敲键盘,一边把小学英语单词记牢
java·后端·python
陈随易10 小时前
C++26 草案被否,二十八年来头一回
前端·后端·程序员
金銀銅鐵11 小时前
同一个 Hello World,Kotlin 和 Java 在字节码层面差在哪
jvm·后端·kotlin
IT_陈寒14 小时前
Redis内存暴涨,没想到是AOF重写捣的鬼
前端·人工智能·后端
ikoala14 小时前
让 Codex 自己查账:一个省 token 的小技巧
前端·后端·ai编程
wo_jack17 小时前
C++ 入门笔记(十六):第16课 vector 模拟实现十问十答 —— 从三指针到迭代器失效
c++·后端·面试·stl·vector