一 流程模块整体介绍
1 流程包括以下模块,之前讲了流程图的设计和部署,当一个流程部署后,即可根据当前的流程图启动一个流程实例了
2 我的任务
我的任务包括流程点上配置执行人的任务(已取消,改为所有任务都要认领后才能在我的任务中出现),还有在可认领任务中认领的任务;
3 可认领任务
包括流程点上设置候选人或者获选角色中该任务可被这些人领取,当一个人领取任务后,其他人便不能再领取该任务
4 所有任务列表
包含项目中所有进行中的任务列表
二 流程模块具体介绍
测试的请假流程图如下:(其中部门经理审核为会签任务)
对应的候选人和候选角色设置如下
1 启动流程实例
/**
* 启动流程
* @param businessKey 业务对象key
* @param processDefinitionKey 流程定义key
* @param map 启动参数
* @param comments 备注说明
* @param singleton 是否只能启动单流程实例,如果为true,即一个业务对象只能启动一个流程定义的一个流程实例,但对不同的流程定义可以分别启动一个流程实例
* @return 是否成功
*/
public boolean startProcess(String businessKey,String processDefinitionKey,Map<String, Object> map,String comments,Boolean singleton){
try{
if(businessKey==null||"".equals(businessKey)){
log.error("ActivitiService startProcess error:business key is null");
return false;
}
if(processDefinitionKey==null||"".equals(processDefinitionKey)){
log.error("ActivitiService startProcess error:process definition key is null");
return false;
}
if(singleton){
List<ProcessInstance> processInstances=runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey,processDefinitionKey).list();
if(processInstances.size()>0){
log.error("ActivitiService startProcess error:can not start singleton process,process instance with the same businessKey and processDefinitionKey already exists");
return false;
}
}
map.put("start", true);
//否则历史记录中没有启动用户名
Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
if (processInstance != null) {
//comments目前是添加到流程启动后生成的task上的
if(comments!=null&&!"".equals(comments)){
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
if (tasks.size() > 0) {
taskService.addComment(tasks.get(0).getId(), processInstance.getProcessInstanceId(),comments);
}
}
return true;
} else {
log.error("ActivitiService startProcess error:no process instance created");
return false;
}
}catch(Exception e){
log.error("ActivitiService startProcess error:",e);
return false;
}
}
其中,最重要的启动流程实例的代码为
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
启动一个流程实例至少需要一个processDefinitionKey,用来确定使用的哪一个流程定义;业务中可以按需要传入参数map等,例如请假流程中可以将请假天数传入到map中,根据天数判断审核请假流程是否需要部门经理审核;
2 认领任务
认领任务的主要代码为
taskService.claim(taskId, userId);
传入任务的id和认领人的id即可
/**
* 接件操作
* @param taskId 任务id
* @param userId 接件用户
* @return 是否成功
*/
public boolean claimTask(String taskId,String processInstanceId,String userId){
try
{
Authentication.setAuthenticatedUserId(userId);
taskService.claim(taskId, userId);
if(processInstanceId==null||"".equals(processInstanceId)) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
processInstanceId=task.getProcessInstanceId();
}
taskService.addComment(taskId,processInstanceId,"认领任务");
return true;
}
catch (Exception e)
{
log.error("ActivitiService claimTask error:",e);
return false;
}
}
3 我的任务-流转任务
认领了任务后,便可以在我的任务中对任务进行操作
若该任务的流转方向只有一条,那么直接执行任务即可;
若任务的流转方向有很多条,那么需选择流转方向,这里的下拉列表是从BpmnModel中获取FlowNode后,flowNode.getOutgoingFlows方法获取的
代码如下:
/**
* 获取当前任务的所有分支条件集合
* 例如:["审核.通过"},{"审核.不通过"}]
* @param task
* @return
*/
public List<String> getOutgoingFlows(Task task) {
try {
String processDefinitionId = task.getProcessDefinitionId();
BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(processDefinitionId);
Execution execution = processEngine.getRuntimeService().createExecutionQuery().executionId(task.getExecutionId()).singleResult();
String activityId = execution.getActivityId();
execution.getProcessInstanceId();
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
List<SequenceFlow> sequenceFlows = flowNode.getOutgoingFlows();
return returnOutgoingString(sequenceFlows,bpmnModel);
}catch (Exception e){
log.error("activitiService getOutgoingFlows error:"+e);
return null;
}
}
public List<String> returnOutgoingString(List<SequenceFlow> sequenceFlows, BpmnModel bpmnModel){
List<String> result = new ArrayList<>();
for (SequenceFlow sequenceFlow : sequenceFlows) {
if(sequenceFlow.getTargetFlowElement() instanceof ExclusiveGateway){
FlowElement flowElement = sequenceFlow.getTargetFlowElement();
FlowNode flowNode1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(flowElement.getId());
List<String> subOutgoingMaps = returnOutgoingString(flowNode1.getOutgoingFlows(),bpmnModel);
if(subOutgoingMaps.size()>0 && subOutgoingMaps!=null) {
result.addAll(subOutgoingMaps);
}
} else {
Matcher m = pattern.matcher(sequenceFlow.getConditionExpression().trim());
Matcher m1 = mutiInstanceFlowPattern.matcher(sequenceFlow.getConditionExpression().trim());
String condition = null;
if(m.find() && m.group(1) != null && !"".equals(m.group(1))){
condition = m.group(1);
} else if(m1.find() && m1.group(1) != null && !"".equals(m1.group(1))){
condition = m1.group(1);
}
if(condition != null){
//返回给前端流转时选择的内容不带ProviderName
if(condition.indexOf("|")>0)
condition=condition.split("\\|")[1];
if(!result.contains(condition)){
result.add(condition);
}
}
}
}
return result;
}
如果当前流程实例对应的任务有多条说明该任务可能是会签任务,执行时,只需执行自己的任务即可;以下代码最重要的为最下面的taskNext方法;
具体过程为:
(1)找到当前的task
(2)为当前的任务添加备注(addComment)
(3)流程变量中添加resultInfo变量
如果当前流程为会签流程,则resultInfo为一个数组,保存了每个会签用户的流转信息;
如果当前流程为普通流程,则resultInfo为一个Object变量,保存了当前流程的流转信息;
/**
* 流程流转
* @param instanceId 流程实例id
* @param map 参数
* @param comments 备注说明
* @param userId 流转谁的任务
* @return 是否成功
*/
public boolean instanceNext(String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
try
{
List<Task> tasks = taskService.createTaskQuery().processInstanceId(instanceId).list();
if (tasks.size() > 0)
{
if (tasks.size() > 1) {
if(userId!=null&&!"".equals(userId))
return instanceOwnNext(instanceId,map,resultInfo,comments,userId);
else {
log.error("ActivitiService instanceNext error:more than one task found,if it is a multi-instance task,please fill the last userId param or use taskNext instead.");
return false;
}
}
else
return taskNext(tasks.get(0).getId(),instanceId,map,resultInfo,comments,userId);
} else
return false;
}
catch (Exception e)
{
log.error("ActivitiService instanceNext error:",e);
return false;
}
}
public boolean instanceOwnNext(String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
try{
List<Task> tasks=taskService.createTaskQuery().processInstanceId(instanceId).taskAssignee(userId).list();
if(tasks.size()>0) {
if (tasks.size() > 1) {
log.info("ActivitiService instanceOwnNext info:more than one task assigned to the user found,all tasks will be process forward.");
}
boolean result=true;
for(Task task:tasks){
result=result&&taskNext(task.getId(),instanceId,map,resultInfo,comments,userId);
}
return result;
}
else
{
log.error("ActivitiService instanceOwnNext error:no task exists");
return false;
}
}catch(Exception e){
log.error("ActivitiService instanceOwnNext error:",e);
return false;
}
}
/**
* 流程流转
* @param instanceId 流程实例id
* @param map 参数
* @param comments 备注说明
* @param userId 流转谁的任务
* @return 是否成功
*/
public boolean instanceNext(String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
try
{
List<Task> tasks = taskService.createTaskQuery().processInstanceId(instanceId).list();
if (tasks.size() > 0)
{
if (tasks.size() > 1) {
if(userId!=null&&!"".equals(userId))
return instanceOwnNext(instanceId,map,resultInfo,comments,userId);
else {
log.error("ActivitiService instanceNext error:more than one task found,if it is a multi-instance task,please fill the last userId param or use taskNext instead.");
return false;
}
}
else
return taskNext(tasks.get(0).getId(),instanceId,map,resultInfo,comments,userId);
} else
return false;
}
catch (Exception e)
{
log.error("ActivitiService instanceNext error:",e);
return false;
}
}
/**
* 流程流转
* @param taskId 任务实例id
* @param instanceId 流程实例id
* @param map 参数
* @param comments 备注说明
* @param userId 流转谁的任务
* @return 是否成功
*/
public boolean taskNext(String taskId,String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
try
{
Task task=taskService.createTaskQuery().taskId(taskId).singleResult();
if(resultInfo !=null){
resultInfo = java.net.URLDecoder.decode(resultInfo,"UTF-8");
}
Authentication.setAuthenticatedUserId(userId);
taskService.addComment(taskId, instanceId,resultInfo!=null?resultInfo:""+";"+comments!=null?comments:"");
if(userId!=null&&!"".equals(userId)){
if(task.getAssignee()!=null&&!task.getAssignee().equals(userId))
log.warn("ActivitiService taskNext warn:task process forward by a user who is not the assignee but " + userId);
}
//如果是会签流程,需要将这些resultInfo累计到一个List中写入map,否则,直接写入map
Map variables = runtimeService.getVariables(task.getExecutionId());
Object nrOfInstances = variables.get("nrOfInstances");//多实例总数
if(nrOfInstances!=null){
int numOfInstances = (int)nrOfInstances;
if(numOfInstances >0) {
int nrOfCompletedInstances = (int)variables.get("nrOfCompletedInstances");//已经循环的次数
List<String> resultInfos = new ArrayList<>();
if(nrOfCompletedInstances > 0) {
Object resultInfoObj = variables.get("resultInfo");
if(resultInfoObj != null) {
resultInfos = (List<String>)variables.get("resultInfo");
}
}
resultInfos.add(resultInfo);
map.put("resultInfo",resultInfos);
}
}
else
map.put("resultInfo",resultInfo);
map.put("start", false);
taskService.complete(taskId,map);
return true;
}
catch (Exception e)
{
log.error("ActivitiService taskNext error:",e);
return false;
}
}
4 我的任务-跳转任务
从当前任务跳转到流程中的任意一个节点中。例如当前任务流转错误后,想要跳回到前一个节点;
第一步,获取跳转任务列表
思路为:根据当前流程实例查找BpmnModel对象,该对象存储了流程图中的相关信息,那么可以从该变量中获取所有用户任务的名称和id
public List<Map> getUserTaskDefines(String taskId){
List<Map> result = null;
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if(task != null){
result = new ArrayList<>();
BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(task.getProcessDefinitionId());
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
for (FlowElement f : flowElements) {
if (f instanceof UserTask) {
if(!f.getName().equals(task.getName())) {
Map map = new HashMap();
map.put("key", f.getName());
map.put("value", f.getId());
result.add(map);
}
}
}
}
//组合返回List<Map>列表,Key为 usetask.name,value为 usertask.id
return result;
}
第二步,跳转任务
主要过程为:
- 取出BpmnModel
- 根据BpmnModel获取当前流程点和目标流程点
- 记录原活动的方向
- 清理活动方向
- 建立新方向
- 添加跳转的流转记录
- 执行任务
- 恢复原方向
public boolean taskJump(Task fromTask,String targetTask,Map<String,Object> map,String comments,String userId){
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
if(userId!=null&&!"".equals(userId)){
if(fromTask.getAssignee()!=null&&!fromTask.getAssignee().equals(userId))
log.warn("ActivitiService taskJump warn:task jumped by a user who is not the assignee but " + userId);
}
TaskService taskService=processEngine.getTaskService();
String processDefinitionId = fromTask.getProcessDefinitionId();
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(processDefinitionId);
FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(targetTask);
Execution execution = processEngine.getRuntimeService().createExecutionQuery().executionId(fromTask.getExecutionId()).singleResult();
String activityId = execution.getActivityId();
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
//记录原活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
//清理活动方向
flowNode.getOutgoingFlows().clear();
//建立新方向
List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(flowNode);
newSequenceFlow.setTargetFlowElement(myFlowNode);
newSequenceFlowList.add(newSequenceFlow);
flowNode.setOutgoingFlows(newSequenceFlowList);
//若不加,则comments里userId为空
Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
taskService.addComment(fromTask.getId(), fromTask.getProcessInstanceId(),"跳转操作。由“"+fromTask.getName()+"”跳转至“"+myFlowNode.getName()+"”。"+(comments!=null?comments:""));
map.put("start", false);
//完成任务
taskService.complete(fromTask.getId(),map);
//恢复原方向
flowNode.setOutgoingFlows(oriSequenceFlows);
return true;
}
5 更换执行人
具体过程为:先取消任务分配(taskService.unClaim(taskId),然后再由更换的执行人认领任务
/**
* 当流程列表为task时用
* 更换执行人
* @param taskId
* @param userId
* @return
*/
public boolean changeAssignee(String taskId, String comments,String userId){
try {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task != null) {
boolean unClaim = activitiService.unclaimTask(task.getId(), userService.getCurrentUser().getUsername());
if (unClaim) {
Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
taskService.addComment(task.getId(), task.getProcessInstanceId(), "更换执行人操作。行人由“" + task.getAssignee() + "”更换为至“" + userId + "”。" + (comments != null ? comments : ""));
return activitiService.claimTask(task.getId(), userId);
} else {
return false;
}
}
return false;
} catch (Exception e){
logger.error("change assignee error:"+e);
return false;
}
}
/**
* 接件后撤件
* @param taskId 任务id
* @param userId 撤谁的件
* @return 是否成功
*/
public boolean unclaimTask(String taskId,String userId){
try
{
if(userId!=null&&!"".equals(userId)){
Task task=taskService.createTaskQuery().taskId(taskId).singleResult();
if(task.getAssignee()!=null && !task.getAssignee().equals(userId))
log.warn("ActivitiService unclaimTask warn:task unclaim by a user who is not the assignee but " + userId);
}
taskService.unclaim(taskId);
return true;
}
catch (Exception e)
{
log.error("ActivitiService unclaimTask error:",e);
return false;
}
}
/**
* 接件操作
* @param taskId 任务id
* @param userId 接件用户
* @return 是否成功
*/
public boolean claimTask(String taskId,String processInstanceId,String userId){
try
{
Authentication.setAuthenticatedUserId(userId);
taskService.claim(taskId, userId);
if(processInstanceId==null||"".equals(processInstanceId)) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
processInstanceId=task.getProcessInstanceId();
}
taskService.addComment(taskId,processInstanceId,"认领任务");
return true;
}
catch (Exception e)
{
log.error("ActivitiService claimTask error:",e);
return false;
}
}
6 更换版本
第一步获取版本列表;
/**
* 查找当前流程定义的所有版本
* @param processDefinitionKey
* @return
*/
public List<Integer> getVersionsByProcessDefinitionKey(String processDefinitionKey) {
try {
List<ProcessDefinition> processDefinitions = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
List<Integer> list = new ArrayList<>();
for (ProcessDefinition processDefinition : processDefinitions) {
list.add(processDefinition.getVersion());
}
return list;
} catch (Exception e) {
log.error("查询流程所有版本号发生错误", e);
return null;
}
}
第二步,使用命令执行器CommandExecutor执行版本变更
/**
* 当前任务流程版本变更到versionSelected
* @param versionSelected
* @param taskId
* @return
*/
public int changeVersion(int versionSelected,String taskId,String comments) {
try {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if(task!=null) {
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).list();
return changeVersion(versionSelected,processInstances,comments);
}
return -1;
} catch (Exception e) {
log.error("更换流程实例版本发生错误,任务id为:"+taskId, e);
return -1;
}
}
/**
*
* 将选中的流程实例(processInstances)变更到选中的版本(versionSelected)
* @param versionSelected
* @param processInstances
* @return
*/
public int changeVersion(int versionSelected, List<ProcessInstance> processInstances,String comments) {
try {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
String processDefinitionKey = processInstances.get(0).getProcessDefinitionKey();
List<String> processPointsId = new ArrayList<>();
int count = 0;
//根据变更版本和processDefinitionKey获取变更的流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).processDefinitionVersion(versionSelected).singleResult();
BpmnModel model = repositoryService.getBpmnModel(processDefinition.getId());
if(model != null) {
Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
for(FlowElement flowElement : flowElements) {
if(flowElement instanceof UserTask) {
processPointsId.add(flowElement.getId());
}
}
}
outerLoop:
for (ProcessInstance processInstance : processInstances) {
//当前执行的任务节点包含在更新的流程定义的任务节点内时,执行版本变换
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
for (Task task : tasks) {
if (!processPointsId.contains(task.getTaskDefinitionKey())) {
count += 1;
continue outerLoop;
}
}
commandExecutor.execute(new SetProcessDefinitionVersionCmd(processInstance.getId(), versionSelected));
Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
taskService.addComment(tasks.get(0).getId(),processInstance.getId(),"流程版本由“"+processInstance.getProcessDefinitionVersion()+"”变更为“"+versionSelected+"”;"+(comments!=null?comments:""));
}
return count;
} catch (Exception e) {
log.error("更换流程实例版本发生错误", e);
return -1;
}
}
7 查看流转记录
public List<Map> getProcessComments(String taskId) {
try {
List<Map> maps = new ArrayList<>();
Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
// List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(pi.getId()).list();
List<HistoricActivityInstance> hais = historyService.createHistoricActivityInstanceQuery().processInstanceId(pi.getId())
// .activityType("userTask")
.list();
// 查询每个历史活动的批注
for (HistoricActivityInstance hai : hais) {
Map<String, Object> map = new HashMap<>();
String starter = null;
//未完成的不显示
if(hai.getEndTime()!=null) {
if (hai.getActivityType().equals("startEvent")) {
ListIterator<HistoricIdentityLink> historicIdentityLinkListIterator = historyService.getHistoricIdentityLinksForProcessInstance(hai.getProcessInstanceId()).listIterator();
while (historicIdentityLinkListIterator.hasNext()) {
HistoricIdentityLink l = historicIdentityLinkListIterator.next();
if (l.getType().equals("starter")) {
starter = l.getUserId();
}
}
CommentEntityImpl c = new CommentEntityImpl();
c.setTime(hai.getEndTime());
c.setUserId(starter);
LoginUser loginUser = userService.findUserByName(starter);
if (loginUser != null) {
map.put("userName", loginUser.getShowName());
} else {
map.put("userName", "");
}
map.put("historicActivityInstance", hai);
map.put("comments", c);
maps.add(map);
} else if (hai.getActivityType().equals("userTask")) {
String historytaskId = hai.getTaskId();
List<Comment> comments = taskService.getTaskComments(historytaskId);
if (comments != null && comments.size() > 0) {
for (Comment comment : comments) {
map = new HashMap<>();
LoginUser loginUser = userService.findUserByName(comment.getUserId());
if (loginUser != null) {
map.put("userName", loginUser.getShowName());
} else {
map.put("userName", "");
}
map.put("historicActivityInstance", hai);
map.put("comments", comment);
maps.add(map);
}
} else {
LoginUser loginUser = userService.findUserByName(hai.getAssignee());
if (loginUser != null) {
map.put("userName", loginUser.getShowName());
} else {
map.put("userName", "");
}
map.put("historicActivityInstance", hai);
map.put("comments", null);
maps.add(map);
}
}
}
}
return maps;
}catch (Exception e){
log.error("getProcessComments error:"+e);
return null;
}
}