流程定义用例实现
平台的流程建模包括了在线流程定义中的比较常见的操作,如:
- 保存草稿
- 发布版本
- 修改
- 流程定义校检
- 设置主版本
- 删除
- 流程的导入导出
- 流程复制
不同的业务操作需要提供不同的应用管理,以支持不同的数据存储的要求与业务实现要求。
类设计
说明:
- BpmDef 流程定义实体类
- BpmModelerController 流程定义建模的保存、检查、修改、发布核心服务对外提供类
- BpmDefController 流程定义控制器,负责流程定义删除、更新、列表查询的对外服务提供类
- BpmDefMapper 为流程定义数据库的操作类
- ActRepService 为Activiti流程库的API封装,如获取流程定义开始节点,结结节点等
流程定义保存、修改、发布
流程定义的保存,只需要在bpmn的设计界面中,实现对流程的基本信息的保存,如流程定义分类,编码,名称,描述,在线的bpmn定义,状态设置为草稿即可。
保存/发布时序图
【说明】
- 根据提交的状态来决定是保存流程定义、修改流程定义、发布流程的区分
- 在保存流程定义之前,处理json,主要是流程定义的节点级的json进行相应的更新
- 处理流程XML中的流程定义名称
- 处理一些子流程与Service节点的特殊节点
- 若是发布流程定义,则需要调用ActRepService实现流程定义的发布,并且生成ActDefId,ActDeployId。并且在BPM_DEF表中记录,为后续的流程启动与任务启动提供数据。
- 流程发布时相应修改流程定义版本,每增加一版本则更新版本号
流程定义修改
流程定义修改是指流程在定义XML发布完成后,即使在流程在运行中,还允许对流程定义进行节点增加、节点属性更改、节点删除等操作,其中节点删除即需要谨慎。若当前节点的任务正在审批,在流程定义中删除该节点时,则会造成该流程实例无法运行。
流程定义修改时,请参考BpmDefService的以下方法:
/**
* 发布流程定义。
* @param bpmDef
* @return
* @throws UnsupportedEncodingException
*/
public BpmDef doModify(BpmDef bpmDef) throws UnsupportedEncodingException {
FlowDefCache.removeByDefId(bpmDef.getActDefId());
actRepService.writeDefXml(bpmDef.getActDepId(),bpmDef.getDesignXml());
update(bpmDef);
return bpmDef;
}
说明:
- 清空该流程定义的缓存,引擎第一次加载Activiti的流程定义会缓存起来
- 直接实现定义的XML回写
- 更新当前bpmDef的表,主要是设计器的XML
流程定义的发布
为了不影响当前正在运行的流程实例,执行流程定义的发布是比较好的办法,他会基于旧版本的数据重新生成一份新的流程定义,并可基于上面进行修改,如删除任务节点,修改任务节点,增加新的任务节点等。
其实现逻辑请参考BpmDefService类以下方法:
/**
* 发布新版流程定义
* @param oldBpmDef
* @param newBpmDef
*/
public void doDeployNewVersion(BpmDef oldBpmDef, BpmDef newBpmDef){
String oldDefId=oldBpmDef.getDefId();
//换成新的
newBpmDef.setDefId(IdGenerator.getIdStr());
//更新为主版本主键Id
newBpmDef.setMainDefId(newBpmDef.getDefId());
newBpmDef.setIsMain(BpmDef.IS_MAIN);
newBpmDef.setStatus(BpmDef.STATUS_DEPLOYED);
//发布Activiti的流程定义
Deployment deployment = activitiDeployService.deploy(newBpmDef.getKey(), newBpmDef.getDesignXml());
newBpmDef.setActDepId(deployment.getId());
ProcessDefinition processDefinition=repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).latestVersion().singleResult();
//获取流程定义Id
if(processDefinition!=null){
newBpmDef.setActDefId(processDefinition.getId());
}
Integer maxVersion=getMaxVersion(newBpmDef.getKey());
//升级流程定义版本
newBpmDef.setVersion(maxVersion+1);
//更新主版本号
updateMainDefId(newBpmDef,oldDefId);
//插入新流程
insert(newBpmDef);
//更新流程定义中的扩展属性定义
doCopyBpmDefProsAndUpdate(newBpmDef);
}
设置主版本
同一流程定义,如报销流程,可允许多个不同的版本,分别为版本1,版本2,版本3,但每次启动流程时,只能启动一个主版本。系统在设计器中刚提供设置主版本。
切换主版本时,只需要把bpmdef表中的对应的记录的main_def_id更新为当前的流程定义行主键,同时把该同一流程定义下的其他版本的记录maindef_id也更新为该主键,同时设置ismain为YES。
参考BpmDefController的以下实现方法即可:
@ApiOperation("设置主版本")
@GetMapping("setMainVersion")
public JsonResult setMainVersion(@ApiParam @RequestParam String defId){
BpmDef bpmDef = bpmDefService.get(defId);
bpmDefService.updateMainDefId(bpmDef,bpmDef.getMainDefId());
bpmDef.setIsMain(BpmDef.IS_MAIN);
bpmDef.setMainDefId(bpmDef.getDefId());
bpmDefService.update(bpmDef);
return JsonResult.Success("设置主版本成功").setShow(false);
}
删除流程定义
流程定义删除时,需要同时删除关联的数据,如流程实例,流程任务,流程审批历史,审批日志等。
public JsonResult del(@ApiParam @RequestParam("ids") String ids) {
String[]idArr=ids.split("[,]");
for(String id:idArr){
BpmDef bpmDef=bpmDefService.get(id);
if(bpmDef!=null && StringUtils.isNotEmpty(bpmDef.getActDepId())){
bpmDefService.delCacadeFlow(bpmDef);
}else{
bpmDefService.delete(id);
}
}
JsonResult result=new JsonResult(true,"");
result.setMessage("成功执行删除操作!");
return result;
}
以下为流程定义对应的流程关联的流程实例的数据实现:
/**
* 删除流程实例时,级联删除其下的所有相关的记录,包括任务,审批历史,跳转记录等。
* <pre>
* 1. 删除任务用户。
* 2. 删除任务
* 3. 删除流程引擎运行数据。
* 4. 删除RUNPATH
* 5. 删除活动日志。
* 6. 删除流程实例
* 7. 删除会签数据
* 8. 删除审批意见数据
* </pre>
* @param instId
*/
@Transactional
public void deleteByInstId(Serializable instId) {
BpmInst bpmInst=get(instId);
if(bpmInst==null){
return;
}
String bpmInstId=bpmInst.getInstId();
String actInstId=bpmInst.getActInstId();
bpmTaskUserMapper.deleteByInstId((String) instId);
bpmTaskMapper.deleteByInstId(bpmInstId);
bpmRuPathService.delByInstId(bpmInstId);
bpmInstLogService.delByInstId(bpmInstId);
bpmInstMapper.deleteById(bpmInstId);
bpmCheckHistoryService.delByInstId(bpmInstId);
bpmInstDataServiceImpl.removeByInstId(bpmInstId);
if(StringUtils.isNotEmpty(actInstId)){
bpmSignDataService.deleteByActInstId(actInstId);
runtimeService.deleteProcessInstance(actInstId, "remove by user force");
}
}
流程的导出
流程的导出,允许用户把测试环境下配置好的流程定义一键导出文件,然后再在生产环境下一键导入即可。
参考BpmDefController的以下实现方法即可:
/**
* 流程设计导出
*/
@ApiOperation("流程设计导出")
@GetMapping("/doExport")
public void doExport(HttpServletResponse response,@ApiParam @RequestParam String defIds)throws Exception{
if(StringUtils.isEmpty(defIds)){
new Exception("导出失败,请选择要导出的记录。");
return;
}
List<BpmDefExp> defJsons=bpmDefExpImpService.exportBpmDef(defIds);
//把每个记录实体转成JSON文件存储
Map<String,String> map=new HashMap<>();
for (BpmDefExp def:defJsons) {
String fileName =def.getBpmDefName()+".json";
String defStr = JSONObject.toJSONString(def);
map.put(fileName,defStr);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String downFileName = "Bpm-Def" + sdf.format(new Date());
FileUtil.downloadZip(response,downFileName,map);
}
流程导入如下所示,参考BpmDefController的以下实现方法即可:
/**
* 流程设计导入
* @param request
* @return
* @throws Exception
*/
@ApiOperation("导入接口")
@RequestMapping("/doImport")
public JsonResult doImport(MultipartHttpServletRequest request) throws Exception {
MultipartFile file = request.getFile("zipFile");
String treeId=request.getParameter("treeId");
String formPcTreeId=request.getParameter("formPcTreeId");
List<AlterSql> delaySqlList = bpmDefExpImpService.importBpmDefs(file,treeId,formPcTreeId);
return JsonResult.Success().setData(delaySqlList).setMessage("导入成功");
}
流程复制
流程复制允许用户现有的流程复制出来,然后变更流程编码与名称,复制完成后,允许用户再进行各种属性的变更。现有的流程与新的流程很多配置是一致的,仅有个别属性的差异。
参考BpmDefController的以下实现方法即可:
/**
* 复制流程定义
* @param bpmDef
* @return
* @throws Exception
*/
@ApiOperation("复制流程定义")
@PostMapping("/copyDef")
public JsonResult copyDef(@RequestBody BpmDef bpmDef) throws Exception{
boolean rtn=bpmDefService.isExistKey(bpmDef.getKey());
if(rtn){
return new JsonResult<>(false, "指定的KEY名称已经被使用!");
}
try {
bpmDefService.copyNew(bpmDef.getDefId(), bpmDef.getKey(), bpmDef.getName(),bpmDef.getDeploy());
} catch (Exception e) {
e.printStackTrace();
return new JsonResult<>(false, "复制失败");
}
return new JsonResult<>(true, "复制成功");
}