流程引擎基础知识
- 流程部署
- 流程取消部署
- 流程发起
- 流程取回
- 流程作废
- 流程委托
- 流程流转
- 常用流程表介绍
- 备注
流程部署
1.后台直接导入bpmn
/*
*流程部署源代码
*/
public void deploy() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment().addClasspathResource("test.bpmn").name("谁家女儿郎").deploy();
}
2.前端画模型
/**
* 创建模型
*/
@RequestMapping("/create")
public Result<String> create(@RequestBody ModelDTO dto, HttpServletRequest request, HttpServletResponse response) {
Result<String> result = new Result<String>();
try {
String modelId = actModelService.save(dto.getName(), dto.getKey(), dto.getDescription());
result.setResult(modelId);
//response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + modelId);
} catch (Exception e) {
e.printStackTrace();
log.error("新建流程失败!");
}
return result;
}
public String save(String name, String key, String description) throws UnsupportedEncodingException {
//新建一个空模型
Model model = repositoryService.newModel();
//metaInfo信息
ObjectNode metaInfo = objectMapper.createObjectNode();
metaInfo.put(ModelDataJsonConstants.MODEL_NAME, name);
metaInfo.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
metaInfo.put(ModelDataJsonConstants.MODEL_REVISION, model.getVersion());
model.setKey(key);
model.setName(name);
model.setMetaInfo(metaInfo.toString());
repositoryService.saveModel(model);
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilset = objectMapper.createObjectNode();
stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
editorNode.set("stencilset", stencilset);
repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes("utf-8"));
return model.getId();
}
/**
* 流程部署
* @param id
* @return
*/
@GetMapping("deploy/{id}")
public Result deploy(@PathVariable("id") String id) {
actModelService.deploy(id);
return Result.OK("流程发布成功");
}
public void deploy(String id) {
try {
Model model = repositoryService.getModel(id);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
String processName = model.getName();
if (!StringUtils.endsWith(processName, ".bpmn20.xml")){
processName += ".bpmn20.xml";
}
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
Deployment deployment = repositoryService.createDeployment().name(model.getName()).addInputStream(processName, in).deploy();
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
if (list.size() == 0){
throw new JeecgBootException("流程定义为空");
}
} catch (Exception e) {
throw new JeecgBootException(e);
}
}
流程取消部署
1.后台直接导入bpmn
@GetMapping("/caceldeploy")
@ApiOperation(notes = "取消部署", value = "取消部署")
public void caceldeploy(String deployId) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.deleteDeployment(deployId, true);
}
2.前端画模型
/**
* 删除流程设计模型
* @param ids
* @return
*/
@GetMapping(value = "/delete")
public Result delete(@RequestParam(name = "ids", required = true) String ids) {
if(CommonRandomUtil.isNotEmpty(ids)) {
for (String id : Arrays.asList(ids.split(","))) {
actModelService.delete(id); //直接删除流程设计模型
}
}
return Result.OK("流程删除成功");
}
//备注:需实现repositoryService的void deleteDeployment(String deploymentId)的接口
流程发起
1.启动流程,后台导入bpmn
@GetMapping("/startProcessInstanceByKey")
@ApiOperation(notes = "创建流程实例", value = "创建流程实例")
public Result<?> startProcessInstanceByKey(String processDefinitionKey, String businessKey, String initiator) {
Map<String, Object> map = new HashMap<>();
map.put("name", "deng");
map.put("reason", "等天黑");
//设置流程发起人
identityService.setAuthenticatedUserId("deng");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
return Result.OK("等天黑");
}
2.流程改造后的启动流程
/**
* 启动流程
* @param userId
* @param businessKey
* @param variables
* @param extActProcessForm
* @return
* @throws BpmException
*/
private ProcessInstance startWorkflow(String userId, String businessKey, Map<String, Object> variables, ExtActProcessForm extActProcessForm) throws BpmException{
// 设置流程发起人
identityService.setAuthenticatedUserId(userId);
variables.put(WorkFlowGlobals.BPM_FORM_BUSINESSKEY, businessKey);
variables.put(WorkFlowGlobals.BPM_FORM_TYPE, extActProcessForm.getFormType());
variables.put(WorkFlowGlobals.BPM_BIZ_TITLE, BpmUtil.getContent(variables,extActProcessForm.getTitleExp()));
variables.put(WorkFlowGlobals.BPM_PROC_DEAL_STYLE, ProcDealStyleEnum.toEnum(extActProcessForm.getFormDealStyle()).getCode());
//根据流程id查询流程信息
ExtActProcess extActProcess = extActProcessMapper.selectById(extActProcessForm.getProcessId());
//获取最新发布的流程定义-----------------核心代码1
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(extActProcess.getProcessKey()).latestVersion().singleResult();
//update-begin--Author:zhoujf Date:20180727 for:流程未发布流程提交异常处理
if(processDefinition==null){
throw new BpmException("流程未发布,请先发布流程!");
}
String formUrl = (String)variables.get(WorkFlowGlobals.BPM_FORM_CONTENT_URL);
String formUrlM = (String)variables.get(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE);
formUrl = BpmUtil.getNodeUrl(variables, formUrl, businessKey);
formUrlM = BpmUtil.getNodeUrl(variables, formUrlM, businessKey);
variables.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL, formUrl);
variables.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, formUrlM);
//根据流程id和部署id查询流程部署节点
// List<ExtActProcessNodeDeployment> nodeList = new ArrayList<ExtActProcessNodeDeployment>();
// LambdaQueryWrapper<ExtActProcessNodeDeployment> queryWrapper = new LambdaQueryWrapper<ExtActProcessNodeDeployment>();
// queryWrapper.eq(ExtActProcessNodeDeployment::getProcessId, extActProcessForm.getProcessId());
// queryWrapper.eq(ExtActProcessNodeDeployment::getDeploymentId, processDefinition.getDeploymentId());
// nodeList = extActProcessNodeDeploymentService.list(queryWrapper);
// if(nodeList!=null&&nodeList.size()>0){
// for(ExtActProcessNodeDeployment node:nodeList){
// if(oConvertUtils.isNotEmpty(node.getModelAndView())){
// variables.put(node.getProcessNodeCode()+":"+WorkFlowGlobals.SUFFIX_BPM_FORM_URL, node.getModelAndView());
// }
// if(oConvertUtils.isNotEmpty(node.getModelAndViewMobile())){
// variables.put(node.getProcessNodeCode()+":"+WorkFlowGlobals.SUFFIX_BPM_FORM_URL_MOBILE, node.getModelAndViewMobile());
// }
// }
// }
//创建流程实例------------------------------------------------核心代码2
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(extActProcess.getProcessKey(), businessKey, variables);
if(processInstance==null||oConvertUtils.isEmpty(processInstance.getProcessInstanceId())){
return null;
}
//将流程实例ID存在流程变量里面 ----------------------------------核心代码3
runtimeService.setVariable(processInstance.getProcessInstanceId(), WorkFlowGlobals.JG_LOCAL_PROCESS_ID, processInstance.getProcessInstanceId());
return processInstance;
}
流程取回
/**
* 我发起的流程,流程追回按钮,流程取回
* 流程追回:删除流程实例,启动表单单据保留,
*
* @param
* @return
*/
@PostMapping(value = "/callBackProcess")
public Result<Map<String, Object>> callBackProcess(@RequestBody HashMap<String, String> map,
HttpServletRequest request) {
Result<Map<String, Object>> result = new Result<Map<String, Object>>();
try {
//添加评定业务流程取回时重置表数据功能
String processInstanceId1 = map.get("processInstanceId");
//Map<String, Object> flowDataByProcInstId = extActFlowDataService.getFlowDataByProcInstId(processInstanceId1);
//extActFlowDataService.updateFormDataById((String) flowDataByProcInstId.get("formDataId"));
String processInstanceId = oConvertUtils.getString(map.get("processInstanceId"));
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
runtimeService.setVariable(pi.getProcessInstanceId(), WorkFlowGlobals.BPM_STATUS, WorkFlowGlobals.PROCESS_CALLBACKPROCESS_STATUS);
runtimeService.deleteProcessInstance(processInstanceId, "发起人流程追回");
result.success("追回成功");
} catch (Exception e) {
e.printStackTrace();
result.error500("追回失败");
}
return result;
}
流程作废
/**
* 我发起的流程,作废按钮
*
* @param
* @return
*/
@PostMapping(value = "/invalidProcess")
public Result<Map<String, Object>> invalidProcess(@RequestBody HashMap<String, String> map,
HttpServletRequest request) {
Result<Map<String, Object>> result = new Result<Map<String, Object>>();
try {
String processInstanceId = oConvertUtils.getString(map.get("processInstanceId"));
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
runtimeService.setVariable(pi.getProcessInstanceId(), WorkFlowGlobals.BPM_STATUS, WorkFlowGlobals.PROCESS_INVALIDPROCESS_STATUS);
runtimeService.deleteProcessInstance(processInstanceId, "发起人流程作废");
result.success("作废成功");
} catch (Exception e) {
e.printStackTrace();
result.error500("作废失败");
}
return result;
}
流程委托
/**
* 委托
*
* @param
* @return
*/
@PostMapping(value = "/taskEntrust111")
public Result<Map<String, Object>> taskEntrust111(@RequestBody HashMap<String, String> map,
HttpServletRequest request) {
Result<Map<String, Object>> result = new Result<Map<String, Object>>();
try {
String taskId = oConvertUtils.getString(map.get("taskId"));
//委托要改成批量委托taskId用逗号分隔
List<String> list = Arrays.asList(taskId.split(","));
for (String s : list) {
String taskAssignee = oConvertUtils.getString(map.get("taskAssignee"));
Task task = taskService.createTaskQuery().taskId(s).active().singleResult();
//判断任务是否签收没有签收需要进行签收
if (oConvertUtils.isEmpty(task.getAssignee())) {
String userId = JwtUtil.getUserNameByToken(request);
taskService.claim(s, userId);
}
taskService.delegateTask(task.getId(), taskAssignee);
}
result.success("委托成功");
} catch (Exception e) {
e.printStackTrace();
result.error500("委托失败");
}
return result;
}
流程流转
/**
* 提交流程处理
*
* @param map
* @param request
* @return
*/
@PostMapping(value = "processComplete")
public Result<Object> processComplete(@RequestBody HashMap<String, String> map,
HttpServletRequest request) {
Long startTime = System.currentTimeMillis();
log.debug("进入方法processComplete内,当前时间戳{}" + startTime);
Result<Object> result = new Result<Object>();
try {
//监听通过request取到参数的信息的处理
request.setAttribute("data", map);
String taskId = oConvertUtils.getString(map.get("taskId"));
//下一节点名称
String nextnode = oConvertUtils.getString(map.get("nextnode"));
//下一步节点的数目(小心歧义)
Integer nextCodeCount = oConvertUtils.getInt(map.get("nextCodeCount"));
// String way = oConvertUtils.getString(request.getParameter("way"));
// ProcessHandle processHandle = activitiService.getProcessHandle(taskId);
//模式类型(单/多分支模式/驳回模式)
String model = oConvertUtils.getString(map.get("processModel"));
//下一步操作人
String nextUser = oConvertUtils.getString(map.get("nextUserId"));
//驳回时选择驳回到哪一节点
String rejectNode = oConvertUtils.getString(map.get("rejectModelNode"));
String accomplishFlag = "";
if (map.get("accomplishFlag") != null) {
accomplishFlag = oConvertUtils.getString(map.get("accomplishFlag"));
}
//TODO 流程变量参数
// Map<String, Object> varmap = var.getVariableMap(processHandle.getTpProcesspros());
Map<String, Object> varmap = new HashMap<String, Object>();
Task task = activitiService.getTask(taskId);
if (task == null) {
return Result.error("该节点为首节点,无法驳回");
}
String processInstanceId = task.getProcessInstanceId();
runtimeService.setVariable(processInstanceId, WorkFlowGlobals.BPM_STATUS, WorkFlowGlobals.BPM_BUS_STATUS_2);
runtimeService.setVariable(processInstanceId, "tenantId", request.getHeader("tenant_id"));
List<String> nextUserList = Arrays.asList(nextUser.split(","));
if (nextUserList != null && nextUserList.size() > 0) {
runtimeService.setVariable(processInstanceId, "nextUser", nextUserList.get(0));
}
if (!"1".equals(model) && !"2".equals(model)) {
// 取得所有历史任务按时间降序排序
List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByTaskCreateTime()
.desc()
.list();
//查出离现在第二近的上一节点处理人
String assignee = "";
int i = 0;
for (HistoricTaskInstance historicTaskInstance : htiList) {
if (historicTaskInstance.getTaskDefinitionKey().equals(rejectNode)) {
assignee = historicTaskInstance.getAssignee();
i++;
if (i == 2) {
break;
}
}
}
runtimeService.setVariable(processInstanceId, "rejectNextBy", assignee);
}
boolean bflag = this.checkUserTaskIsHuiQian(taskId, nextnode);
if (bflag) {
varmap.put(WorkFlowGlobals.ASSIGNEE_USER_ID_LIST, nextUser);
runtimeService.setVariable(processInstanceId, WorkFlowGlobals.ASSIGNEE_USER_ID_LIST, nextUser);
if (!"".equals(accomplishFlag)) {
varmap.put("accomplish_flag", accomplishFlag);
}
}
//判断是否是委托任务,被委托人先解决委托
if (StringUtils.isNotBlank(task.getOwner())) {
DelegationState delegationState = task.getDelegationState();
switch (delegationState) {
case PENDING:
taskService.resolveTask(taskId);
break;
case RESOLVED:
//委托任务已经完成
break;
default:
//不是委托任务
break;
}
}
String rejectDescription = "";
if ("1".equals(model)) {
//---update-begin-----autor:zhoujf------date:20190927-------for:并行网关停滞不前问题--------
//判断是否是并行网关
boolean isParallelGateway = this.checkParallelGateway(task.getProcessDefinitionId(), nextnode);
//单分支模式
if ("end".equals(nextnode)) {
if (nextCodeCount == 1 || isParallelGateway) {
taskService.complete(taskId, varmap);
} else {
activitiService.goProcessTaskNode(taskId, nextnode, varmap);
}
} else {
//多分支模式
if (nextCodeCount == 1 || isParallelGateway) {
taskService.complete(taskId, varmap);
} else {
activitiService.goProcessTaskNode(taskId, nextnode, varmap);
}
//---update-end-----autor:zhoujf------date:20190927-------for:并行网关停滞不前问题--------
//如果下个节点不是会签节点,动态指派下一步处理人
if (!bflag) {
//会签情况下,该SQL会报错
String nextTskId = activitiService.getTaskIdByProins(processInstanceId, nextnode);
//---update-begin-----autor:scott------date:20190925-------for:TASK #3188 下一步节点不是任务节点的情况下,不走指派逻辑不然报错。比如:下一步是个网关--------
if (oConvertUtils.isNotEmpty(nextUser) && oConvertUtils.isNotEmpty(nextTskId)) {
//---update-end-----autor:scott------date:20190925-------for:TASK #3188 下一步节点不是任务节点的情况下,不走指派逻辑不然报错。比如:下一步是个网关--------
if (nextUser.indexOf(",") < 0) {
//指定单个执行人
taskService.setAssignee(nextTskId, nextUser);
} else {
//---update-begin-----autor:scott------date:20190925-------for:TASK #3187 【疑似BUG】 出差申请 下一步操作人 选择多个
//指定多人
taskService.setAssignee(nextTskId, null);
for (String userid : nextUser.split(",")) {
if (oConvertUtils.isNotEmpty(userid)) {
taskService.addCandidateUser(nextTskId, userid);
}
}
//---update-end-----autor:scott------date:20190925-------for:TASK #3187 【疑似BUG】 出差申请 下一步操作人 选择多个
}
}
} else {
runtimeService.setVariable(processInstanceId, "assigneeUserIdList", nextUser);
}
}
} else if ("2".equals(model)) {
//多分支模式
taskService.complete(taskId, varmap);
} else {
runtimeService.setVariable(processInstanceId, WorkFlowGlobals.BPM_STATUS, WorkFlowGlobals.PROCESS_REJECTPROCESS_STATUS);
//驳回模式
activitiService.goProcessTaskNode(taskId, rejectNode, varmap);
String nextTskId = activitiService.getTaskIdByProins(processInstanceId, rejectNode);
// 取得所有历史任务按时间降序排序
List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByTaskCreateTime()
.desc()
.list();
//查出离现在第二近的上一节点处理人
String assignee = "";
int i = 0;
for (HistoricTaskInstance historicTaskInstance : htiList) {
if (historicTaskInstance.getTaskDefinitionKey().equals(rejectNode)) {
assignee = historicTaskInstance.getAssignee();
i++;
if (i == 2) {
break;
}
}
}
// runtimeService.setVariable(processInstanceId,"rejectNextBy",assignee);
//TODO 这行代码替换了下面注释的部分
try {
taskService.setAssignee(nextTskId, assignee);
} catch (ActivitiIllegalArgumentException e) {
log.info("taskId is null");
}
// if(oConvertUtils.isNotEmpty(nextUser)){
// if(nextUser.indexOf(",") < 0){
// //指定单个执行人
// taskService.setAssignee(nextTskId,nextUser);
// }else{
// //指定多人
// taskService.setAssignee(nextTskId,null);
// //---update-begin-----autor:scott------date:20190930------for:TASK #3187 【疑似BUG】 出差申请 下一步操作人 选择多个
// for (String userid : nextUser.split(",")) {
// if (oConvertUtils.isNotEmpty(userid)) {
// taskService.addCandidateUser(nextTskId, userid);
// }
// }
// //---update-end-----autor:scott------date:20190930-------for:TASK #3187 【疑似BUG】 出差申请 下一步操作人 选择多个
// }
// }
//TODO 流程委托、驳回 日志问题
Task nexttask = activitiService.getTask(nextTskId);
//---update-begin-----autor:scott------date:20191203------for:驳回人显示真实名字,不显示账号
String assigneeUserName = task.getAssignee();
LoginUser loginUser = sysBaseAPI.getUserByName(task.getAssignee());
if (loginUser != null) {
assigneeUserName = loginUser.getRealname();
}
//---update-end-----autor:scott------date:20191203------for:驳回人显示真实名字,不显示账号
rejectDescription = assigneeUserName + "驳回任务 〔" + task.getName() + "〕 →〔" + nexttask.getName() + "〕 ";
// List<HistoricTaskInstance> hiTaskInstList = historyService.createHistoricTaskInstanceQuery().taskId(taskId).list();
// ActHiTaskinst actHiTaskinst = this.systemService.get(ActHiTaskinst.class, taskId);
// actHiTaskinst.setDescription(rejectDescription);
// actHiTaskinst.setDeleteReason(rejectDescription);
// this.systemService.saveOrUpdate(actHiTaskinst);
// 流程委托、驳回 日志问题
}
String username = JwtUtil.getUserNameByToken(request);
LoginUser user = sysBaseAPI.getUserByName(username);
// 任务抄送处理
String ccUserNames = oConvertUtils.getString(map.get("ccUserIds"));
//任务抄送记录
taskCc(ccUserNames, task, username);
//每个task节点执行完成后,处理审批日志
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String reason = oConvertUtils.getString(map.get("reason"));
ExtActBpmLog bpmlog = new ExtActBpmLog();
if (processInstance != null) {
bpmlog.setBusinessKey(processInstance.getBusinessKey());
bpmlog.setProcName(processInstance.getName());
}
// bpmlog.setOpStatus(opStatus);
bpmlog.setOpTime(new Date());
// bpmlog.setOpType(WorkFlowGlobals.BPM_OP_TYPE_1);
bpmlog.setOpUserId(username);
if (user != null) {
bpmlog.setOpUserName(user.getRealname());
}
bpmlog.setProcInstId(processInstanceId);
if (oConvertUtils.isNotEmpty(rejectDescription)) {
reason = reason + " 『 " + rejectDescription + " 』";
}
bpmlog.setRemarks(reason);
bpmlog.setTaskDefKey(task.getTaskDefinitionKey());
bpmlog.setTaskId(taskId);
bpmlog.setTaskName(task.getName());
extActBpmLogService.save(bpmlog);
//保存附件
String fileList = oConvertUtils.getString(map.get("fileList"));
saveBpmFiles(bpmlog.getId(), fileList);
} catch (BpmException e) {
result.error500("任务执行失败:" + e.getMessage());
e.printStackTrace();
} catch (ActivitiException e) {
result.error500("任务执行失败:" + e.getMessage());
e.printStackTrace();
Object actObj = e.getCause();
if (actObj != null) {
Throwable throwable = (Throwable) actObj;
if (throwable.getCause() != null && throwable.getCause() instanceof BpmException) {
result.error500("任务执行失败:" + throwable.getCause().getMessage());
}
}
} catch (Exception e) {
result.error500("任务执行失败:" + e.getMessage());
e.printStackTrace();
Object actObj = e.getCause();
if (actObj != null) {
Throwable throwable = (Throwable) actObj;
if (throwable.getCause() != null && throwable.getCause() instanceof BpmException) {
result.error500("任务执行失败:" + throwable.getCause().getMessage());
}
}
}
Long endTime = System.currentTimeMillis();
log.debug("方法processComplete结束前,当前时间戳{}" + endTime);
log.debug("耗时(毫秒):{}", endTime - startTime);
return result;
}
常用流程表介绍
1.历史节点表—act_hi_actinst
流程历史节点表常用来查询流程走过的节点
2.历史变量表—act_hi_varinst
流程历史节点表常用来查询流程走到的节点的元数据(业务数据)
3.运行时任务节点表—act_ru_lask
运行时加点表表明当前流程走到的节点
4.运行时流程变量数据表—act_ru_variable
运行时加点表表明当前流程走到的节点信息(业务数据)
5.流程部署信息表—act_re_deployment
流程部署后流程存储的地方—主要字段id(流程部署id)
6.流程定义数据表—act_re_prodef
流程定义
7.运行时流程人员表—act_ru_identitylink
每个节点的人员信息
8.运行时流程实例表—act_ru_execution
关键表,集合了流程实例id,流程定义id,流程节点id,以及业务表id
9.运行各个表之间的关系
备注
流程实例id贯穿运行表,运行表业务字段,历史表,历史表业务字段,流程实例表,属于比较重要的字段