项目说明

该项目综合参考了:chenhai201 和 secsea

在进行下面的项目实践时,请先生成一个spring boot项目,并配置swagger2,开发工具建议使用IDEA。为了方便正文的描述,下面先给出项目的整体目录结构。

springboot整合activit工作流 springboot整合activiti流程设计器_activiti-modeler

项目已经上传到github,需要可以自行下载

Activitig下载

首先从github上下载Activiti的源码,命令如下

git clone https://github.com/Activiti/Activiti.git
git checkout 5.23.0-release

项目依赖

在项目中加上对activiti的依赖,整体项目pom如下所示

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.self</groupId>
    <artifactId>activitidemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>activitidemo</name>
    <description>Demo project for Spring Boot Activiti</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <activiti.version>5.22.0</activiti.version>
        <swagger.version>2.6.1</swagger.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--<dependency>-->
            <!--<groupId>org.springframework.cloud</groupId>-->
            <!--<artifactId>spring-cloud-starter-eureka</artifactId>-->
        <!--</dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-actuator</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-rest</artifactId>
            <version>${activiti.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-css</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svg-dom</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svggen</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-explorer</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-diagram-rest</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-simple-workflow</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</artifactId>
            <version>${activiti.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--<version>5.1.19</version>-->
            <!--<version>8.0.11</version>-->
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

整合编辑器代码

1)在org.self.activitidemo.config包下建立activiti.modeler.exploer包,然后在下载的Activiti源码中,找到目录modules\activiti-webapp-explorer2\src\main\java\org\activiti\explorer\servlet,然后复制FilterServletOutputStream.java、GenericResponseWrapper.java、JsonpCallbackFilter.java到上面建立的activiti.modeler.exploer包下,如下所示

springboot整合activit工作流 springboot整合activiti流程设计器_activiti流程编辑器_02

2)从目录modules\activiti-webapp-explorer2中复制src\main\resources路径下的stencilset.json文件到本地项目的resources目录下

3)从目录modules\activiti-webapp-explorer2中复制src\main\webapp路径下的文件diagram-viewer和editor-app目录,以及modeler.html文件

4)从目录modules\activiti-webapp-explorer2中复制src\main\java\org\activiti\reset\editor路径下的文件main和model到controller下的editor包

如下所示:

springboot整合activit工作流 springboot整合activiti流程设计器_spring boot activiti_03

修改及扩展代码

1)找到controller.editor.main和controller.editor.model中的StencilsetRestResource.java、ModelEditorJsonRestResource.java、ModelSaveRestResource.java,统一在类上加

@RequestMapping(value = "/service")

2)在Spring boot的启动类ActivitidemoApplication,加上如下注解【ComponentScan里面的第二个包记得换成自己的包】

@ComponentScan({"org.activiti.rest.diagram", "org.self.activitidemo"})
@EnableAutoConfiguration(exclude = {
		org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class,
		org.activiti.spring.boot.SecurityAutoConfiguration.class,
		org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration.class
})

并在启动类中加上Bean,如下

@Bean
public JsonpCallbackFilter filter(){
    return new JsonpCallbackFilter();
}

3)修改resource/static/editor-app下的app-cfg.js为

ACTIVITI.CONFIG = {
    'contextRoot' : '/service',
};

4)在constant包下,添加两个辅助类Pagination.java和ResponseConstantManager.java

package org.self.activitidemo.constant;

import java.util.List;

/**
 * 分页类
 */
public class Pagination<T> {

    public static int DEFAULT_PAGENUMBER = 1;
    public static int DEFAULT_PAGESIZE = 10;

    private List<T> rows = null; //当前返回的记录列表
    private int rowTotal = 0; //总记录数
    private int pageTotal = 0; //总页数


    protected int pageNumber = DEFAULT_PAGENUMBER;  //页码
    protected int pageSize = DEFAULT_PAGESIZE; //每页记录数

    public Pagination() {
        this(DEFAULT_PAGENUMBER, DEFAULT_PAGESIZE);
    }

    public Pagination(int pageNumber, int pageSize) {
        setPageNumber(pageNumber);//pageNumber从1开始不是从0开始
        setPageSize(pageSize);
    }

    public static Pagination getInstance(int pageNumber, int pageSize) {
        return new Pagination(pageNumber, pageSize);
    }

    public static Pagination getInstance2Top(int top) {
        return new Pagination(1, top);
    }

    public List<T> getRows() {
        return rows;
    }

    public void setRows(List<T> rows) {
        this.rows = rows;
    }

    public int getRowTotal() {
        return rowTotal;
    }

    public void setRowTotal(int rowTotal) {
        this.rowTotal = rowTotal;
    }

    public int getPageTotal() {
        return pageTotal;
    }

    public void setPageTotal(int pageTotal) {
        this.pageTotal = pageTotal;
    }

    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getStart(){
        return (getPageNumber()-1)*getPageSize();
    }
    public int getEnd(){
        return getStart() + getPageSize();
    }
}
package org.self.activitidemo.constant;

/**
 * 用于维护响应需要的常量信息
 */
public class ResponseConstantManager {
    public static final String STATUS_SUCCESS = "success";
    public static final String STATUS_FAIL = "fail";

}

在service包下,加入ModelServer.java接口类,建立service.impl包,在service.impl下加入ModelServiceImpl.java,如下

package org.self.activitidemo.service;

import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;

/** 流程模型管理的接口 */
public interface ModelService {
    /** 新建一个模型 */
    HashMap<String, Object> newModel(String modelName, String description, String key);

    /** 获取所有模型 */
    HashMap<String, Object> modelList();

    /** 获取指定页码的模型 */
    HashMap<String, Object> modelsPage(int pageSize, int pageNumber);

    /** 删除指定模型 */
    HashMap<String, Object> deleteModel(String modelId);

    /** 部署模型 */
    HashMap<String, Object> deployModel(String modelId);

    /** 上传已有xml文件,并生成相应模型*/
    HashMap<String, Object> uploadModel(MultipartFile modelFile);



}
package org.self.activitidemo.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.explorer.util.XmlUtil;
import org.apache.commons.lang3.StringUtils;
import org.self.activitidemo.constant.Pagination;
import org.self.activitidemo.constant.ResponseConstantManager;
import org.self.activitidemo.service.ModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;

@Service
public class ModelServiceImpl implements ModelService {
    private static final Logger logger = LoggerFactory.getLogger(ModelServiceImpl.class);

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ObjectMapper objectMapper;

    @Override
    public HashMap<String, Object> newModel(String modelName, String description, String key) {
        HashMap<String, Object> result = new HashMap<>();

        //初始化一个空模型
        Model model = repositoryService.newModel();

        //int revision = 1;

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, modelName);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
//        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

        model.setName(modelName);
        model.setKey(key);
        model.setMetaInfo(modelNode.toString());

        repositoryService.saveModel(model);
        String id = model.getId();

        //完善ModelEditorSource
        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.put("stencilset", stencilSetNode);
        try {
            repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
            result.put("status", ResponseConstantManager.STATUS_SUCCESS);
            result.put("modelId", id);
        } catch (UnsupportedEncodingException e) {
            result.put("status", ResponseConstantManager.STATUS_FAIL);
            result.put("message", e.toString());
        }
        return result;
    }

    @Override
    public HashMap<String, Object> modelList() {
        HashMap<String, Object> result = new HashMap<>();
        List<Model> models = repositoryService.createModelQuery().orderByCreateTime().asc().list();
        result.put("status", ResponseConstantManager.STATUS_SUCCESS);
        result.put("models", models);
        return result;
    }

    @Override
    public HashMap<String, Object> modelsPage(int pageNumber, int pageSize) {
        HashMap<String, Object> result = new HashMap<>();

        Pagination pagination = new Pagination(pageNumber, pageSize);
        int totalCount = (int) repositoryService.createModelQuery().count();
        int totalPages = (int) Math.ceil(totalCount / pageSize);
        pagination.setRowTotal(totalCount);
        pagination.setPageTotal(totalPages);

        List<Model> models = repositoryService.createModelQuery()
                .orderByCreateTime().asc().listPage(pagination.getStart(), pagination.getEnd());
        pagination.setRows(models);
        result.put("status", ResponseConstantManager.STATUS_SUCCESS);
        result.put("modelsPage", pagination);
        return result;
    }

    @Override
    public HashMap<String, Object> deleteModel(String modelId) {
        HashMap<String, Object> result = new HashMap<>();
        repositoryService.deleteModel(modelId);
        result.put("status", ResponseConstantManager.STATUS_SUCCESS);
        return result;
    }

    @Override
    public HashMap<String, Object> deployModel(String modelId) {
        HashMap<String, Object> result = new HashMap<>();

        //获取模型
        Model modelData = repositoryService.getModel(modelId);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

        if (bytes == null) {
            result.put("status", ResponseConstantManager.STATUS_FAIL);
            result.put("message", "模型数据为空,请先设计流程并成功保存,再进行发布。");
            return result;
        }
        try {
            JsonNode modelNode = new ObjectMapper().readTree(bytes);

            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if (model.getProcesses().size() == 0) {
                result.put("status", ResponseConstantManager.STATUS_FAIL);
                result.put("message", "数据模型不符要求,请至少设计一条主线流程。");
            }
            //debug
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
//            System.out.println(new String(bpmnBytes, "UTF-8"));

            //发布流程
            String processName = modelData.getName() + ".bpmn20.xml";
//            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
//                    .name(modelData.getName())
//                    .addString(processName, new String(bpmnBytes, "UTF-8"));
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addBpmnModel(processName, model);

            Deployment deployment = deploymentBuilder.deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
        } catch (Exception e) {
            result.put("status", ResponseConstantManager.STATUS_FAIL);
            result.put("message", e.toString());
        }
        return result;
    }

    @Override
    public HashMap<String, Object> uploadModel(MultipartFile modelFile) {
        HashMap<String, Object> result = new HashMap<>();
        InputStreamReader in = null;
        try {
            try {
                boolean validFile = false;
                String fileName = modelFile.getOriginalFilename();
                if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
                    validFile = true;
                    XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
                    in = new InputStreamReader(new ByteArrayInputStream(modelFile.getBytes()), "UTF-8");
                    XMLStreamReader xtr = xif.createXMLStreamReader(in);
                    BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

                    if (bpmnModel.getMainProcess() == null || bpmnModel.getMainProcess().getId() == null) {
//                        notificationManager.showErrorNotification(Messages.MODEL_IMPORT_FAILED,
//                                i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_BPMN_EXPLANATION));
                        result.put("status", ResponseConstantManager.STATUS_FAIL);
                        result.put("message", "数据模型无效,必须有一条主流程");
                    } else {
                        if (bpmnModel.getLocationMap().isEmpty()) {
//                            notificationManager.showErrorNotification(Messages.MODEL_IMPORT_INVALID_BPMNDI,
//                                    i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_BPMNDI_EXPLANATION));
                            result.put("status", ResponseConstantManager.STATUS_FAIL);
                            result.put("message", "locationMap为空");
                        } else {
                            String processName = null;
                            if (StringUtils.isNotEmpty(bpmnModel.getMainProcess().getName())) {
                                processName = bpmnModel.getMainProcess().getName();
                            } else {
                                processName = bpmnModel.getMainProcess().getId();
                            }
                            Model modelData;
                            modelData = repositoryService.newModel();
                            ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
                            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processName);
                            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
                            modelData.setMetaInfo(modelObjectNode.toString());
                            modelData.setName(processName);

                            repositoryService.saveModel(modelData);

                            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
                            ObjectNode editorNode = jsonConverter.convertToJson(bpmnModel);

                            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
//                            System.out.println(new String(bpmnBytes, "UTF-8"));
//                            System.out.println(editorNode);

                            repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
                            result.put("status", ResponseConstantManager.STATUS_SUCCESS);
                            result.put("modelId", modelData.getId());
                        }
                    }
                } else {
//                    notificationManager.showErrorNotification(Messages.MODEL_IMPORT_INVALID_FILE,
//                            i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_FILE_EXPLANATION));
                    result.put("status", ResponseConstantManager.STATUS_FAIL);
                    result.put("message", "后缀名无效");
                    System.out.println("err3");
                }
            } catch (Exception e) {
//                String errorMsg = e.getMessage().replace(System.getProperty("line.separator"), "<br/>");
//                notificationManager.showErrorNotification(Messages.MODEL_IMPORT_FAILED, errorMsg);
                result.put("status", ResponseConstantManager.STATUS_FAIL);
                result.put("message", e.toString());
            }
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
//                    notificationManager.showErrorNotification("Server-side error", e.getMessage());
                    result.put("status", ResponseConstantManager.STATUS_FAIL);
                    result.put("message", e.toString());
                }
            }
        }
        return result;
    }
}

在controlle.clientr包下,加入 ModelController.java,代码如下:

package org.self.activitidemo.controller.client;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.self.activitidemo.service.ModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;

/**
 * 流程模型Model操作相关
 * Created by chenhai on 2017/5/23.
 */
@Api(description = "流程模型Model操作相关", tags = {"activitimodeler"})
@RestController
@RequestMapping("models")
public class ModelController {
    private  final static Logger logger = LoggerFactory.getLogger(ModelController.class);

    @Autowired
    private ModelService modelService;

    /**
     * 新建一个空模型
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    @ApiOperation(value = "新建一个空模型")
    @PostMapping(value = "newModel")
    public ResponseEntity<?> newModel(@RequestParam(value = "modelName") String modelName,
                                   @RequestParam(value = "description") String description,
                                   @RequestParam(value = "key") String key) throws UnsupportedEncodingException {

        HashMap<String, Object> responseBody = modelService.newModel(modelName, description, key);
        return ResponseEntity.status(HttpStatus.OK).body(responseBody);
    }

    /**
     * 获取所有模型
     *
     * @return
     */
    @ApiOperation(value = "获取所有模型")
    @GetMapping("/getAll")
    public ResponseEntity<?> modelList() {
        HashMap<String, Object> responseBody = modelService.modelList();
        return ResponseEntity.status(HttpStatus.OK).body(responseBody);
    }

    /**
     * 删除模型
     *
     * @param modelId
     * @return
     */
    @ApiOperation(value = "删除模型")
    @DeleteMapping("delete/{modelId}")
    public ResponseEntity<?> deleteModel(@PathVariable("modelId") String modelId) {
        HashMap<String, Object> responseBody = modelService.deleteModel(modelId);
        return ResponseEntity.status(HttpStatus.OK).body(responseBody);
    }

    /**
     * 发布模型为流程定义
     *
     * @param modelId
     * @return
     */
    @ApiOperation(value = "发布模型为流程定义")
    @PostMapping("deploy/{modelId}")
    public ResponseEntity<?> deploy(@PathVariable("modelId") String modelId) {
        HashMap<String, Object> responseBody = modelService.deployModel(modelId);
        return ResponseEntity.status(HttpStatus.OK).body(responseBody);
    }

    @ApiOperation(value = "上传一个已有模型")
    @PostMapping(value = "/uploadFile")
    public ResponseEntity<?> deployUploadedFile(@RequestParam("modelFile") MultipartFile modelFile) {
        HashMap<String, Object> responseBody = modelService.uploadModel(modelFile);
        return ResponseEntity.status(HttpStatus.OK).body(responseBody);
    }
}

5)修改controller.editor.model.ModelSaveRestResource.java,如下

@RestController
@RequestMapping(value = "/service")
public class ModelSaveRestResource implements ModelDataJsonConstants {

  protected static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);

  @Autowired
  private RepositoryService repositoryService;

  @Autowired
  private ObjectMapper objectMapper;

  @RequestMapping(value="/model/{modelId}/save", method = RequestMethod.PUT)
  @ResponseStatus(value = HttpStatus.OK)
  public void saveModel(@PathVariable String modelId, @RequestParam("name") String name,
                        @RequestParam("json_xml") String json_xml, @RequestParam("svg_xml") String svg_xml,
                        @RequestParam("description") String description) {//对接收参数进行了修改
    try {

      Model model = repositoryService.getModel(modelId);

      ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());

      modelJson.put(MODEL_NAME, name);
      modelJson.put(MODEL_DESCRIPTION, description);
      model.setMetaInfo(modelJson.toString());
      model.setName(name);

      repositoryService.saveModel(model);

      repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));

      InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8"));
      TranscoderInput input = new TranscoderInput(svgStream);

      PNGTranscoder transcoder = new PNGTranscoder();
      // Setup output
      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
      TranscoderOutput output = new TranscoderOutput(outStream);

      // Do the transformation
      transcoder.transcode(input, output);
      final byte[] result = outStream.toByteArray();
      repositoryService.addModelEditorSourceExtra(model.getId(), result);
      outStream.close();

    } catch (Exception e) {
      LOGGER.error("Error saving model", e);
      throw new ActivitiException("Error saving model", e);
    }
  }
}

上面 操作之后整体目录结构如下所示:

springboot整合activit工作流 springboot整合activiti流程设计器_spring_04

6)进行数据库配置

先进行配置文件application.properties,内容如下

spring.application.name=activitidemo
server.port=8082

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://XXXX:3306/activiti_demo?ccharacterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=XXX
spring.datasource.password=XXX

spring.jpa.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true

#关闭activiti的自动部署机制
spring.activiti.check-process-definitions=false

从上述的项目github地址中,下载db下的sql文件,并运行生成activiti的数据库表

测试

运行Spring Boot项目,访问http://localhost:8082/swagger-ui.html,界面图如下所示:

springboot整合activit工作流 springboot整合activiti流程设计器_java_05

点击newModel,填充参数,生成一个新的模型图,获取模型编号,如下

springboot整合activit工作流 springboot整合activiti流程设计器_spring_06

访问地址:http://localhost:8082/modeler.html?modelId=297501

界面如下

springboot整合activit工作流 springboot整合activiti流程设计器_spring boot activiti_07

随便编辑,然后保存

自定义编辑器访问地址

加入thymeleaf依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

加入application.properties配置

#thymeleaf
spring.mvc.view.prefix=classpath:/templates
spring.mvc.view.suffix=.html
#spring.thymeleaf.prefix=classpath:/templates
spring.thymeleaf.mode=LEGACYHTML5
#spring.thymeleaf.cache=false

在resources加入templates目录,同时加入modeler.html文件

!doctype html>
<!--[if lt IE 7]>
<html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>
<html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>
<html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js"> <!--<![endif]-->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Activiti Editor</title>
    <meta name="description" content="">
    <meta name="viewport"
          content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, width=device-width">
    <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->

    <link rel="Stylesheet" media="screen" href="editor-app/libs/ng-grid-2.0.7.min.css" type="text/css"/>
    <link rel="stylesheet" href="editor-app/libs/bootstrap_3.1.1/css/bootstrap.min.css"/>

    <link rel="Stylesheet" media="screen" href="editor-app/editor/css/editor.css" type="text/css"/>
    <link rel="stylesheet" href="editor-app/css/style.css" type="text/css"/>

	<link rel="stylesheet" href="editor-app/css/style-common.css">
    <link rel="stylesheet" href="editor-app/css/style-editor.css">

</head>
<body>

	<div class="navbar navbar-fixed-top navbar-inverse" role="navigation" id="main-header">
	    <div class="navbar-header">
            <a href="" ng-click="backToLanding()" class="navbar-brand"
               title="{{'GENERAL.MAIN-TITLE' | translate}}"><span
                    class="sr-only">{{'GENERAL.MAIN-TITLE' | translate}}</span></a>
        </div>
	</div>
	
	<!--[if lt IE 9]>
	<div class="unsupported-browser">
	    <p class="alert error">You are using an unsupported browser. Please upgrade your browser in order to use the
	        editor.</p>
	</div>
	<![endif]-->
	
	<div class="alert-wrapper" ng-cloak>
	    <div class="alert fadein {{alerts.current.type}}" ng-show="alerts.current" ng-click="dismissAlert()">
	        <i class="glyphicon"
	           ng-class="{'glyphicon-ok': alerts.current.type == 'info', 'glyphicon-remove': alerts.current.type == 'error'}"></i>
	        <span>{{alerts.current.message}}</span>
	
	        <div class="pull-right" ng-show="alerts.queue.length > 0">
	            <span class="badge">{{alerts.queue.length + 1}}</span>
	        </div>
	    </div>
	</div>
	
	<div id="main" class="wrapper full clearfix" ng-style="{height: window.height + 'px'}" ng-app="activitiModeler" ng-include="'editor-app/editor.html'">
	</div>

	<!--[if lt IE 9]>
	<script src="editor-app/libs/es5-shim-15.3.4.5/es5-shim.js"></script>
	<script src="editor-app/libs/json3_3.2.6/lib/json3.min.js"></script>
	<![endif]-->
	
	<script src="editor-app/libs/jquery_1.11.0/jquery.min.js"></script>
	<script src="editor-app/libs/jquery-ui-1.10.3.custom.min.js"></script>
	
	<script src="editor-app/libs/angular_1.2.13/angular.min.js"></script>
	<script src="editor-app/libs/angular_1.2.13/angular-animate.min.js"></script>
	<script src="editor-app/libs/bootstrap_3.1.1/js/bootstrap.min.js"></script>
	<script src="editor-app/libs/angular-resource_1.2.13/angular-resource.min.js"></script>
	<script src="editor-app/libs/angular-cookies_1.2.13/angular-cookies.min.js"></script>
	<script src="editor-app/libs/angular-sanitize_1.2.13/angular-sanitize.min.js"></script>
	<script src="editor-app/libs/angular-route_1.2.13/angular-route.min.js"></script>
	<script src="editor-app/libs/angular-translate_2.4.2/angular-translate.min.js"></script>
	<script src="editor-app/libs/angular-translate-storage-cookie/angular-translate-storage-cookie.js"></script>
	<script src="editor-app/libs/angular-translate-loader-static-files/angular-translate-loader-static-files.js"></script>
	<script src="editor-app/libs/angular-strap_2.0.5/angular-strap.min.js"></script>
	<script src="editor-app/libs/angular-strap_2.0.5/angular-strap.tpl.min.js"></script>
	<script src="editor-app/libs/momentjs_2.5.1/momentjs.min.js"></script>
	
	<script src="editor-app/libs/ui-utils.min-0.0.4.js" type="text/javascript"></script>
	<script src="editor-app/libs/ng-grid-2.0.7-min.js" type="text/javascript"></script>
	<script src="editor-app/libs/angular-dragdrop.min-1.0.3.js" type="text/javascript"></script>
	<script src="editor-app/libs/mousetrap-1.4.5.min.js" type="text/javascript"></script>
	<script src="editor-app/libs/jquery.autogrow-textarea.js" type="text/javascript"></script>
	
	<script src="editor-app/libs/prototype-1.5.1.js" type="text/javascript"></script>
	<script src="editor-app/libs/path_parser.js" type="text/javascript"></script>
	
	<script src="editor-app/libs/angular-scroll_0.5.7/angular-scroll.min.js" type="text/javascript"></script>
	
	<!-- Configuration -->
	<script src="editor-app/app-cfg.js?v=1"></script>
	<script src="editor-app/editor-config.js" type="text/javascript"></script>
	<script src="editor-app/configuration/url-config.js" type="text/javascript"></script>
	
	<script src="editor-app/editor/i18n/translation_en_us.js" type="text/javascript"></script>
	<script src="editor-app/editor/i18n/translation_signavio_en_us.js" type="text/javascript"></script>
	<script src="editor-app/editor/oryx.debug.js" type="text/javascript"></script>
	
	<script src="editor-app/app.js"></script>
	
	<script src="editor-app/eventbus.js" type="text/javascript"></script>
	
	<script src="editor-app/editor-controller.js" type="text/javascript"></script>
	<script src="editor-app/stencil-controller.js" type="text/javascript"></script>
	<script src="editor-app/toolbar-controller.js" type="text/javascript"></script>
	<script src="editor-app/header-controller.js" type="text/javascript"></script>
	<script src="editor-app/select-shape-controller.js" type="text/javascript"></script>
	
	<script src="editor-app/editor-utils.js" type="text/javascript"></script>
	<script src="editor-app/configuration/toolbar-default-actions.js" type="text/javascript"></script>
	
	<script src="editor-app/configuration/properties-default-controllers.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-execution-listeners-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-event-listeners-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-assignment-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-fields-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-form-properties-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-in-parameters-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-multiinstance-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-out-parameters-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-task-listeners-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-sequenceflow-order-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-condition-expression-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-signal-definitions-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-signal-scope-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-message-definitions-controller.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-message-scope-controller.js" type="text/javascript"></script>
	
	<script src="editor-app/configuration/toolbar.js" type="text/javascript"></script>
	<script src="editor-app/configuration/toolbar-custom-actions.js" type="text/javascript"></script>
	
	<script src="editor-app/configuration/properties.js" type="text/javascript"></script>
	<script src="editor-app/configuration/properties-custom-controllers.js" type="text/javascript"></script>

</body>
</html>

在controller.client下加入EditorController.java

package org.self.activitidemo.controller.client;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;


//这里要使用Controller,因为RestController是返回json数据的
@Api(tags = "EditorController", description = "模型编辑器")
@Controller
public class EditorController {
    /**
     * 等同于访问:modeler.html?modelId=300001,这个是静态资源直接访问
     * @return
     */
    @ApiOperation(value = "进入流程编辑器,需要接入模型参数,editor?modelId=XXX")
    @GetMapping(value = "editor")
    public String edtior() {
        return "modeler";
    }
}