概述

流程定义配置完成后,需要进行流程的启动,启动完成后,平台会生产流程实例的数据。平台支持:

  1. 流程启动
  2. 流程作废
  3. 流程恢复
  4. 流程明细
  5. 流程干预

流程实例关联查询,如:

  1. 我的流程草稿
  2. 我的流程
  3. 我的已办

表设计

为了更好基于流程实例与业务作关联,我们增加了bpm_inst表,并且进行了以下扩展的表设计:

是否主键 字段名 字段描述 数据类型 长度 可空 备注
INST_ID_ 实例ID VARCHAR(64) 64    
  DEF_ID_ 流程定义ID VARCHAR(64) 64    
  ACT_INST_ID_ Activiti实例ID VARCHAR(64) 64 Activiti实例ID
  ACT_DEF_ID_ Activiti定义ID VARCHAR(64) 64   Activiti定义ID
  SOL_ID_ 解决方案ID_ VARCHAR(64) 64 解决方案ID_
  INST_NO_ 流程实例单号 VARCHAR(50) 50 流程实例工单号
  IS_USE_BMODEL_ 单独使用业务模型 VARCHAR(20) 20 单独使用业务模型,YES=表示不带任何表单视图
  SUBJECT_ 标题 VARCHAR(255) 255 标题
  STATUS_ 运行状态 VARCHAR(20) 20 运行状态
  VERSION_ 版本 INT   版本
  SOL_KEY_ 业务解决方案KEY VARCHAR(64) 64 业务解决方案KEY
  BUS_KEY_ 业务键ID VARCHAR(64) 64 业务键ID
  CHECK_FILE_ID_ 审批正文依据ID VARCHAR(64) 64 审批正文依据ID
  FORM_INST_ID_ 业务表单数据 VARCHAR(64) 64 业务表单数据ID
  IS_TEST_ 是否为测试 VARCHAR(20) 20 是否为测试
  ERRORS_ 出错 TEXT    
  END_TIME_ 结束时间 DATETIME   结束时间
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用机构ID
  CREATE_BY_ 公共 - 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 公共 - 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 公共 - 更新时间 DATETIME   更新时间
  DATA_SAVE_MODE_ 数据保存模式 VARCHAR(10) 10 数据保存模式(all,json,db)
  SUPPORT_MOBILE_ 支持手机端 INT   支持手机端
  BO_DEF_ID_ BO定义ID VARCHAR(20) 20 BO定义ID
  BILL_NO_ 单号 VARCHAR(255) 255 单号
  START_DEP_ID_ 发起部门ID VARCHAR(64) 64 发起部门ID
  START_DEP_FULL_ 发起部门全名 VARCHAR(300) 300 发起部门全名
  IS_LIVE_ 是否复活 VARCHAR(64) 64 是否复活
  LIVE_INST_ID_ 复活的流程实例 VARCHAR(64) 64 复活的流程实例

说明:

  • Activiti的流程定义Id(actdef_id)
  • Activiti的流程实例Id(actinst_id)
  • 与业务的关联主键 (buskey)
  • 单号 用来记录不同的审批事项的唯一标识
  • 状态 记录流程是在运行还是结束

类设计

说明

  1. BpmInstController 流程实例的对外数据交互控制类
  2. BpmInstServiceImpl 流程实例的业务逻辑处理类
  3. SystemClient 系统业务管理模块的客户端,基于feign的调用封装,用来获取系统的分类、数据字典数据等。
  4. UserClient 用户模块的客户端,基于feign的调用封装,用来获取组织架构的数据,如通过用户Id获取用户数据等
  5. TaskExecutorService 用来获取任务节点的执行人员
  6. BpmInstLogServiceImpl 流程实例日志服务类
  7. RuntimeService Activiti的实例运行服务类
  8. BpmDefService 扩展的流程定义的服务类
  9. BpmTaskMapper 扩展任务数据映射操作类
  10. FormDataService 单据数据服务类,基于feign的调用处理
  11. GroovyEngine Groove脚本引擎
  12. BpmCheckHistoryServiceImpl 流程审批历史服务类
  13. BpmTaskSkipService 流程任务跳过服务类
  14. ProcessHandlerExecutor 流程处理器执行类
  15. ProcessScriptEngine 带有流程实例上下文数据的脚本操作引擎
  16. BpmRuPathServiceImpl 流程实例的执行路径的服务类
  17. BpmInstDataServiceImpl 流程实例数据服务类
  18. BpmTaskUserMapper 流程任务用户数据映射类
  19. BpmSignDataServiceImpl 会签数据服务类
  20. BpmTaskService 扩展流程数据业务逻辑处理类
  21. ActRepService Activiti库的操作服务类

流程启动

流程启动是流程业务与业务单据数据关联的一个过程,通过流程的任务审批,驱动不同的流程业务往下流转。

流程启动的代码实现请参考BpmInstServiceImpl以下方法代码:

  1. @GlobalTransactional
  2. public BpmInst doStartProcess(ProcessStartCmd cmd, BpmDef bpmDef) throws Exception {
  3. //初始流程实例数据。
  4. BpmInst bpmInst = initBpmInst(cmd,bpmDef);
  5. //获取流程组配置
  6. ProcessConfig processConfig = bpmDefService.getProcessConfig(bpmDef.getActDefId());
  7. //处理表单数据
  8. handBusinessData(bpmInst,cmd,processConfig);
  9. //处理流程实例数据保存。
  10. handBpmInst( bpmInst, cmd, bpmDef, processConfig);
  11. //处理流程变量。
  12. handVars(cmd, bpmDef, bpmInst,processConfig);
  13. //在启动时执行前置处理器
  14. processHandlerExecutor.handStartBeforeHandler(processConfig);
  15. //调用Activiti引擎启动流程
  16. ProcessInstance processInstance = runtimeService.startProcessInstanceById(bpmDef.getActDefId(),bpmInst.getBusKey(), cmd.getVars());
  17. bpmInst.setActInstId(processInstance.getId());
  18. //更新Act流程实例至流程变量中
  19. runtimeService.setVariable(processInstance.getId(),BpmInstVars.ACT_INST_ID.getKey(),processInstance.getId());
  20. //在流程结束时处理后置处理器。
  21. processHandlerExecutor.handStartAfterHandler(processConfig,bpmInst);
  22. //保存流程实例
  23. saveBpmInst(cmd,bpmInst,true);
  24. //跳过第一个节点。
  25. handFirstJump(cmd,processConfig,bpmInst);
  26. //任务跳过
  27. IExecutionCmd nextCmd=ProcessHandleUtil.getProcessCmd();
  28. taskSkipService.handSkipTask(nextCmd);
  29. return bpmInst;
  30. }

说明

  • 该方法依赖于Seata的全局事务,以保证流程启时与外部表单的数据保存时的一致性。
  • 在页面提交的单据数据需要遵行单据的数据结构格式要求
  • 调用Activiti的流程启动接口,传入流程定义Id,业务键,流程变量,并且返回activiti的流程引擎Id
  • 根据流程级的配置,是否跳过第一个任务节点等
  • 根据流程配置的前置与后置处理器,相应触发处理器的业务逻辑调用。

流程启动Cmd的参数

ProcessStartCmd 流程的启动参数说明

属性 描述 必须
defId 流程定义ID defId与defKey必需传入一个
defKey 流程定义Key defId与defKey必需传入一个
actDefId Activiti的流程Id  
busKey 业务键
billType 单据类型  
instId 流程实例Id,当从草稿中启动流程时才需要传入  
checkType 审批类型,默认为同意,值为AGREE
opinionName 审批名称,同意(AGREE),不同意(REFUSE)  
opinion 审批意见内容  
opFiles 提交意见可带附件  
destNodeId 目标节点Id,即提交完成后可跳到某一节点上  
vars 变量,提交至流程实例中的变量  
formData 表单的数据,格式如:{ form1:{ field1:’a’, field2:’b’ },form2:{ field3:’c’, field4:’d’ } }  
nodeUserIds 下一步节点及执行人员,{userTask1:’1,2,3’,userTask2:’2,3,4’}  

流程的前置与后置处理器

流程在执行启动时,若在流程定义中配置了前置与后置处理器,即可在activiti的流程启动时,执行对应的流程触发器。

前置处理器:

  1. //在启动时执行前置处理器
  2. processHandlerExecutor.handStartBeforeHandler(processConfig);

用户的自定义前置处理器,即可类似如下定义:

  1. package com.redxun.bpm.activiti.processhandler;
  2. import com.redxun.bpm.activiti.config.ProcessConfig;
  3. import com.redxun.bpm.core.entity.BpmInst;
  4. import com.redxun.bpm.core.entity.BpmTask;
  5. import com.redxun.bpm.core.entity.IExecutionCmd;
  6. import com.redxun.bpm.core.entity.ProcessStartCmd;
  7. public class SalesOrderHandler implements ProcessHandler{
  8. @Override
  9. public String getName() {
  10. return null;
  11. }
  12. @Override
  13. public void endHandle(BpmInst bpmInst) {
  14. }
  15. /**
  16. * 任务前置处理器
  17. * @param processConfig
  18. * @param cmd
  19. * @param bpmInst
  20. */
  21. @Override
  22. public void processStartAfterHandle(ProcessConfig processConfig, ProcessStartCmd cmd, BpmInst bpmInst) {
  23. //在此编写前置的业务处理,如获取单据的数据处理业务
  24. }
  25. @Override
  26. public void processStartPreHandle(ProcessStartCmd cmd) {
  27. //在此编写前置的业务处理,如获取单据的数据处理业务
  28. }
  29. @Override
  30. public void taskAfterHandle(IExecutionCmd cmd, String nodeId, String busKey) {
  31. }
  32. @Override
  33. public void taskPreHandle(IExecutionCmd cmd, BpmTask task, String busKey) {
  34. }
  35. }

编写完成后,在流程定义的流程级的前置处理器选择该配置

后置处理器:

  1. //在流程结束时处理后置处理器。
  2. processHandlerExecutor.handStartAfterHandler(processConfig,bpmInst);

流程挂起与恢复

流程实例在执行过程中,可以对它进行挂起,挂起的流程实例的任务不允许往下执行。实现方式只是需要对其状态进行修改即可,如下所示:

参考BpmInstController:

  1. /**
  2. * 更新流程实例状态
  3. * @param instId
  4. * @param status
  5. * @return
  6. */
  7. @ApiOperation(value = "更新流程实例状态")
  8. @PostMapping("updateProcessStatus")
  9. public JsonResult updateProcessStatus(@ApiParam @RequestParam(value="instId") String instId,@ApiParam @RequestParam(value="status") String status){
  10. bpmInstService.updateStatusByInstId(instId,status);
  11. return new JsonResult(true,"成功更新流程状态!");
  12. }

传入的状态值包括:

RUNNING:运行中
SUPSPEND: 挂起

流程实例作废

平台允许对运行中的流程实例进行作废,参考BpmInstController的以下方法:

  1. /**
  2. * 作废流程实例
  3. * @param instId
  4. * @param reason
  5. * @return
  6. */
  7. @ApiOperation(value = "作废流程实例")
  8. @PostMapping("cancelProcess")
  9. public JsonResult cancelProcess(@ApiParam @RequestParam(value="instId") String instId,@ApiParam @RequestParam(value="reason") String reason){
  10. bpmInstService.cancelProcess(instId,reason);
  11. return new JsonResult(true,"成功更新流程状态!");
  12. }
  1. /**
  2. * 作废流程实例
  3. * @param instId
  4. * @param reason
  5. */
  6. @Transactional
  7. public void cancelProcess(String instId,String reason){
  8. BpmInst bpmInst=get(instId);
  9. if(bpmInst==null){
  10. return;
  11. }
  12. ProcessConfig processConfig = bpmDefService.getProcessConfig(bpmInst.getActDefId());
  13. String script=processConfig.getEndProcessScript();
  14. //执行流程结束时配置的脚本
  15. if(StringUtils.isNotEmpty(script)){
  16. JsonResult formData = getFormData(instId,processConfig,"NO");
  17. JSONObject formDataJson=new JSONObject();
  18. for(BpmView bpmView:(List<BpmView>)formData.getData()){
  19. formDataJson.put(bpmView.getBoAlias(),bpmView.getData());
  20. }
  21. IExecutionCmd cmd = ProcessHandleUtil.getProcessCmd();
  22. Map<String, Object> contextData = new HashMap<>(formDataJson.size() + 2);
  23. if (formDataJson != null) {
  24. Set<Map.Entry<String, Object>> ents = formDataJson.entrySet();
  25. for (Map.Entry<String, Object> ent : ents) {
  26. contextData.put(ent.getKey(), ent.getValue());
  27. }
  28. }
  29. contextData.put("cmd", cmd);
  30. contextData.put("vars", getVariables(bpmInst.getActInstId()));
  31. processScriptEngine.exeScript(script, contextData);
  32. }
  33. //更新流程实例作废状态
  34. bpmInstMapper.updateStatusByInstId(instId,BpmInstStatus.CANCEL.name());
  35. //加上实例的日志
  36. bpmInstLogService.addInstLog(instId,"进行流程实例的作废,作废原因为:" + reason );
  37. //插入作废的流程审批历史
  38. List<BpmTask> bpmTasks = bpmTaskService.getByInstId(instId);
  39. if(bpmTasks.size()>0){
  40. String curUserId = ContextUtil.getCurrentUserId();
  41. insertCheckHistory(bpmTasks.get(0),curUserId,curUserId,"CANCEL","CANCEL",reason);
  42. }
  43. //删除流程实例
  44. runtimeService.deleteProcessInstance(bpmInst.getActInstId(),reason);
  45. //删除任务
  46. bpmTaskMapper.deleteByInstId(instId);
  47. }

说明:

流程实例作废时,需要完成以下事项:

  1. 流程实例结束的脚本调用
  2. 更新实例状态为作废状态
  3. 添加实例作废的日志
  4. 添加审批历史
  5. 删除Activiti的流程实例
  6. 删除对应的流程任务

流程明细

流程明细需要展示流程的明细信息,包括实例的明细信息,单据的信息,流程图的信息,审批日志。

后台准备的单据数据,参考BpmInstService

  1. /**
  2. * 获取流程实例的相关数据
  3. * @param instId
  4. * @param isMobile
  5. * @param defaultWrite
  6. * @return
  7. */
  8. public BpmInstDetail getInstDetail( String instId, String isMobile,Boolean defaultWrite){
  9. BpmInst bpmInst=get(instId);
  10. BpmDef bpmDef=bpmDefService.get(bpmInst.getDefId());
  11. ProcessConfig processConfig=bpmDefService.getProcessConfig(bpmDef.getActDefId());
  12. //获取单据的数据
  13. JsonResult dataResult=getFormData( instId, processConfig,isMobile,defaultWrite);
  14. BpmInstDetail detail=new BpmInstDetail();
  15. //设置实例主数据
  16. detail.setBpmInst(bpmInst);
  17. //设置单据的数据
  18. detail.setFormData(dataResult);
  19. //设置流程级的配置
  20. detail.setProcessConfig(processConfig);
  21. //审批意见记录
  22. List<BpmCheckHistory> opinionHistoryList= bpmCheckHistoryService.getOpinionNameNotEmpty(instId);
  23. detail.setBpmCheckHistories(opinionHistoryList);
  24. return detail;
  25. }

单据明细

在单据通过FormClient可加载单据的数据与表单页面,通过表单页面可加载对应的业务数据,如在前端,通过以下Vue组件进行加载展示:

  1. <rx-forms ref="rxForms"> </rx-forms>

在页面动态加载单据并设置单据的数据,如下所示:

  1. BpmInstApi.getInstDetail(this.instId).then(res => {
  2. var contextData={
  3. type:"detail",
  4. instNo:res.bpmInst.instNo,
  5. instId:self.instId,
  6. opinionHistorys:res.bpmCheckHistories
  7. };
  8. this.status=res.bpmInst.status;
  9. self.$refs.rxForms.setData(res.formData.data,true,contextData);
  10. })

后台准备单据的数据:

  1. /**
  2. * 获取单据的数据
  3. * @param instId
  4. * @param processConfig
  5. * @param isMobile
  6. * @param defaultWrite
  7. * @return
  8. */
  9. private JsonResult getFormData(String instId,ProcessConfig processConfig,String isMobile,Boolean defaultWrite){
  10. FormConfig formConfig= getForms(processConfig);
  11. //检查单据的配置是否为空,若不空,设置PC单据与移动端单据
  12. if(BeanUtil.isNotEmpty(formConfig)){
  13. formConfig.setFormpc(setReadOnly(formConfig.getFormpc()));
  14. formConfig.setMobile(setReadOnly(formConfig.getMobile()));
  15. }
  16. /**
  17. * 通过Feign的服务获取单据的基本信息结果
  18. */
  19. JsonResult result= formDataService.getByInstId(processConfig.getDataSetting(),
  20. processConfig.getBoDefs().getValue(),
  21. formConfig,
  22. instId,
  23. isMobile,
  24. defaultWrite);
  25. return result;
  26. }

调用时序图:

说明:

最终是通过FormClient来进行实现,FormClient是访问单据模块的基础接口的封装,主要是调用单据模块的Restful API

审批历史

实例审批历史数据展示

实现效果:

参考BpmCheckHistoryController的按实例获取审批历史

  1. @ApiOperation("查看流程的审批历史")
  2. @GetMapping("getCheckHistorys")
  3. public List<BpmCheckHistory> getCheckHistorys(@ApiParam @RequestParam String instId){
  4. return bpmCheckHistoryService.getByInstId(instId);
  5. }

审批历史表设计

BPM_CHECK_HISTORY 表

是否主键 字段名 字段描述 数据类型 长度 可空 备注
HIS_ID_ HIS_ID_ VARCHAR(64) 64    
  ACT_DEF_ID_ ACT流程定义ID VARCHAR(64) 64 ACT流程定义ID
  INST_ID_ 流程实例ID VARCHAR(64) 64 流程实例ID
  TREE_ID_ 分类ID VARCHAR(64) 64 分类ID
  SUBJECT_ 主题 VARCHAR(64) 64 主题
  NODE_NAME_ 节点名称 VARCHAR(255) 255 节点名称
  NODE_ID_ 节点Key VARCHAR(255) 255   节点Key
  TASK_ID_ 任务ID VARCHAR(64) 64 任务ID
  CM_ST_TASK_ID_ 回复的沟通任务ID VARCHAR(64) 64 回复的沟通任务ID
  COMPLETE_TIME_ 完成时间 DATETIME   完成时间
  DURATION_ 持续时长 BIGINT   持续时长
  DURATION_VAL_ 有效审批时长 BIGINT   有效审批时长
  OWNER_ID_ 任务所属人ID VARCHAR(64) 64 任务所属人ID
  HANDLER_ID_ 处理人ID VARCHAR(64) 64 处理人ID
  AGENT_USER_ID_ 被代理人 VARCHAR(64) 64 被代理人
  CHECK_STATUS_ 审批状态 VARCHAR(50) 50 审批状态
  JUMP_TYPE_ 跳转类型 VARCHAR(50) 50 跳转类型
  REMARK_ 意见备注 VARCHAR(512) 512 意见备注
  OPINION_NAME_ 字段意见名称 VARCHAR(50) 50 字段意见名称
  HANDLE_DEP_ID_ 处理部门ID VARCHAR(64) 64 处理部门ID
  HANDLE_DEP_FULL_ 处理部门全名 VARCHAR(300) 300 处理部门全名
  ENABLE_MOBILE_ 是否支持手机 SMALLINT   是否支持手机
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

说明:

  1. 流程审批历史通过流程实例Id与ActInstId关联,通过该字段可以获取流程实例对应的审批历史记录
  2. 流程审批历史记录用户的每次审批记录
  3. 记录每次审批的审批意见与审批状态

流程干预

流程的干预指是管理员在流程实例的运行过程中,可以以下的流程相关属性进行干预变更:

  1. 单据的数据
  2. 节点人员
  3. 流程变量
  4. 流程跳转路径
  5. 流程配置属性更改

效果图:

单据的数据

单据的展现与流程明细中的单据明细获取的接口一样,差别是这边允许对单据的所有权限均是开放出来,以允许进行编辑与修改,以达到支持基于上面进行单据数据变更处理。

  1. <rx-forms ref="rxForms"></rx-forms>

参考BpmInstOperator.vue其加载的方法如下:

  1. loadDetail() {
  2. let self = this;
  3. if(!this.instId){
  4. this.instId=this.$route.params.instId;
  5. }
  6. BpmInstApi.getInstDetailForInterpose(this.instId).then(res => {
  7. this.bpmInstDetail=res;
  8. this.actInstId=this.bpmInstDetail.bpmInst.actInstId;
  9. this.defId=this.bpmInstDetail.bpmInst.defId;
  10. let contextData={
  11. instId:self.instId,
  12. opinionHistorys:res.bpmCheckHistories
  13. };
  14. self.$refs.rxForms.setData(res.formData.data,false,contextData);
  15. })
  16. },

后端代码BpmInstController参考:

  1. /**
  2. * 获取实例明细
  3. * @param instId
  4. * @return
  5. */
  6. @GetMapping("getInstDetailForInterpose")
  7. public BpmInstDetail getInstDetailForInterpose(@RequestParam String instId,@RequestParam(required = false,defaultValue = "NO") String isMobile) {
  8. BpmInstDetail detail = bpmInstService.getInstDetail(instId, isMobile, true);
  9. return detail;
  10. }

说明:
bpmInstService.getInstDetail最后的参数为true则代表可写,设置为false则表示单据是只读状态。

节点人员

节点的人员分为四种级别:

  • 任务实例已分配的人员,即正在审批的人员。
  • 已执行的历史审批节点人员
  • 前置已经设置好的节点人员,但任务节点实例尚未创建,我们称之为可干预的节点人员配置
  • 流程定义配置级的人员

如下图所示:

在界面中呈现节点的人员时,我们通过在后台扩展类按以上优先级分别展示不同的节点人员,
分别展示为节点人员配置,干预人员配置,审批人与历史审批节点人员。

具体实现参考BpmInstController以下实现方法:

  1. /**
  2. * 获取所有的流程节点及节点对应的人员
  3. * @param actInstId
  4. * @return
  5. */
  6. @ApiOperation("通过实例Id获取所有的任务节点执行人员列表")
  7. @GetMapping("getTaskNodeUsers")
  8. public List getTaskNodeUsers(@ApiParam("actInstId") @RequestParam("actInstId") String actInstId){
  9. List<TaskNodeUser> taskNodeUserList=new ArrayList<>();
  10. BpmInst bpmInst=bpmInstService.getByActInstId(actInstId);
  11. // 获得当前的待办任务
  12. List<BpmTask> curTasks = bpmTaskService.getByActInstId(actInstId);
  13. //取得流程的所有人工节点
  14. Collection<FlowNode> userNodes=actRepService.getUserNodes(bpmInst.getActDefId());
  15. //取得到之前在流程任务节点中增加的所有的节点人员Id映射
  16. String nodeUserIds=(String)runtimeService.getVariable(actInstId,BpmInstVars.NODE_USER_IDS.getKey());
  17. Map<String,Object> nodeUserIdMap=null;
  18. if(StringUtils.isNotEmpty(nodeUserIds)){
  19. nodeUserIdMap = JSON.parseObject(nodeUserIds).getInnerMap();
  20. }
  21. //查找流程所有的节点,并且把审批中的、未审批的、已审批的节点的人员计算全部计算出来
  22. for (FlowNode flowNode : userNodes) {
  23. TaskNodeUser taskNodeUser = new TaskNodeUser(flowNode.getId(),flowNode.getName());
  24. // 1 查找已经审批的
  25. List<BpmCheckHistory> BpmCheckHistories = bpmCheckHistoryService.getByInstIdNodeId(bpmInst.getInstId(), flowNode.getId());
  26. List<TaskExecutor> checkExecutors = new ArrayList<TaskExecutor>();
  27. Set<String> userIdSet = new HashSet<String>();
  28. for (BpmCheckHistory history : BpmCheckHistories) {
  29. if (StringUtils.isEmpty(history.getHandlerId())
  30. || userIdSet.contains(history.getHandlerId())) {
  31. continue;
  32. }
  33. userIdSet.add(history.getHandlerId());
  34. IUser osUser = userClient.findByUserId(history.getHandlerId());
  35. if (osUser != null) {
  36. checkExecutors.add(TaskExecutor.getUser(osUser.getUserId(), osUser.getFullName()));
  37. }
  38. }
  39. taskNodeUser.getCheckExecutors().addAll(checkExecutors);
  40. // 2 从正在运行中的流程任务实例中获得执行人
  41. for (BpmTask task : curTasks) {
  42. if (task.getKey().equals(flowNode.getId())) {
  43. Collection<TaskExecutor> taskExecutors = bpmTaskService.getTaskExecutors(task.getTaskId());
  44. taskNodeUser.getRunExecutors().addAll(taskExecutors);
  45. taskNodeUser.setRunning(true);
  46. //if(BpmTask.TYPE_FLOW_TASK.equals(task.getTaskType())) {
  47. //设置为当前运行的任务Id,只需要获取一个当前其中一个任务Id即可
  48. taskNodeUser.setTaskId(task.getTaskId());
  49. // }
  50. }
  51. }
  52. // 3 从流程变量中获得其人员列表
  53. if (nodeUserIdMap!=null && nodeUserIdMap.containsKey(flowNode.getId())) {
  54. Set<TaskExecutor> operatorExecutors = new HashSet<>();
  55. String userIds = (String) nodeUserIdMap.get(flowNode.getId());
  56. if (StringUtils.isNotEmpty(userIds)) {
  57. String[] uIds = userIds.split("[,]");
  58. for (String uId : uIds) {
  59. IUser osUser = userClient.findByUserId(uId);
  60. operatorExecutors.add(TaskExecutor.getUser(osUser.getUserId(),osUser.getFullName()));
  61. }
  62. taskNodeUser.getOperateExecutors().addAll(operatorExecutors);
  63. }
  64. }
  65. //4 从流程配置文件中获取人员信息
  66. Map<String, Object> variables = runtimeService.getVariables(actInstId);
  67. UserTaskConfig userTaskConfig= (UserTaskConfig) bpmDefService.getNodeConfig(bpmInst.getActDefId(),flowNode.getId());
  68. Set<TaskExecutor> taskExecutors = taskExecutorService.getTaskExecutors(userTaskConfig.getUserConfigs(), variables);
  69. taskNodeUser.getConfigExecutors().addAll(taskExecutors);
  70. taskNodeUserList.add(taskNodeUser);
  71. }
  72. return taskNodeUserList;
  73. }

流程变量

平台对Activiti的流程变量部分不进行扩展,因此保留了Acitiviti的原生API操作。

获取流程实例所有的变量

  1. Map<String, Object> varMaps = runtimeService.getVariables(actInstId);

添加及更新流程变量

  1. runtimeService.setVariable(actInstId, var.getKey(), val);

删除流程变量

  1. runtimeService.removeVariable(actInstId,varKey);

干预流程跳转

标准的BPMN流程定义中,节点间的跳转需要严格遵循流程定义的跳转规则,若需要实现不同的节点跳转,需要进行扩展实现,平台提供了一个标准的节点可实现节点间的跳转:

  1. /**
  2. * 流程干预跳转
  3. * @param taskNodeJump
  4. * @return
  5. * @throws Exception
  6. */
  7. @ApiParam("任务干预跳转")
  8. @PostMapping("jumpToNode")
  9. public JsonResult jumpToNode(@RequestBody TaskNodeJump taskNodeJump) throws Exception {
  10. try{
  11. BpmTask bpmTask=bpmTaskService.get(taskNodeJump.getTaskId());
  12. if(bpmTask==null){
  13. List<BpmTask> bpmTasks = bpmTaskService.getByActInstId(taskNodeJump.getActInstId());
  14. if(bpmTasks.size()>0){
  15. bpmTask=bpmTasks.get(0);
  16. }
  17. }
  18. if(bpmTask==null){
  19. return new JsonResult(false,"当前任务已经完成,不允许干预跳转");
  20. }
  21. ProcessNextCmd cmd=new ProcessNextCmd();
  22. cmd.setTaskId(bpmTask.getTaskId());
  23. cmd.setCheckType(TaskOptionType.INTERPOSE.name());
  24. cmd.setDestNodeId(taskNodeJump.getDestNodeId());
  25. //设置目标节点的人员
  26. cmd.setNodeUserIds(bpmTask.getKey(),taskNodeJump.getToUserIds());
  27. cmd.setOpinion(taskNodeJump.getOpinion());
  28. return completeTask(cmd);
  29. } catch (Exception ex){
  30. ex.printStackTrace();
  31. String message=ExceptionUtil.getExceptionMessage(ex);
  32. JsonResult jsonResult=new JsonResult(false,"任务干预跳转失败!");
  33. jsonResult.setMessage(message);
  34. return jsonResult;
  35. }
  36. }

流程属性变更

流程属性的变更可通过流程节点的人员通过流程设计器那边进行节点与流程级的属性变更,然后通过流程保存、变更、节点属性的变更处理。

流程抄送

流程抄送是指在任务办理过程中,把流程实例的相关信息抄送给一个或多个人进行查看,抄送的时机为包括:

  1. 任务办理创建或完成时
  2. 流程开始时
  3. 流程结束中

存储设计

BPM_INST_CC 流程实例抄送主表

流程实例的抄送主表,记录某一次实例的抄送主表信息。

是否主键 字段名 字段描述 数据类型 长度 可空 备注
ID_ 主键 VARCHAR(64) 64   主键
  SUBJECT_ 主题 VARCHAR(64) 64 主题
  NODE_ID_ 节点ID VARCHAR(64) 64 节点ID
  NODE_NAME_ NODE_NAME_ VARCHAR(64) 64  
  FROM_USER_ 发送人 VARCHAR(64) 64 发送人
  FROM_USER_ID_ 发送人ID VARCHAR(64) 64 发送人ID
  INST_ID_ 流程实例ID VARCHAR(64) 64 流程实例ID
  DEF_ID_ 流程定义ID VARCHAR(64) 64 流程定义ID
  CC_TYPE_ 类型 VARCHAR(20) 20 类型
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

BPM_INST_CP 流程实例抄送人员表

BPM流程实例抄送人,记录流程实例抄送给哪些人查看,是否已读。

是否主键 字段名 字段描述 数据类型 长度 可空 备注
ID_ 主键 VARCHAR(64) 64   主键
  CC_ID_ 抄送ID VARCHAR(64) 64 抄送ID
  INST_ID_ 实例ID VARCHAR(64) 64 实例ID
  USER_ID_ 接收用户ID VARCHAR(64) 64 接收用户ID
  IS_READ_ 是否阅读 VARCHAR(64) 64 是否阅读
  TENANT_ID_ 租用ID VARCHAR(64) 64 租用用户Id
  CREATE_DEP_ID_ 创建部门ID VARCHAR(64) 64 创建部门ID
  CREATE_BY_ 创建人ID VARCHAR(64) 64 创建人ID
  CREATE_TIME_ 创建时间 DATETIME   创建时间
  UPDATE_BY_ 更新人ID VARCHAR(64) 64 更新人ID
  UPDATE_TIME_ 更新时间 DATETIME   更新时间

类设计


关键类说明:

CopyListener 是抄送的监听类,主要负责抄送监听事件的发送后的抄送的数据插入处理与抄送人员的消息通知。

生成抄送时序实现图

文档更新时间: 2020-10-12 09:25   作者:csx