环境搭建【这里直接讲解自定义流程】

集成 Activiti Modeler

下载源码

我这里选用的是 Activiti 5.23.0 版本的页面,下载 zip,解压
Activiti 5.23.0 源码

pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?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>
<groupId>com.activiti6</groupId>
<artifactId>activiti6-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<druid.version>1.1.6</druid.version>
<activiti.version>6.0.0</activiti.version>
<apache.xmlgraphics.version>1.7</apache.xmlgraphics.version>
</properties>

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

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

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

<!-- mysql 驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- Druid DataSource -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>

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

<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-css</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svg-dom</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
</dependencies>

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

</project>

代码集成

前端代码集成
1
2
3
4
5
6
7
8
9
10
11
12
在项目中的 resource 文件夹下新建一个 static 文件夹

SpringBoot 能自动读取 static 目录下的静态文件,因此文件夹名称不可随意更改

找到 activiti-webapp-explorer2 包

将 webapp 下的 diagram-viewer 文件夹,editor-app 文件夹,modeler.html 文件复制到 static 下

diagram-viewer:流程图跟踪组件
editor-app:目录中包含设计器里面所有的资源:angular.js、oryx.js以及配套的插件及css
modeler.html:流程设计器的主页面,用来引入各种web资源
路径:Activiti-5.23.0\modules\activiti-webapp-explorer2\src\main\webapp

在这里插入图片描述

后端代码集成

将 Activit 5.23.0 项目中 resource 文件夹下的 stencilset.json 放到自己项目的 resource 目录下
路径:Activiti-5.23.0\modules\activiti-webapp-explorer2\src\main\resources
在我提供的demo中有汉化版本,这里只有国际版本
在这里插入图片描述
找到 activiti-modeler 包

将里面的 StencilsetRestResource.java,ModelEditorJsonRestResource.java,ModelSaveRestResource.java 文件放到自己的项目里

  • StencilsetRestResource.java:用于读取 stencilset.json 文件
  • ModelEditorJsonRestResource.java:用户获取流程数据
  • ModelSaveRestResource.java:用于保存流程数据
    路径:Activiti-5.23.0\modules\activiti-modeler\src\main\java\org\activiti\rest\editor
    在这里插入图片描述
    在这里插入图片描述

    项目结构

    在这里插入图片描述
    创建流程与部署流程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    package com.activiti6.controller;

    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;

    import javax.servlet.http.HttpServletResponse;

    import org.activiti.bpmn.model.BpmnModel;
    import org.activiti.editor.constants.ModelDataJsonConstants;
    import org.activiti.editor.language.json.converter.BpmnJsonConverter;
    import org.activiti.engine.HistoryService;
    import org.activiti.engine.RepositoryService;
    import org.activiti.engine.RuntimeService;
    import org.activiti.engine.repository.Deployment;
    import org.activiti.engine.repository.Model;
    import org.activiti.engine.runtime.ProcessInstance;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.ModelAndView;

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

    /**
    * 流程控制器
    * zhudunfrng
    */
    @Controller
    public class ModelerController{

    private static final Logger logger = LoggerFactory.getLogger(ModelerController.class);

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RuntimeService runtimeService;


    @RequestMapping("index")
    public ModelAndView index(ModelAndView modelAndView) {
    modelAndView.setViewName("index");
    modelAndView.addObject("modelList", repositoryService.createModelQuery().list());
    return modelAndView;
    }

    /**
    * 跳转编辑器页面
    * @return
    */
    @GetMapping("editor")
    public String editor(){
    return "modeler";
    }


    /**
    * 创建模型
    * @param response
    * @param name 模型名称
    * @param key 模型key
    */
    @RequestMapping("/create")
    public void create(HttpServletResponse response,String name,String key) throws IOException {
    logger.info("创建模型入参name:{},key:{}",name,key);
    Model model = repositoryService.newModel();
    ObjectNode modelNode = objectMapper.createObjectNode();
    modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
    modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, "");
    modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
    model.setName(name);
    model.setKey(key);
    model.setMetaInfo(modelNode.toString());
    //存入表act_re_model
    repositoryService.saveModel(model);
    createObjectNode(model.getId());
    response.sendRedirect("/editor?modelId="+ model.getId());
    logger.info("创建模型结束,返回模型ID:{}",model.getId());
    }

    /**
    * 创建模型时完善ModelEditorSource,这里是对画布的相关设置
    * @param modelId
    */
    @SuppressWarnings("deprecation")
    private void createObjectNode(String modelId){
    logger.info("创建模型完善ModelEditorSource入参模型ID:{}",modelId);
    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(modelId,editorNode.toString().getBytes("utf-8"));
    } catch (Exception e) {
    logger.info("创建模型时完善ModelEditorSource服务异常:{}",e);
    }
    logger.info("创建模型完善ModelEditorSource结束");
    }

    /**
    * 发布流程///流程部署
    * @param modelId 模型ID
    * @return
    */
    @ResponseBody
    @RequestMapping("/publish")
    public Object publish(String modelId){
    logger.info("流程部署入参modelId:{}",modelId);
    Map<String, String> map = new HashMap<String, String>();
    try {
    Model modelData = repositoryService.getModel(modelId);//获取相应的模型信息,act_re_model
    byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());//获取相应的流程文件信息,act_ge_bytearray
    if (bytes == null) {//没有相应的流程文件
    logger.info("部署ID:{}的模型数据为空,请先设计流程并成功保存,再进行发布",modelId);
    map.put("code", "FAILURE");
    return map;
    }
    JsonNode modelNode = new ObjectMapper().readTree(bytes);//解析相应的流程文件
    BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
    //部署
    Deployment deployment = repositoryService.createDeployment()
    .name(modelData.getName())//模型名称
    .addBpmnModel(modelData.getKey()+".bpmn20.xml", model)
    .deploy();//部署相应的流程
    modelData.setDeploymentId(deployment.getId());//获取流程部署后的流程id
    repositoryService.saveModel(modelData);//保存到act_re_model表中
    map.put("code", "SUCCESS");
    } catch (Exception e) {
    logger.info("部署modelId:{}模型服务异常:{}",modelId,e);
    map.put("code", "FAILURE");
    }
    logger.info("流程部署出参map:{}",map);
    return map;
    }

    /**
    * 撤销流程定义
    * @param modelId 模型ID
    * @return
    */
    @ResponseBody
    @RequestMapping("/revokePublish")
    public Object revokePublish(String modelId){
    logger.info("撤销发布流程入参modelId:{}",modelId);
    Map<String, String> map = new HashMap<String, String>();
    Model modelData = repositoryService.getModel(modelId);//获取相应的模型信息,act_re_model
    if(null != modelData){
    try {
    /**
    * 参数不加true:为普通删除,如果当前规则下有正在执行的流程,则抛异常
    * 参数加true:为级联删除,会删除和当前规则相关的所有信息,包括历史
    */
    //根据流程的部署ID删除act_re_procdef、act_re_deployment表中的数据,后面的true是将还有未完成任务的流程强制删除
    repositoryService.deleteDeployment(modelData.getDeploymentId(),true);
    map.put("code", "SUCCESS");
    } catch (Exception e) {
    logger.error("撤销已部署流程服务异常:{}",e);
    map.put("code", "FAILURE");
    }
    }
    logger.info("撤销发布流程出参map:{}",map);
    return map;
    }

    /**
    * 删除流程实例 流程正在运行中
    * @param modelId 模型ID
    * @return
    */
    @ResponseBody
    @RequestMapping("/delete")
    public Object deleteProcessInstance(String modelId){
    logger.info("删除流程实例入参modelId:{}",modelId);
    Map<String, String> map = new HashMap<String, String>();
    Model modelData = repositoryService.getModel(modelId);//获取相应的模型信息,act_re_model
    if(null != modelData){
    try {
    //流程启动后会在act_ru_execution表中查到,根据流程定义的key进行查询
    ProcessInstance pi = runtimeService.createProcessInstanceQuery().processDefinitionKey(modelData.getKey()).singleResult();
    if(null != pi) {
    //根据流程实例ID进行删除,act_ru_execution
    runtimeService.deleteProcessInstance(pi.getId(), "");
    //删除历史流程实例act_hi_procinst
    historyService.deleteHistoricProcessInstance(pi.getId());
    }
    map.put("code", "SUCCESS");
    } catch (Exception e) {
    logger.error("删除流程实例服务异常:{}",e);
    map.put("code", "FAILURE");
    }
    }
    logger.info("删除流程实例出参map:{}",map);
    return map;
    }

    /**
    * 启动流程
    */
    @RequestMapping("/start/{id}")
    public void startProcess(@PathVariable("id")String processDefinitionId){
    ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
    //从act_ru_exection中查询
    System.out.println("流程启动成功:\n"+"执行实例id:"+processInstance.getId()
    +"\n流程定义id:"+processInstance.getProcessDefinitionId()
    +"\n流程实例id:"+processInstance.getProcessInstanceId());
    }
    }

    demo源码
    部分内容引自