Activiti6简明教程

作者: jessehua | 来源:发表于2018-04-17 19:28 被阅读1446次

一、为什么选择Activiti

工作流引擎对比

二、核心7大接口、28张表

7大接口
(一)7大接口
  1. RepositoryService:提供一系列管理流程部署和流程定义的API。
  2. RuntimeService:在流程运行时对流程实例进行管理与控制。
  3. TaskService:对流程任务进行管理,例如任务提醒、任务完成和创建任务等。
  4. IdentityService:提供对流程角色数据进行管理的API,这些角色数据包括用户组、用户及它们之间的关系。
  5. ManagementService:提供对流程引擎进行管理和维护的服务。
  6. HistoryService:对流程的历史数据进行操作,包括查询、删除这些历史数据。
  7. FormService:表单服务。
(二)28张表
28张表

1、act_ge_ 通用数据表,ge是general的缩写
2、act_hi_ 历史数据表,hi是history的缩写,对应HistoryService接口
3、act_id_ 身份数据表,id是identity的缩写,对应IdentityService接口
4、act_re_ 流程存储表,re是repository的缩写,对应RepositoryService接口,存储流程部署和流程定义等静态数据
5、act_ru_ 运行时数据表,ru是runtime的缩写,对应RuntimeService接口和TaskService接口,存储流程实例和用户任务等动态数据

三、创建BPMN业务流程模型

1.将Activiti提供的流程设计器应用activiti-app.war部署到Tomcat的webapps目录。
2.创建新的MySql数据库。修改activiti-app\WEB-INF\classes\META-INF\activiti-app目录下的activiti-app.properties配置文件,默认使用H2内存数据库,创建的模型重启后会丢失,改成使用MySql数据库。
3.浏览器访问http://localhost:8080/activiti-app,登录账户:admin:test
4.创建一个请假审批流程图

请假审批流程图
  • 给每个用户任务指派候选组(有权限执行当前任务的角色)


    指派候选组
    指派候选组
  • 排他网关设置条件分支表达式


    设置条件分支
    设置条件分支

    5.导出流程图为.bpmn20.xml文件


    导出xml文件

四、Spring Boot与Activiti 6.0整合

1.在POM文件中添加依赖

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>6.0.0</version>
</dependency>

2.将导出的.bpmn20.xml文件拷贝到项目文件夹/resources/processes下
3.application.properties文件添加配置项

spring.activiti.database-schema-update=true

databaseSchemaUpdate配置项可以设置流程引擎启动和关闭时数据库执行的策略。 databaseSchemaUpdate有以下四个值:

  • false:false为默认值,设置为该值后,Activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配时,将在启动时抛出异常。
  • true:设置为该值后,Activiti会对数据库中所有的表进行更新,如果表不存在,则Activiti会自动创建。
  • create-drop:Activiti启动时,会执行数据库表的创建操作,在Activiti关闭时,执行数据库表的删除操作。
  • drop-create:Activiti启动时,执行数据库表的删除操作在Activiti关闭时,会执行数据库表的创建操作。

4.启动应用,会在数据库里创建28张表,表创建好之后停止应用。application.properties文件修改配置项

#每次应用启动不检查Activiti数据表是否存在及版本号是否匹配,提升应用启动速度
spring.activiti.database-schema-update=false

5.application.properties文件增加配置项

#保存历史数据级别设置为full最高级别,便于历史数据的追溯
spring.activiti.history-level=full

对于历史数据,保存到何种粒度,Activiti提供了history-level属性对其进行配置。history-level属性有点像log4j的日志输出级别,该属性有以下四个值:

  • none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
  • activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
  • audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
  • full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
    6.完成以上步骤,就可以在程序中使用自动注入的方式,使用Activiti的7大接口。
@Autowired
private RuntimeService runtimeService;

@Autowired
private TaskService taskService;

@Autowired
private IdentityService identityService;

@Autowired
private RepositoryService repositoryService;

@Autowired
private ProcessEngine processEngine;

@Autowired
private HistoryService historyService;

五、项目中的用户、角色与Activiti中的用户、用户组整合

每个项目都有自己的用户、角色表,Activiti也有自己的用户、用户组表。因此项目中的用户、角色与Activiti中的用户、用户组要做整合。

//项目中每创建一个新用户,对应的要创建一个Activiti用户
//两者的userId和userName一致
User admin=identityService.newUser("1");
admin.setLastName("admin");
identityService.saveUser(admin);

//项目中每创建一个角色,对应的要创建一个Activiti用户组
Group adminGroup=identityService.newGroup("1");
adminGroup.setName("admin");
identityService.saveGroup(adminGroup);

//用户与用户组关系绑定
identityService.createMembership("1","1");

六、请假审批流程

1.请假申请和请假审批数据库表设计
表设计原则:流程数据和业务数据相分离。Activiti相关表只负责流程的跳转、走向等。流程中产生的业务表单数据、审批意见、附件等存储在开发人员定义的业务表中。流程数据和业务数据之间通过processInstanceId(流程实例ID)和业务数据主键相互关联。

为什么不使用Activiti相关表来存储表单数据和附件?


activiti参数表

Activiti为了应用的灵活性和通用性采用了纵表的方式存储表单数据。假设一条请假申请表单数据有10个字段,那就需要10条记录存储原本横表只需要一条记录存储的数据。采用纵表的方式会有如下问题:

  • 会有大量的冗余数据并且数据量会急剧的增长
  • 查询语句复杂,查询效率低
  • 尤其不适合做后期的统计报表分析
activiti附件表

Activiti存储附件使用Blob数据格式,文件存储在数据库里,数据库的数据文件会变得超大,不利于数据库备份和迁移。
请假申请表结构


请假申请表

请假审批表结构


请假审批表
2.填写请假申请表单,启动流程实例
填写请假申请
//启动流程实例,字符串"vacation"是BPMN模型文件里process元素的id
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacation");
//流程实例启动后,流程会跳转到请假申请节点
Task vacationApply = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
//设置请假申请任务的执行人
taskService.setAssignee(vacationApply.getId(), req.getUserId().toString());

//设置流程参数:请假天数和表单ID
//流程引擎会根据请假天数days>3判断流程走向
//formId是用来将流程数据和表单数据关联起来
Map<String, Object> args = new HashMap<>();
args.put("days", req.getDays());
args.put("formId", formId);

//完成请假申请任务
taskService.complete(vacationApply.getId(), args);

3.待审批列表


待审批列表
//查出当前登录用户所在的用户组
List<Group> groups = identityService.createGroupQuery()
        .groupMember(String.valueOf(userId)).list();
List<String> groupNames = groups.stream()
        .map(group -> group.getName()).collect(Collectors.toList());

//查询用户组的待审批请假流程列表
List<Task> tasks = taskService.createTaskQuery()
        .processDefinitionKey("vacation")
        .taskCandidateGroupIn(groupNames)
        .listPage(pageNum - 1, pageSize);

//根据流程实例ID查询请假申请表单数据
List<String> processInstanceIds = tasks.stream()
        .map(task -> task.getProcessInstanceId())
        .collect(Collectors.toList());
List<VacationApplyBasicPO> vacationApplyList = 
        vacationRepository.getVacationApplyList(processInstanceIds);

4.请假审批功能


请假审批
//查询当前审批节点
Task vacationAudit = taskService.createTaskQuery()
        .taskId(req.getTaskId()).singleResult();

if (req.getAuditResult() == 1) {//审批通过
    //设置流程参数:审批ID
    Map<String, Object> args = new HashMap<>();
    args.put("auditId", auditId);

    //设置审批任务的执行人
    taskService.claim(vacationAudit.getId(), req.getUserId().toString());
    //完成审批任务
    taskService.complete(vacationAudit.getId(), args);
} else {
    //审批不通过,结束流程
    runtimeService.deleteProcessInstance(vacationAudit.getProcessInstanceId(), auditId);
}

5.查看流程图功能


查看流程图
//controller层代码
@RequestMapping(value = "/image", method = RequestMethod.GET)
public void image(HttpServletResponse response,
 @RequestParam String processInstanceId) {
    try {
        InputStream is = vacationService.getDiagram(processInstanceId);
        if (is == null)
            return;

        response.setContentType("image/png");

        BufferedImage image = ImageIO.read(is);
        OutputStream out = response.getOutputStream();
        ImageIO.write(image, "png", out);

        is.close();
        out.close();
    } catch (Exception ex) {
        logger.error("查看流程图失败", ex);
    }
}

//service层代码
@Override
public InputStream getDiagram(String processInstanceId) {
    //获得流程实例
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(processInstanceId).singleResult();
    String processDefinitionId = StringUtils.EMPTY;
    if (processInstance == null) {
        //查询已经结束的流程实例
        HistoricProcessInstance processInstanceHistory =
                historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(processInstanceId).singleResult();
        if (processInstanceHistory == null)
            return null;
        else
            processDefinitionId = processInstanceHistory.getProcessDefinitionId();
    } else {
        processDefinitionId = processInstance.getProcessDefinitionId();
    }

    //使用宋体
    String fontName = "宋体";
    //获取BPMN模型对象
    BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
    //获取流程实例当前的节点,需要高亮显示
    List<String> currentActs = Collections.EMPTY_LIST;
    if (processInstance != null)
        currentActs = runtimeService.getActiveActivityIds(processInstance.getId());

    return processEngine.getProcessEngineConfiguration()
            .getProcessDiagramGenerator()
            .generateDiagram(model, "png", currentActs, new ArrayList<String>(),
                    fontName, fontName, fontName, null, 1.0);
}

相关文章

网友评论

  • 5014327167d2:你好,问个问题,我在activiti-app设计流程时,task的name如果是中文就无法保存成功,英文就可以,是不是编码需要设置,(centos上tomcat 部署的)
    jessehua:@KyleXY_f665 你好,我是在win10上部署的,没有你说的问题。centos你试试:
    修改tomcat的java启动编码为UTF-8
    在catalina.sh中添加JAVA_OPTS参数

    set JAVA_OPTS=-Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8
  • d4c92be09845:楼主你好,想问下 activiti5 和 6 在编码方面区别大吗? 从5过度到6需要注意些什么
    jessehua:看一下这篇Activiti6新特性 http://www.shareniu.com/article/30.htm
    jessehua:@海海海_41a9 你好,我没用过5啊,直接使用的6。
  • bffbf28f05e5:我想问一下webapp下面的activiti-admin.war 和 activiti-rest.war 有什么用,还有楼主的文章写的很好,对我这个刚刚接触activiti的新手很有帮助
    jessehua:@僵尸先生_3faf 你好,感谢支持。activiti-rest.war的作用是可以通过调用REST API的方式实现文章中七大接口的功能。比如:访问http://host/activiti-rest/service/deployments可以列出所有的流程部署信息。activiti-admin.war是管理员web应用,可以管理和监控Activiti流程引擎。
  • df86303dbaeb:你好,我流程跑通了,但是我一直不明白填写请假申请那一步,arg.put("formId",formId)那个formId从哪来的,是请假申请表的主id吗,我感觉没什么用啊
    df86303dbaeb:@jessehua 好的谢谢
    jessehua:@free_023b 你好,formId是请假申请表的主键,启动请假流程实例之后,保存申请表单就有了formId,然后存入activiti参数表。我当时这么写考虑的是今后可能会有直接通过activiti接口查询对应表单数据的场景,所以做了冗余。
  • a494e836695a:您好,请问你们集成6.0的流程设计器到项目中没?我是用spring boot2.0 集成activiti6.0,可以使用了但是想把流程设计器也集成到项目中,就把webapp文件放到项目中了,结果每次登录流程设计器页面都报404,activiti-app/app/authentication
    a494e836695a:@jessehua 嗯,你说的我都清楚,我现在做的产品不仅仅是在开发阶段使用,所以集成到应用中了,但是仅仅是官网提供的app的war包,集成遇到问题了。主要是想问你集成过6.0的流程设计器没,如果是的话,想请教一下如何集成进去的,需要修改那些内容。谢谢!
    jessehua:@licf10086 你好,6.0提供的流程设计器是一个war包,一个独立的应用,很难集成到你的应用里,不过如果使用5的话是可以集成的。如果只是开发人员在开发阶段使用,那集成的意义不大。
  • df86303dbaeb:请问怎么将流程图导成bpmn20.xml文件
    df86303dbaeb:@jessehua 好的谢谢
    jessehua:你好,在流程设计器里把流程设计好之后,点击保存图标,点击'save and close editor'按钮,然后在新界面中点击你刚保存的流程,右上角有个下载按钮,点击就可以导出为bpmn20.xml了
  • f8889dc64aea:请问下设计器是哪个啊?
    f8889dc64aea:@jessehua 非常感谢!
    jessehua:官方下载activiti-6.0.0.zip,在war目录底下有个activiti-app.war
  • a36c0317e2f6:写的很好
    jessehua:@鱼房子 谢谢
  • 橘子_好多灰:最近也在开发工作流系统,我用的是5.22.0
    jessehua:@好多灰 动态生成表单,要开发表单设计器,工作量很大
    jessehua:@好多灰 是的呀,每添加一个流程,都要设计对应的表单数据表,不用activiti存储表单数据的原因,文章里写了。
    橘子_好多灰:你这这种设计是不是每次创建一个流程都要给这种流程设计表
  • Jack8211:有实际的工程源代码吗?求分享 QQ374494120
    jessehua:不好意思啊,示例是用公司的开发框架为基础写的,还不能分享
  • clicklife:一直在研究flowable6
    jessehua:@clicklife 暂时还没有需要用flowable方面的需求,不想给自己挖坑:grin:
  • IT人故事会:我会持续关注的!
    jessehua:感谢:smile:

本文标题:Activiti6简明教程

本文链接:https://www.haomeiwen.com/subject/crupkftx.html