Springboot+Activiti6+在线流程编辑器整合

一、引入依赖

pom.xml下引入activiti相关库


        
            org.activiti
            activiti-spring-boot-starter-basic
            ${activiti.version}
        

        
            org.activiti
            activiti-spring-boot-starter-rest-api
            ${activiti.version}
        

        
            org.activiti
            activiti-spring-boot-starter-actuator
            ${activiti.version}
        

        
            org.activiti
            activiti-explorer
            5.22.0
            
                
                    org.slf4j
                    slf4j-log4j12
                
            
        

        
            org.apache.xmlgraphics
            batik-codec
            1.7
        

        
            org.apache.xmlgraphics
            batik-css
            1.7
        

        
            org.apache.xmlgraphics
            batik-svg-dom
            1.7
        

        
            org.apache.xmlgraphics
            batik-svggen
            1.7
        
        

二、SpringBoot配置

application.yml文件下spring配置中加入activiti配置

#activiti配置
  activiti:
    #数据库策略false(默认) true create-drop drop-create
    database-schema-update: true
    #工作流日志级别
    history-level: activity
    #自动部署验证设置:true-开启(默认)
    check-process-definitions: false

三、编写启动类

加入禁用activiti中自动集成的security的权限验证。

可以解决:

1、与springboot中security冲突

2、让我们我们访问时不会弹出登录界面

@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
        org.activiti.spring.boot.SecurityAutoConfiguration.class})

如下图

Springboot+Activiti6+在线流程编辑器整合_第1张图片

四、整合在线设计器(activiti modeler 组件)

1、拉取源码到本地,地址:https://github.com/Activiti/Activiti/tree/5.x

Springboot+Activiti6+在线流程编辑器整合_第2张图片

2、提取页面相关的静态文件,将源码中例子文件夹内 diagram-viewer,editor-app,modeler.html,stencilset.json拷贝到项目中resource下或其下某指定目录

其中的editor-app就是编辑器,modeler.html是编辑器的入口页面。
diagram-viewer是流程跟踪插件,虽然这次用不着,但之后会用到。
还有一个界面组件文件,在resource下,名称叫stencilset.json。本身是英文的,可以通过替换它来达到汉化的效果。但现在还是先把它放到项目中去。

例如:我没直接放到resource下,而是放在其下的static目录下,如图:

Springboot+Activiti6+在线流程编辑器整合_第3张图片

①、源码中相关文件指定位置如图:

Springboot+Activiti6+在线流程编辑器整合_第4张图片

 Springboot+Activiti6+在线流程编辑器整合_第5张图片

 3、提取activiti-modeler项目中的StencilsetRestResource.java(模具集Rest资源),ModelEditorJsonRestResource.java(模板编辑),ModelSaveRestResource.java (模型保存)放置到项目中。

例如:我将以上三个文件统一放在项目的公用目录——activiti相关目录下

Springboot+Activiti6+在线流程编辑器整合_第6张图片

方案一:在源码中找到相关文件,将源文件copy到自己项目中,源码文件位置如图:

Springboot+Activiti6+在线流程编辑器整合_第7张图片

Springboot+Activiti6+在线流程编辑器整合_第8张图片

 方案二:可直接copy下面代码直接使用

ModelEditorJsonRestResource.java

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
@RequestMapping(value = "/activitiService")
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;
  }
}

ModelSaveRestResource.java

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

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.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * 模型保存
 * @author Tijs Rademakers
 */
@RestController
@RequestMapping(value = "/activitiService")
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, @RequestBody MultiValueMap values) {
    try {
      
      Model model = repositoryService.getModel(modelId);
      
      ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
      
      modelJson.put(MODEL_NAME, values.getFirst("name"));
      modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
      model.setMetaInfo(modelJson.toString());
      model.setName(values.getFirst("name"));
      
      repositoryService.saveModel(model);
      
      repositoryService.addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
      
      InputStream svgStream = new ByteArrayInputStream(values.getFirst("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("模型保存失败", e);
      throw new ActivitiException("模型保存失败", e);
    }
  }
}

StencilsetRestResource.java

import java.io.InputStream;

import org.activiti.engine.ActivitiException;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * 加载模板集
 * @author Tijs Rademakers
 */
@RestController
@RequestMapping(value = "/activitiService")
public class StencilsetRestResource {
  
  @RequestMapping(value="/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
  public @ResponseBody String getStencilset() {
    InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("static/stencilset.json");
    try {
      return IOUtils.toString(stencilsetStream, "utf-8");
    } catch (Exception e) {
      throw new ActivitiException("Error while loading stencil set", e);
    }
  }
}

4、以上3个java文件及所有静态页面使用的文件准备好后,开始最后的微调。

①、3个java文件上加上@RequestMapping注解,并指定值,如图:

Springboot+Activiti6+在线流程编辑器整合_第9张图片

②、修改app-cfg.js

Springboot+Activiti6+在线流程编辑器整合_第10张图片

 ③、修改StencilsetRestResource.java,将对应的stencilset.json文件所在路径完整对应,如图:

 Springboot+Activiti6+在线流程编辑器整合_第11张图片

至此相关整合已全部完成,接下来我们模拟创建个实例看下效果吧

五、模拟实例

1、创建一个controller,提供一个调用模拟接口

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/activiti")
public class ActivitiController {
    /**
     * 创建模型
     */
    @RequestMapping("/create")
    public void create(HttpServletRequest request, HttpServletResponse response) {
        try {
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

            RepositoryService repositoryService = processEngine.getRepositoryService();

            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.put("stencilset", stencilSetNode);
            Model modelData = repositoryService.newModel();

            ObjectNode modelObjectNode = objectMapper.createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, "hello1111");
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            String description = "hello1111";
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
            modelData.setMetaInfo(modelObjectNode.toString());
            modelData.setName("hello1111");
            modelData.setKey("12313123");

            //保存模型
            repositoryService.saveModel(modelData);
            repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
            System.out.println(request.getContextPath() + "/modeler.html?modelId=" + modelData.getId());
            response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + modelData.getId());
        } catch (Exception e) {
            System.out.println("创建模型失败:");
        }
    }

}

2、验证是否成功。访问http://localhost:9000/activiti/create

Springboot+Activiti6+在线流程编辑器整合_第12张图片

访问中可能出现的问题:

1、No mapping for GET /autherror

问题分析:

在使用拦截器时,在配置拦截器的时候,由于在 Spring Boot 2.0 之前,我们都是直接继承 WebMvcConfigurerAdapter 类,然后重写 addInterceptors 方法来实现拦截器的配置。但是在 Spring Boot 2.0 之后,该方法已经被废弃了(当然,也可以继续用),取而代之的是 WebMvcConfigurationSupport 方法,如下

1
2
3
4
5
6
7
8
9

@Configuration
public class MyInterceptorConfig extends WebMvcConfigurationSupport {

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

但是,我们会发现会发现一个缺陷,那就是静态资源被拦截了。项目中集成了thymeleaf,使用th:src="/xxx/xxx"这些获取静态资源的方法便会被拦截,这就需要我们手动将静态资源放开。

即除了在 MyInterceptorConfig 配置类中重写 addInterceptors 方法,还需要再重写一个方法 addResourceHandlers,用来将静态资源放开:

1
2
3
4
5
6
7
8
9

/**
 * 用来指定静态资源不被拦截,否则继承WebMvcConfigurationSupport这种方式会导致静态资源无法直接访问
 * @param registry
 */
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    super.addResourceHandlers(registry);
}

当然还有更简单的方法,就是我们可以不继承 WebMvcConfigurationSupport 类,直接实现 WebMvcConfigurer 接口,然后重写 addInterceptors 方法,将自定义的拦截器添加进去即可

1
2
3
4
5
6
7
8

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 实现WebMvcConfigurer不会导致静态资源被拦截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}```

解决方案:

手动配置,WebMvcConfig.java

package com.ihrm;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Config - WebMvc
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    //解决  No mapping for GET 访问静态资源问题
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/META-INF/resources/");
    }
}

2、Bad request

Springboot+Activiti6+在线流程编辑器整合_第13张图片

 问题分析:

@RequestBody MultiValueMap不支持内容类型'application/x-www-form-urlencoded; charset = UTF-8',因为我们的请求是:

Content-Type:

application/x-www-form-urlencoded; charset=UTF-8

Springboot+Activiti6+在线流程编辑器整合_第14张图片

 解决方案:

修改ModelSaveRestResource.java
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ihrm.activiti.editor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
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.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
 * 模型保存
 *
 * @author Tijs Rademakers
 */
@RestController
@RequestMapping(value = "/activitiService")
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, String name, String description, String json_xml,
                          String svg_xml) {
        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("模型保存失败", e);
        }
    }
}

六、汉化

找到stencilset.json,将内容替换

{
  "title" : "BPMN 2.0标准工具",
  "namespace" : "http://b3mn.org/stencilset/bpmn2.0#",
  "description" : "BPMN process editor",
  "propertyPackages" : [ {
    "name" : "process_idpackage",
    "properties" : [ {
      "id" : "process_id",
      "type" : "String",
      "title" : "流程名称",
      "value" : "process",
      "description" : "流程的特殊唯一的名称标识",
      "popular" : true
    } ]
  }, {
    "name" : "overrideidpackage",
    "properties" : [ {
      "id" : "overrideid",
      "type" : "String",
      "title" : "Id",
      "value" : "",
      "description" : "Unique identifier of the element.",
      "popular" : true
    } ]
  }, {
    "name" : "namepackage",
    "properties" : [ {
      "id" : "name",
      "type" : "String",
      "title" : "名称",
      "value" : "",
      "description" : "元素名称",
      "popular" : true,
      "refToView" : "text_name"
    } ]
  }, {
    "name" : "documentationpackage",
    "properties" : [ {
      "id" : "documentation",
      "type" : "Text",
      "title" : "描述",
      "value" : "",
      "description" : "元素描述",
      "popular" : true
    } ]
  }, {
    "name" : "process_authorpackage",
    "properties" : [ {
      "id" : "process_author",
      "type" : "String",
      "title" : "流程作者",
      "value" : "",
      "description" : "流程定义者姓名",
      "popular" : true
    } ]
  }, {
    "name" : "process_versionpackage",
    "properties" : [ {
      "id" : "process_version",
      "type" : "String",
      "title" : "流程版本",
      "value" : "",
      "description" : "标识文档版本",
      "popular" : true
    } ]
  }, {
    "name" : "process_namespacepackage",
    "properties" : [ {
      "id" : "process_namespace",
      "type" : "String",
      "title" : "目标命名空间",
      "value" : "http://www.activiti.org/processdef",
      "description" : "工作流目标命名空间",
      "popular" : true
    } ]
  }, {
    "name" : "asynchronousdefinitionpackage",
    "properties" : [ {
      "id" : "asynchronousdefinition",
      "type" : "Boolean",
      "title" : "异步",
      "value" : "false",
      "description" : "Define the activity as asynchronous.",
      "popular" : true
    } ]
  }, {
    "name" : "exclusivedefinitionpackage",
    "properties" : [ {
      "id" : "exclusivedefinition",
      "type" : "Boolean",
      "title" : "单独",
      "value" : "false",
      "description" : "Define the activity as exclusive.",
      "popular" : true
    } ]
  }, {
    "name" : "executionlistenerspackage",
    "properties" : [ {
      "id" : "executionlisteners",
      "type" : "multiplecomplex",
      "title" : "执行监听器",
      "value" : "",
      "description" : "Listeners for an activity, process, sequence flow, start and end event.",
      "popular" : true
    } ]
  }, {
    "name" : "tasklistenerspackage",
    "properties" : [ {
      "id" : "tasklisteners",
      "type" : "multiplecomplex",
      "title" : "任务监听器",
      "value" : "",
      "description" : "Listeners for a user task",
      "popular" : true
    } ]
  }, {
    "name" : "eventlistenerspackage",
    "properties" : [ {
      "id" : "eventlisteners",
      "type" : "multiplecomplex",
      "title" : "事件监听器",
      "value" : "",
      "description" : "Listeners for any event happening in the Activiti Engine. It's also possible to rethrow the event as a signal, message or error event",
      "popular" : true
    } ]
  }, {
    "name" : "usertaskassignmentpackage",
    "properties" : [ {
      "id" : "usertaskassignment",
      "type" : "Complex",
      "title" : "代理",
      "value" : "",
      "description" : "Assignment definition for the user task",
      "popular" : true
    } ]
  }, {
    "name" : "formpropertiespackage",
    "properties" : [ {
      "id" : "formproperties",
      "type" : "Complex",
      "title" : "动态表单属性",
      "value" : "",
      "description" : "Definition of the form with a list of form properties",
      "popular" : true
    } ]
  }, {
    "name" : "formkeydefinitionpackage",
    "properties" : [ {
      "id" : "formkeydefinition",
      "type" : "String",
      "title" : "自定义表单",
      "value" : "",
      "description" : "用户任务表单编号",
      "popular" : true
    } ]
  }, {
    "name" : "duedatedefinitionpackage",
    "properties" : [ {
      "id" : "duedatedefinition",
      "type" : "String",
      "title" : "到期日期",
      "value" : "",
      "description" : "用户任务到期时间",
      "popular" : true
    } ]
  }, {
    "name" : "prioritydefinitionpackage",
    "properties" : [ {
      "id" : "prioritydefinition",
      "type" : "String",
      "title" : "优先级",
      "value" : "",
      "description" : "用户任务优先级",
      "popular" : true
    } ]
  }, {
    "name" : "duedatedefinitionpackage",
    "properties" : [ {
      "id" : "duedatedefinition",
      "type" : "String",
      "title" : "到期日期",
      "value" : "",
      "description" : "Due date of the user task.",
      "popular" : true
    } ]
  }, {
    "name" : "servicetaskclasspackage",
    "properties" : [ {
      "id" : "servicetaskclass",
      "type" : "String",
      "title" : "监听类",
      "value" : "",
      "description" : "Class that implements the service task logic.",
      "popular" : true
    } ]
  }, {
    "name" : "servicetaskexpressionpackage",
    "properties" : [ {
      "id" : "servicetaskexpression",
      "type" : "String",
      "title" : "表达式",
      "value" : "",
      "description" : "Service task logic defined with an expression.",
      "popular" : true
    } ]
  }, {
    "name" : "servicetaskdelegateexpressionpackage",
    "properties" : [ {
      "id" : "servicetaskdelegateexpression",
      "type" : "String",
      "title" : "委托表达式",
      "value" : "",
      "description" : "Service task logic defined with a delegate expression.",
      "popular" : true
    } ]
  }, {
    "name" : "servicetaskfieldspackage",
    "properties" : [ {
      "id" : "servicetaskfields",
      "type" : "Complex",
      "title" : "字段",
      "value" : "",
      "description" : "Field extensions",
      "popular" : true
    } ]
  }, {
    "name" : "servicetaskresultvariablepackage",
    "properties" : [ {
      "id" : "servicetaskresultvariable",
      "type" : "String",
      "title" : "Result variable name",
      "value" : "",
      "description" : "Process variable name to store the service task result.",
      "popular" : true
    } ]
  }, {
    "name" : "scriptformatpackage",
    "properties" : [ {
      "id" : "scriptformat",
      "type" : "String",
      "title" : "脚本格式",
      "value" : "",
      "description" : "Script format of the script task.",
      "popular" : true
    } ]
  }, {
    "name" : "scripttextpackage",
    "properties" : [ {
      "id" : "scripttext",
      "type" : "Text",
      "title" : "脚本",
      "value" : "",
      "description" : "Script text of the script task.",
      "popular" : true
    } ]
  }, {
    "name" : "ruletask_rulespackage",
    "properties" : [ {
      "id" : "ruletask_rules",
      "type" : "String",
      "title" : "规则",
      "value" : "",
      "description" : "Rules of the rule task.",
      "popular" : true
    } ]
  }, {
    "name" : "ruletask_variables_inputpackage",
    "properties" : [ {
      "id" : "ruletask_variables_input",
      "type" : "String",
      "title" : "输入变量",
      "value" : "",
      "description" : "Input variables of the rule task.",
      "popular" : true
    } ]
  }, {
    "name" : "ruletask_excludepackage",
    "properties" : [ {
      "id" : "ruletask_exclude",
      "type" : "Boolean",
      "title" : "除外",
      "value" : "false",
      "description" : "Use the rules property as exclusion.",
      "popular" : true
    } ]
  }, {
    "name" : "ruletask_resultpackage",
    "properties" : [ {
      "id" : "ruletask_result",
      "type" : "String",
      "title" : "返回变量",
      "value" : "",
      "description" : "Result variable of the rule task.",
      "popular" : true
    } ]
  }, {
    "name" : "mailtasktopackage",
    "properties" : [ {
      "id" : "mailtaskto",
      "type" : "Text",
      "title" : "接收人",
      "value" : "",
      "description" : "The recipients if the e-mail. Multiple recipients are defined in a comma-separated list.",
      "popular" : true
    } ]
  }, {
    "name" : "mailtaskfrompackage",
    "properties" : [ {
      "id" : "mailtaskfrom",
      "type" : "Text",
      "title" : "发件人",
      "value" : "",
      "description" : "The sender e-mail address. If not provided, the default configured from address is used.",
      "popular" : true
    } ]
  }, {
    "name" : "mailtasksubjectpackage",
    "properties" : [ {
      "id" : "mailtasksubject",
      "type" : "Text",
      "title" : "主题",
      "value" : "",
      "description" : "The subject of the e-mail.",
      "popular" : true
    } ]
  }, {
    "name" : "mailtaskccpackage",
    "properties" : [ {
      "id" : "mailtaskcc",
      "type" : "Text",
      "title" : "转发",
      "value" : "",
      "description" : "The cc's of the e-mail. Multiple recipients are defined in a comma-separated list",
      "popular" : true
    } ]
  }, {
    "name" : "mailtaskbccpackage",
    "properties" : [ {
      "id" : "mailtaskbcc",
      "type" : "Text",
      "title" : "密送",
      "value" : "",
      "description" : "The bcc's of the e-mail. Multiple recipients are defined in a comma-separated list",
      "popular" : true
    } ]
  }, {
    "name" : "mailtasktextpackage",
    "properties" : [ {
      "id" : "mailtasktext",
      "type" : "Text",
      "title" : "内容",
      "value" : "",
      "description" : "The content of the e-mail, in case one needs to send plain none-rich e-mails. Can be used in combination with html, for e-mail clients that don't support rich content. The client will then fall back to this text-only alternative.",
      "popular" : true
    } ]
  }, {
    "name" : "mailtaskhtmlpackage",
    "properties" : [ {
      "id" : "mailtaskhtml",
      "type" : "Text",
      "title" : "Html",
      "value" : "",
      "description" : "A piece of HTML that is the content of the e-mail.",
      "popular" : true
    } ]
  }, {
    "name" : "mailtaskcharsetpackage",
    "properties" : [ {
      "id" : "mailtaskcharset",
      "type" : "String",
      "title" : "Charset",
      "value" : "",
      "description" : "Allows to change the charset of the email, which is necessary for many non-English languages. ",
      "popular" : true
    } ]
  }, {
    "name" : "callactivitycalledelementpackage",
    "properties" : [ {
      "id" : "callactivitycalledelement",
      "type" : "String",
      "title" : "被调用元素",
      "value" : "",
      "description" : "Process reference.",
      "popular" : true
    } ]
  }, {
    "name" : "callactivityinparameterspackage",
    "properties" : [ {
      "id" : "callactivityinparameters",
      "type" : "Complex",
      "title" : "输入参数",
      "value" : "",
      "description" : "Definition of the input parameters",
      "popular" : true
    } ]
  }, {
    "name" : "callactivityoutparameterspackage",
    "properties" : [ {
      "id" : "callactivityoutparameters",
      "type" : "Complex",
      "title" : "输出参数",
      "value" : "",
      "description" : "Definition of the output parameters",
      "popular" : true
    } ]
  }, {
    "name" : "cameltaskcamelcontextpackage",
    "properties" : [ {
      "id" : "cameltaskcamelcontext",
      "type" : "String",
      "title" : "Camel内容",
      "value" : "",
      "description" : "An optional camel context definition, if left empty the default is used.",
      "popular" : true
    } ]
  }, {
    "name" : "muletaskendpointurlpackage",
    "properties" : [ {
      "id" : "muletaskendpointurl",
      "type" : "String",
      "title" : "终端url",
      "value" : "",
      "description" : "A required endpoint url to sent the message to Mule.",
      "popular" : true
    } ]
  }, {
    "name" : "muletasklanguagepackage",
    "properties" : [ {
      "id" : "muletasklanguage",
      "type" : "String",
      "title" : "语言",
      "value" : "",
      "description" : "A required definition for the language to resolve the payload expression, like juel.",
      "popular" : true
    } ]
  }, {
    "name" : "muletaskpayloadexpressionpackage",
    "properties" : [ {
      "id" : "muletaskpayloadexpression",
      "type" : "String",
      "title" : "有效载荷表达式",
      "value" : "",
      "description" : "A required definition for the payload of the message sent to Mule.",
      "popular" : true
    } ]
  }, {
    "name" : "muletaskresultvariablepackage",
    "properties" : [ {
      "id" : "muletaskresultvariable",
      "type" : "String",
      "title" : "返回变量",
      "value" : "",
      "description" : "An optional result variable for the payload returned.",
      "popular" : true
    } ]
  }, {
    "name" : "conditionsequenceflowpackage",
    "properties" : [ {
      "id" : "conditionsequenceflow",
      "type" : "Complex",
      "title" : "流转条件",
      "value" : "",
      "description" : "The condition of the sequence flow",
      "popular" : true
    } ]
  }, {
    "name" : "defaultflowpackage",
    "properties" : [ {
      "id" : "defaultflow",
      "type" : "Boolean",
      "title" : "默认流转",
      "value" : "false",
      "description" : "Define the sequence flow as default",
      "popular" : true,
      "refToView" : "default"
    } ]
  }, {
    "name" : "conditionalflowpackage",
    "properties" : [ {
      "id" : "conditionalflow",
      "type" : "Boolean",
      "title" : "Conditional flow",
      "value" : "false",
      "description" : "Define the sequence flow with a condition",
      "popular" : true
    } ]
  }, {
    "name" : "timercycledefinitionpackage",
    "properties" : [ {
      "id" : "timercycledefinition",
      "type" : "String",
      "title" : "循环时间(例:R3/PT10H)",
      "value" : "",
      "description" : "Define the timer with a ISO-8601 cycle.",
      "popular" : true
    } ]
  }, {
    "name" : "timerdatedefinitionpackage",
    "properties" : [ {
      "id" : "timerdatedefinition",
      "type" : "String",
      "title" : "开始时间(ISO-8601)",
      "value" : "",
      "description" : "Define the timer with a ISO-8601 date definition.",
      "popular" : true
    } ]
  }, {
    "name" : "timerdurationdefinitionpackage",
    "properties" : [ {
      "id" : "timerdurationdefinition",
      "type" : "String",
      "title" : "持续时间(例:PT5M)",
      "value" : "",
      "description" : "Define the timer with a ISO-8601 duration.",
      "popular" : true
    } ]
  }, {
    "name" : "timerenddatedefinitionpackage",
    "properties" : [ {
      "id" : "timerenddatedefinition",
      "type" : "String",
      "title" : "结束时间(ISO-8601)",
      "value" : "",
      "description" : "Define the timer with a ISO-8601 duration.",
      "popular" : true
    } ]
  }, {
    "name" : "messagerefpackage",
    "properties" : [ {
      "id" : "messageref",
      "type" : "String",
      "title" : "消息引用",
      "value" : "",
      "description" : "Define the message name.",
      "popular" : true
    } ]
  }, {
    "name" : "signalrefpackage",
    "properties" : [ {
      "id" : "signalref",
      "type" : "String",
      "title" : "信号引用",
      "value" : "",
      "description" : "Define the signal name.",
      "popular" : true
    } ]
  }, {
    "name" : "errorrefpackage",
    "properties" : [ {
      "id" : "errorref",
      "type" : "String",
      "title" : "错误引用",
      "value" : "",
      "description" : "Define the error name.",
      "popular" : true
    } ]
  }, {
    "name" : "cancelactivitypackage",
    "properties" : [ {
      "id" : "cancelactivity",
      "type" : "Boolean",
      "title" : "取消活动",
      "value" : "true",
      "description" : "Should the activity be cancelled",
      "popular" : true,
      "refToView" : [ "frame", "frame2" ]
    } ]
  }, {
    "name" : "initiatorpackage",
    "properties" : [ {
      "id" : "initiator",
      "type" : "String",
      "title" : "发起人",
      "value" : "",
      "description" : "Initiator of the process.",
      "popular" : true
    } ]
  }, {
    "name" : "textpackage",
    "properties" : [ {
      "id" : "text",
      "type" : "String",
      "title" : "Text",
      "value" : "",
      "description" : "The text of the text annotation.",
      "popular" : true,
      "refToView" : "text"
    } ]
  }, {
    "name" : "multiinstance_typepackage",
    "properties" : [ {
      "id" : "multiinstance_type",
      "type" : "kisbpm-multiinstance",
      "title" : "多实例类型",
      "value" : "None",
      "description" : "Repeated activity execution (parallel or sequential) can be displayed through different loop types",
      "popular" : true,
      "refToView" : "multiinstance"
    } ]
  }, {
    "name" : "multiinstance_cardinalitypackage",
    "properties" : [ {
      "id" : "multiinstance_cardinality",
      "type" : "String",
      "title" : "基数(多实例)",
      "value" : "",
      "description" : "Define the cardinality of multi instance.",
      "popular" : true
    } ]
  }, {
    "name" : "multiinstance_collectionpackage",
    "properties" : [ {
      "id" : "multiinstance_collection",
      "type" : "String",
      "title" : "集合(多实例)",
      "value" : "",
      "description" : "Define the collection for the multi instance.",
      "popular" : true
    } ]
  }, {
    "name" : "multiinstance_variablepackage",
    "properties" : [ {
      "id" : "multiinstance_variable",
      "type" : "String",
      "title" : "元素变量(多实例)",
      "value" : "",
      "description" : "Define the element variable for the multi instance.",
      "popular" : true
    } ]
  }, {
    "name" : "multiinstance_conditionpackage",
    "properties" : [ {
      "id" : "multiinstance_condition",
      "type" : "String",
      "title" : "完成条件(多实例)",
      "value" : "",
      "description" : "Define the completion condition for the multi instance.",
      "popular" : true
    } ]
  }, {
    "name" : "isforcompensationpackage",
    "properties" : [ {
      "id" : "isforcompensation",
      "type" : "Boolean",
      "title" : "是否为补偿",
      "value" : "false",
      "description" : "一个标志,标识是否这个活动的目的是为了补偿.",
      "popular" : true,
      "refToView" : "compensation"
    } ]
  }, {
    "name" : "sequencefloworderpackage",
    "properties" : [ {
      "id" : "sequencefloworder",
      "type" : "Complex",
      "title" : "流动顺序",
      "value" : "",
      "description" : "Order outgoing sequence flows.",
      "popular" : true
    } ]
  }, {
    "name" : "signaldefinitionspackage",
    "properties" : [ {
      "id" : "signaldefinitions",
      "type" : "multiplecomplex",
      "title" : "信号定义",
      "value" : "",
      "description" : "Signal definitions",
      "popular" : true
    } ]
  }, {
    "name" : "messagedefinitionspackage",
    "properties" : [ {
      "id" : "messagedefinitions",
      "type" : "multiplecomplex",
      "title" : "消息定义",
      "value" : "",
      "description" : "Message definitions",
      "popular" : true
    } ]
  }, {
    "name" : "istransactionpackage",
    "properties" : [ {
      "id" : "istransaction",
      "type" : "Boolean",
      "title" : "是否事务处理子过程",
      "value" : "false",
      "description" : "A flag that identifies whether this sub process is of type transaction.",
      "popular" : true,
      "refToView" : "border"
    } ]
  }, {
    "name" : "terminateAllpackage",
    "properties" : [ {
      "id" : "terminateAll",
      "type" : "Boolean",
      "title" : "终止全部",
      "value" : "false",
      "description" : "Enable to terminate the process instance",
      "popular" : true
    } ]
  } ],
  "stencils" : [ {
    "type" : "node",
    "id" : "BPMNDiagram",
    "title" : "BPMN-Diagram",
    "description" : "A BPMN 2.0 diagram.",
    "view" : "\n\n  \n  \n    \n    \n    \t\n  \n",
    "icon" : "diagram.png",
    "groups" : [ "Diagram" ],
    "mayBeRoot" : true,
    "hide" : true,
    "propertyPackages" : [ "process_idpackage", "namepackage", "documentationpackage", "process_authorpackage", "process_versionpackage", "process_namespacepackage", "executionlistenerspackage", "eventlistenerspackage", "signaldefinitionspackage", "messagedefinitionspackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ ]
  }, {
    "type" : "node",
    "id" : "StartNoneEvent",
    "title" : "事件",
    "description" : "A start event without a specific trigger",
    "view" : "\n\n  \n  \n  \t\n  \n  \n    \n\t\n  \n",
    "icon" : "startevent/none.png",
    "groups" : [ "启动事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "initiatorpackage", "formkeydefinitionpackage", "formpropertiespackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "Startevents_all", "StartEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "StartTimerEvent",
    "title" : "定时事件",
    "description" : "A start event with a timer trigger",
    "view" : "\n\n  \n  \n  \t\n  \n  \n    \n    \n    \n    \n   \n\t\n  \n",
    "icon" : "startevent/timer.png",
    "groups" : [ "启动事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "timercycledefinitionpackage", "timerdatedefinitionpackage", "timerdurationdefinitionpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "Startevents_all", "StartEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "StartSignalEvent",
    "title" : "信号事件",
    "description" : "A start event with a signal trigger",
    "view" : "\n\n  \n  \n  \t\n  \n  \n\n    \n    \n    \n\t\n  \n",
    "icon" : "startevent/signal.png",
    "groups" : [ "启动事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "signalrefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "Startevents_all", "StartEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "StartMessageEvent",
    "title" : "消息事件",
    "description" : "A start event with a message trigger",
    "view" : "\n\n  \n  \n  \t\n  \n  \n    \n    \n    \n    \n    \n\t\n  \n",
    "icon" : "startevent/message.png",
    "groups" : [ "启动事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "messagerefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "Startevents_all", "StartEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "StartErrorEvent",
    "title" : "异常事件",
    "description" : "A start event that catches a thrown BPMN error",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \n    \n\t\n  \n",
    "icon" : "startevent/error.png",
    "groups" : [ "启动事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "errorrefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "Startevents_all", "StartEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "UserTask",
    "title" : "用户活动",
    "description" : "分配给特定人的任务 ",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n\t\n\t\n\t\t\n\t\t\n\t\n  \n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.user.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "usertaskassignmentpackage", "formkeydefinitionpackage", "duedatedefinitionpackage", "prioritydefinitionpackage", "formpropertiespackage", "tasklistenerspackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "ServiceTask",
    "title" : "服务任务",
    "description" : "An automatic task with service logic",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n\t\n\t\n\t\n\t\n  \n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.service.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "servicetaskclasspackage", "servicetaskexpressionpackage", "servicetaskdelegateexpressionpackage", "servicetaskfieldspackage", "servicetaskresultvariablepackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "ScriptTask",
    "title" : "脚本任务",
    "description" : "An automatic task with script logic",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n\t\n\t\n\t\t\n\t\n  \n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.script.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "scriptformatpackage", "scripttextpackage", "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "BusinessRule",
    "title" : "规则任务",
    "description" : "An automatic task with rule logic",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n  \t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\n\t\n\t\t\n\t\t\n    \n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.business.rule.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "ruletask_rulespackage", "ruletask_variables_inputpackage", "ruletask_excludepackage", "ruletask_resultpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "ReceiveTask",
    "title" : "接受任务",
    "description" : "An task that waits for a signal",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n    \n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.receive.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "ManualTask",
    "title" : "手工任务",
    "description" : "An automatic task with no logic",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n    \n    \t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.manual.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "MailTask",
    "title" : "邮件任务",
    "description" : "An mail task",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n    \n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.send.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "mailtasktopackage", "mailtaskfrompackage", "mailtasksubjectpackage", "mailtaskccpackage", "mailtaskbccpackage", "mailtasktextpackage", "mailtaskhtmlpackage", "mailtaskcharsetpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "CamelTask",
    "title" : "Camel任务",
    "description" : "An task that sends a message to Camel",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n\t\n\t\n\t\t\n\t\n  \n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.camel.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "cameltaskcamelcontextpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "MuleTask",
    "title" : "Mule任务",
    "description" : "An task that sends a message to Mule",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n\t\n\t\n\t\t\n\t\n  \n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.mule.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "muletaskendpointurlpackage", "muletasklanguagepackage", "muletaskpayloadexpressionpackage", "muletaskresultvariablepackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "SendTask",
    "title" : "Send task",
    "description" : "An task that sends a message",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n\t\t\n\t\t\n    \n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/list/type.send.png",
    "groups" : [ "活动列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "ActivitiesMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "SubProcess",
    "title" : "子流程",
    "description" : "子流程范围",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n    \n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/expanded.subprocess.png",
    "groups" : [ "结构列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "istransactionpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "EventSubProcess",
    "title" : "事件子流程",
    "description" : "一个事件周期的子流程",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n\t\n    \t\n\t\t\n    \t\n\t\n\t\n  \n",
    "icon" : "activity/event.subprocess.png",
    "groups" : [ "结构列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "all" ]
  }, {
    "type" : "node",
    "id" : "CallActivity",
    "title" : "调用活动",
    "description" : "一个调用活动",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n\t\n    \n\t\n\t\t\n\t\t\n    \n\t\n\t\t\n\t\n\t\n\t\n\t\t\n\t\n\n\t\n\t\t\n\t\n  \n",
    "icon" : "activity/task.png",
    "groups" : [ "结构列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "callactivitycalledelementpackage", "callactivityinparameterspackage", "callactivityoutparameterspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "Activity", "sequence_start", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "ExclusiveGateway",
    "title" : "互斥网关",
    "description" : "一个选择的网关",
    "view" : "\n\n  \n  \n    \n  \t\t\t\t\t\n  \n  \n    \n    \n      \n      \n    \n\t\n\t\n\t\n  \n\n",
    "icon" : "gateway/exclusive.databased.png",
    "groups" : [ "网关列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "sequencefloworderpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "GatewaysMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "ParallelGateway",
    "title" : "并行网关",
    "description" : "一个并行的网关",
    "view" : "\n\n   \n  \n    \n  \n  \n    \n    \n    \n\t\n\t\n  \n\n",
    "icon" : "gateway/parallel.png",
    "groups" : [ "网关列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "sequencefloworderpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "GatewaysMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "InclusiveGateway",
    "title" : "包容性网关",
    "description" : "一个包容性网关",
    "view" : "\n\n  \n    \n  \n  \n\n    \n    \n    \n\t\n\t\n  \n\n",
    "icon" : "gateway/inclusive.png",
    "groups" : [ "网关列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "sequencefloworderpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "GatewaysMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "EventGateway",
    "title" : "事件网关",
    "description" : "一个事件网关",
    "view" : "\n\n  \n    \n  \n  \n   \n  \t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\n\t\n\t\n\t\n\t\n  \t\n\t\n\n",
    "icon" : "gateway/eventbased.png",
    "groups" : [ "网关列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "sequencefloworderpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "GatewaysMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "BoundaryErrorEvent",
    "title" : "边界错误事件",
    "description" : "一个捕捉BPMN异常的边界事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \n    \n    \n\t\n  \n",
    "icon" : "catching/error.png",
    "groups" : [ "边界事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "errorrefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "BoundaryEventsMorph", "IntermediateEventOnActivityBoundary" ]
  }, {
    "type" : "node",
    "id" : "BoundaryTimerEvent",
    "title" : "定时边界事件",
    "description" : "一个定时触发的边界事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \t\n    \n    \n    \n    \n    \n    \n    \t\n\t\n  \n",
    "icon" : "catching/timer.png",
    "groups" : [ "边界事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "timercycledefinitionpackage", "timerdatedefinitionpackage", "timerdurationdefinitionpackage", "timerenddatedefinitionpackage", "cancelactivitypackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "BoundaryEventsMorph", "IntermediateEventOnActivityBoundary" ]
  }, {
    "type" : "node",
    "id" : "BoundarySignalEvent",
    "title" : "边界信号事件",
    "description" : "一个信号触发的边界事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \t\n    \n    \n    \n    \n\t\n\t\n  \n",
    "icon" : "catching/signal.png",
    "groups" : [ "边界事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "signalrefpackage", "cancelactivitypackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "BoundaryEventsMorph", "IntermediateEventOnActivityBoundary" ]
  }, {
    "type" : "node",
    "id" : "BoundaryMessageEvent",
    "title" : "边界消息事件",
    "description" : "一个边界消息事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \t\n    \n    \t\n    \n    \n    \n\t\n\t\t\n\t\n\t\n\t\n  \n",
    "icon" : "catching/message.png",
    "groups" : [ "边界事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "messagerefpackage", "cancelactivitypackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "BoundaryEventsMorph", "IntermediateEventOnActivityBoundary" ]
  }, {
    "type" : "node",
    "id" : "BoundaryCancelEvent",
    "title" : "边界取消事件",
    "description" : "一个边界取消事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n  \n    \n    \n    \n    \n\t\n  \n",
    "icon" : "catching/cancel.png",
    "groups" : [ "边界事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "BoundaryEventsMorph", "IntermediateEventOnActivityBoundary" ]
  }, {
    "type" : "node",
    "id" : "BoundaryCompensationEvent",
    "title" : "边界修正事件",
    "description" : "一个边界修正事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n\t\n    \n    \n    \n    \n    \n\t\n \n",
    "icon" : "catching/compensation.png",
    "groups" : [ "边界事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "BoundaryEventsMorph", "IntermediateEventOnActivityBoundary", "all" ]
  }, {
    "type" : "node",
    "id" : "CatchTimerEvent",
    "title" : "中间定时器捕获事件",
    "description" : "定时器触发的中间捕获事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \t\n    \n    \n    \n    \n    \n    \n    \t\n\t\n  \n",
    "icon" : "catching/timer.png",
    "groups" : [ "中间捕获事件列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "timercycledefinitionpackage", "timerdatedefinitionpackage", "timerdurationdefinitionpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "sequence_end", "CatchEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "CatchSignalEvent",
    "title" : "中间信号捕获事件",
    "description" : "信号触发的捕获事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \t\n    \n    \n    \n    \n\t\n\t\n  \n",
    "icon" : "catching/signal.png",
    "groups" : [ "中间捕获事件列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "signalrefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "sequence_end", "CatchEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "CatchMessageEvent",
    "title" : "中间消息捕获事件",
    "description" : "一个消息触发的中间捕获事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \t\n    \n    \t\n    \n    \n    \n\t\n\t\t\n\t\n\t\n\t\n  \n",
    "icon" : "catching/message.png",
    "groups" : [ "中间捕获事件列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "messagerefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "sequence_start", "sequence_end", "CatchEventsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "ThrowNoneEvent",
    "title" : "中间抛出事件",
    "description" : "无触发器的中间抛出事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n  \n    \n    \n\t\n  \n",
    "icon" : "throwing/none.png",
    "groups" : [ "中间抛出事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "ThrowEventsMorph", "sequence_start", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "ThrowSignalEvent",
    "title" : "信号中间抛出事件",
    "description" : "一个信号触发的中间抛出事件",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \n    \n\t\n  \n",
    "icon" : "throwing/signal.png",
    "groups" : [ "中间抛出事件" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "signalrefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "ThrowEventsMorph", "sequence_start", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "EndNoneEvent",
    "title" : "结束任务",
    "description" : "一个无触发器的结束任务",
    "view" : "\n\n  \n  \n  \t\n  \n  \n    \n\t\n  \n",
    "icon" : "endevent/none.png",
    "groups" : [ "结束任务列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "EndEventsMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "EndErrorEvent",
    "title" : "结束错误任务",
    "description" : "An end event that throws an error event",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n    \n    \n    \n\t\n  \n",
    "icon" : "endevent/error.png",
    "groups" : [ "结束任务列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "errorrefpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "EndEventsMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "EndCancelEvent",
    "title" : "结束取消任务",
    "description" : "A cancel end event",
    "view" : "\n\n  \n  \n  \t\n  \n  \n    \n    \n    \n\t\n  \n",
    "icon" : "endevent/cancel.png",
    "groups" : [ "结束任务列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "EndEventsMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "EndTerminateEvent",
    "title" : "终结任务",
    "description" : "A terminate end event",
    "view" : "\n\n  \n  \n  \t\n  \n  \n    \n    \n    \n\t\n  \n",
    "icon" : "endevent/terminate.png",
    "groups" : [ "结束任务列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "terminateAllpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "EndEventsMorph", "sequence_end", "all" ]
  }, {
    "type" : "node",
    "id" : "Pool",
    "title" : "池",
    "description" : "A pool to stucture the process definition",
    "view" : "\n\n  \n  \n  \t\n  \t\n  \t\n  \t\n  \t\n  \n  \n    \n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t  \t\n  \t\n    \n    \n\t\n\t\n\t\n\t\n    \n    \n  \n",
    "icon" : "swimlane/pool.png",
    "groups" : [ "泳道列表" ],
    "layout" : [ {
      "type" : "layout.bpmn2_0.pool"
    } ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "process_idpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "canContainArtifacts", "all" ]
  }, {
    "type" : "node",
    "id" : "Lane",
    "title" : "泳道",
    "description" : "A lane to stucture the process definition",
    "view" : "\n\n  \n  \n  \n     \n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n  \t\t\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n    \n\t\n  \n",
    "icon" : "swimlane/lane.png",
    "groups" : [ "泳道列表" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "PoolChild", "canContainArtifacts", "all" ]
  }, {
    "type" : "edge",
    "id" : "SequenceFlow",
    "title" : "顺序流",
    "description" : "顺序流定义活动的执行顺序",
    "view" : "\r\n\r\n\t\r\n\t  \t\r\n\t  \t\t\r\n\t\t\t\r\n\t  \t\r\n\t  \t\r\n\t  \t\t\r\n\t  \t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\r\n\t\r\n",
    "icon" : "connector/sequenceflow.png",
    "groups" : [ "连接对象" ],
    "layout" : [ {
      "type" : "layout.bpmn2_0.sequenceflow"
    } ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "conditionsequenceflowpackage", "executionlistenerspackage", "defaultflowpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "ConnectingObjectsMorph", "all" ]
  }, {
    "type" : "edge",
    "id" : "MessageFlow",
    "title" : "消息流",
    "description" : "Message flow to connect elements in different pools.",
    "view" : "\r\n\r\n\t\r\n\t\t\r\n\t  \t\t\r\n\t  \t\t\r\n\t  \t\r\n\r\n\t  \t\r\n\t  \t\t\r\n\t  \t\r\n\t\r\n\t\r\n\t    \r\n\t\t\r\n\t\r\n",
    "icon" : "connector/messageflow.png",
    "groups" : [ "连接对象" ],
    "layout" : [ {
      "type" : "layout.bpmn2_0.sequenceflow"
    } ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "ConnectingObjectsMorph", "all" ]
  }, {
    "type" : "edge",
    "id" : "Association",
    "title" : "注释",
    "description" : "连接一个注释到指定元素",
    "view" : "\r\n\r\n\t\r\n\t    \r\n\t\t\r\n\t\r\n",
    "icon" : "connector/association.undirected.png",
    "groups" : [ "连接对象" ],
    "layout" : [ {
      "type" : "layout.bpmn2_0.sequenceflow"
    } ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "ConnectingObjectsMorph", "all" ]
  }, {
    "type" : "edge",
    "id" : "DataAssociation",
    "title" : "日期注释",
    "description" : "连接一个日期注释到指定元素",
    "view" : "\r\n\r\n\t\r\n\t  \t\r\n\t  \t\t\r\n\t  \t\r\n\t\r\n\t\r\n\t    \r\n\t\t\r\n\t\r\n",
    "icon" : "connector/association.unidirectional.png",
    "groups" : [ "连接对象" ],
    "layout" : [ {
      "type" : "layout.bpmn2_0.sequenceflow"
    } ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "ConnectingObjectsMorph", "all" ]
  }, {
    "type" : "node",
    "id" : "TextAnnotation",
    "title" : "文本注释",
    "description" : "连接一个文本注释到指定元素",
    "view" : "\n\n  \n  \n  \t\n  \n  \n  \n  \n    \n    \n\t\n  \n",
    "icon" : "artifact/text.annotation.png",
    "groups" : [ "加工" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "textpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "all" ]
  }, {
    "type" : "node",
    "id" : "DataStore",
    "title" : "Data store",
    "description" : "Reference to a data store.",
    "view" : "\r\n\r\n\t\r\n\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\t\t \t\r\n\t\t\r\n\t\t\t \r\n\t\r\n\r\n",
    "icon" : "dataobject/data.store.png",
    "groups" : [ "Artifacts" ],
    "propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage" ],
    "hiddenPropertyPackages" : [ ],
    "roles" : [ "all" ]
  } ],
  "rules" : {
    "cardinalityRules" : [ {
      "role" : "Startevents_all",
      "incomingEdges" : [ {
        "role" : "SequenceFlow",
        "maximum" : 0
      } ]
    }, {
      "role" : "Endevents_all",
      "outgoingEdges" : [ {
        "role" : "SequenceFlow",
        "maximum" : 0
      } ]
    } ],
    "connectionRules" : [ {
      "role" : "SequenceFlow",
      "connects" : [ {
        "from" : "sequence_start",
        "to" : [ "sequence_end" ]
      } ]
    }, {
      "role" : "Association",
      "connects" : [ {
        "from" : "sequence_start",
        "to" : [ "TextAnnotation" ]
      }, {
        "from" : "sequence_end",
        "to" : [ "TextAnnotation" ]
      }, {
        "from" : "TextAnnotation",
        "to" : [ "sequence_end" ]
      }, {
        "from" : "BoundaryCompensationEvent",
        "to" : [ "sequence_end" ]
      }, {
        "from" : "TextAnnotation",
        "to" : [ "sequence_start" ]
      }, {
        "from" : "BoundaryCompensationEvent",
        "to" : [ "sequence_start" ]
      } ]
    }, {
      "role" : "DataAssociation",
      "connects" : [ {
        "from" : "sequence_start",
        "to" : [ "DataStore" ]
      }, {
        "from" : "sequence_end",
        "to" : [ "DataStore" ]
      }, {
        "from" : "DataStore",
        "to" : [ "sequence_end" ]
      }, {
        "from" : "DataStore",
        "to" : [ "sequence_start" ]
      } ]
    }, {
      "role" : "IntermediateEventOnActivityBoundary",
      "connects" : [ {
        "from" : "Activity",
        "to" : [ "IntermediateEventOnActivityBoundary" ]
      } ]
    } ],
    "containmentRules" : [ {
      "role" : "BPMNDiagram",
      "contains" : [ "all" ]
    }, {
      "role" : "SubProcess",
      "contains" : [ "sequence_start", "sequence_end", "from_task_event", "to_task_event", "EventSubProcess", "TextAnnotation", "DataStore" ]
    }, {
      "role" : "EventSubProcess",
      "contains" : [ "sequence_start", "sequence_end", "from_task_event", "to_task_event", "TextAnnotation", "DataStore" ]
    }, {
      "role" : "Pool",
      "contains" : [ "Lane" ]
    }, {
      "role" : "Lane",
      "contains" : [ "sequence_start", "sequence_end", "EventSubProcess", "TextAnnotation", "DataStore" ]
    } ],
    "morphingRules" : [ {
      "role" : "ActivitiesMorph",
      "baseMorphs" : [ "UserTask" ],
      "preserveBounds" : true
    }, {
      "role" : "GatewaysMorph",
      "baseMorphs" : [ "ExclusiveGateway" ]
    }, {
      "role" : "StartEventsMorph",
      "baseMorphs" : [ "StartNoneEvent" ]
    }, {
      "role" : "EndEventsMorph",
      "baseMorphs" : [ "StartNoneEvent" ]
    }, {
      "role" : "CatchEventsMorph",
      "baseMorphs" : [ "CatchTimerEvent" ]
    }, {
      "role" : "ThrowEventsMorph",
      "baseMorphs" : [ "ThrowNoneEvent" ]
    }, {
      "role" : "BoundaryEventsMorph",
      "baseMorphs" : [ "ThrowNoneEvent" ]
    }, {
      "role" : "BoundaryCompensationEvent",
      "baseMorphs" : [ "BoundaryCompensationEvent" ]
    }, {
      "role" : "TextAnnotation",
      "baseMorphs" : [ "TextAnnotation" ]
    }, {
      "role" : "DataStore",
      "baseMorphs" : [ "DataStore" ]
    } ]
  }
}

Springboot+Activiti6+在线流程编辑器整合_第15张图片

七、业务实战

1、模型管理

主要功能:模型创建、发布、编辑、删除。是主要使用activiti在线编辑器的模块。如图:

Springboot+Activiti6+在线流程编辑器整合_第16张图片

 Springboot+Activiti6+在线流程编辑器整合_第17张图片

 前端代码就不附上了,以下为后端核心代码:

ActivitiModelController.java
package com.ihrm.activiti.controller;

import com.alibaba.fastjson.JSON;
import com.ihrm.activiti.service.ActivitiModelService;
import com.ihrm.common.dto.Result;
import com.ihrm.common.dto.ResultCode;
import com.ihrm.common.exception.CommonException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@RestController
//4.swagger
@Api(tags = "工作流-模型管理")
@RequestMapping("/activiti/model")
public class ActivitiModelController {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ActivitiModelService activitiModelService;

    /**
     * 查询列表
     */
    @ApiOperation(value = "查询列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "map", value = "查询条件:{" +
                    "name$like=名称模糊 ," +
                    "name=名称 ," +
                    "id=id ," +
                    "key=key ," +
                    "}", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "pageNumber", value = "页码", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "pageSize", value = "每页数量", dataType = "string", paramType = "query", required = true)})
    @RequestMapping(value = "/page", method = RequestMethod.GET)
    public Result page(@RequestParam(value = "map") String map, @PageableDefault(value = 20) Pageable pageable) {
        Map reqMap = JSON.parseObject(map, Map.class);
        Page page = activitiModelService.page(reqMap, pageable);
        return new Result(ResultCode.SUCCESS, page);
    }


    /**
     * 创建模型
     */
    @ApiOperation(value = "创建模型")
    @RequestMapping(value = "/create" , method = RequestMethod.GET)
    public void create(HttpServletRequest request, HttpServletResponse response) throws CommonException {
        try {
            Model model = activitiModelService.create();
            response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + model.getId());
        } catch (IOException e) {
            throw new CommonException(ResultCode.FAIL);
        }
    }

    /**
     * 根据ID查询
     *
     * @param id
     * @param request
     * @param response
     */
    @ApiOperation(value = "根据ID查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "唯一标识", dataType = "string", paramType = "path", required = true)})
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public void findById(@PathVariable(value = "id") String id, HttpServletRequest request, HttpServletResponse response) throws CommonException {
        try {
            response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + id);
        } catch (IOException e) {
            throw new CommonException(ResultCode.NO_EXISTS);
        }
    }

    /**
     * 物理删除
     *
     * @param id
     * @return
     */
    @ApiOperation(value = "根据id删除(物理删除)")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "唯一标识", dataType = "string", paramType = "path", required = true)})
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public Result del(@PathVariable(value = "id") String id) {
        repositoryService.deleteModel(id);
        return new Result(ResultCode.SUCCESS);
    }

    /**
     * 关闭本次操作
     * @param id
     * @param response
     */
    @ApiOperation(value = "关闭本次操作")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "唯一标识", dataType = "string", paramType = "path", required = true)})
    @RequestMapping(value = "/close/{id}", method = RequestMethod.GET)
    public void close(@PathVariable(value = "id") String id, HttpServletResponse response) {
        Model model = repositoryService.getModel(id);
        if (model != null && !model.hasEditorSourceExtra()) {
            repositoryService.deleteModel(id);
        }
    }

    /**
     * 发布
     *
     * @param id
     */
    @ApiOperation(value = "发布")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "唯一标识", dataType = "string", paramType = "path", required = true)})
    @RequestMapping(value = "/deploy/{id}", method = RequestMethod.PUT)
    public Result deploy(@PathVariable(value = "id") String id){
        boolean deploy = false;
        try {
            deploy = activitiModelService.deploy(id);
        } catch (CommonException e) {
            return e.getResult() != null ? e.getResult() : new Result(e.getResultCode());
        }
        return deploy ? Result.SUCCESS() : Result.FAIL();
    }
}


ActivitiModelServiceImpl.java
package com.ihrm.activiti.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ihrm.activiti.service.ActivitiModelService;
import com.ihrm.activiti.validator.ModelIntegrityValidator;
import com.ihrm.common.dto.Result;
import com.ihrm.common.dto.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.ihrm.common.utils.CommonUtils;
import com.ihrm.common.utils.IConstant;
import com.ihrm.common.utils.ShiroUtils;
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.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.validation.ProcessValidator;
import org.activiti.validation.ProcessValidatorFactory;
import org.activiti.validation.ValidationError;
import org.activiti.validation.validator.ValidatorSet;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Service - 工作流模板
 */
@Service
public class ActivitiModelServiceImpl implements ActivitiModelService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 分页查询
     *
     * @param reqMap
     * @param pageable
     * @return
     */
    @Override
    public Page page(Map reqMap, Pageable pageable) {
        ModelQuery modelQuery = repositoryService.createModelQuery().notDeployed().latestVersion().orderByLastUpdateTime().desc();
        String _likeName = (String) reqMap.get("name$like");
        String _name = (String) reqMap.get("name");
        String _id = (String) reqMap.get("id");
        String _key = (String) reqMap.get("key");

        if (StringUtils.isNoneBlank(_likeName)) {
            modelQuery.modelNameLike(_likeName);
        }

        if (StringUtils.isNoneBlank(_name)) {
            modelQuery.modelName(_name);
        }

        if (StringUtils.isNoneBlank(_id)) {
            modelQuery.modelId(_id);
        }

        if (StringUtils.isNoneBlank(_key)) {
            modelQuery.modelKey(_key);
        }

        modelQuery.modelTenantId(ShiroUtils.getCompanyId());

        List models = modelQuery.listPage(pageable.getPageNumber(), pageable.getPageSize());
        return CommonUtils.listConvertToPage(models, pageable);
    }

    /**
     * 创建模型
     *
     * @return
     */
    @Override
    public Model create() throws CommonException {
        //初始化一个空模型
        Model model = repositoryService.newModel();

        try {
            //设置一些默认信息
            String name = "流程-" + System.currentTimeMillis();
            String description = "";
            int revision = 1;
            String key = com.ihrm.common.utils.StringUtils.getShortStr(name);

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

            model.setName(name);
            model.setKey(key);
            model.setMetaInfo(modelNode.toString());
            model.setTenantId(ShiroUtils.getCompanyId());//设置租户

            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);

            repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new CommonException(ResultCode.FAIL);
        }
        return model;
    }

    /**
     * 部署
     *
     * @param id
     * @return
     */
    @Override
    @Transactional
    public boolean deploy(String id) throws CommonException {
        Model modelData = repositoryService.getModel(id);
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        JsonNode editor = null;
        try {
            editor = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
        } catch (IOException e) {
            throw new CommonException(Result.FAIL());
        }
        BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editor);

        //一、校验模型正确性
        //创建模型校验器工厂
        ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
        //创建默认模型校验器
        ProcessValidator processValidator = processValidatorFactory.createDefaultProcessValidator();
        //加入自定义校验器
        List validatorSets = processValidator.getValidatorSets();
        //①、模型完整性校验器
        ValidatorSet modelIntegrityValidatorSet = new ValidatorSet("验证模型完整性");
        modelIntegrityValidatorSet.addValidator(new ModelIntegrityValidator());
        validatorSets.add(modelIntegrityValidatorSet);
        Collections.reverse(validatorSets);

        //进行模型校验
        List validate = processValidator.validate(bpmnModel);

        //如果校验错误集合长度大于1,则说明校验出错,遍历打印出错信息
        if(validate.size()>=1){
            for (ValidationError validationError : validate) {
                throw new CommonException(Result.FAIL(validationError.getProblem()));
            }
        }

        //二、部署
        BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
        byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

        String processName = modelData.getName();
        if (!StringUtils.endsWith(processName, IConstant.BPMN20)) {
            processName += IConstant.BPMN20;
        }
        Deployment deployment = repositoryService.createDeployment()
                .name(modelData.getName())
                .key(modelData.getKey())
                .addBytes(processName , bpmnBytes)
                .deploy();

        //2、保存模型部署信息
        modelData.setDeploymentId(deployment.getId());
        repositoryService.saveModel(modelData);

        //3、设置流程分类
        List list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
        for (ProcessDefinition processDefinition : list) {
            repositoryService.setProcessDefinitionCategory(processDefinition.getId(), modelData.getCategory());
        }

        return list.size() > 0;
    }
}

数据表信息参考《activiti数据表》

1、创建/编辑模型时:act_re_model、act_ge_bytearray

act_re_model(模型)

 act_ge_bytearray(二进制内容表)


2、发布时:act_re_model 、act_re_procdef、act_re_deployment、act_ge_bytearray

act_re_model(模型)

 act_re_deployment(部署)

 act_ge_bytearray(二进制内容表)

 act_re_procdef(流程定义) 


开发中个性需求记录

需求一:在线编辑器中关闭、保存并关闭按钮,点击完成后从编辑页面跳转到列表页。

问题:未改动前,点击完成报404错误

分析:resources\static\editor-app\configuration\toolbar-default-actions.js

查看相关函数源码:

closeEditor: function (services) {
    window.location.href = "./";
},
$scope.saveAndClose = function () {
    $scope.save(function () {
       window.location.href = "./";
    });
};

可以看出源码中业务的最终指向是在线编辑所放置的项目根路由。

方案:

closeEditor: function (services) {
    // window.location.href = "./";
    if (window.confirm('确定要放弃此次编辑吗?')){
        //1、关闭前对于未点击保存的模型做删除处理
        var modelMetaData = services.$scope.editor.getModelMetaData();
        location.href = "/activiti/model/close/" + modelMetaData.modelId;
        //2、返回上一页面
        window.history.back(-1);
    }
},
​​​​​​​$scope.saveAndClose = function () {
    $scope.save(function () {
        // window.location.href = "./";
        window.history.back(-1);//返回上一页面
    });
};

需求二:模型发布时加入自定义校验

详述:发布模型时对模型的正确性进行校验,例如:

1、是否是完整的流程:存在起始/结束事件,含有用户活动等

思路:Springboot+Activiti6+在线流程编辑器整合_第18张图片

如何自定义校验: 

Springboot+Activiti6+在线流程编辑器整合_第19张图片

源码:

package com.ihrm.activiti.validator;

import cn.hutool.core.collection.CollectionUtil;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.validation.ValidationError;
import org.activiti.validation.validator.ProcessLevelValidator;
import org.apache.commons.lang3.StringUtils;

import java.util.List;

/**
 * 模型完整性验证器
 */
public class ModelIntegrityValidator extends ProcessLevelValidator {
    private Process process;
    private BpmnModel bpmnModel;
    private List errors;

    public ModelIntegrityValidator() {
    }

    @Override
    protected void executeValidation(BpmnModel bpmnModel, Process process, List errors) {
        this.bpmnModel = bpmnModel;
        this.process = process;
        this.errors = errors;

        //0、校验流程信息
        this.validateProcessInfo();
        //1、校验开始事件
        this.validateStartEvents();
        //2、校验结束事件
        this.validateEndEvents();
        //3、验证userTasks
        this.validateUserTasks();
        //4、校验连线
        this.validateSequenceFlows();

        //TODO
    }

    /**
     * 验证流程信息
     */
    private void validateProcessInfo() {
        //1、验证process_id
        String processId = process.getId();
        if(StringUtils.isBlank(processId)){
            addError("未设置流程ID");
        }else{
            //2、验证NCName的有效性
            if(!processId.matches("^[a-zA-z].*")){
                addError("设置的流程ID为无效ID");
            }
        }

        //2、验证流程名称
        String name = process.getName();
        if(StringUtils.isBlank(name)){
            addError("未设置流程名称");
        }
    }


    /**
     * 验证连线
     */
    protected void validateSequenceFlows() {
        List flowElements = process.findFlowElementsOfType(SequenceFlow.class);
        if(CollectionUtil.isEmpty(flowElements)){
            addError("未设置【连线】");
        }else{
            //1、验证连线是否连接节点
            for(SequenceFlow sequenceFlow : flowElements){
                String sourceRef = sequenceFlow.getSourceRef();
                String targetRef = sequenceFlow.getTargetRef();
                if(StringUtils.isBlank(sourceRef) || StringUtils.isBlank(targetRef)){
                    addError("【连线】存在断点");
                    break;
                }
            }
        }
    }

    /**
     * 验证开始事件
     */
    protected void validateStartEvents(){
        List startEvents = process.findFlowElementsOfType(StartEvent.class);
        //1、是否存在开始节点
        if(CollectionUtil.isEmpty(startEvents)){
            addError("未设置【开始】节点");
        }else{
            //2、是否存在多个节点
            if(startEvents.size() > 1){
                addError("存在多个【开始】节点");
            }else{
                //3、校验节点连线情况
                StartEvent startEvent = startEvents.get(0);
                checkLinks(startEvent);
            }
        }
    }

    /**
     * 验证结束事件
     */
    protected void validateEndEvents(){
        List endEvents = process.findFlowElementsOfType(EndEvent.class);
        //1、是否存在结束节点
        if(CollectionUtil.isEmpty(endEvents)){
            addError("未设置【结束】节点");
        }else{
            //2、是否存在多个节点
            if(endEvents.size() > 1){
                addError("存在多个【结束】节点");
            }else{
                //3、校验节点连线情况
                EndEvent endEvent = endEvents.get(0);
                checkLinks(endEvent);
            }
        }
    }

    /**
     * 验证userTasks
     */
    protected void validateUserTasks(){
        List userTasks = process.findFlowElementsOfType(UserTask.class);
        //1、是否存在用户节点
        if(CollectionUtil.isEmpty(userTasks)){
            addError("未设置【用户】节点");
        }else{
            //2、检验所有用户节点连线情况
            for(UserTask userTask : userTasks){
                String name = userTask.getName();
                if(StringUtils.isBlank(name)){
                    addError("某用户节点未设置名称");
                    break;
                }

                checkLinks(userTask);
            }
        }
    }

    /**
     * 添加错误
     * @param message
     */
    private void addError(String message){
        ValidationError validationError = new ValidationError();
        validationError.setProblem(message);
        validationError.setWarning(false);
        errors.add(validationError);
    }

    /**
     * 检测节点连线情况
     * @param flowElement
     */
    private void checkLinks(FlowElement flowElement) {
        //1、开始节点的校验
        if(flowElement instanceof StartEvent){
            StartEvent startEvent = (StartEvent) flowElement;
            checkOutGoing(StringUtils.isBlank(startEvent.getName()) ? "开始" : startEvent.getName()  , startEvent.getOutgoingFlows());
        }

        //2、结束节点的校验
        if(flowElement instanceof EndEvent){
            EndEvent endEvent = (EndEvent) flowElement;
            checkIncoming(StringUtils.isBlank(endEvent.getName()) ? "结束" : endEvent.getName()  , endEvent.getIncomingFlows());
        }

        //3、用户节点的校验
        if(flowElement instanceof UserTask){
            UserTask userTask = (UserTask) flowElement;
            checkIncoming(StringUtils.isBlank(userTask.getName()) ? "未知用户" : userTask.getName()  , userTask.getIncomingFlows());
            checkOutGoing(StringUtils.isBlank(userTask.getName()) ? "未知用户" : userTask.getName()  , userTask.getOutgoingFlows());
        }
    }

    /**
     * 检测指向
     * @param nodeName
     * @param sequenceFlows
     */
    private void checkOutGoing(String nodeName , List sequenceFlows){
        if(CollectionUtil.isEmpty(sequenceFlows)){
            addError("【"+ nodeName +"】节点未设置与后续节点的连线");
        }else{
            for(SequenceFlow sequenceFlow : sequenceFlows){
                //存在多个分支时,则进行条件校验
                if(sequenceFlows.size() > 1){
                    String conditionExpression = sequenceFlow.getConditionExpression();
                    if(StringUtils.isBlank(conditionExpression)){
                        addError("【"+ nodeName +"】节点后面存在多条路径分支,存在某分支未配置条件");
                        break;
                    }
                }
            }
        }
    }

    /**
     * 检测进入
     * @param nodeName
     * @param sequenceFlows
     */
    private void checkIncoming(String nodeName , List sequenceFlows){
        if(CollectionUtil.isEmpty(sequenceFlows)){
            addError("【"+ nodeName +"】节点未设置与前段节点的连线");
        }else{
            for(SequenceFlow sequenceFlow : sequenceFlows){
                //存在多个分支时,则进行条件校验
                if(sequenceFlows.size() > 1){
                    String conditionExpression = sequenceFlow.getConditionExpression();
                    if(StringUtils.isBlank(conditionExpression)){
                        addError("【"+ nodeName +"】节点前面存在多条路径分支,存在某分支未配置条件");
                        break;
                    }
                }
            }
        }
    }
}


开发中问题记录

问题一、流程图片乱码问题

Springboot+Activiti6+在线流程编辑器整合_第20张图片

 springboot解决方案:

加入activiti配置:

package com.ihrm.common.config;

import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;

/**
 * Config - 工作流
 */
@Configuration
public class ActivitiConfig implements ProcessEngineConfigurationConfigurer {
    /**
     * 解決工做流生成图片乱码问题
     *
     * @param processEngineConfiguration processEngineConfiguration
     */
    @Override
    public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
        processEngineConfiguration.setActivityFontName("宋体");
        processEngineConfiguration.setAnnotationFontName("宋体");
        processEngineConfiguration.setLabelFontName("宋体");
    }
}

2、流程配置

数据表信息参考《activiti数据表》

1、启用时:

act_hi_procinst(流程实例历史

 

act_ru_execution(执行)

act_ru_identitylink(参与者)

 

act_hi_actinst(环节历史信息)

 act_ru_task(任务)

八、activiti数据表

1. 数据库表的命名

Activiti的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。

2.数据表的分类

activiti的28张数据表组成了严密的工作流存储机制,总共有如下六大类。

  • ACT_GE: general,用来保存一些通用数据。
  • ACT_RE: repository,存储表,用来保存工作流定义、部署等信息。
  • ACT_ID: identity,身份表,用来保存user、user group以及两者之间绑定关系的数据。
  • ACT_RU: runtime,运行时表,用来保存流程运行时的数据,如工作流实例、权限、参数等等。
  • ACT_HI: history,用来保存流程的历史数据。

3.数据表的含义

Springboot+Activiti6+在线流程编辑器整合_第21张图片

此外还有两张表:ACT_EVT_LOG和ACT_PROCDEF_INFO没有按照规则来,两者分别属于HI和RE。

4. 数据库表结构

4.1 ACT_RE_ (流程定义表)

Springboot+Activiti6+在线流程编辑器整合_第22张图片

ACT_RE_DEPLOYMENT(部署)

Springboot+Activiti6+在线流程编辑器整合_第23张图片

ACT_RE_MODEL(模型)

Springboot+Activiti6+在线流程编辑器整合_第24张图片

ACT_RE_PROCDEF (流程定义)

Springboot+Activiti6+在线流程编辑器整合_第25张图片

4.2 ACT_RU_ (运行实例表)

Springboot+Activiti6+在线流程编辑器整合_第26张图片

ACT_RU_EVENT_SUBSCR(事件订阅)

Springboot+Activiti6+在线流程编辑器整合_第27张图片

ACT_RU_EXECUTION(执行)

Springboot+Activiti6+在线流程编辑器整合_第28张图片

 ACT_RU_IDENTITYLINK(参与者)

Springboot+Activiti6+在线流程编辑器整合_第29张图片

ACT_RU_JOB(异步作业)

Springboot+Activiti6+在线流程编辑器整合_第30张图片

ACT_RU_TASK(任务)

Springboot+Activiti6+在线流程编辑器整合_第31张图片

ACT_RU_VARIABLE(变量)

Springboot+Activiti6+在线流程编辑器整合_第32张图片

4.3 ACT_HI_  (流程历史记录)

Springboot+Activiti6+在线流程编辑器整合_第33张图片

ACT_HI_ACTINST(环节历史信息)

Springboot+Activiti6+在线流程编辑器整合_第34张图片

ACT_HI_ATTACHMENT(附件)

Springboot+Activiti6+在线流程编辑器整合_第35张图片

 ACT_HI_COMMENT(评论)

Springboot+Activiti6+在线流程编辑器整合_第36张图片

 ACT_HI_DETAIL(历史详情信息)

Springboot+Activiti6+在线流程编辑器整合_第37张图片

ACT_HI_IDENTITYLINK(参与者历史)

Springboot+Activiti6+在线流程编辑器整合_第38张图片

 ACT_HI_PROCINST(流程实例历史)

Springboot+Activiti6+在线流程编辑器整合_第39张图片

 ACT_HI_TASKINST(任务历史)

Springboot+Activiti6+在线流程编辑器整合_第40张图片

ACT_HI_VARINST(变量历史)

Springboot+Activiti6+在线流程编辑器整合_第41张图片

4.4 ACT_GE_ (普通数据)

Springboot+Activiti6+在线流程编辑器整合_第42张图片

ACT_GE_BYTEARRAY(二进制内容表)

所有二进制内容都会保存在这个表里,比如部署的process.bpmn20.xml, process.png, user.form, 附件,bean序列化为二进制的流程变量。

Springboot+Activiti6+在线流程编辑器整合_第43张图片

ACT_GE_PROPERTY(全局参数表)

全局参数,默认三个参数next.dbid,IdGenerator区间,schema.history,自动执行sql历史,schema.version,当前sql版本。

Springboot+Activiti6+在线流程编辑器整合_第44张图片

4.5 ACT_ID_ (用户用户组表)

ACT_ID_GROUP(群组)

Springboot+Activiti6+在线流程编辑器整合_第45张图片

ACT_ID_INFO(用户详细信息)

Springboot+Activiti6+在线流程编辑器整合_第46张图片

ACT_ID_MEMBERSHIP(用户群组关系)

ACT_ID_USER(用户基本信息)

Springboot+Activiti6+在线流程编辑器整合_第47张图片

4.6 其他

ACT_EVT_LOG(事件日志表)

默认不开启

Springboot+Activiti6+在线流程编辑器整合_第48张图片

ACT_PROCDEF_INFO(流程定义更新信息)

 ​​​

Springboot+Activiti6+在线流程编辑器整合_第49张图片

九、学习Activiti-各种Service

ManagementService

  ManagementService是一般用来对Activiti流程引擎的管理和维护。

RepositoryService

  从名字上可以看出来,repository,仓库、贮藏,对应到Activiti中,也就是“资源的管理”,比如流程定义的控制管理(发布、删除、挂起、激活....);

DynamicBpmnService

  RepositoryService可以用来部署流程定义(使用xml形式定义好的),一旦部署到Activiti(解析后保存到DB),那么流程定义就不会再变了,除了修改xml定义文件内容;

  而DynamicBpmnService就允许我们在程序运行过程中去修改流程定义,比如修改流程定义中的分配角色、优先级、流程流转的条件...

RuntimeService

  可以通过RuntimeService对流程实例进行相关的操作,比如管理流程实例的角色、分配人...以及执行信息的查询

TaskService

  TaskService可以对任务进行管理和查询。

HistoryService

  HistoryService提供了查询历史信息的功能,包括流程实例信息、参与者信息、完成时间....

你可能感兴趣的:(JAVA,spring)