尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

# 随时会签——审批人临时拉人商量

# 随时会签——审批人临时拉人商量 随时会签审批人临时拉人商量非科班野生程序员深耕政务信息化20年这套自研Java Web框架支撑过省级社保、跨省医保结算等核心民生系统。上一篇系列文写了外挂会签——不用Activiti的multiInstance用独立子流程关联表实现会签。这篇是前传和补充从前后端完整链路看处长审批到一半临时想问纪检科意见这种BPMN画不出来的需求怎么落地。最后感谢豆包、智谱、OpenCode决策是我做的代码是我搓的文字是他们总结的。背景Activiti的multiInstance多实例可以实现并行审批。但有几个问题必须预先设计在BPMN里——流程定义时就确定好哪些环节需要会签参与者写死——运行时不能动态改变谁参与不能临时发起——审批人走着走着突然想拉人商量BPMN里没画这个节点就做不了政务场景的真实情况是处长审批到一半觉得这个事项需要征求纪检科意见。这个决定是运行时做的不是设计时能预见的。核心思路不用Activiti的multiInstance。在万能表单里放一个会签按钮点击后启动一个独立的Activiti流程实例keyhq在T_HJS_s关联表里记录父子流程关系会签发起人选择参与者每个参与者独立审批主流程等待所有人签完才能继续主流程 T_HJS_s 表 会签子流程 ┌──────────────┐ ┌───────────────┐ ┌────────────┐ │ 科长审批 │ │ proc_inst_id_ │ │ 参与者A │ │ 处长审批 →───┤──pre_proc │ pre_proc_inst │←───────── 子流程实例A │ │ _inst_id_ │ │ │ │ │ 点击会签 │ │ │←───────── 子流程实例B│ 参与者B │ │ 局长审批 │ └───────────────┘ └────────────┘ └──────────────┘ 不修改BPMN 关联父子 独立流程定义前端一个按钮触发onlyFrom.jsp 中的会签按钮functionhq(){vardsbrowise.byId(...).getBinding().getDataStore();varmatter_nameds.getRowSet().getRow(0).getItemValue(matter_name);if(!matter_name||matter_namenull){matter_name会签;}varURLwebRootDir/BusinessAction?BusinesshqActionstarthqkeyhquserid%userid%preinsid%insid%matter_namematter_name;vardialogDialogUtil.showDialog({title:发起会签,url:encodeURI(URL),height:document.body.clientHeight*0.8,width:document.body.clientWidth*0.8,isClose:true,resizable:false,dialogData:{},modal:true,isExpand:false,onComplete:function(){gethqinfo();// 会签对话框关闭后刷新会签状态}});}点击按钮 → 打开一个对话框 → 对话框里是会签发起人选择参与者的界面。会签发起人界面onlyFrom_hq_sq.jsp发起人看到的是主流程的表单数据只读一棵用户树选择会签参与者提交按钮// onlyFrom_hq_sq.jsp 的提交functionsave(){varselectedNodesbrowise.byId(persistTree).getSelectedNodes();if(selectedNodes.length1){MessageBox.alert({title:提示信息,message:请选择任务接收人!});return;}// 遍历选中的参与者为每个人启动一个会签任务for(vari0;iselectedNodes.length;i){varuseridselectedNodes[i].getData().psn_id;// ... 收集参与者列表}// 调用 completeS后端为每个参与者启动子流程}会签参与者界面onlyFrom_hq.jsp每个会签参与者在待办列表里看到一个会签任务。打开后看到的是主流程的表单数据只读通过/不通过选择意见填写框// onlyFrom_hq.jsp —— 加载主流程表单数据functionseach(){// 关键insid 是子流程实例IDpreinsid 是主流程实例IDvarURLwebRootDir/BusinessAction?BusinessflowActionbinddataGtaskid%taskid%insid%preinsid%formid%sqlId1%;vardata{url:URL,sync:false,load:function(dc1){vardsdc1.getSingleDataStore();varrowds.getRowSet().getRow(0);if(!row){rowds.getRowSet().addRow();}// 绑定数据到表单varfrom01browise.byId(...);from01.getBinding().bind(row);// 所有字段设为只读dojo.forEach(from01.getDescendants(),function(widget){try{widget.setDisabled(true);}catch(E){}});}};browise.Action.requestData(data,dc,true);}关键点参与者看的是主流程的表单数据通过preinsid反查但所有字段只读。参与者只需要选择通过/不通过、填写意见。提交时functionsave(){vardcnewbrowise.ds.DataCenter();dc.setParameter(taskid,%insid%);dc.setParameter(insid,%insid%);// 子流程实例IDdc.setParameter(preinsid,%preinsid%);// 主流程实例IDdc.setParameter(pass,browise.byId(PASS).getValue());dc.setParameter(cause,browise.byId(CAUSE).getValue());varURLwebRootDir/BusinessAction?BusinesshqActioncompleteP;browise.Action.requestData(data,dc,true);}后端独立流程 关联表starthq() —— 启动会签publicDataCenterstarthq(DataCentercenter,...)throwsException{Stringkeyrequest.getParameter(key);// hqStringuseridrequest.getParameter(userid);Stringpreinsidrequest.getParameter(preinsid);// 主流程实例IDStringmatter_namerequest.getParameter(matter_name);DBUtil.BeginTrans(null,false);// 1. 启动一个全新的Activiti流程实例HashMapString,StringtasksprocessService.start(key,userid);// 2. 查主流程的表单keyStringformkey1processService.getTaskFormInsId(preinsid);// 3. 在 T_HJS_s 关联表里保存父子关系DataCenterdcnewDataCenter();DataStoredsnewDataStore(ds);ds.setParameter(ins,T_HJS_s);ds.setParameter(up,T_HJS_s);Rowrowds.getRowset().add();row.setItemValue(proc_inst_id_,tasks.get(insid));// 会签子流程row.setItemValue(pre_proc_inst_id_,preinsid);// 主流程row.setItemValue(matter_name,matter_name会签);row.set_t(1);CommonSQLcontrolsqlcl(CommonSQLcontrol)BeanFactory.getBean(CommonSQL);sqlcl.CommonSaveofbyseach(dc,null,null);DBUtil.EndTrans();// 4. 跳转到会签发起人界面response.sendRedirect(request.getContextPath()/pages/workflow/hq/cform_hq_sq.jsp?idformkeytaskidtasks.get(id)insidtasks.get(insid)preinsidpreinsidid1formkey1);returnnull;}流程processService.start(hq, userid)启动一个全新的流程实例key是hq在T_HJS_s表里插入一行proc_inst_id_是子流程pre_proc_inst_id_是主流程跳转到发起人选择参与者的界面completeP() —— 会签参与者提交publicDataCentercompleteP(DataCentercenter,...)throwsException{DBUtil.BeginTrans(null,false);StringinsidString.valueOf(center.getParameters(insid));// 子流程StringpreinsidString.valueOf(center.getParameters(preinsid));// 主流程StringpassString.valueOf(center.getParameters(pass));StringcauseString.valueOf(center.getParameters(cause));// 1. 保存会签审批信息到 T_HJ_s 表DataStoredsnewDataStore(ds);ds.setParameter(ins,T_HJ_s);Rowrowds.getRowset().add();row.setItemValue(proc_inst_id_,insid);row.setItemValue(pre_proc_inst_id_,preinsid);row.setItemValue(user_id,userid);row.setItemValue(pass,pass);row.setItemValue(cause,cause);row.setItemValue(DEPT_ID,dept_id);row.set_t(1);sqlcl.CommonSaveofbyseach(dc,null,null);// 2. 推进子流程processService.passProcessNo(center,request,response);// 3. 检查子流程是否全部完成HashMapString,StringmapprocessService.currTask(insid);if(mapnull){// 子流程没有活动任务了 → 会签全部完成 → 通知发起人Stringuserid1processService.getFirstTaskAssignee(taskid);StringtitleprocessService.getInsTitle(insid);MyMsgdaonewMyMsg();dao.setPid(insid);dao.setUserid(userid1);dao.setMsg(事项title已办结!);insertmsg(dao);}DBUtil.EndTrans();returnnewdc;}会签参与者提交后审批结果通过/不通过/意见存到T_HJ_s表子流程推进一步passProcessNo检查这个子流程是否结束了——如果结束了给发起人发一条站内消息gethqinfo() —— 查询会签状态主流程的万能表单调这个方法看会签是否完成// flowControl.javahq daonewhq();dao.setSearchMethod(selectC);// 查剩余未完成数dao.setPreProcInstId(insid);Listhqlist(Listhq)dao.search();intcountInteger.parseInt(list.get(0).getTaskId());booleancanNexttrue;if(count0)canNextfalse;// 还有人没签完dao.setSearchMethod(selectAll);// 查所有会签记录list(Listhq)dao.search();for(inti0;ilist.size();i){// 填充部门、姓名、结果通过/不通过/未签批/放弃、意见、时间if(0.equals(list.get(i).getPass())){canNextfalse;// 有人不通过也不能继续}}if(canNext){newdc.setCode(0);// 可以继续走主流程}else{newdc.setCode(-1);// 会签未完成主流程等待}四种会签状态状态含义判断条件未签批还没处理enddate为空放弃放弃会签procInstId为 null通过签了同意pass 1不通过签了不同意pass 0前端会签未完成时禁用提交按钮// onlyFrom.jsp 中 gethqinfo() 的回调functiongethqinfo(){varURLwebRootDir/BusinessAction?BusinessflowActiongethqinfoinsid%insid%;vardata{url:URL,sync:false,load:function(dc1){vards_hqjijdc1.getSingleDataStore();if(ds_hqjij.getRecordCount()1){browise.byId(task_hqyij).hide();// 没有会签记录隐藏会签区域}else{browise.byId(task_hqyij).getBinding().setDataStore(ds_hqjij);browise.byId(task_hqyij).show();}if(dc1.getCode()0){browise.byId(tj001).setDisabled(false);// 会签完成可以提交}else{browise.byId(tj001).setDisabled(true);// 会签未完成禁用提交}}};browise.Action.requestData(data,dc,true);}审批人点了会签按钮后提交按钮立即变灰。只有所有人签完或有人放弃提交按钮才恢复。这是前端做的互锁后端的canNext判断是兜底。完整流程时序1. 处长审批中点击会签按钮 ↓ 2. 前端 hq() → 打开会签对话框 ↓ 3. 后端 starthq() → 启动独立流程实例 → 存 T_HJS_s 关联表 ↓ 4. 会签发起人界面 → 选择参与者用户树上勾选→ 提交 ↓ 5. 后端 completeS() → 为每个参与者分配子流程任务 ↓ 6. 每个参与者的待办列表出现会签任务 ↓ 7. 参与者打开会签任务 → 看到主流程表单只读→ 选通过/不通过 填意见 → 提交 ↓ 8. 后端 completeP() → 保存审批结果到 T_HJ_s → 推进子流程 ↓ 9. 所有参与者完成 → 子流程结束 → 通知发起人 ↓ 10. 主流程的 gethqinfo() 返回 code0 → 提交按钮恢复 → 处长继续审批为什么这样做为什么是独立流程而不是multiInstance不需要预定义——multiInstance要在BPMN里画节点我们的会签是运行时临时发起的参与者动态指定——发起人当场选人不是预定义的加签减签改数据库——在T_HJS_s表加减记录就行任何节点都能发起——不管主流程走到哪一步都能点会签为什么用关联表而不是流程变量Activiti的流程变量绑定在流程实例上。会签的子流程和主流程是不同的流程实例流程变量不共享。用数据库表关联最简单直接——pre_proc_inst_id_一关联查询方便不会丢失。决策原则会签是运行时的动作不是设计时的约束。BPMN定义的是流程的骨架——先A再B再C。但A到B之间需不需要拉人商量这个决定不应该在设计时就定死。放在运行时让审批人自己决定才是政务场景需要的灵活性。你的项目里会签是怎么做的是multiInstance还是其他方案有没有遇到过临时想拉人商量的需求欢迎评论区聊聊。系列导航回到总纲政务工作流实战二BPMN画不出来的七个设计决策上一篇“相对部门”政务OA最绕的审批路由下一篇流程串流程父子关联与数据回填作者许彰午| 非科班野生程序员深耕政务信息化20年标签#Java #Activiti #工作流 #会签 #外挂子流程 #政务信息化
返回列表