предисловие
Как разработчик, вы более или менее знакомы с механизмами рабочего процесса, такими как activiti, jbpm и т. д. Эти механизмы рабочего процесса относительно тяжелые. Это не очень элегантно для внедрения в небольшие проекты. Эта статья в основном кратко знакомит с принципом механизма рабочего процесса и описывает, как медленно построить механизм рабочего процесса, который вам подходит.
узел процесса
Когда проекту требуется несколько различных процедур (процессов) или он разделен на несколько этапов для завершения, определенная процедура или определенный этап завершаются, другая процедура или определенный этап начинают переходную точку (точку категории или момент времени), называемую узлом процесса. Общие узлы процесса: начальный конец, узел задачи, узел условия, узел ветвления, узел слияния, узел подпроцесса, конечный узел и т. д. Для простоты здесь объясняется только самый простой процесс (начало->задача->конец).
начальный узел
В начале процесса узел, выполняющий некоторые операции инициализации.
-
Введите СТАРТ
-
Класс модели StartModel
-
Описание недвижимости
Имя свойства тип инструкция id String Идентификатор узла, уникальный идентификатор узла процесса name String Имя узла, указывающее, что делает узел nextNodeId String идентификатор следующего узла ext Map Расширенные свойства
узел задачи
Узел сгенерирует задачу, и следующий шаг можно будет выполнить только после завершения задачи.
- Тип ЗАДАЧА
- Класс модели ТаскМодел
- Описание недвижимости
Имя свойства | тип | инструкция |
---|---|---|
id | String | Идентификатор узла, уникальный идентификатор узла процесса |
name | String | Имя узла, указывающее, что делает узел |
nextNodeId | String | идентификатор следующего узла |
performType | String | Метод участия (ЛЮБОЙ->Любой участник может выполнить следующий шаг после обработки, ВСЕ->Все участники могут выполнить следующий шаг) |
expireTime | String | Ожидаемое время завершения (когда ожидается завершение созданной задачи) |
reminderTime | String | Время напоминания (если задание не выполнено, когда будет напоминание) |
reminderRepeat | Integer | Интервал напоминания (минуты) (если задача не обрабатывается, каково правило напоминания) |
autoExecute | Boolean | Выполняться ли автоматически (если задача не обрабатывается и должна выполняться автоматически) |
ext | Map | Расширенные свойства |
конечный узел
Этот узел указывает, что процесс был завершен, и здесь инициируется событие завершения процесса.
- Введите КОНЕЦ
- Класс модели EndModel
- Описание недвижимости
Имя свойства | тип | инструкция |
---|---|---|
id | String | Идентификатор узла, уникальный идентификатор узла процесса |
name | String | Имя узла, указывающее, что делает узел |
ext | Map | Расширенные свойства |
Образец определения процесса
- Процесс публикации рукописи Центра новостей
{
"flowId":"cmsNews",
"flowName":"新闻中心稿件发布",
"nodeList": [
{
"id":"start",
"nodeType": "START",
"name":"开始",
"nextNodeId": "collect"
},
{
"id":"collect",
"nodeType": "TASK",
"name":"采编新闻稿件",
"performType":"ANY",
"nextNodeId": "firstReview",
"ext":{
"userIds":"1,2,3"
}
},
{
"id":"firstReview",
"nodeType": "TASK",
"name":"党群部宣传科初审",
"performType":"ANY",
"nextNodeId": "secondReview",
"ext":{
"userIds":"1,2,3"
}
},
{
"id":"secondReview",
"nodeType": "TASK",
"name":"党群部二审",
"performType":"ANY",
"nextNodeId": "end",
"ext":{
"userIds":"1,2,3"
}
},
{
"id":"end",
"nodeType": "END",
"name":"结束"
}
]
}
дизайн структуры таблицы
- flow_define (определение потока)
поле | тип | нулевой | дефолт | Примечания |
---|---|---|---|---|
id | char(36) | False | первичный ключ | |
flow_define_code | varchar(16) | True | Кодирование определения процесса | |
flow_name | varchar(100) | True | Имя процесса | |
description | varchar(255) | True | Описание процесса | |
flow_define | mediumtext | True | Определение процесса (строка json) | |
create_time | datetime | True | время создания | |
update_time | datetime | True | Время обновления | |
is_deleted | tinyint(1) unsigned | True | 0 | Удалять ли (1->удалить ДА, 0->не удалять НЕТ) |
- flow_work (экземпляр процесса)
поле | тип | нулевой | дефолт | Примечания |
---|---|---|---|---|
id | char(36) | False | первичный ключ | |
flow_name | varchar(100) | True | Имя процесса | |
flow_define_code | varchar(32) | True | Кодирование определения процесса | |
last_operator | char(36) | True | последний идентификатор оператора | |
current_node_id | char(36) | True | идентификатор текущего узла | |
next_node_id | char(36) | True | идентификатор следующего узла | |
flow_define | mediumtext | True | Определение процесса (строка json) | |
status | int(6) | True | Статус процесса (10->Старт СТАРТ, 30->Конец КОНЕЦ, 40->Отмена ОТМЕНА) | |
flow_param | mediumtext | True | Параметр процесса json | |
create_time | datetime | True | время создания | |
update_time | datetime | True | Время обновления | |
is_deleted | tinyint(1) unsigned | True | 0 | Удалять ли (1->удалить ДА, 0->не удалять НЕТ) |
- flow_task (задача процесса)
поле | тип | нулевой | дефолт | Примечания |
---|---|---|---|---|
id | char(36) | False | первичный ключ | |
flow_work_id | char(36) | True | идентификатор процесса | |
flow_node_id | char(36) | True | идентификатор узла процесса | |
task_name | varchar(100) | True | название миссии | |
operator | char(36) | True | идентификатор пользователя оператора | |
actor_user_id | char(36) | True | id исполняющего пользователя | |
status | varchar(6) | True | 10 | Статус задачи (10->Новое СОЗДАНО, 20->ЗАВЕРШЕНО выполнено, 30->Срок действия истек, 40->Отмена ОТМЕНА, 50->Отклонено ОТКЛОНИТЬ) |
service_type | varchar(32) | True | тип бизнеса | |
service_id | varchar(36) | True | связанный бизнес-идентификатор | |
finish_time | datetime | True | Полное время | |
flow_param | mediumtext | True | Параметр процесса json | |
create_time | datetime | True | время создания | |
update_time | datetime | True | Время обновления | |
is_deleted | tinyint(1) unsigned | True | 0 | Удалять ли (1->удалить ДА, 0->не удалять НЕТ) |
Описание ключевого слова процесса
- О файлах определения процесса
Файл определения процесса — это файл, который описывает взаимосвязь между каждым узлом процесса и описывается здесь файлом json, который хранится в поле flow_define таблицы flow_define.
- Об анализе файла определения процесса
Разбор файла определения процесса заключается в анализе каждого узла процесса в файле определения процесса и сборке его в модель процесса.
- Об экземплярах процессов
Экземпляр процесса — это экземпляр процесса, сгенерированный с использованием определения процесса, и сгенерированный экземпляр процесса хранится в таблице flow_work. (Эта операция инициируется бизнес-системой, то есть инициируется процесс.)
- О задачах
Задача будет сгенерирована на узле задач, то есть при выполнении узла задач процессор узла будет использоваться для назначения задач участвующему персоналу, а назначенные задачи сохраняются в таблице flow_task. В бизнес-системе задачи вошедшего в систему пользователя можно получить, запросив эту таблицу.
- О выполнении задач завершения
После того, как участники обработают задачу, бизнес-система вызовет метод завершения задачи, а механизм процесса изменит статус завершения задачи и переведет процесс на следующий узел.
- Об отклонении задач
Когда участник отклоняет задачу, бизнес-система вызывает метод отклонения задачи, а механизм процесса возвращает ход выполнения процесса к предыдущему узлу.
Описание потока основной обработки
FlowWorkCtl.java
- Запустить экземпляр процесса
- Определение процесса загрузки
- Разобрать файл определения процесса
- Вызовы контроллера обработчика узла для выполнения обработки узла
- Если это начальный узел, нужно опубликовать только начальное событие.
- Если это узел задач, в соответствии с конфигурацией узла отправляйте задачи, публикуйте начальные события и обновляйте статус узлов экземпляров процесса (не обязательно, просто записи).
- Если это конечный узел, измените экземпляр процесса, чтобы завершить и опубликовать конечное событие.
- выполнять задачи
- получить экземпляр задачи
- Получить экземпляр процесса
- Разобрать файл определения процесса
- Поставить задачу как выполненную
- Определить, завершена ли задача узла
- Если он завершен, получить следующий узел и вызвать узел для обработки выполнения контроллера
- Если не заполнено, не нужно обрабатывать
- отклонить задачу
- получить экземпляр задачи
- Получить экземпляр процесса
- Разобрать файл определения процесса
- Установить задачу как отклоненную
- Изменить задачи других участников узла как отмененные
- Получите предыдущую модель узла и вызовите узел для обработки выполнения контроллера.
начать кодирование
Каталог проекта
com.mole.modules.flow
├── entity # 实体类,与表结构对应
├── FlowDefine.java # 流程定义实体
├── FlowTask.java # 流程任务实体
└── FlowWork.java # 流程实例实体
├── enums # 错误码枚举
└── FlowErrEnum.java
├── event # 事件
├── FlowEndNodeEvent.java # 流程结束事件
├── FlowStartNodeEvent.java # 流程开始事件
├── FlowTaskCreatedEvent.java # 流程任务创建事件
└── FlowTaskNode.Event.java # 任务开始执行事件
├── mapper # 持久层
├── FlowDefineMapper.java
├── FlowTaskMapper.java
└── FlowWorkMapper.java
├── param # 参数实体
└── FlowParam.java # 流程参数实体
├── model # 模型层
├── BaseModel.java # 基础节点模型
├── EndModel.java # 结束节点模型
├── FlowModel.java # 流程模型
├── StartModel.java # 开始节点模型
└── TaskModel.java # 任务节点模型
├── parser
└── FlowNodeParser.java # 节点解析器
├── processor
├── impl
├── EndNodeProcessorImpl.java # 结束节点处理实现类
├── StartNodeProcessorImpl.java # 开始节点处理实现类
└── TaskNodeProcessorImpl.java # 任务节点处理实现类
├── FlowNodeProcessor.java # 节点处理接口
└── FlowNodeProcessorCtl.java # 节点处理器控制器
├── service
└── FlowNodeUserService.java # 自定义节点参与人接口
└── FlowWorkCtl.java # 流程控制类-引擎
основной класс
Класс модели
- FlowModel.java
package com.mole.modules.flow.model;
import java.io.Serializable;
import java.util.List;
/**
* 流程模型
* @author MLD
*
*/
public class FlowModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = -3978388377128544781L;
/**
* 流程id
*/
private String flowId;
/**
* 流程名称
*/
private String flowName;
/**
* 流程节点
*/
private List<BaseModel> nodeList;
// ....get .....set 略
/**
* 通过id获取节点
* @param nodeId
* @return
*/
public BaseModel getNodeModel(String nodeId) {
if(null == nodeList || nodeList.isEmpty()) {
return null;
}
for(BaseModel node : nodeList) {
if(node.getId().equals(nodeId)){
return node;
}
}
return null;
}
/**
* 获取开始节点模型
* @return
*/
public StartModel getStartModel() {
if(null == nodeList || nodeList.isEmpty()) {
return null;
}
for(BaseModel node : nodeList) {
if(node instanceof StartModel){
return (StartModel)node;
}
}
return null;
}
/**
* 通过当前节点的上一个节点
* @param nodeId
* @return
*/
public BaseModel getPreNodeModel(String nodeId) {
if(null == nodeList || nodeList.isEmpty()) {
return null;
}
for(BaseModel node : nodeList) {
if(nodeId.equals(node.getNextNodeId())){
return node;
}
}
return null;
}
/**
* 获取结束节点模型
* @return
*/
public EndModel getEndModel() {
if(null == nodeList || nodeList.isEmpty()) {
return null;
}
for(BaseModel node : nodeList) {
if(node instanceof EndModel){
return (EndModel)node;
}
}
return null;
}
}
- BaseModel.java
package com.mole.modules.flow.model;
import java.io.Serializable;
import java.util.Map
public class BaseModel implements Serializable {
/**
* 节点id
*/
private String id;
/**
* 节点名称
*/
private String name;
/**
* 节点类型
*/
private String nodeType;
/**
* 下一个节点id
*/
private String nextNodeId;
/**
* 扩展属性
*/
private Map<String,Object> ext;
}
- StartModel.java
package com.mole.modules.flow.model;
import java.io.Serializable;
public class StartModel extends BaseModel implements Serializable {
}
- TaskModel.java
package com.mole.modules.flow.model;
import java.io.Serializable;
public class TaskModel extends BaseModel implements Serializable {
/**
* 参与方式(
* ANY->任何一个参与者处理完即可执行下一步,
* ALL->所有参与者都完成,才可执行下一步
* )
*/
private String performType;
/**
* 期望完成时间(产生的任务期望啥时间完成)
*/
private String expireTime;
/**
* 提醒时间(如任务未处理,啥时候提醒)
*/
private String reminderTime;
/**
* 提醒间隔(分钟)(如任务未处理,提醒规则是什么)
*/
private Integer reminderRepeat;
/**
* 是否自动执行(如任务未处理且到期,是否自动执行)
*/
private Boolean autoExecute;
}
- EndModel
package com.mole.modules.flow.model;
import java.io.Serializable;
public class EndModel extends BaseModel implements Serializable {
}
Класс разбора узла процесса
Преобразование определения процесса json в соответствующую модель процесса
- FlowNodeParser.java
package com.mole.modules.flow.parser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mole.modules.framework.util.JsonUtil;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.EndModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.model.StartModel;
import com.mole.modules.flow.model.TaskModel;
/**
* 节点解析器
* @author MLD
*
*/
public class FlowNodeParser {
private ObjectMapper objectMapper = new ObjectMapper();
private final static String FLOW_ID="flowId";
private final static String FLOW_NAME="flowName";
private final static String NODE_LIST="nodeList";
private final static String NODE_TYPE="nodeType";
/**
* 解析流程定义
* @param flowDefine
* @return
* @throws IOException
*/
public FlowModel parser(String flowDefine) throws IOException {
JsonNode root = objectMapper.readTree(flowDefine);
if(!root.has(FLOW_ID)||!root.has(FLOW_NAME)||!root.has(NODE_LIST)) {
}
String flowId = root.get(FLOW_ID).asText();
String flowName = root.get(FLOW_NAME).asText();
JsonNode nodeList = root.get("nodeList");
if(!nodeList.isArray()) {
return null;
}
FlowModel flowModel = new FlowModel();
flowModel.setFlowId(flowId);
flowModel.setFlowName(flowName);
flowModel.setNodeList(new ArrayList<BaseModel>());
Iterator<JsonNode> nodes = nodeList.elements();
while (nodes.hasNext()) {
JsonNode node = nodes.next();
if(node.has(NODE_TYPE)) {
String nodeType = node.get(NODE_TYPE).asText();
if("START".equals(nodeType)) {
// 开始节点
StartModel model = objectMapper.readValue(node.toString(), StartModel.class);
flowModel.getNodeList().add(model);
} else if("TASK".equals(nodeType)) {
// 任务节点
TaskModel model = objectMapper.readValue(node.toString(), TaskModel.class);
flowModel.getNodeList().add(model);
} else if("END".equals(nodeType)) {
// 结束节点
EndModel model = objectMapper.readValue(node.toString(), EndModel.class);
flowModel.getNodeList().add(model);
}
}
}
return flowModel;
}
}
Класс обработчика узла
Процессор узла в основном выполняет различную обработку планирования на разных узлах, например, начальный узел переводит процесс на следующий шаг, узел задач генерирует задачи, а конечный узел завершает процесс.
- FlowNodeProcessor.java
package com.mole.modules.flow.processor;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.param.FlowParam;
/**
* 节点处理器接口
* @author MLD
*
*/
public interface FlowNodeProcessor {
/**
* 要处理的节点类型
* @return
*/
public String getNodeType();
/**
* 流程节点处理方法
* @param flowWork 流程实例
* @param flowModel 当前流程模型
* @param currentNodeModel 当前节点模型
* @param flowParam 流程参数
*/
public void process(FlowWork flowWork,FlowModel flowModel,BaseModel currentNddeModel,FlowParam flowParam);
}
- StartNodeProcessorImpl.java
package com.mole.modules.flow.processor.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.event.FlowStartNodeEvent;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.model.StartModel;
import com.mole.modules.flow.param.FlowParam;
import com.mole.modules.flow.processor.FlowNodeProcessor;
/**
* 开始节点处理器
* @author MLD
*
*/
@Component
public class StartNodeProcessorImpl implements FlowNodeProcessor {
@Autowired(required=false)
private FlowStartNodeEvent flowStartEvent;
@Override
public void process(FlowWork flowWork, FlowModel flowModel, BaseModel currentNddeModel,FlowParam flowParam) {
// 开始节点事件--
if(null != flowStartEvent) {
StartModel startModel = flowModel.getStartModel();
flowStartEvent.onEvent(flowWork, startModel, flowParam);
}
}
@Override
public String getNodeType() {
return "START";
}
}
- TaskNodeProcessorImpl.java
package com.mole.modules.flow.processor.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.mole.framework.base.ServiceException;
import com.mole.framework.base.YesNoEnum;
import com.mole.framework.util.JsonUtil;
import com.mole.framework.util.StringUtil;
import com.mole.modules.flow.entity.FlowTask;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.enums.FlowErrEnum;
import com.mole.modules.flow.event.FlowTaskCreatedEvent;
import com.mole.modules.flow.event.FlowTaskNodeEvent;
import com.mole.modules.flow.mapper.FlowTaskMapper;
import com.mole.modules.flow.mapper.FlowWorkMapper;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.model.TaskModel;
import com.mole.modules.flow.param.FlowParam;
import com.mole.modules.flow.processor.FlowNodeProcessor;
import com.mole.modules.flow.service.FlowNodeUserService;
/**
* 任务节点处理器
* @author MLD
*
*/
@Component
public class TaskNodeProcessorImpl implements FlowNodeProcessor {
@Autowired
private FlowWorkMapper flowWorkMapper;
@Autowired
private FlowTaskMapper flowTaskMapper;
@Autowired(required=false)
private FlowNodeUserService flowNodeUserService;
@Autowired(required=false)
private FlowTaskNodeEvent flowTaskStartEvent;
@Autowired(required=false)
private FlowTaskCreatedEvent flowTaskCreatedEvent;
@Override
public void process(FlowWork flowWork, FlowModel flowModel, BaseModel currentNddeModel,FlowParam flowParam) {
// 下一个节点
// BaseModel nextNodeModel = flowModel.getNodeModel(flowWork.getNextNodeId());
TaskModel taskModel = (TaskModel)currentNddeModel;
// 1. 创建任务
createTask(flowWork, flowParam, taskModel);
// 2. 修改当前流程实例状态
if(null != flowTaskStartEvent) {
flowTaskStartEvent.OnEvent(flowWork, taskModel, flowParam);
}
}
/**
* 创建任务
* @param flowWork
* @param param
* @param taskModel
*/
private int createTask(FlowWork flowWork,FlowParam flowParam,TaskModel taskModel){
Date now = new Date();
String flowWorkId = flowWork.getId();
List<String> userIds = flowParam.getUserIds();
if(null==userIds) {
userIds = new ArrayList<>();
}
if(null!=flowNodeUserService) {
List<String> list = flowNodeUserService.loadUser(taskModel, flowParam);
if(null!=list) {
for(String id:list) {
if(!userIds.contains(id)) {
userIds.add(id);
}
}
}
}
if(userIds.isEmpty()) {
throw new ServiceException(FlowErrEnum.FLOW86000009);
}
for(String actorUserId:userIds) {
// 2. 创建一个任务
FlowTask flowTask = new FlowTask();
flowTask.setCreateTime(now);
flowTask.setFlowWorkId(flowWorkId);
flowTask.setIsDeleted(YesNoEnum.NO);
flowTask.setServiceId(flowParam.getServiceId());
flowTask.setServiceType(flowParam.getServiceType());
flowTask.setTaskName(taskModel.getName());
flowTask.setUpdateTime(now);
flowTask.setOperator(flowParam.getOperator());
flowTask.setStatus(FlowTask.StatusEnum.CREATED);
flowTask.setFlowNodeId(taskModel.getId());
flowTask.setActorUserId(actorUserId);
flowTask.setFlowParam(JsonUtil.toJson(flowParam));
if(StringUtil.isNotEmpty(flowParam.getTitle())) {
flowTask.setTaskName(flowParam.getTitle());
}
flowTaskMapper.insertSelective(flowTask);
if(null != flowTaskCreatedEvent) {
flowTaskCreatedEvent.onEvent(flowWork, flowTask, flowParam);
}
}
return userIds.size();
}
@Override
public String getNodeType() {
return "TASK";
}
}
- EndNodeProcessorImpl.java
package com.mole.modules.flow.processor.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.event.FlowEndNodeEvent;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.EndModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.param.FlowParam;
import com.mole.modules.flow.processor.FlowNodeProcessor;
@Component
public class EndNodeProcessorImpl implements FlowNodeProcessor{
@Autowired(required=false)
private FlowEndNodeEvent flowEndEvent;
@Override
public void process(FlowWork flowWork, FlowModel flowModel, BaseModel currentNddeModel,FlowParam flowParam) {
if(null == flowEndEvent) {
EndModel endModel = flowModel.getEndModel();
flowEndEvent.onEvent(flowWork, endModel, flowParam);;
}
}
@Override
public String getNodeType() {
return "END";
}
}
Класс управления процессором узла
Процессор вызывается единообразно классом управления процессором узла.
- FlowNodeProcesstorCtl.java
package com.mole.modules.flow.processor;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.mapper.FlowWorkMapper;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.EndModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.model.StartModel;
import com.mole.modules.flow.param.FlowParam;
/**
* 节点处理器控制器
* @author MLD
*
*/
@Component
public class FlowNodeProcesstorCtl {
@Autowired(required=false)
private List<FlowNodeProcessor> flowNodeProcessorList;
@Autowired
private FlowWorkMapper flowWorkMapper;
/**
* 节点流程处理器控制
* @param flowWork 当前流程实例
* @param flowModel 当前流程实例模型
* @param model 当前节点模型
* @param param 流程参数
*/
public void process(FlowWork flowWork,FlowModel flowModel, BaseModel model,FlowParam param){
FlowWork upFlowWork = new FlowWork();
if(model == null) {
return;
}
// 执行开始节点
for(FlowNodeProcessor processor:flowNodeProcessorList) {
if(processor.getNodeType().equals(model.getNodeType())){
BaseModel nextNodeModel = flowModel.getNodeModel(model.getNextNodeId());
if(model instanceof StartModel) { //开始节点
// 执行开始模型
processor.process(flowWork, flowModel, model,param);
// 执行下一个模型
process(flowWork,flowModel,nextNodeModel, param);
} else if(model instanceof EndModel) {
// 结束节点
upFlowWork.setStatus(FlowWork.StatusEnum.END);
Date now = new Date();
upFlowWork.setId(flowWork.getId());
upFlowWork.setCurrentNodeId(model.getId());
upFlowWork.setNextNodeId("");
upFlowWork.setLastOperator(param.getOperator());
upFlowWork.setUpdateTime(now);
flowWorkMapper.updateByPrimaryKeySelective(upFlowWork);
} else { // 非开始和结束节点
processor.process(flowWork, flowModel, model,param);
Date now = new Date();
upFlowWork.setId(flowWork.getId());
upFlowWork.setCurrentNodeId(model.getId());
upFlowWork.setNextNodeId(model.getNextNodeId());
upFlowWork.setLastOperator(param.getOperator());
upFlowWork.setUpdateTime(now);
flowWorkMapper.updateByPrimaryKeySelective(upFlowWork);
}
// 存在一个满足条件的,即可中断,其实设计上也仅会有一个相同类型的。
break;
}
}
}
}
Класс интерфейса событий узла
Интерфейс событий узла, реализованный бизнес-системой
- FlowStartNodeEvent
package com.mole.modules.flow.event;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.model.StartModel;
import com.mole.modules.flow.param.FlowParam;
/**
* 流程实例开始事件-由业务系统实现
* @author MLD
*
*/
public interface FlowStartNodeEvent {
/**
* @param flowWork 当前流程实例
* @param startModel 开始节点模型
* @param param 流程参数
*/
public void onEvent(FlowWork flowWork, StartModel startModel,FlowParam flowParam);
}
- FlowTaskNodeEvent.java
package com.mole.modules.flow.event;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.model.TaskModel;
import com.mole.modules.flow.param.FlowParam;
/**
* 任务开始事件-由业务系统实现
* @author MLD
*
*/
public interface FlowTaskNodeEvent {
/**
* 任务开始事件
* @param flowWork 当前流程实例
* @param taskModel 任务节点模型
* @param flowParam 流程参数
*/
public void OnEvent(FlowWork flowWork,TaskModel taskModel,FlowParam flowParam);
}
- FlowTaskCreatedEvent.java
package com.mole.modules.flow.event;
import com.mole.modules.flow.entity.FlowTask;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.param.FlowParam;
/**
* 创建任务事件-由业务系统实现
* @author MLD
*
*/
public interface FlowTaskCreatedEvent {
/**
*
* @param flowWork 当前流程实例
* @param flowTask 当前任务实例
* @param flowParam 流程参数
*/
public void onEvent(FlowWork flowWork,FlowTask flowTask,FlowParam flowParam);
}
- FlowEndNodeEvent.java
package com.mole.modules.flow.event;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.model.EndModel;
import com.mole.modules.flow.param.FlowParam;
/**
* 流程实例结束事件-由业务系统实现
* @author MLD
*
*/
public interface FlowEndNodeEvent {
/**
*
* @param flowWork 当前流程实例
* @param endModel 结束节点
* @param flowParam 流程参数
*/
public void onEvent(FlowWork flowWork,EndModel endModel, FlowParam flowParam);
}
Пользовательский класс интерфейса участника узла задачи
package com.mole.modules.flow.service;
import java.util.List;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.param.FlowParam;
/**
* 自定义节点参与人接口-由业务系统实现
* @author MLD
*
*/
public interface FlowNodeUserService {
/**
* 加载节点参与人
* @param nodeModel 节点模型
* @param param 流程参数
* @return
*/
public List<String> loadUser(BaseModel nodeModel,FlowParam param);
}
Класс управления процессом
Классы, предоставляемые процессам бизнес-операций
- FlowWorkCtl.java
package com.mole.modules.flow;
import java.io.IOException;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.entity.Condition;
import com.mole.framework.base.ServiceException;
import com.mole.framework.base.YesNoEnum;
import com.mole.framework.util.JsonUtil;
import com.mole.modules.flow.entity.FlowDefine;
import com.mole.modules.flow.entity.FlowTask;
import com.mole.modules.flow.entity.FlowWork;
import com.mole.modules.flow.enums.FlowErrEnum;
import com.mole.modules.flow.mapper.FlowDefineMapper;
import com.mole.modules.flow.mapper.FlowTaskMapper;
import com.mole.modules.flow.mapper.FlowWorkMapper;
import com.mole.modules.flow.model.BaseModel;
import com.mole.modules.flow.model.FlowModel;
import com.mole.modules.flow.model.StartModel;
import com.mole.modules.flow.param.FlowParam;
import com.mole.modules.flow.parser.FlowNodeParser;
import com.mole.modules.flow.processor.FlowNodeProcesstorCtl;
/**
* 流程控制类-引擎
* @author MLD
*
*/
@Component
public class FlowWorkCtl {
/**
* 通过流程定义编码启动流程
* @param flowDefineCode 流程定义编码
* @param param 流程启动入参
*/
@Autowired
private FlowDefineMapper flowDefineMapper;
@Autowired
private FlowNodeParser flowNodeParser;
@Autowired
private FlowWorkMapper flowWorkMapper;
@Autowired
private FlowNodeProcesstorCtl flowNodeProcesstorCtl;
@Autowired
private FlowTaskMapper flowTaskMapper;
/**
* 启动一个流程
* @param flowDefineCode
* @param operator 操作者用户id
* @param param
*/
public FlowWork startProcess(String flowDefineCode,String operator,FlowParam param){
// TODO 加载流程配置
FlowDefine q = new FlowDefine();
q.setFlowDefineCode(flowDefineCode);
q.setIsDeleted(YesNoEnum.NO);
if(null==param.getOperator()) {
param.setOperator(operator);
}
FlowDefine flowDefine = flowDefineMapper.selectOne(q);
if(null == flowDefine) {
// 流程定义不存在
throw new ServiceException(FlowErrEnum.FLOW86000001);
}
FlowModel flowModel = null;
try {
flowModel = flowNodeParser.parser(flowDefine.getFlowDefine());
} catch (IOException e) {
// 流程解析异常
throw new ServiceException(FlowErrEnum.FLOW86000002);
}
StartModel startModel = flowModel.getStartModel();
Date now = new Date();
// 创建一个新流程
FlowWork flowWork = new FlowWork();
flowWork.setCreateTime(now);
flowWork.setCurrentNodeId(startModel.getId());
flowWork.setFlowDefine(flowDefine.getFlowDefine());
flowWork.setFlowName(flowDefine.getFlowName());
flowWork.setIsDeleted(YesNoEnum.NO);
flowWork.setNextNodeId(startModel.getNextNodeId());
flowWork.setStatus(FlowWork.StatusEnum.START);
flowWork.setUpdateTime(now);
flowWork.setFlowParam(JsonUtil.toJson(param));
flowWork.setFlowDefineCode(flowDefineCode);
flowWork.setLastOperator(operator);
flowWorkMapper.insertSelective(flowWork);
// 执行开始模型
flowNodeProcesstorCtl.process(flowWork, flowModel, startModel, param);
return flowWork;
}
/**
* 完成一个任务
* @param flowTaskId 流程任务id
* @param operator 操作者用户id
* @param param 流程参数
*/
public void completeTask(String flowTaskId,String operator,FlowParam param) {
if(null==param.getOperator()) {
param.setOperator(operator);
}
FlowTask task = flowTaskMapper.selectByPrimaryKey(flowTaskId);
if(null == task || YesNoEnum.YES.equals(task.getIsDeleted())) {
// 流程任务不存在
throw new ServiceException(FlowErrEnum.FLOW86000007);
}
// 判断操作者是否在分派的用户里
if(!task.getActorUserId().equals(operator)) {
// 该用户无流程任务
throw new ServiceException(FlowErrEnum.FLOW86000008);
}
if(FlowTask.StatusEnum.FINISHED.equals(task.getStatus())) {
// 已完成,就不处理了
return;
}
FlowWork flowWork = flowWorkMapper.selectByPrimaryKey(task.getFlowWorkId());
if(null==flowWork) {
// 流程实例不存在
throw new ServiceException(FlowErrEnum.FLOW86000005);
}
FlowModel flowModel = null;
try {
flowModel = flowNodeParser.parser(flowWork.getFlowDefine());
} catch (IOException e) {
// 流程解析异常
throw new ServiceException(FlowErrEnum.FLOW86000002);
}
BaseModel currentNodeModel = flowModel.getNodeModel(task.getFlowNodeId());
BaseModel nextNodeModel = flowModel.getNodeModel(currentNodeModel.getNextNodeId());
Date now = new Date();
FlowTask upTask = new FlowTask();
upTask.setId(task.getId());
upTask.setUpdateTime(now);
upTask.setStatus(FlowTask.StatusEnum.FINISHED);
upTask.setFinishTime(now);
// 修改当前任务为已完成
flowTaskMapper.updateByPrimaryKeySelective(upTask);
int count = 0;
if("ALL".equals(currentNodeModel.getNodeType())) {
// 所有人都完成,才能走下一步流程
Condition qTaskCondition = new Condition(FlowTask.class);
qTaskCondition.createCriteria().andEqualTo("flowWorkId", task.getFlowWorkId())
.andEqualTo("flowNodeId", task.getFlowNodeId())
.andEqualTo("status", FlowTask.StatusEnum.CREATED)
.andEqualTo("isDeleted", YesNoEnum.NO)
.andNotEqualTo("actorUserId", operator);
count = flowTaskMapper.selectCountByCondition(qTaskCondition);
} else {
// ANY 任务一个节点完成都可以走下一步,别的任务要修改为已取消
Condition upTaskCondition = new Condition(FlowTask.class);
upTaskCondition.createCriteria().andEqualTo("flowWorkId", task.getFlowWorkId())
.andEqualTo("flowNodeId", task.getFlowNodeId())
.andEqualTo("status", FlowTask.StatusEnum.CREATED)
.andEqualTo("isDeleted", YesNoEnum.NO)
.andNotEqualTo("actorUserId", operator);
FlowTask upTaskToCancel = new FlowTask();
upTaskToCancel.setUpdateTime(now);
upTaskToCancel.setStatus(FlowTask.StatusEnum.CANCEL);
flowTaskMapper.updateByPrimaryKeySelective(upTaskToCancel);
}
if (count==0) {
// 3. 执行下一步流程
flowNodeProcesstorCtl.process(flowWork, flowModel, nextNodeModel, param);
}
}
/**
* 驳回一个任务
* @param flowTaskId 流程任务id
* @param operator 操作者用户id
* @param flowParam 流程参数
*/
public void rejectTask(String flowTaskId,String operator,FlowParam flowParam) {
if(null==flowParam.getOperator()) {
flowParam.setOperator(operator);
}
FlowTask task = flowTaskMapper.selectByPrimaryKey(flowTaskId);
if(null == task || YesNoEnum.YES.equals(task.getIsDeleted())) {
// 流程任务不存在
throw new ServiceException(FlowErrEnum.FLOW86000007);
}
// 判断人是否在里面
if(!task.getActorUserId().equals(operator)) {
// 该用户无流程任务
throw new ServiceException(FlowErrEnum.FLOW86000008);
}
if(FlowTask.StatusEnum.FINISHED.equals(task.getStatus())) {
// 已完成,就不处理了
return;
}
FlowWork flowWork = flowWorkMapper.selectByPrimaryKey(task.getFlowWorkId());
if(null==flowWork) {
// 流程实例不存在
throw new ServiceException(FlowErrEnum.FLOW86000005);
}
FlowModel flowModel = null;
try {
flowModel = flowNodeParser.parser(flowWork.getFlowDefine());
} catch (IOException e) {
// 流程解析异常
throw new ServiceException(FlowErrEnum.FLOW86000002);
}
Date now = new Date();
FlowTask upTask = new FlowTask();
upTask.setId(task.getId());
upTask.setUpdateTime(now);
upTask.setStatus(FlowTask.StatusEnum.REJECT);
upTask.setFinishTime(now);
// 修改当前任务为驳回
flowTaskMapper.updateByPrimaryKeySelective(upTask);
// 中断当前节点任务
Condition upTaskCondition = new Condition(FlowTask.class);
upTaskCondition.createCriteria().andEqualTo("flowWorkId", task.getFlowWorkId())
.andEqualTo("flowNodeId", task.getFlowNodeId())
.andEqualTo("status", FlowTask.StatusEnum.CREATED)
.andEqualTo("isDeleted", YesNoEnum.NO)
.andNotEqualTo("actorUserId", operator);
FlowTask upTaskToCancel = new FlowTask();
upTaskToCancel.setUpdateTime(now);
upTaskToCancel.setStatus(FlowTask.StatusEnum.CANCEL);
flowTaskMapper.updateByPrimaryKeySelective(upTaskToCancel);
// 获取上一个节点
BaseModel preNodeModel = flowModel.getPreNodeModel(task.getFlowNodeId());
// 执行上一个节点处理器
flowNodeProcesstorCtl.process(flowWork, flowModel, preNodeModel, flowParam);
}
}
пример вызова
- запустить экземпляр процесса
String flowDefineCode = "yzq_invite_bid";
FlowParam flowParam = new FlowParam();
flowParam.setServiceId(UUID.randomUUID().toString());
flowParam.setServiceType("yzq_invite_bid");
flowParam.setUserIds(new ArrayList<>());
flowParam.getUserIds().add("1");
Sting operator = "1";
flowWorkCtl.startProcess(flowDefineCode,operator, flowParam);
- выполнить задание
String flowTaskId = "flowTaskId";
FlowParam flowParam = new FlowParam();
flowParam.setServiceId(UUID.randomUUID().toString());
flowParam.setServiceType("yzq_invite_bid");
flowParam.setUserIds(new ArrayList<>());
flowParam.getUserIds().add("1");
Sting operator = "1";
flowWorkCtl.completeTask(flowTaskId, operator, flowParam);
- отклонить задачу
String flowTaskId = "flowTaskId";
FlowParam flowParam = new FlowParam();
flowParam.setServiceId(UUID.randomUUID().toString());
flowParam.setServiceType("yzq_invite_bid");
flowParam.setUserIds(new ArrayList<>());
flowParam.getUserIds().add("1");
Sting operator = "1";
flowWorkCtl.rejectTask(flowTaskId, operator, flowParam);
Фреймворк, используемый в этом проекте
- springboot 2.0
- tk.mybatis
разное
Изначально хотел с открытым исходным кодом, но проект и фреймворк компании слишком связаны, и извлечь его непросто.Когда у меня будет время, я рассмотрю возможность извлечения этой части кода отдельно от исходников. Другое дело, что в настоящее время рассматривается только простейший последовательный процесс, а сложные процессы, включающие условия, ответвления, слияния, подпроцессы и т. д., пока не рассматривались. Но первоначальное намерение состояло в том, чтобы просто делать это шаг за шагом и медленно анализировать принцип.