JEECG集成ACTIVITI

JEECG集成ACTIVITI


演示地址

集成处理

后端

POM依赖

<?xml version="1.0"?>
<project
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
	xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.jeecgframework.boot</groupId>
		<artifactId>d-parent</artifactId>
		<version>2.2.0</version>
	</parent>
	<artifactId>d-twins-activiti</artifactId>
	<name>d-activiti</name>
	<url>http://maven.apache.org</url>
	<properties>
		<activiti.version>5.22.0</activiti.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.activiti</groupId>
			<artifactId>activiti-engine</artifactId>
			<version>${activiti.version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.mybatis</groupId>
					<artifactId>mybatis</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.activiti</groupId>
			<artifactId>activiti-spring</artifactId>
			<version>${activiti.version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.codehaus.jackson</groupId>
					<artifactId>jackson-core-asl</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>slf4j-log4j12</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.activiti</groupId>
			<artifactId>activiti-modeler</artifactId>
			<version>${activiti.version}</version>
			<exclusions>
				<exclusion>
					<artifactId>spring-security-config</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
				<exclusion>
					<artifactId>spring-security-core</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
				<exclusion>
					<artifactId>spring-security-crypto</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
				<exclusion>
					<artifactId>spring-security-web</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.activiti</groupId>
			<artifactId>activiti-diagram-rest</artifactId>
			<version>${activiti.version}</version>
			<exclusions>
				<exclusion>
					<artifactId>spring-security-config</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
				<exclusion>
					<artifactId>spring-security-core</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
				<exclusion>
					<artifactId>spring-security-crypto</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
				<exclusion>
					<artifactId>spring-security-web</artifactId>
					<groupId>org.springframework.security</groupId>
				</exclusion>
			</exclusions>
		</dependency>

		<!-- UUID -->
		<dependency>
			<groupId>com.fasterxml.uuid</groupId>
			<artifactId>java-uuid-generator</artifactId>
			<version>3.1.3</version>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>1.4.197</version>
		</dependency>
	</dependencies>
</project>

新增JSON处理过滤器

过滤器的作用是:在查看流程图时,有这两个带callback的请求,而这个过滤器作用是把这两个链接的结果进行处理。假如不这样做,前端看不到对应的流程图形。

在这里插入图片描述


package org.jeecg.activiti.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JsonpCallbackFilter implements Filter {
	private static final Logger log = LoggerFactory.getLogger(JsonpCallbackFilter.class);

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		Map<String, String[]> parms = httpRequest.getParameterMap();

		if (parms.containsKey("callback")) {
			if (log.isDebugEnabled())
				log.debug("Wrapping response with JSONP callback {}", parms.get("callback")[0]);

			OutputStream out = httpResponse.getOutputStream();

			GenericResponseWrapper wrapper = new GenericResponseWrapper(httpResponse);

			chain.doFilter(request, wrapper);

			// handles the content-size truncation
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
			outputStream.write(new String(parms.get("callback")[0] + "(").getBytes());
			outputStream.write(wrapper.getData());
			outputStream.write(new String(");").getBytes());
			byte jsonpResponse[] = outputStream.toByteArray();

			wrapper.setContentType("text/javascript;charset=UTF-8");
			wrapper.setContentLength(jsonpResponse.length);

			out.write(jsonpResponse);

			out.close();

		} else {
			chain.doFilter(request, response);
		}
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// default implements
	}

	@Override
	public void destroy() {
		// default implements
	}
}


package org.jeecg.activiti.filter;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GenericResponseWrapper extends HttpServletResponseWrapper {

	private ByteArrayOutputStream output;
	private int contentLength;
	private String contentType;

	public GenericResponseWrapper(HttpServletResponse response) {
		super(response);
		output = new ByteArrayOutputStream();
	}

	public byte[] getData() {
		return output.toByteArray();
	}

	public ServletOutputStream getOutputStream() {
		return new FilterServletOutputStream(output);
	}

	public PrintWriter getWriter() {
		return new PrintWriter(getOutputStream(), true);
	}

	public void setContentLength(int length) {
		this.contentLength = length;
		super.setContentLength(length);
	}

	public int getContentLength() {
		return contentLength;
	}

	public void setContentType(String type) {
		this.contentType = type;
		super.setContentType(type);
	}

	public String getContentType() {
		return contentType;
	}
}


package org.jeecg.activiti.filter;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;

public class FilterServletOutputStream extends ServletOutputStream{

	private DataOutputStream stream;
	  @SuppressWarnings("unused")
	  private WriteListener writeListener;

	  public FilterServletOutputStream(OutputStream output) {
	    stream = new DataOutputStream(output);
	  }

	  public void write(int b) throws IOException {
	    stream.write(b);
	  }

	  public void write(byte[] b) throws IOException {
	    stream.write(b);
	  }

	  public void write(byte[] b, int off, int len) throws IOException {
	    stream.write(b, off, len);
	  }
	  
	  @Override
	  public void setWriteListener(WriteListener writeListener) {
	    this.writeListener =  writeListener;
	  }

	  @Override
	  public boolean isReady() {
	    return true;
	  }

}

ACTIVITI 配置文件

查看源码可以知道DynamicDataSourceAutoConfiguration是JEECG的动态数据源核心自动配置类
继承该类可以获取数据源DataSource

package org.jeecg.activiti;


import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.persistence.StrongUuidGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.jeecg.activiti.filter.JsonpCallbackFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.context.WebApplicationContext;

import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;


@Configuration
public class ActivitiConfiguration extends DynamicDataSourceAutoConfiguration{

	
	@Autowired 
	private WebApplicationContext webApplicationContext;
	
	@Bean
    public FilterRegistrationBean<JsonpCallbackFilter> jsonpCallbackFilter() {
        FilterRegistrationBean<JsonpCallbackFilter> frBean = new FilterRegistrationBean<>();
        frBean.setFilter(new JsonpCallbackFilter());
        frBean.addUrlPatterns("/*");
        return frBean;
    }
	
	@Bean("uuidGenerator")
	public StrongUuidGenerator strongUuidGenerator() {
		return new StrongUuidGenerator();
	}
	
	@Bean
	public PlatformTransactionManager platformTransactionManager () {
		return new DataSourceTransactionManager(this.dataSource(this.dynamicDataSourceProvider(this.dynamicDataSourceCreator(webApplicationContext))));
	}
	
	@Bean("processEngineConfiguration")
	public SpringProcessEngineConfiguration springProcessEngineConfiguration () {
		SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
		processEngineConfiguration.setDataSource(this.dataSource(this.dynamicDataSourceProvider(this.dynamicDataSourceCreator(webApplicationContext))));
		processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
		processEngineConfiguration.setJobExecutorActivate(false);
		processEngineConfiguration.setHistory("full");
		processEngineConfiguration.setTransactionManager(platformTransactionManager());
		processEngineConfiguration.setDbIdentityUsed(true);
		processEngineConfiguration.setActivityFontName("\u5B8B\u4F53");
		processEngineConfiguration.setLabelFontName("\u5B8B\u4F53");
		processEngineConfiguration.setIdGenerator(strongUuidGenerator());
		return processEngineConfiguration;
	}
	
	@Bean("processEngine")
	public ProcessEngineFactoryBean processEngineFactoryBean () {
		ProcessEngineFactoryBean engineFactoryBean = new ProcessEngineFactoryBean();
		springProcessEngineConfiguration().buildProcessEngine();
		engineFactoryBean.setProcessEngineConfiguration(springProcessEngineConfiguration());
		return engineFactoryBean;
	}
	
	
	@Bean("repositoryService")
	public RepositoryService repositoryService () {
		return processEngineFactoryBean().getProcessEngineConfiguration().getRepositoryService();
	}
	
	@Bean("runtimeService")
	public RuntimeService runtimeService () {
		return processEngineFactoryBean().getProcessEngineConfiguration().getRuntimeService();
	}
	
	@Bean("historyService")
	public HistoryService historyService() {
		return processEngineFactoryBean().getProcessEngineConfiguration().getHistoryService();
	}
	
	@Bean("taskService")
	public TaskService taskService () {
		return processEngineFactoryBean().getProcessEngineConfiguration().getTaskService();
	}
	
	@Bean("formService")
	public FormService formService () {
		return processEngineFactoryBean().getProcessEngineConfiguration().getFormService();
	}
	
	@Bean("identityService")
	public IdentityService identityService() {
		return processEngineFactoryBean().getProcessEngineConfiguration().getIdentityService();
	}
	
	@Bean("managementService")
	public ManagementService managementService() {
		return processEngineFactoryBean().getProcessEngineConfiguration().getManagementService();
	}
	
}

启动类扫描org.activiti.rest

查看源码activiti-moderler jar包可以知道:ModelEditorJsonRestResource 是获取流程图JSON的类。以及ModelSaveRestResource是流程保存的类。还有StencilsetRestResource是获取国际化资源的类,假如配置官方的ACTIVITI EDITOR,那么则需要扫描org.activiti.rest,否则moderler.html打开会有404的错误。下图是源码ModelEditorJsonRestResource。

/* Licensed under the Apache License, Version 2.0 (the "License");
package org.activiti.rest.editor.model;

import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * @author Tijs Rademakers
 */
@RestController
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
  
  protected static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);
  
  @Autowired
  private RepositoryService repositoryService;
  
  @Autowired
  private ObjectMapper objectMapper;
  
  @RequestMapping(value="/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
  public ObjectNode getEditorJson(@PathVariable String modelId) {
    ObjectNode modelNode = null;
    
    Model model = repositoryService.getModel(modelId);
      
    if (model != null) {
      try {
        if (StringUtils.isNotEmpty(model.getMetaInfo())) {
          modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
        } else {
          modelNode = objectMapper.createObjectNode();
          modelNode.put(MODEL_NAME, model.getName());
        }
        modelNode.put(MODEL_ID, model.getId());
        ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
            new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
        modelNode.put("model", editorJsonNode);
        
      } catch (Exception e) {
        LOGGER.error("Error creating model JSON", e);
        throw new ActivitiException("Error creating model JSON", e);
      }
    }
    return modelNode;
  }
}

启动类设置,到此后端配置完毕。

@Slf4j
@EnableSwagger2
@SpringBootApplication(
scanBasePackages={
        "org.jeecg",
        "org.activiti.rest"
})
@EnableScheduling
public class JeecgApplication extends SpringBootServletInitializer{

  public static void main(String[] args) throws UnknownHostException {

    ConfigurableApplicationContext application = SpringApplication.run(JeecgApplication.class, args);
    Environment env = application.getEnvironment();
    String ip = InetAddress.getLocalHost().getHostAddress();
    String port = env.getProperty("server.port");
    String path = env.getProperty("server.servlet.context-path");
    log.info("\n----------------------------------------------------------\n\t" +
        "Application Jeecg-Boot is running! Access URLs:\n\t" +
        "Local: \t\thttp://localhost:" + port + path + "/\n\t" +
        "External: \thttp://" + ip + ":" + port + path + "/\n\t" +
        "Swagger-UI: \t\thttp://" + ip + ":" + port + path + "/doc.html\n" +
        "----------------------------------------------------------");
  }
  
  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
      return application.sources(JeecgApplication.class);
  }

  /**
  * tomcat-embed-jasper引用后提示jar找不到的问题
  */
  @Bean
  public TomcatServletWebServerFactory tomcatFactory() {
   return new TomcatServletWebServerFactory() {
    @Override
    protected void postProcessContext(Context context) {
     ((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
    }
   };
  }
}

前端

下载资源文件ACTIVITI-EDITOR

下载地址
由于采用前后端分离,这里我们将ACTIVITI-EDITOR 放入public文件夹下

在这里插入图片描述

配置url-config.js

作用是配置url
1、 获取json
2、 获取国际化资源stencilset.json、这里我修改了一下 直接从前端获取,不走后台
3、 保存json
启动1、3就是指向我们之前后端扫描org.activiti.rest下的ModelEditorJsonRestResource 、ModelSaveRestResource

/*
 * Activiti Modeler component part of the Activiti project
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
var KISBPM = KISBPM || {};

KISBPM.URL = {

    getModel: function(modelId) {
        return ACTIVITI.CONFIG.contextRoot + '/model/' + modelId + '/json';
    },

    getStencilSet: function() {
        return '/activiti/stencilset.json?version=' + Date.now();
    },

    putModel: function(modelId) {
        return ACTIVITI.CONFIG.contextRoot + '/model/' + modelId + '/save';
    }
};

功能介绍

创建模型

    @PostMapping(value = "create")
	public Result<?> create(@RequestBody ModelEntity model) {
		try {
			ObjectMapper objectMapper = new ObjectMapper();
			ObjectNode editorNode = objectMapper.createObjectNode();
			editorNode.put("id", "canvas");
			editorNode.put("resourceId", "canvas");
			ObjectNode stencilSetNode = objectMapper.createObjectNode();
			stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
			editorNode.set("stencilset", stencilSetNode);
			Model modelData = repositoryService.newModel();

			ObjectNode modelObjectNode = objectMapper.createObjectNode();
			modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, model.getName());
			modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
			String description = StringUtils.defaultString(model.getMetaInfo());
			modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
			modelData.setMetaInfo(modelObjectNode.toString());
			modelData.setName(model.getName());
			modelData.setKey(StringUtils.defaultString(model.getKey()));

			repositoryService.saveModel(modelData);
			repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));

			return Result.ok(modelData.getId());
		} catch (Exception e) {
			logger.error("创建模型失败:", e);
		}
		return null;
	}

部署流程

@PostMapping(value = "deploy/{modelId}")
	public Result<?> deploy(@PathVariable("modelId") String modelId) {
		try {
			Model modelData = repositoryService.getModel(modelId);
			ObjectNode modelNode = (ObjectNode) new ObjectMapper()
					.readTree(repositoryService.getModelEditorSource(modelData.getId()));
			byte[] bpmnBytes = null;

			BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
			bpmnBytes = new BpmnXMLConverter().convertToXML(model);

			String processName = modelData.getName() + ".bpmn20.xml";
			Deployment deployment = repositoryService.createDeployment().name(modelData.getName())
					.addString(processName, new String(bpmnBytes)).deploy();
			
			modelData.setDeploymentId(deployment.getId());
			repositoryService.saveModel(modelData);
			return Result.ok(deployment.getId());
		} catch (Exception e) {
			logger.error("根据模型部署流程失败:modelId={}", modelId, e);
		}
		return null;
	}

导出模型xml

@GetMapping(value = "export/{modelId}/{type}")
	public void export(@PathVariable("modelId") String modelId, @PathVariable("type") String type,
			HttpServletResponse response) {
		try {
			Model modelData = repositoryService.getModel(modelId);
			BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
			byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());

			JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
			BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);

			// 处理异常
			if (bpmnModel.getMainProcess() == null) {
				response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());
				response.getOutputStream().println("no main process, can't export for type: " + type);
				response.flushBuffer();
				return;
			}

			String filename = "";
			byte[] exportBytes = null;

			String mainProcessId = bpmnModel.getMainProcess().getId();

			if (type.equals("bpmn")) {

				BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
				exportBytes = xmlConverter.convertToXML(bpmnModel);

				filename = mainProcessId + ".bpmn20.xml";
			} else if (type.equals("json")) {

				exportBytes = modelEditorSource;
				filename = mainProcessId + ".json";

			}

			ByteArrayInputStream in = new ByteArrayInputStream(exportBytes);
			IOUtils.copy(in, response.getOutputStream());

			response.setHeader("Content-Disposition", "attachment; filename=" + filename);
			response.flushBuffer();
		} catch (Exception e) {
			logger.error("导出model的xml文件失败:modelId={}, type={}", modelId, type, e);
		}
	}

删除模型

@DeleteMapping(value = "delete")
	public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
		repositoryService.deleteModel(id);
		return Result.ok("删除成功");
	}

上传流程xml

@PostMapping(value = "/uploadXml/{modelId}")
	public Result<?> uploadXml(@RequestParam(value = "file", required = false) MultipartFile file,
			@PathVariable("modelId") String modelId) {

		String fileName = file.getOriginalFilename();

		try {
			InputStream fileInputStream = file.getInputStream();
			Deployment deployment = null;

			String extension = FilenameUtils.getExtension(fileName);
			if (extension.equals("zip") || extension.equals("bar")) {
				ZipInputStream zip = new ZipInputStream(fileInputStream);
				deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
			} else {
				deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
			}

			List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
					.deploymentId(deployment.getId()).list();
			String processDefinitionId = list.get(0).getId();
			convertToModel(processDefinitionId, modelId);
		} catch (Exception e) {
			logger.error("error on deploy process, because of file input stream", e);
		}

		return Result.ok();
	}

	public void convertToModel(String processDefinitionId, String modelId)
			throws UnsupportedEncodingException, XMLStreamException {
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(processDefinitionId).singleResult();
		InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
				processDefinition.getResourceName());
		XMLInputFactory xif = XMLInputFactory.newInstance();
		InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
		XMLStreamReader xtr = xif.createXMLStreamReader(in);
		BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

		BpmnJsonConverter converter = new BpmnJsonConverter();
		com.fasterxml.jackson.databind.node.ObjectNode modelNode = converter.convertToJson(bpmnModel);
		Model modelData = repositoryService.createModelQuery().modelId(modelId).singleResult();
		modelData.setKey(processDefinition.getKey());
		modelData.setName(processDefinition.getResourceName());
		modelData.setCategory(processDefinition.getDeploymentId());
		repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
	}

发起流程

@PostMapping("start")
	public Result<?> start(String tableId, String recordId, String modelId,
			@RequestParam(value = "title", required = false) String title,
			@RequestParam(value = "nextUserId", required = false) String nextUserId, HttpServletRequest request) {
		if (ObjectUtils.isEmpty(modelId)) {
			return Result.error("modelId为空");
		}
		ProcessDefinition processDefinition = sysFlowTableService.queryProcessDefinitionKeyByModelId(modelId);
		if (ObjectUtils.isEmpty(processDefinition)) {
			return Result.error("未绑定流程");
		}
		String token = TokenUtils.getTokenByRequest(request);
		String username = JwtUtil.getUsername(token);
		identityService.setAuthenticatedUserId(username);
		String businessKey = tableId + "@" + recordId;
		// 判断key是否被占用
		List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
				.processInstanceBusinessKey(businessKey).list();
		if (!CollectionUtils.isEmpty(instances)) {
			return Result.error("业务key已经被占用");
		}
		if (StringUtils.isEmpty(title)) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			title = String.format("%s于%s发起了流程 %s", username, sdf.format(new Date()), 
					StringUtils.isEmpty(processDefinition.getName()) ? "" : "[" + processDefinition.getName() + "]");
		}
		ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processInstanceName(title)
				.processDefinitionId(processDefinition.getId()).businessKey(businessKey).start();
		if (!StringUtils.isEmpty(nextUserId)) {
			List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
			for (Task task : taskList) {
				taskService.setAssignee(task.getId(), nextUserId);
			}
		}
		Result<String> result = new Result<>();
		result.setResult(processInstance.getId());
		return result;
	}

完成任务

    @SuppressWarnings("unchecked")
	@PutMapping("complete/{taskId}")
	public Result<?> complete(@PathVariable String taskId, @RequestBody Map<String, Object> map) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task == null) {
			return Result.error("任务不存在");
		}
		String processInstanceId = task.getProcessInstanceId();
		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		identityService.setAuthenticatedUserId(sysUser.getUsername());
		Map<String, Object> variables = (Map<String, Object>) map.get("variables");
		Object comment = map.get("comment");
		if (!ObjectUtils.isEmpty(comment)) {
			taskService.addComment(taskId, processInstanceId, comment.toString());
		}
		Object operation = map.get("operation");
		if (!ObjectUtils.isEmpty(operation)) {
			taskService.addComment(taskId, processInstanceId, "operation", operation.toString());
		}
		if (variables != null && !variables.isEmpty()) {
			taskService.complete(taskId, variables);
		} else {
			taskService.complete(taskId);
		}
		Object nextNodeAssgine = map.get("nextNodeAssgine");
		if (!ObjectUtils.isEmpty(nextNodeAssgine)) {
			List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
    		if (!CollectionUtils.isEmpty(list)) {
    			for (Task t : list) {
        			taskService.setAssignee(t.getId(), nextNodeAssgine.toString());   
    			}
    		}
		}
		Result<String> result = new Result<>();
		result.setSuccess(true);
		return result;
	}

驳回任务

@PutMapping("rejectTaskToPrevious/{taskId}")
	public Result<?> rejectTaskToPrevious(@PathVariable String taskId, 
			@RequestParam(value = "targetId", required = false) String targetId) {
		if (StringUtils.isEmpty(targetId)) {
			Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
			if (task == null) {
				return Result.error("任务不存在");
			}
			String processInstanceId = task.getProcessInstanceId();
			List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByTaskCreateTime().asc().list();
		    if (historicTaskInstances.size() == 1) {
		    	return Result.error("此节点不支持驳回");
		    }
			String previousTaskId = historicTaskInstances.get(historicTaskInstances.size() - 2).getId();
			flowService.rejectTask(taskId, previousTaskId);
		} else {
			flowService.rejectTask(taskId, targetId);
		}
		return Result.ok();	
	}
@Override
	public void rejectTask(String currentTaskId, String targetTaskId) {
		HistoricTaskInstance targetHistoryTask = historyService.createHistoricTaskInstanceQuery().taskId(targetTaskId).singleResult();
		String targetTaskDefinitionId = targetHistoryTask.getTaskDefinitionKey();
		ProcessDefinitionImpl definitionImpl = (ProcessDefinitionImpl) repositoryService.getProcessDefinition(targetHistoryTask.getProcessDefinitionId());
		ActivityImpl targetActivityImpl = definitionImpl.findActivity(targetTaskDefinitionId);
		HistoricTaskInstance currentHistoryTask = historyService.createHistoricTaskInstanceQuery().taskId(currentTaskId).singleResult();
		String currentTaskDefinitionId = currentHistoryTask.getTaskDefinitionKey();
		ActivityImpl currentActivityImpl = definitionImpl.findActivity(currentTaskDefinitionId);
		List<PvmTransition> pvmTransitions = currentActivityImpl.getOutgoingTransitions();
		List<PvmTransition> copyTansitions = new ArrayList<>();
		for (PvmTransition pvmTransition : pvmTransitions) {
			copyTansitions.add(pvmTransition);
		}
		currentActivityImpl.getOutgoingTransitions().clear();
		TransitionImpl transitionImpl = currentActivityImpl.createOutgoingTransition();
		transitionImpl.setDestination(targetActivityImpl);
		HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(targetHistoryTask.getProcessInstanceId()).singleResult();
		// 完成惹怒
		taskService.complete(currentTaskId, historicProcessInstance.getProcessVariables());
		String assignee = targetHistoryTask.getAssignee();
		if (!StringUtils.isEmpty(assignee)) {
			Task task = taskService.createTaskQuery().processInstanceId(historicProcessInstance.getId()).singleResult();
			taskService.setAssignee(task.getId(), assignee);
		}
		currentActivityImpl.getOutgoingTransitions().remove(transitionImpl);
		for (PvmTransition pvmTransition : copyTansitions) {
			currentActivityImpl.getOutgoingTransitions().add(pvmTransition);
		}
	}

上传附件

@PostMapping("addAttachmentFile/{taskId}")
	public Result<?> addAttachmentFile (@RequestParam("file") MultipartFile file, @PathVariable String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String attachmentType = file.getContentType() + ";" + FilenameUtils.getExtension(file.getOriginalFilename());
		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		identityService.setAuthenticatedUserId(sysUser.getUsername());
		try {
			Attachment attachment = taskService.createAttachment(attachmentType, taskId, task.getProcessInstanceId(), file.getOriginalFilename(), file.getOriginalFilename(), file.getInputStream());
		    return Result.ok(attachment.getId());
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			return Result.error("上传失败");
		}
	}

下载附件

@GetMapping("downloadAttachment/{attachmentId}")
	public void downloadAttachment (@PathVariable String attachmentId, HttpServletResponse response) {
		Attachment attachment = taskService.getAttachment(attachmentId);
		InputStream inputStream = taskService.getAttachmentContent(attachmentId);
		String contentType = StringUtils.substringBefore(attachment.getType(), ";");  
		response.addHeader("Content-Type", contentType + ";charset=UTF-8");  
		// 根据规则获取文件的扩展名  
		String extensionFileName = StringUtils.substringAfter(attachment.getType(), ";");  
		// 以附件名字和扩展名作为下载的文件名 
		String fileName = attachment.getName() + "." + extensionFileName;  
		response.setHeader("Content-Disposition", "attachment; filename=" + fileName);  
		try {
			IOUtils.copy(new BufferedInputStream(inputStream), response.getOutputStream());
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
	}

任务POJO

@Data
public class TaskModel implements Serializable{
	
    /**
	 * 
	 */
	private static final long serialVersionUID = -2814330901636164039L;
	// 标题
	private String title;
	// 流程编号
	private String processKey;
	// 任务Id
	private String taskId;
	// 流程名称
	private String processName;
	// 流程实例
	private String instanceId;
	// 发起人
	private String starter;
	// 开始时间
	private String task_create_time;
	// 当前环节
    private String taskName;
    // 结束时间
    private String task_end_time;
    // businessKey
    private String business_key;
    // 当前审批人
    private String approver;
    // 状态
    private String state;
    // 到期日
    private String dueDate;
    // 实例开始时间
    private String start_time;
    // 实例结束时间
    private String end_time;
    // 耗时
    private String duration;
    
    
}

查询发起流程

public Page<TaskModel> queryStart(Map<String, String> params, int start, int limit) {
		String starter = params.get("starter");
		org.springframework.util.Assert.notNull(starter, "发起人不能为空");
		String processName = params.get("flowName");
		String processCode = params.get("flowCode");
		String state = params.get("state");
		HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
		if (!StringUtils.isEmpty(processName)) {
			query.processDefinitionName(processName);
		}
		if (!StringUtils.isEmpty(processCode)) {
			List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery().processDefinitionKeyLike(processCode).list();
			List<String> keys = definitions.stream().map(ProcessDefinition::getKey).collect(Collectors.toList());
			keys.add(UUID.randomUUID().toString());
			query.processDefinitionKeyIn(keys);
		}
		List<HistoricProcessInstance> historicProcessInstanceList = query.startedBy(starter)
				.orderByProcessInstanceStartTime().desc().listPage(start, limit);
		long total = query.startedBy(starter).count();
		TaskModel taskModel = null;
		List<TaskModel> taskList = new ArrayList<>();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		for (HistoricProcessInstance historicProcessInstance : historicProcessInstanceList) {
			taskModel = new TaskModel();
			taskModel.setProcessKey(historicProcessInstance.getProcessDefinitionId());
			taskModel.setProcessName(historicProcessInstance.getProcessDefinitionName());
			if (!ObjectUtils.isEmpty(historicProcessInstance.getStartTime())) {
				taskModel.setStart_time(sdf.format(historicProcessInstance.getStartTime()));
			}
			if (!ObjectUtils.isEmpty(historicProcessInstance.getEndTime())) {
				taskModel.setEnd_time(sdf.format(historicProcessInstance.getEndTime()));
			}
			taskModel.setInstanceId(historicProcessInstance.getId());
			taskModel.setBusiness_key(historicProcessInstance.getBusinessKey());
			taskModel.setStarter(starter);
			taskModel.setProcessName(historicProcessInstance.getProcessDefinitionName());
			taskModel.setTitle(historicProcessInstance.getName());
			if (!ObjectUtils.isEmpty(historicProcessInstance.getEndTime())) {
				taskModel.setState("已结束");
				taskModel.setEnd_time(sdf.format(historicProcessInstance.getEndTime()));
				taskModel.setDuration(TimeUtils.getTime(historicProcessInstance.getEndTime(), historicProcessInstance.getStartTime()));
				if (StringUtils.isEmpty(state) || "已结束".equals(state)) {
					taskList.add(taskModel);
				}
			} else {
				List<Task> task = taskService.createTaskQuery().processInstanceId(historicProcessInstance.getId())
						.list();
				for (Task taskItem : task) {
					taskModel.setState("运行中");
					taskModel.setTaskName(taskItem.getName());
					if (!ObjectUtils.isEmpty(taskItem.getDueDate())) {
						taskModel.setDueDate(sdf.format(taskItem.getDueDate()));
					}
					if (!ObjectUtils.isEmpty(taskItem.getCreateTime())) {
						taskModel.setTask_create_time(sdf.format(taskItem.getCreateTime()));
					}
					taskModel.setDuration(TimeUtils.getTime(new Date(), historicProcessInstance.getStartTime()));
					taskModel.setTaskId(taskItem.getId());
					TaskModel copyModel = new TaskModel();
					BeanUtils.copyProperties(taskModel, copyModel);
					if (StringUtils.isEmpty(state) || "运行中".equals(state)) {
						taskList.add(copyModel);
					}
				}

			}
		}
		Page<TaskModel> page = new Page<TaskModel>();
		page.setTotal(total);
		page.setRecords(taskList);
		return page;
	}

查询待办

public Page<TaskModel> queryTask(Map<String, String> params, int start, int limit) {
		String assgineId = params.get("assgineId");
		String processName = params.get("processName");
		String starterId = params.get("starterId");
		String isGroupTask = params.get("isGroupTask");
		NativeTaskQuery nativeTaskQuery = taskService.createNativeTaskQuery();
		StringBuilder filter = new StringBuilder();
		// 审批人
		if (!StringUtils.isEmpty(assgineId)) {
			// 是否是组任务
			if ("1".equals(isGroupTask)) {
				filter.append(
						" AND ((A.ASSIGNEE_ IS NULL AND (B.USER_ID_ = #{assgineId} OR B.GROUP_ID_ IN (SELECT GROUP_ID_ FROM ACT_ID_MEMBERSHIP WHERE USER_ID_ = #{assgineId}))))");
			} else {
				filter.append(" AND ( A.ASSIGNEE_ =  #{assgineId}) ");
			}
			nativeTaskQuery.parameter("assgineId", assgineId);
		}
		// 流程名称
		if (!StringUtils.isEmpty(processName)) {
			filter.append(" AND D.NAME_ LIKE #{processName} ");
			nativeTaskQuery.parameter("processName", "%" + processName + "%");
		}
		// 发起人
		if (!StringUtils.isEmpty(starterId)) {
			filter.append(" AND E.START_USER_ID_ = #{starterId} ");
			nativeTaskQuery.parameter("starterId", starterId);
		}
		StringBuilder sql = new StringBuilder();
		sql.append(" SELECT A.* FROM ACT_RU_TASK A ");
		// 是否是组任务
		if ("1".equals(isGroupTask)) {
			sql.append(" LEFT JOIN ACT_RU_IDENTITYLINK B ON B.TASK_ID_ = A.ID_ ");
			sql.append(" LEFT JOIN ACT_RU_IDENTITYLINK C ON C.PROC_INST_ID_ = A.PROC_INST_ID_ ");
		}
		sql.append(" LEFT JOIN ACT_RU_EXECUTION D ON A.EXECUTION_ID_ = D.ID_ ");
		sql.append(" LEFT JOIN ACT_HI_PROCINST E ON A.PROC_INST_ID_ = E.PROC_INST_ID_ ");
		sql.append(" WHERE 1=1 ");
		if (!StringUtils.isEmpty(filter.toString())) {
			sql.append(filter);
		}
		sql.append(" order by A.CREATE_TIME_ DESC ");
		nativeTaskQuery.sql(sql.toString());
		List<Task> tasks = nativeTaskQuery.listPage(start, limit);
		List<TaskModel> taskList = new ArrayList<>();
		TaskModel taskModel = null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		for (Task task : tasks) {
			taskModel = new TaskModel();
			String instanceId = task.getProcessInstanceId();
			HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
					.processInstanceId(instanceId).singleResult();
			taskModel.setTitle(historicProcessInstance.getName());
			taskModel.setProcessKey(historicProcessInstance.getProcessDefinitionKey());
			taskModel.setTaskId(task.getId());
			taskModel.setProcessName(historicProcessInstance.getProcessDefinitionName());
			if (!ObjectUtils.isEmpty(task.getCreateTime())) {
				taskModel.setTask_create_time(sdf.format(task.getCreateTime()).toString());
			}
			taskModel.setInstanceId(instanceId);
			String startId = historicProcessInstance.getStartUserId();
			taskModel.setStarter(startId);
			taskModel.setTaskName(task.getName());
			taskList.add(taskModel);
		}
		String countSql = String.format("SELECT COUNT(*) FROM (%s) tmp; ", sql.toString());
		long count = nativeTaskQuery.sql(countSql).count();
		Page<TaskModel> page = new Page<TaskModel>();
		page.setTotal(count);
		page.setRecords(taskList);
		return page;
	}

查询已办

public Page<TaskModel> queryApproved(Map<String, String> params, int start, int limit) {
		String flowName = params.get("flowName");
		NativeHistoricProcessInstanceQuery query = historyService.createNativeHistoricProcessInstanceQuery();
		StringBuilder sb = new StringBuilder();
		sb.append(" select A.* from ACT_HI_PROCINST A where 1=1 ");
		StringBuilder filters = new StringBuilder();
		if (!StringUtils.isEmpty(flowName)) {
			filters.append(" and A.NAME_ like #{processName} ");
			query.parameter("processName", flowName);
		}
		sb.append(filters.toString());
		sb.append("  ORDER BY A.START_TIME_ DESC ");
		query.sql(sb.toString());
		List<HistoricProcessInstance> executions = query.listPage(start, limit);
		List<TaskModel> taskList = new ArrayList<>();
		TaskModel taskModel = null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		for (HistoricProcessInstance historicProcessInstance : executions) {
			taskModel = new TaskModel();
			String processInstanceId = historicProcessInstance.getId();
			String processDefinitionId = historicProcessInstance.getProcessDefinitionId();
			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
					.processDefinitionId(processDefinitionId).singleResult();
			taskModel.setProcessName(processDefinition.getName());
			if (!ObjectUtils.isEmpty(historicProcessInstance.getStartTime())) {
				taskModel.setStart_time(sdf.format(historicProcessInstance.getStartTime()));
			}
			taskModel.setTitle(historicProcessInstance.getName());
			taskModel.setProcessKey(historicProcessInstance.getProcessDefinitionKey());
			taskModel.setProcessName(historicProcessInstance.getProcessDefinitionName());
			taskModel.setInstanceId(processInstanceId);
			taskModel.setStarter(historicProcessInstance.getStartUserId());
			if (ObjectUtils.isEmpty(historicProcessInstance.getEndTime())) {
				List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
				for (Task task : list) {
					TaskModel copyTask = new TaskModel();
					BeanUtils.copyProperties(taskModel, copyTask);
					copyTask.setTaskId(task.getId());
					copyTask.setTaskName(task.getName());
					if (!ObjectUtils.isEmpty(task.getCreateTime())) {
						copyTask.setTask_create_time(sdf.format(task.getCreateTime()));
					}
					copyTask.setApprover(task.getAssignee());
					taskList.add(copyTask);
				}
			} else {
				taskList.add(taskModel);
			}
		}
		Page<TaskModel> page = new Page<TaskModel>();
		page.setTotal(query.sql(String.format("select count(*) from (%s) tmp ", sb.toString())).count());
		page.setRecords(taskList);
		return page;
	}

下一篇: Springboot集成Flowable

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值