# Flowable 工作流框架 **Repository Path**: silverconx/newfiber-workflow-release ## Basic Information - **Project Name**: Flowable 工作流框架 - **Description**: 基于Flowable的工作流框架。支持BPMN流程图,提供低侵入性、低开发量、快速集成、高复用的工作流组件。实现了一键启动、提交、查询工作流等功能。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 11 - **Forks**: 6 - **Created**: 2023-11-03 - **Last Updated**: 2025-06-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 工作流对接文档 [TOC] ## 历史版本 | 版本 | 日期 | 备注 | | :---: | :--------: | :----------------------------------------------------------: | | 1.0.0 | 2021/08/20 | 初版 | | 1.1.0 | 2021/08/31 | 「会签」「消息通知」「功能优化」 | | 1.2.0 | 2022/02/28 | 「框架由Activiti6.0.0变更为Flowable6.0.1」
「加入流程图自定义颜色功能」
「加入会签一票否决」
「功能优化」 | | 1.3.0 | 2023/02/22 | 「框架由Flowable6.0.1变更为Flowable6.8.0」
「集成到通用产品框架」
「加入数据库分库功能」
「MySQL触发器改为视图」 | | 1.4.0 | 2023/03/17 | 「添加附件上传及展示功能」 | | 1.5.0 | 2023/11/13 | 「集成国产数据库」
开源 | ## 概述 ​ 工作流,从业务角度看,就是审核流程,其表现就是控制不同的角色看到并执行不同的数据;不同的操作使得数据的流向不同,一般情况下会有申请,审核等步骤; ​ 从技术实现的角度看,就是通过控制一个业务表数据的状态字段,用户在执行过程中实现数据的状态变更。 ### 模块特性 ​ 1)基于开源Flowable 6.8.0工作流框架。 ​ 2)基于BPMN图形拖拽方式定义工作流程。 ​ 3)支持在流程图中静态指定各个节点任务的执行人/角色。 ​ 4)支持在流程中动态指定各个节点任务的执行人/角色。 ​ 5)支持会签、邮件/短信通知功能。 ​ 6)代码侵入性低,集成方便。 ​ 7)集成国产达梦、人大金仓数据库。 ### 模块架构 ​ 官方提供的Flowable框架提供了工作流引擎,包括流程、实例、任务等核心功能,已经能够满足业务需求。但是对于日常开发而言还不够友好,还需要解决几个问题: ​ 1、由于Flowable框架和业务系统解耦,任务的代办/已办状态在工作流引擎里面,业务系统无法直接感知代办/已办状态,需要添加额外的操作才能在业务数据里面保存状态信息。 ​ 2、Flowable官网框架提供的API和业务系统不贴合,编码过程中会产生很多无用代码。如果做一层代码封装用来适应业务,并且配合代码生成器,可以减少80%的手动编码。 ​ 为了解决这些问题,在基础框架之上开发了工作流SDK工具包*newfiber-common-workflow*。工具包使用回调方式更新业务数据状态,并且构建了一套核心类,实现了一键启动、提交、查询工作流等功能。业务系统引入工具包后,配合核心类,可以在最少编码的情况下实现工作流。 架构图 ### 业务时序 下载 ### 核心类设计 ![工作流UML](https://s2.loli.net/2022/06/20/j3Tce1rL46QqYaH.png) ## 技术选型 **1、系统环境** - Java EE 8 - Servlet 3.0 - Apache Maven 3 - Redis > 3 - MySQL > 5.7 - Node >= 12 **2、主框架** - Spring Boot 2.3.x - Spring Cloud Hoxton.SR9 - Spring Framework 5.2.x - Spring Security 5.2.x **3、持久层** - Apache MyBatis 3.5.x - Baomidou MyBatis Plus 3.5.x - Hibernate Validation 6.0.x - Alibaba Druid 1.2.x ## 对接流程 ### 概述 ​ 在业务模块对接过程中,主要包括前端和后端。前端主要用于BPMN流程图的绘制,后端主要用于实现业务逻辑。 ​ 1)前端:前端开发人员将前端界面集成到业务模块后,绘制BPMN流程图。在极端情况下,如果业务系统无法集成前端界面,也可以通过后端接口直接导入BPMN文件,BPMN文件可以由第三方工具绘制后导出。 ​ 2)后端:后端完成业务表结构设计后,在MySQL中执行视图,在代码中引入工作流模块依赖,并依照文档「后端代码对接」开始编码。 ### BPMN流程图 ​ BPMN全称业务流程建模与标注,通过一系列的节点符号来定义一个流程图,包括开始事件、可执行节点、节点可执行的动作、结束事件等。工作流模块提供了前端页面来绘制BPMN流程图,通过拖拽节点符号,连接各个节点来绘制。如下图所示:其中定义了一个管网巡查申请流程。 ![image-20210818150617211](https://s2.loli.net/2022/06/20/GsHgceCW9XfLt5a.png) ​ BPMN中主要涉及到的符号包括:开始事件、人工任务、单一网关、结束事件。在绘制BPMN流程图中有相应的规范,接下来一一进行讲解。 #### 流程图编辑器 ​ 支持通用BPMN流程图编辑器。可以在Flowable Git上下载对应版本的War包,启动后可访问流程编辑器: [Release Flowable 6.0.1 release · flowable/flowable-engine · GitHub](https://github.com/flowable/flowable-engine/releases/tag/flowable-6.0.1) ​ 除了使用官网的War包启动设计器之外,也可以使用*newfiber-workflow-design*,启动后也是流程设计器。 #### BPMN定义 ​ 点击BPMN编辑器的空白处,在下方的编辑栏编辑BPMN定义的相关信息。重点关注: ​ 1)流程唯一标识:对应后端代码中的*IWorkflowDefinition.WorkflowKey* ​ 2)流程名称:对应后端代码中的*IWorkflowDefinition.WorkflowName* ![BPMN定义](https://s2.loli.net/2022/06/20/NUPFZMi7udJjw1a.png) #### 开始事件 ​ 开始事件用来定义一个流程的开始,任何流程都应包含该事件。按钮位置在【开始事件】-->【开始事件】。重点关注: ​ 1)ID:节点唯一编号,对应业务数据中的status字段。 ​ 2)名称:节点名称。 开始事件按钮图标 ![开始事件定义](https://s2.loli.net/2022/06/20/wV7cKNlxWpn9TiE.png) #### 人工任务 ​ 人工任务也就是在流程中需要人工来执行、审核的任务,例如需要用户/角色来审核。按钮位置在【活动】-->【人工任务】。重点关注: ​ 1)ID:节点唯一编号,对应业务数据中的status字段。 ​ 2)名称:节点名称。 ​ 3)任务派遣:指派任务到指定的执行人/角色,即只有被指派的人/角色才有任务该节点下的数据执行权限。 人工任务按钮图标 ![人工任务定义](https://s2.loli.net/2022/06/20/XRYEnLGszmytFrh.png) ​ 在人工任务中包含了数据执行权限,现包括以下两种方式指定: ​ 1)在流程图中静态指定执行人/角色。即指定固定的人/角色,任务只能由指定的人/角色执行,其他人/角色无权操作。 ​ 点击【任务派遣】,在弹出的页面中选择【Identity store(身份仓库)】,在【assignment(指派)】中包括三个选项: 静态指派执行人、角色 ​ ● Assigned to single user(指派给单个用户):将任务执行权限指派给单个的用户。 ​ ● Candidate users(候选用户):将任务执行权限指派给一个或多个候选用户,这些用户都有可执行权限,其中任何一个用户执行则算该数据执行完成。 ​ ● Candidate groups(候选组/角色):将任务执行权限指派给一个或多个候选角色,这些角色下的用户都有可执行权限,其中任何一个用户执行则算该数据执行完成。 ​ 2)在流程中动态指定执行人/角色。即在流程流转过程中动态指定,可以通过上一个节点执行后来指定下一个节点的执行人/角色。 动态指派执行人、角色 ​ 点击【任务派遣】,在弹出的页面中选择【Fixed value(灵活变量)】,其中包括三个输入框,输入框用于声明执行人/角色的变量,然后在程序中动态指定。注:三个输入框互斥,同时只能指定一个。 ​ ● Assignee(指派给单个用户):其值固定为${*approveUserId*}。在流程启动或者提交时代码中动态赋值变量来指定,对应后端代码中的*WorkflowStartReq.nextTaskApproveUserId*和*WorkflowSubmitReq.nextTaskApproveUserId*。注:${*approveUserId*}不能改变,否则后端代码无法识别。 ​ ● Candidate users(候选用户),暂未开放,后续看业务模块是否需要。 ​ ● Candidate groups(候选组/角色):其值固定为${*approveRoleId*},在流程启动或者提交时代码中动态赋值变量来指定,对应后端代码中的*WorkflowStartReq.nextTaskApproveRoleId*和*WorkflowSubmitReq.nextTaskApproveRoleId*。注:${*approveRoleId*}不能改变,否则后端代码无法识别。 #### 单一网关 ​ 在用户执行任务时,会有不同的动作,比如通过、不通过等,不同的动作会指向不同的结果。BPMN提供了网关来实现这一功能,我们一般用到的是单一网关(有的也叫互斥网关)。按钮位置在【网关】-->【单一网关】。重点关注: ​ 1)网关流出的节点。详情见下方的【顺序流】。 单一网关按钮图标 ![单一网关定义](https://s2.loli.net/2022/06/20/ZfI6koxsm1UjrWh.png) #### 结束事件 ​ 结束事件用来定义一个流程的结束,任何流程都应包含该事件。按钮位置在【结束事件】-->【结束事件】。重点关注: ​ 1)ID:节点唯一编号,其值需固定配置为「end」,对应业务数据中的status字段。 ​ 2)名称:节点名称。 ​ 3)执行监听器:用于监听结束事件的完成。需将其配置为*com.newfiber.workflow.support.listener.WorkflowEndListener* 结束事件按钮图标 ![结束事件定义](https://s2.loli.net/2022/06/20/g8w5tTbaod2SHUQ.png) 结束事件监听器 #### 顺序流 ​ 在BPMN流程图中,除【结束事件】,其他所有节点都会指向下一个节点,其指向的方向用顺序流表示,也就是一条实线实心箭头。每条顺序流都有其各自的跳转条件,在单一网关中,不同的执行动作就是通过顺序流的跳转条件来控制。重点关注: ​ 1)名称:顺序流对应的名称,也就是可执行的动作,对应前端界面可操作的按钮,工作流模块提供了接口供前端来查询当前节点的下一步可执行节点,接口详情见接口文档:/workflow-model/nextTasks。 ​ 2)跳转条件:跳转到对应节点的条件。其值为EL表达式:${approveResult=='true'}。其中「approveResult」为变量名,不能改变;「true」为变量值,用于控制不能的跳转,可以自行制定。例如单一网关的「通过」、「不通过」可分别配置为「${approveResult=='true'}」、${approveResult=='false'}。在后端代码中对应的控制变量为WorkflowSubmitReq.approveResult。需要注意的是每条顺序流都需要指定跳转条件(「开始事件外」),否则程序无法识别。 ![顺序流定义](https://s2.loli.net/2022/06/20/dhMyfq1voTJWZmY.png) 顺序流跳转条件 ### 数据库集成 #### MySQL视图 ​ 工作流模块有自己独立的数据库表,用于支持其自身的功能。例如:在流程图编辑的过程中,可以指定业务系统中的用户或者角色,这些数据是维护在工作流模块自身的表中的。因此需要在业务模块和工作流模块之间进行数据同步,这里提供了MySQL视图实现其数据同步功能。 ​ 这里提供了对业务模块用户/角色/用户角色关系表的视图脚本,其脚本文件为:/doc/sql/workflow/view.sql。重点关注以下几张表: ​ 1)ACT_ID_USER:工作流用户表 ​ 2)ACT_ID_GROUP:工作流组/角色表 ​ 3)ACT_ID_MEMBERSHIP:工作流用户组/角色关系表 工作流用户视图如下: ```sql CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `ACT_ID_USER` AS SELECT `newfiber_standard`.`sys_user`.`user_id` AS `ID_`, 1 AS `REV_`, `newfiber_standard`.`sys_user`.`nick_name` AS `FIRST_`, `newfiber_standard`.`sys_user`.`nick_name` AS `LAST_`, `newfiber_standard`.`sys_user`.`user_name` AS `DISPLAY_NAME_`, `newfiber_standard`.`sys_user`.`email` AS `EMAIL_`, '123456' AS `PWD_`, NULL AS `PICTURE_ID_`, NULL AS `TENANT_ID_` FROM `newfiber_standard`.`sys_user` WHERE (`newfiber_standard`.`sys_user`.`del_flag` = 0) ``` #### 业务表结构设计 ​ 为支持业务模块的数据流转,业务表需要加入以下两个字段: ​ 1)workflow_instance_id:工作流实例编号,用于保持业务模块与工作流模块的关联关系。 ​ 2)status:用户完成业务数据的状态流转 #### 数据库分库 ​ Flowable官方默认提供了47张表,如果都和业务表放在一起,那数据库就太杂乱了,之前的版本就想做分库,但是没有找到合适的方法。 ​ Flowable框架暴露了接口用来自定义*SpringProcessEngineConfiguration*,修改里面的*dataSource*属性可以实现数据库分库,修改自定义flowable属性即可实现分库。 ```yaml flowable: database-schema: newfiber_standard_workflow driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1/newfiber_standard_workflow?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true username: root password: 123456 ``` ### 后端集成 ​ 后端采用SpringBoot框架,Maven管理项目依赖,对接方式如下: ​ 1)工作流在通用工具包中*newfiber-common-workflow*,在pom文件中引入工作流模块依赖: ```java com.newfiber newfiber-common-workflow ``` ​ 2)在ApplicationStart类中引入工作流模块路径,完成依赖注入: ```java // 加入工作流模块路径:"com.newfiber.workflow" @SpringBootApplication( scanBasePackages = {"com.newfiber.business", "com.newfiber.workflow"}) public class WorkflowParentApplication { public static void main(String[] args) { SpringApplication.run(WorkflowParentApplication.class, args); } } ``` ​ 3)开始工作流编码,主要包括以下步骤: ​ ● 创建枚举类实现*IWorkflowDefinition*接口,完成工作流基本信息定义。 ​ ● Server层实现*IWorkflowCallback*接口并重写方法,完成业务回调方法实现。 ​ ● Server层调用*ActivitiProcessService*类中的方法,完成工作流的启动,执行等操作。 ​ ● 可通过*WorkflowPageHelper*工具类来辅助完成业务数据的分页查询操作。 #### IWorkflowDefinition ​ 不同的工作流有不同的定义,工作流模块提供了*IWorkflowDefinition*接口规范工作流的定义,接口方法签名下: ```java public interface IWorkflowDefinition { /** * 工作流编号 * @return 工作流编号 */ String getWorkflowKey(); /** * 工作流名称 * @return 工作流名称 */ String getWorkflowName(); /** * 业务实体表名,用于分页查询业务数据 * @return 业务实体表名 */ default String getTableName() { return "t"; } /** * 业务实体表主键类型,用于分页查询业务数据 * @return 业务实体表主键类型 */ default Class getTableIdType(){ return String.class; } } ``` ​ 在业务系统中需要实现该接口,例如定义一个巡查申请的工作流,其代码如下: ```java public enum EWorkflowDefinition implements IWorkflowDefinition { /** */ PatrolApply("PatrolApply", "巡查申请", "t", Integer.class); EWorkflowDefinition( String workflowKey, String workflowName, String tableName, Class tableIdType) { this.workflowKey = workflowKey; this.workflowName = workflowName; this.tableName = tableName; this.tableIdType = tableIdType; } private final String workflowKey; private final String workflowName; private final String tableName; private final Class tableIdType; @Override public String getWorkflowKey() { return workflowKey; } @Override public String getWorkflowName() { return workflowName; } @Override public String getTableName() { return tableName; } @Override public Class getTableIdType() { return tableIdType; } } ``` #### IWorkflowCallback ​ 在业务模块开始、提交工作流后,需要执行更新关联id、状态等操作,这里提供了*IWorkflowCallback*回调接口用来实现该功能。其方法签名如下: ```java public interface IWorkflowCallback { /** * 业务实体类型 * @return 业务实体类型 */ default Class getEntityClass(){ return ReflectionKit.getInterfaceGeneric(this); } /** * 工作流定义 * @return 工作流定义 */ IWorkflowDefinition getWorkflowDefinition(); /** * 更新业务实体的工作流实体编号 * @param businessKey 业务实体编号 * @param workflowInstanceId 工作流实体编号 */ void refreshWorkflowInstanceId(Object businessKey, String workflowInstanceId); /** * 更新业务数据状态 * @param businessKey 业务实体编号 * @param status 状态 */ void refreshStatus(Object businessKey, String status); } ``` ​ 业务模块的Server需要实现该接口,例如巡查申请的Server,其核心代码如下: ```java @Service public class PurchaseApplyServiceImpl implements IWorkflowCallback { @Override public IWorkflowDefinition getWorkflowDefinition() { return EWorkflowDefinition.PurchaseApply; } @Override public void refreshWorkflowInstanceId(Object businessKey, String workflowInstanceId) { PurchaseApply condition = new PurchaseApply(); condition.setId(Integer.parseInt(businessKey.toString())); condition.setWorkflowInstanceId(workflowInstanceId); updateById(condition); } @Override public void refreshStatus(Object businessKey, String status) { PurchaseApply condition = new PurchaseApply(); condition.setId(Integer.parseInt(businessKey.toString())); condition.setStatus(status); updateById(condition); } } ``` #### ActivitiProcessService ​ *ActivitiProcessService*提供了工作流程中涉及到的核心方法,包括工作流开始、提交等方法,其核心方法签名如下: ```java /** * 启动工作流 * @param workflowCallback 回调接口 * @param businessKey 业务编号 * @param startReq 启动参数 * @return 工作流实体编号 */ String startWorkflow(IWorkflowCallback workflowCallback, Object businessKey, WorkflowStartReq startReq); /** * 提交工作流 * @param callback 回调接口 * @param businessKey 业务编号 * @param submitReq 提交结果 * @return 业务编号 */ String submitWorkflow(IWorkflowCallback callback, Object businessKey, WorkflowSubmitReq submitReq); ``` ​ 业务模块的Server需要注入该接口,并调用其启动和提交方法完成工作流的数据流转,例如巡查申请的Server,其核心代码如下: ```java @Service public class PatrolApplyServiceImpl implements PatrolApplyService, IWorkflowCallback { @Resource private ActivitiProcessService activitiProcessService; @Override @Transactional(rollbackFor = Exception.class) public void create(PatrolApplyCreateReq req) { PatrolApply patrolApply = new PatrolApply(); BeanUtils.copyProperties(req, patrolApply); this.save(patrolApply); // 启动工作流 activitiProcessService.startWorkflow(this, patrolApply.getId(), req); } @Override public void approve(PatrolApplyApproveReq req) { // 提交工作流 activitiProcessService.submitWorkflow(this, req.getId(), req); } } ``` #### WorkflowPageHelper ​ 在业务数据查询过程中,会涉及到数据查询权限的问题,其中的权限包括两个部分: ​ 1)数据状态权限:用户只能查看到处于某一状态下的数据。 ​ 2)数据执行权限:用户只能查看属于自己的可执行的数据。 ​ 对于第一种数据权限,可以通过业务表的status字段控制,业务系统实现起来较为简单。而第二种权限实现起来则较为复杂,这是因为业务模块和工作流模块都只相互存放了对方的数据ID,导致业务模块必须先调用工作流模块查询用户可执行的数据ID,然后才能执行业务查询操作,其调用时序图如下所示: 数据权限查询时序图 ​ 为解决业务模块调用方法繁琐的问题,这里提供了WorkflowPageHelper工具类。可以实现在分页查询业务数据时,自动加入用户权限数据查询的功能。其核心方法签名如下: ```java /** * 开始分页 * @param pageNum 开始页数 * @param pageSize 每页数量 * @param orderBy 排序字段 * @param userId 用户编号 * @param taskKey 任务编号,对应相应的状态节点 * @param workflowCallback 回调接口 * @return 分页参数实体 */ public static Page startPage(int pageNum, int pageSize, String orderBy, Object userId, String taskKey, IWorkflowCallback workflowCallback) /** * 开始分页 * @param workflowPageReq 分页接口参数 * @param workflowCallback 回调接口 * @return 分页参数实体 */ public static Page startPage(WorkflowPageReq workflowPageReq, IWorkflowCallback workflowCallback) ``` ​ 其也是基于开源工具PageHelper,用法类似。例如巡查申请分页查询,其核心代码如下: ```java @Override public PageInfo page(PatrolApplyPageReq req) { PatrolApply condition = new PatrolApply(); BeanUtils.copyProperties(req, condition); // 执行分页操作 WorkflowPageHelper.startPage(req, this); List list = patrolApplyDao.selectByCondition(condition); return new PageInfo<>(list); } ``` ## 功能特性 ### 会签 ​ 在流程业务管理中,任务通常都是由一个人去处理的。而多个人同时处理一个任务,根据多个人的审核结果来确定流程的走向,这种任务我们称之为会签任务。 ​ 在绘制BPMN流程图时,添加将【人工任务】配置为会签节点,重点关注: ​ 1)多实例类型:选择「Parallel」。 ​ 2)集合(多实例):配置为approveUserIdList。会签节点的审核人列表,对应代码中的*WorkflowStartReq.nextTaskApproveUserIdList*和*WorkflowSubmitReq.nextTaskApproveUserIdList*。 ​ 3)元素的变量(多实例):集合的变量,会签节点的审核人,需要和「任务派遣」定义的审核人对应。 ​ 4)任务派遣:会签节点的审核人。 ![会签节点配置](https://s2.loli.net/2022/06/20/A6nCscxbvaIEi5H.png) ​ 配置完成后,当执行到该节点时,会为每个会签人员分配一个任务,所有人通过才会通过。 #### 一票否决 ​ 会签实现多个人同时审批,任意一个人不同意时,会签任务结束。 ​ 1)在完成条件(多实例)中添加以下配置,当满足表达式的条件时,会签节点结束,进入下一个节点:*${(approveResult=='false')||(nrOfCompletedInstances/nrOfInstances==1)}* ​ ●*approveResult=='false'*:「approveResult」为「false」时会签结束,即一票否决。 ​ ●*nrOfCompletedInstances/nrOfInstances==1*:框架中维护了两个变量「nrOfCompletedInstances」--Number Of Completed Instances(已完成实例数)和「nrOfInstances」--Number Of Instances(实例数),当两者相同时会签节点结束。 ![消息通知监听器节点](https://s2.loli.net/2022/06/20/rmbHMSFBacEi2qY.png) ### 消息通知 ​ 在工作流执行到流程中的某个节点时,可以向节点的执行人发送邮件/短信通知。现支持三种方式: ​ 1)使用工作流默认的邮件通知 ​ 2)使用自定义消息模板发送通知 ​ 3)使用代码调用的方式发送通知 在配置文件中添加以下配置: ```yaml spring: mail: host: smtp.exmail.qq.com username: xiongkai@chinhangroup.com password: 123456 properties: mail: smtp: auth: true ``` 在需要发送消息的【人工任务】中配置消息通知监听器*com.newfiber.workflow.support.listener.WorkflowNotificationListener* ![消息通知监听器节点](https://s2.loli.net/2022/06/20/pPUM5hqayH2D4Ix.png) 消息通知监听器 #### 默认邮件通知 ​ 在需要进行消息通知的任务中添加监听器,当执行到该任务时,会自动向该任务的执行人发送一封邮件,邮件内容为 *[%workflowKey]您有一条待办任务:[%businessKey]*。如果需要自定义发送的内容,或者需要发送短信,可以参照下文的「自定义消息模板」和「方法调用」。 #### 自定义消息模板 ​ 如果不想使用默认的消息模板,可以自定义消息模板。这里提供了接口来实现自定义消息模板:IWorkflowEmailNotification用于定义邮件模板,IWorkflowSmsNotification用于定义短信模板,其方法签名如下。其中短信模板现仅支持腾讯云短信,需要在腾讯云中申请短信签名及短信模板后再使用。(注:任务需要配置*WorkflowNotificationListener*监听器) ##### IWorkflowEmailNotification ```java public interface IWorkflowEmailNotification extends IWorkflowNotification{ /** * 通知模板 * @return 通知模板 */ String getNotificationTemplate(); } ``` ##### IWorkflowSmsNotification ```java public interface IWorkflowSmsNotification extends IWorkflowNotification{ /** * 短信签名 * @return 短信签名 */ default String getSmsSign(){ return null; } /** * 短信模板编号 * @return 短信模板编号 */ default String getSmsTemplateCode(){ return null; } } ``` ​ 使用时,首先需要在业务代码中创建枚举实现以上接口。这里以同时发送邮件和短信为例:分别配置了邮件模板和短信模板。 ```java public enum ECountersignNotification implements IWorkflowEmailNotification, IWorkflowSmsNotification { /** * */ AfterApprove("AfterApprove", "[%s]您有一条待办任务:[%s],请在[%s]前完成", "新钜物联","10945341"); private final String notificationTask; private final String notificationTemplate; private final String smsSign; private final String smsTemplateCode; ECountersignNotification(String notificationTask, String notificationTemplate, String smsSign, String smsTemplateCode) { this.notificationTask = notificationTask; this.notificationTemplate = notificationTemplate; this.smsSign = smsSign; this.smsTemplateCode = smsTemplateCode; } @Override public String getNotificationTask() { return notificationTask; } @Override public String getNotificationTemplate() { return notificationTemplate; } @Override public String getSmsSign() { return smsSign; } @Override public String getSmsTemplateCode() { return smsTemplateCode; } } ``` ​ 然后业务模块的Server需要实现*IWorkflowCallback*接口的*getWorkflowNotification()*方法,并返回模板定义类。例如会签申请的Server,其核心代码如下: ```java public interface IWorkflowCallback { /** * 工作流通知 * @return 工作流通知 */ default IWorkflowNotification[] getWorkflowNotification(){ return null; } } ``` ```java public class CountersignServiceImpl implements IWorkflowCallback { @Override public IWorkflowNotification[] getWorkflowNotification() { return ECountersignNotification.values(); } } ``` ​ 在发送通知时,可以动态指定消息模板的参数。参数内容通过*WorkflowStartReq.notificationTemplateArgs*或者*WorkflowSubmitReq.notificationTemplateArgs*传入。 #### 方法调用 ​ 如果不想使用默认邮件通知和自定义消息模板,也可以直接通过代码调用的方式发送通知。方法签名如下: ```java public interface ActivitiProcessService { /** * 发送邮件通知 * @param email 邮箱 * @param content 内容 * @return 是否成功 */ boolean sendEmailNotification(String email, String content); /** * 发送短信通知 * @param mobile 手机号 * @param smsSign 短信签名 * @param smsTemplateCode 短息模板编号 * @param templateArgs 模板参数 * @return 是否成功 */ boolean sendSmsNotification(String mobile, String smsSign, String smsTemplateCode, List templateArgs); } ``` ### 文件上传及查询 #### 上传 ​ 工作流框架集成到通用产品框架后,节点附件上传走的通用文件上传,文件数据通过「refType」+「refField」和业务数据关联。 ​ ● refType:关联类型/业务类型;例如巡查任务:patrolTask ​ ●refField:关联字段;可同时区分节点和文件类型,例如开始图片:start.picture,结束视频:finish.video #### 查询 ​ 在「列表查询历史活动记录」接口*`/workflow-process/list-history-activity`*中,需要返回文件数据,由于涉及到依赖关系问题,工作流模块无法直接调用系统文件模块查询。所以现在的方案是通过AOP切面实现文件查询功能,涉及到的关键类: ​ 1、*com.newfiber.common.core.annotation.WorkflowFileWrapper*:文件包装注解 ​ 2、com.newfiber.common.security.aspect.WorkflowFileAspect:文件包装AOP ​ 对于每个节点的文件,默认会以`refField.startsWith(节点状态)`过滤(即每个节点只能查询自身上传的文件)。同时查询条件中暴露了字段`fileRefFieldPattern 查询关联文件匹配符(例查询refField like '%picture%', 则传picture)`,用于定制化查询。 ## 集成国产数据库 ​ 修改`newfiber-common-datasource`包的POM依赖,切换数据库组件。 ```xml cn.com.kingbase kingbase8 ``` ### 人大金仓 #### 链接配置 ```yaml spring: datasource: driver-class-name: com.kingbase8.Driver url: jdbc:kingbase8://192.168.30.91:54321/newfiber_standard username: system password: system2023 ``` ### 达梦 #### 链接配置 ```yaml datasource: driver-class-name: dm.jdbc.driver.DmDriver url: jdbc:dm://192.168.30.93:5236/newfiber_standard?schema=newfiber_standard username: SYSDBA password: SYSDBA ``` #### 工作流框架适配 ​ 由于达梦不同于人大金仓,数据库不支持原生的隐式数据类型转换,会导致工作流在启动初始化时报错,所以对工作流源码进行了改造用以适配。 ​ 项目模块如果引用了工作流,需要用`/resources/liquibase-core-4.9.1.jar`包替换掉本地maven仓库对应的包才可正常启动。 ### 总结 ​ 总的来看,对接工作流模块需要完成以下步骤: ​ 1)画符合规范的BPMN流程图。 ​ 2)编写符合业务模块的MySQL视图并执行。 ​ 3)设计业务表。 ​ 4)完成后端代码编码。 ## 问题及补充 ### BPMN绘制规范 ​ 在工作流开发过程中,BPMN绘制占很重要的部分,可以由懂业务的项目经理或者开发人员来绘制。在绘制过程中要特别注意上文「BPMN流程图」中提到的「重点关注」事项,其中的绘制规范涉及到和后端代码的交互,如果不遵守会导致后端代码无法识别。重点关注: ​ ● BPMN流程图要指定「流程唯一标识」和「名称」 ​ ● 各个节点要指定「ID」和「名称」 ​ ● 人工任务节点可指定用户/角色权限,对于变量名的指定要符合文档规范 ​ ● 结束事件的ID固定为「end」,并且要指定「执行监听器」 ​ ● 每条顺序流都要指定「跳转条件」(「开始事件外」) ### 相关SQL #### 已办 ```sql SELECT RES.* from ACT_HI_TASKINST RES WHERE RES.END_TIME_ is not null and ( EXISTS(select LINK.ID_ from ACT_HI_IDENTITYLINK LINK where LINK.USER_ID_ = 1 and LINK.TASK_ID_ = RES.ID_) or RES.ASSIGNEE_ = 1 or RES.OWNER_ = 1 ) and exists ( select 1 from ACT_RE_PROCDEF D WHERE RES.PROC_DEF_ID_ = D.ID_ and D.KEY_ like 'PatrolCase' ) order by RES.ID_ asc; SELECT RES.* from ACT_HI_TASKINST RES left join ACT_RE_PROCDEF D on RES.PROC_DEF_ID_ = D.ID_ where D.KEY_ = 'PatrolCase' ``` #### 代办 ```sql SELECT RES.* from ACT_RU_TASK RES WHERE exists ( select 1 from ACT_RE_PROCDEF D WHERE RES.PROC_DEF_ID_ = D.ID_ and D.KEY_ = 'projectQualityReformRecord' ) and (RES.ASSIGNEE_ = 1 or ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = RES.ID_ and LINK.TYPE_ = 'candidate' and (LINK.USER_ID_ = 1 or ( LINK.GROUP_ID_ IN ( 89010001 ) ) )))) order by RES.ID_ asc ```