基于Springboot+Flowable的工作流实战
一、工作流引擎
在OA或一些流程处理的系统项目中,常常会涉及工作流开发相关。Flowable是一个流行的轻量级的采用Java开发的业务流程引擎,是基于Activity5.0的一个分支开发的。通过Flowable流程引擎,我们可以部署BPMN2.0的流程定义(一般为XML文件),通过流程定义创建流程实例,查询和访问流程相关的实例与数据,等等。
flowable的github地址:https://github.com/flowable/flowable-engine
activiti的github地址: https://github.com/Activiti/Activiti
二、 activiti与flowable的区别
flowable项目源自于activiti,通过两个框架的发展史即知。在2016.7~2017.5期间activiti团队内部已经产生了重大的分歧,于是原班核心人员(activiti5以及6比较核心的leader)Tijs Rademakers和Joram Barrez等便去开发flowable框架了,原来的activiti6以及activiti5代码则留给 Salaboy团队进行开发和维护。flowable是基于activiti-6.0.0.Beta4分支开发的。目前Flowable已经修复了activiti6很多的bug,可以实现零成本从activiti迁移到flowable。
三、Flowable项目开发依赖包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--flowable工作流依赖-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.4.1</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
</dependencies>
</dependencies>
四、一个工作流开发实战
1 、要实现的需求目标
实现一个二级审批流程,由申请人发起对资源使用的申请,经过初级审批人员的审批,然后再经过高级审批人员的审批,都审批通过方可批准申请人的资源使用,如果出现一人不同意,则申请人的申请失败。
2、资源申请流程图
3、对外集成方式
由于flowable支持与springboot的无缝集成,通过springboot来开发RESTful接口,内部通过调用flowable引擎实现。
4、具体实现过程
(1)创建一个springboot项目,编写flowable引擎的配置类;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.ProcessEngine;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FlowableBeanConfig {
@Bean
public DefaultClockConfig defaultClock() {
return new DefaultClockConfig();
}
@Bean
public ProcessEngine processEngine(SpringProcessEngineConfiguration configuration, DefaultClockConfig clock) {
configuration.setClock(clock);
//为解决flowable图片中的中文乱码
//configuration.setActivityFontName("宋体");
//configuration.setLabelFontName("宋体");
//configuration.setAnnotationFontName("宋体");
return configuration.buildProcessEngine();
}
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
@Bean
public ManagementService managementService(ProcessEngine processEngine) {
return processEngine.getManagementService();
}
}
(2)定义工作流流程的配置文件,并放置于src/main/resources/processes目录下;
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="requestResourceApprovalProcess" name="Request Resource Approval " isExecutable="true">
<startEvent id="starter" name="Starter"></startEvent>
<serviceTask id="sendJuniorRejectEmail" name="发送初级审批拒绝邮件" activiti:class="com.weishao.approval.delegate.SendJuniorRejectionMailDelegate"></serviceTask>
<endEvent id="juniorRejectEnd" name="Junior Reject End"></endEvent>
<sequenceFlow id="flow5" sourceRef="sendJuniorRejectEmail" targetRef="juniorRejectEnd"></sequenceFlow>
<userTask id="seniorApproval" name="高级审批" activiti:assignee="${seniorAdmin}"></userTask>
<userTask id="juniorApproval" name="初级审批" activiti:assignee="${juniorAdmin}"></userTask>
<exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway1"></exclusiveGateway>
<sequenceFlow id="juniorSuccessFlow" name="同意" sourceRef="exclusivegateway1" targetRef="seniorApproval">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved=='Y'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="juniorRejectFlow" name="拒绝" sourceRef="exclusivegateway1" targetRef="sendJuniorRejectEmail">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved=='N'}]]></conditionExpression>
</sequenceFlow>
<exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway2"></exclusiveGateway>
<sequenceFlow id="flow7" sourceRef="seniorApproval" targetRef="exclusivegateway2"></sequenceFlow>
<endEvent id="approvalSuccessEnd" name="Approval Success End"></endEvent>
<sequenceFlow id="seniorSuccessFlow" name="同意" sourceRef="exclusivegateway2" targetRef="sendApprovalSuccessEmail">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved=='Y'}]]></conditionExpression>
</sequenceFlow>
<serviceTask id="sendSeniorRejectEmail" name="发送高级审批拒绝邮件" activiti:class="com.weishao.approval.delegate.SendSeniorRejectionMailDelegate"></serviceTask>
<endEvent id="seniorRejectEnd" name="Senior Reject End"></endEvent>
<sequenceFlow id="seniorRejectFlow" name="拒绝" sourceRef="exclusivegateway2" targetRef="sendSeniorRejectEmail">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved=='N'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow9" sourceRef="sendSeniorRejectEmail" targetRef="seniorRejectEnd"></sequenceFlow>
<sequenceFlow id="flow11" sourceRef="juniorApproval" targetRef="exclusivegateway1"></sequenceFlow>
<sequenceFlow id="flow12" sourceRef="starter" targetRef="juniorApproval"></sequenceFlow>
<serviceTask id="sendApprovalSuccessEmail" name="发送审批通过邮件" activiti:class="com.weishao.approval.delegate.SendApprovalSuccessEmailDelegate"></serviceTask>
<sequenceFlow id="flow13" sourceRef="sendApprovalSuccessEmail" targetRef="approvalSuccessEnd"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_requestResourceApprovalProcess">
<bpmndi:BPMNPlane bpmnElement="requestResourceApprovalProcess" id="BPMNPlane_requestResourceApprovalProcess">
<bpmndi:BPMNShape bpmnElement="starter" id="BPMNShape_starter">
<omgdc:Bounds height="35.0" width="35.0" x="45.0" y="118.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sendJuniorRejectEmail" id="BPMNShape_sendJuniorRejectEmail">
<omgdc:Bounds height="71.0" width="171.0" x="340.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="juniorRejectEnd" id="BPMNShape_juniorRejectEnd">
<omgdc:Bounds height="35.0" width="35.0" x="408.0" y="385.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="seniorApproval" id="BPMNShape_seniorApproval">
<omgdc:Bounds height="78.0" width="121.0" x="575.0" y="95.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="juniorApproval" id="BPMNShape_juniorApproval">
<omgdc:Bounds height="81.0" width="115.0" x="170.0" y="95.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
<omgdc:Bounds height="40.0" width="40.0" x="405.0" y="113.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
<omgdc:Bounds height="40.0" width="40.0" x="765.0" y="115.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="approvalSuccessEnd" id="BPMNShape_approvalSuccessEnd">
<omgdc:Bounds height="35.0" width="35.0" x="1140.0" y="117.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sendSeniorRejectEmail" id="BPMNShape_sendSeniorRejectEmail">
<omgdc:Bounds height="71.0" width="192.0" x="690.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="seniorRejectEnd" id="BPMNShape_seniorRejectEnd">
<omgdc:Bounds height="35.0" width="35.0" x="768.0" y="385.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sendApprovalSuccessEmail" id="BPMNShape_sendApprovalSuccessEmail">
<omgdc:Bounds height="75.0" width="141.0" x="920.0" y="96.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="425.0" y="301.0"></omgdi:waypoint>
<omgdi:waypoint x="425.0" y="385.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="juniorSuccessFlow" id="BPMNEdge_juniorSuccessFlow">
<omgdi:waypoint x="445.0" y="133.0"></omgdi:waypoint>
<omgdi:waypoint x="575.0" y="134.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="16.0" width="32.0" x="445.0" y="133.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="juniorRejectFlow" id="BPMNEdge_juniorRejectFlow">
<omgdi:waypoint x="425.0" y="153.0"></omgdi:waypoint>
<omgdi:waypoint x="425.0" y="230.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="16.0" width="32.0" x="425.0" y="153.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
<omgdi:waypoint x="696.0" y="134.0"></omgdi:waypoint>
<omgdi:waypoint x="765.0" y="135.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="seniorSuccessFlow" id="BPMNEdge_seniorSuccessFlow">
<omgdi:waypoint x="805.0" y="135.0"></omgdi:waypoint>
<omgdi:waypoint x="920.0" y="133.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="16.0" width="32.0" x="805.0" y="135.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="seniorRejectFlow" id="BPMNEdge_seniorRejectFlow">
<omgdi:waypoint x="785.0" y="155.0"></omgdi:waypoint>
<omgdi:waypoint x="786.0" y="230.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="16.0" width="32.0" x="785.0" y="155.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
<omgdi:waypoint x="786.0" y="301.0"></omgdi:waypoint>
<omgdi:waypoint x="785.0" y="385.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
<omgdi:waypoint x="285.0" y="135.0"></omgdi:waypoint>
<omgdi:waypoint x="405.0" y="133.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12">
<omgdi:waypoint x="80.0" y="135.0"></omgdi:waypoint>
<omgdi:waypoint x="170.0" y="135.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow13" id="BPMNEdge_flow13">
<omgdi:waypoint x="1061.0" y="133.0"></omgdi:waypoint>
<omgdi:waypoint x="1140.0" y="134.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
(3)编写service内部服务接口定义类;
import java.util.List;
import java.util.Map;
import com.weishao.approval.pojo.HistoryProcessDTO;
import com.weishao.approval.pojo.ProcessInstanceDTO;
import com.weishao.approval.pojo.ProcessStatusDTO;
import com.weishao.approval.pojo.TaskInstanceDTO;
public interface IProcessService {
/**
* 启动一个审批流程
*
* @param resourceId 请求的资源ID
* @param requestUser 请求发起用户
* @param juniorAdmin 初级审批用户
* @param seniorAdmin 中级审批用户
* @return
*/
ProcessInstanceDTO startProcess(String resourceId, String requestUser, String juniorAdmin, String seniorAdmin);
/**
* 获取某人的待办任务列表
*
* @param assignee
* @return
*/
List<TaskInstanceDTO> getTaskInstance(String assignee);
/**
* 审批一个任务
* @param bool 是否审批通过
* @param description 审批意见
* @param taskId 审批任务ID
*/
void completeTask(Boolean bool, String comment, String taskId);
/**
* 查询一个流程实例的进展状态
*
* @param processInstanceId
* @return
*/
List<ProcessStatusDTO> queryProcessStatus(String processInstanceId);
/**
* 查询一个流程实例的关联变量数据
*
* @param processInstanceId
* @return
*/
Map<String,Object> queryProcessVariables(String processInstanceId);
/**
* 获取某人的审批历史数据
*
* @param assignee
* @return
*/
List<HistoryProcessDTO> getHistoryProcess(String assignee);
}
(4)编写基于flowable的具体核心实现类
import java.util.Map;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.weishao.approval.constant.ConstantValues;
import com.weishao.approval.pojo.HistoryProcessDTO;
import com.weishao.approval.pojo.ProcessInstanceDTO;
import com.weishao.approval.pojo.ProcessStatusDTO;
import com.weishao.approval.pojo.TaskInstanceDTO;
import com.weishao.approval.service.IProcessService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@Slf4j
@Service("FlowableProcessService")
public class FlowableProcessServiceImpl implements IProcessService {
@Autowired
RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
public boolean isFinished(String processInstanceId) {
return historyService.createHistoricProcessInstanceQuery()
.finished().processInstanceId(processInstanceId)
.count() > 0;
}
@Override
public ProcessInstanceDTO startProcess(String resourceId, String requestUser, String juniorAdmin, String seniorAdmin) {
//这里将启动时相关参数附带到流程实例变量中,为后续接口和BPMN查询使用
Map<String, Object> variables = new HashMap<>();
variables.put("resourceId", resourceId);// 请求的资源ID
variables.put("requestUser", requestUser);// 请求发起用户
variables.put("juniorAdmin", juniorAdmin); // 初级审批用户
variables.put("seniorAdmin", seniorAdmin); // 高级审批用户
ProcessInstance instance=runtimeService.startProcessInstanceByKey(ConstantValues.FLOWABLE_PROCESS_BPMN_KEY, variables);
ProcessInstanceDTO ret=new ProcessInstanceDTO();
ret.setProcessInstanceId(instance.getProcessInstanceId());
ret.setProcessDeploymentId(instance.getDeploymentId());
return ret;
}
@Override
public List<TaskInstanceDTO> getTaskInstance(String assignee) {
List<TaskInstanceDTO> result=new ArrayList<TaskInstanceDTO>();
List<Task> tasks= taskService.createTaskQuery().taskAssignee(assignee).orderByTaskCreateTime().desc().list();
for(Task t: tasks) {
String taskId=t.getId();
Map<String, Object> processVariables = taskService.getVariables(taskId);
Date createTime=t.getCreateTime();
String requestUser=(String) processVariables.get("requestUser");
String resourceId=(String) processVariables.get("resourceId");
TaskInstanceDTO td=new TaskInstanceDTO();
td.setTaskId(taskId);
td.setTaskName(t.getName());
td.setProcessInstanceId(t.getProcessInstanceId());
td.setRequestUser(requestUser);
td.setResourceId(resourceId);
td.setCreateTime(createTime);
result.add(td);
}
return result;
}
@Override
public void completeTask(Boolean bool, String comment, String taskId) {
Map<String, Object> taskVariables = new HashMap<>();
taskVariables.put("approved", bool?"Y":"N");
//审核结果和审核意见都封装为JSON然后放在评论里,后续需要进行逆操作。
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> map=new HashMap<String,String>();
map.put("approved", bool?"Y":"N");
map.put("comment", comment);
try {
String json = objectMapper.writeValueAsString(map);
taskService.addComment(taskId, null, json);
taskService.complete(taskId, taskVariables);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public List<ProcessStatusDTO> queryProcessStatus(String processInstanceId) {
List<ProcessStatusDTO> result = new ArrayList<ProcessStatusDTO>();
List<HistoricTaskInstance> htis = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId).list();
if(htis.size()<=0) {
throw new RuntimeException("Process instance [" + processInstanceId + "] not exist");
}
for (HistoricTaskInstance hti : htis) {
String taskId = hti.getId();
String taskName = hti.getName();
String assignee = hti.getAssignee();
Date createTime = hti.getCreateTime();
String comment = null;
String approved=null;
List<Comment> comments = taskService.getTaskComments(taskId);
if (comments.size() > 0) {
comment = comments.get(0).getFullMessage();
if(null!=comment) {
//这里进行评论的JSON数据的逆操作提取数据
ObjectMapper mapper = new ObjectMapper();
try {
@SuppressWarnings("unchecked")
Map<String,Object> data = mapper.readValue(comment, Map.class);
approved=data.get("approved").toString();
comment=data.get("comment").toString();
} catch (Exception e) {
log.error("error in :",e);
}
}
}
ProcessStatusDTO pd=new ProcessStatusDTO();
pd.setTaskName(taskName);
pd.setAssignee(assignee);
pd.setCreateTime(createTime);
pd.setApproved(approved);
pd.setComment(comment);
result.add(pd);
}
return result;
}
@Override
public Map<String,Object> queryProcessVariables(String processInstanceId){
List<HistoricVariableInstance> hvis = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(processInstanceId).list();
if (hvis == null) {
throw new RuntimeException("Process instance [" + processInstanceId + "] not exist");
}
Map<String,Object> ret=new HashMap<String,Object>();
for(HistoricVariableInstance var: hvis) {
ret.put(var.getVariableName(), var.getValue());
}
return ret;
}
@Override
public List<HistoryProcessDTO> getHistoryProcess(String assignee) {
List<HistoryProcessDTO> result=new ArrayList<HistoryProcessDTO>();
List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
.taskAssignee(assignee).finished().orderByHistoricActivityInstanceEndTime().desc().list();
for(HistoricActivityInstance h : activities) {
HistoryProcessDTO d=new HistoryProcessDTO();
d.setProcessInstanceId(h.getProcessInstanceId());
d.setTaskId(h.getTaskId());
d.setStartTime(h.getStartTime());
d.setEndTime(h.getEndTime());
result.add(d);
}
return result;
}
}
(5) 最后完成controller控制器类的编写;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import javax.validation.constraints.NotBlank;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.weishao.approval.pojo.HistoryProcessDTO;
import com.weishao.approval.pojo.ProcessInstanceDTO;
import com.weishao.approval.pojo.ProcessStatusDTO;
import com.weishao.approval.pojo.ResponseResult;
import com.weishao.approval.pojo.TaskInstanceDTO;
import com.weishao.approval.service.IProcessService;
@Validated
@Api(tags = { "审批流程接口" })
@RequestMapping(value = "/process")
@RestController
public class ApprovalController {
@Autowired
@Qualifier("FlowableProcessService")
private IProcessService flowableProcessService;
@RequestMapping(value = "/begin", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ApiOperation(value = "启动一个审批流程", notes = "根据给定的用户参数来启动一个审批流程")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "resourceId", value = "请求审批的资源ID", required = true, dataType = "String"),
@ApiImplicitParam(paramType = "query", name = "requestUser", value = "发起请求的用户ID", required = true, dataType = "String"),
@ApiImplicitParam(paramType = "query", name = "juniorAdmin", value = "初级审批的用户ID", required = true, dataType = "String"),
@ApiImplicitParam(paramType = "query", name = "seniorAdmin", value = "高级审批的用户ID", required = true, dataType = "String") })
public ResponseResult startProcessInstance(@NotBlank(message = "resourceId不能为空") @RequestParam(value = "resourceId") String resourceId,
@NotBlank(message = "requestUser不能为空") @RequestParam(value = "requestUser") String requestUser,
@NotBlank(message = "juniorAdmin不能为空") @RequestParam(value = "juniorAdmin") String juniorAdmin,
@NotBlank(message = "seniorAdmin不能为空") @RequestParam(value = "seniorAdmin") String seniorAdmin) {
ProcessInstanceDTO instance=flowableProcessService.startProcess(resourceId, requestUser, juniorAdmin, seniorAdmin);
return ResponseResult.success(instance);
}
@RequestMapping(value = "/tasks", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ApiOperation(value = "获取指定用户的待办任务", notes = "根据给定的用户来获取其待办任务列表")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "assignee", value = "用户ID", required = true, dataType = "String") })
public ResponseResult getTasks(@NotBlank(message = "assignee不能为空") @RequestParam(value = "assignee") String assignee) {
List<TaskInstanceDTO> tasks = flowableProcessService.getTaskInstance(assignee);
return ResponseResult.success(tasks);
}
@RequestMapping(value = "/complete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ApiOperation(value = "审批指定的任务", notes = "审批指定的任务")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "approved", value = "审批结果", required = true, dataTypeClass = Boolean.class),
@ApiImplicitParam(paramType = "query", name = "comment", value = "审批意见", required = true, dataTypeClass = String.class),
@ApiImplicitParam(paramType = "query", name = "taskId", value = "任务ID", required = true, dataTypeClass = String.class) })
public ResponseResult complete(@RequestParam(value = "approved") Boolean approved,
@NotBlank(message = "comment不能为空") @RequestParam(value = "comment") String comment,
@NotBlank(message = "taskId不能为空") @RequestParam(value = "taskId") String taskId) {
flowableProcessService.completeTask(approved, comment, taskId);
return ResponseResult.success("ok");
}
@RequestMapping(value = "/status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ApiOperation(value = "获取一个流程的进展状态", notes = "根据流程ID获取一个流程的进展状态")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "processInstanceId", value = "流程实例ID", required = true, dataType = "String") })
public ResponseResult getProcessesStatus(@NotBlank(message = "processInstanceId不能为空") @RequestParam(value = "processInstanceId") String processInstanceId) {
Map<String,Object> vars=flowableProcessService.queryProcessVariables(processInstanceId);
List<ProcessStatusDTO> status = flowableProcessService.queryProcessStatus(processInstanceId);
String approved="N";//这里"Y"代表审批同意,"N"代表审批不同意
if(status.size()>=2) {
approved="Y";
}
for(ProcessStatusDTO s: status) {
if(null==s.getApproved() || s.getApproved().equals("N")) {
approved="N";
}
}
vars.put("approved", approved);
Map<String,Object> ret=new HashMap<String,Object>();
ret.put("param", vars);
ret.put("status", status);
return ResponseResult.success(ret);
}
@RequestMapping(value = "/history", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ApiOperation(value = "获取指定用户的历史流程", notes = "根据给定的用户参数来获取其流程信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "assignee", value = "用户ID", required = true, dataType = "String") })
public ResponseResult getHistoryProcesses(@NotBlank(message = "assignee不能为空") @RequestParam(value = "assignee") String assignee) {
List<HistoryProcessDTO> tasks = flowableProcessService.getHistoryProcess(assignee);
return ResponseResult.success(tasks);
}
}
上述的配置文件与JavaDelegate的实现暂且忽略,可直接参考实战源码。
5、附上实战源码
上述实战源码:https://gitee.com/inrgihc/ApprovalWorkflow
动态创建工作流的实战源码:https://github.com/tangyibo/workflow
上一篇: VBS编程教程 (第2篇)
推荐阅读
-
详解MySQL主从复制实战 - 基于GTID的复制
-
详解MySQL主从复制实战 - 基于日志点的复制
-
详解MySQL主从复制实战 - 基于日志点的复制
-
详解MySQL主从复制实战 - 基于GTID的复制
-
ASP.NET Core 实战:基于 Dapper 扩展你的数据访问方法
-
使用ASP.NET Web Api构建基于REST风格的服务实战系列教程——使用Repository模式构建数据库访问层
-
ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露
-
Python实战——基于股票的金融数据量化分析
-
基于机器学习(machine learning)的SEO实战日记2--寻找切入点
-
基于机器学习(machine learning)的SEO实战日记4--数据抓取