基于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.gitee.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.gitee.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.gitee.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.gitee.approval.pojo.HistoryProcessDTO;
import com.gitee.approval.pojo.ProcessInstanceDTO;
import com.gitee.approval.pojo.ProcessStatusDTO;
import com.gitee.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.gitee.approval.constant.ConstantValues;
import com.gitee.approval.pojo.HistoryProcessDTO;
import com.gitee.approval.pojo.ProcessInstanceDTO;
import com.gitee.approval.pojo.ProcessStatusDTO;
import com.gitee.approval.pojo.TaskInstanceDTO;
import com.gitee.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.gitee.approval.pojo.HistoryProcessDTO;
import com.gitee.approval.pojo.ProcessInstanceDTO;
import com.gitee.approval.pojo.ProcessStatusDTO;
import com.gitee.approval.pojo.ResponseResult;
import com.gitee.approval.pojo.TaskInstanceDTO;
import com.gitee.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/approval-workflow

动态创建工作流的实战源码:https://github.com/tangyibo/workflow

  • 5
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: Spring Boot是一个基于Spring框架的快速开发框架,可以帮助开发者快速搭建基于Spring的应用程序。Flowable是一个程引擎,可以帮助开发者快速实现业务程的自动化管理。Spring Boot和Flowable的结合可以让开发者更加方便地实现业务程的自动化管理,提高开发效率和业务效率。 ### 回答2: SpringBoot Flowable是一款基于SpringBootFlowable和RESTful风格的Web应用程序开发框架。SpringBoot框架是一个独立的、基于Spring框架的应用程序开发框架,它简化了Spring应用程序的配置和部署,并提供快速的开发、测试和部署功能。Flowable框架是基于BPMN2.0标准的一个开源的程引擎框架,它提供了丰富的程定义、任务管理、历史记录、事件跟踪等功能,便于企业进行复杂业务程的管理和优化。 SpringBoot Flowable框架的主要特点包括: 1.高效简便:SpringBoot Flowable采用SpringBoot框架简化了Spring应用程序的配置和部署,同时结合Flowable框架,使程定义和任务管理更加简便高效。 2.易于扩展:SpringBoot Flowable框架采用RESTful风格的接口设计,与其它系统集成更加简单方便,同时也支持二次开发和自定义扩展。 3.丰富的功能:SpringBoot Flowable框架集成了Flowable框架所提供的全部功能,包括程定义、任务管理、历史记录、事件跟踪等,支持企业复杂业务程的管理和优化。 4.支持多平台:SpringBoot Flowable框架可以部署在不同系统平台上,如Windows、Linux等,实现跨平台共享和应用。 在使用SpringBoot Flowable框架的过程中,需要掌握SpringBoot框架和Flowable框架的相关知识,同时注意提升系统的安全性和稳定性。可以参考SpringBootFlowable的官方文档进行学习和参考,也可以借助其它资料和开发者社区进行交和分享。 ### 回答3: Spring Boot是一个开源的Java开发框架,可以快速地开发基于Java的Web应用程序。它提供了许多内置的功能和自动配置,使开发者能够轻松地构建可扩展的、高度可用的应用程序。而Flowable是一个开源的工作引擎,它基于BPMN(Business Process Model and Notation)标准,可以帮助企业完成复杂的业务程,实现业务可视化、自动化转和智能化控制。 Spring Boot和Flowable的结合使得开发者能够轻松地使用Flowable来构建业务程。使用Spring Boot与Flowable编写代码可以快速地实现程引擎的集成和管理,同时也方便了开发人员使用Spring Boot框架进行Web开发。 Spring Boot可以使用简便的方式来配置Flowable,使用它提供的自动配置和starter等模块来进行程引擎的集成和管理。同时,Spring Boot还提供了丰富的开发工具和插件,使得开发者能够轻松地进行代码调试和测试。 在使用Spring Boot和Flowable进行业务程开发时,开发者可以使用Flowable提供的任务列表、程修改等功能,可以轻松地修改业务程,使得业务程变得更加智能化和高效化。同时,Spring Boot与Flowable的结合也使得业务程的开发变得更加简单和可靠。 总之,Spring Boot和Flowable的结合可以使开发者轻松地构建高可用、高效率、智能化的业务程,并且简化了开发和管理程引擎的过程,帮助企业实现业务的自动化和智能化管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值