欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Springboot+Flowable的工作流实战

程序员文章站 2022-07-04 20:26:40
...

一、工作流引擎

在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、资源申请流程图

基于Springboot+Flowable的工作流实战

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