first commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package com.budwk.app.flow.config;
|
||||
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.engine.cfg.Configuration;
|
||||
import com.budwk.app.flow.engine.core.FlowEngineImpl;
|
||||
import com.budwk.app.flow.engine.impl.NutzContext;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@IocBean
|
||||
public class FlowConfiguration {
|
||||
|
||||
@IocBean
|
||||
public FlowEngine flowEngine(NutzContext nutzContext) {
|
||||
FlowEngine engine = new FlowEngineImpl().configure(new Configuration(nutzContext));
|
||||
return engine;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.budwk.app.flow.constant;
|
||||
|
||||
/**
|
||||
* 流程常量
|
||||
*/
|
||||
public interface FlowConst {
|
||||
// 业务流程号
|
||||
String BUSINESS_NO = "BUSINESS_NO";
|
||||
// 超级管理员ID
|
||||
String ADMIN_ID = "17a7f8ad3ee947b4a26175049a9c253d";
|
||||
// 自动执行ID
|
||||
String AUTO_ID = "flow.auto";
|
||||
String PROCESS_NAME_KEY = "name";
|
||||
String PROCESS_DISPLAY_NAME_KEY = "displayName";
|
||||
String PROCESS_TYPE = "type";
|
||||
// 流程定义id,key
|
||||
String PROCESS_DEFINE_ID_KEY = "processDefineId";
|
||||
// 流程设计id,key
|
||||
String PROCESS_DESIGN_ID_KEY = "processDesignId";
|
||||
// 流程任务id
|
||||
String PROCESS_TASK_ID_KEY = "processTaskId";
|
||||
// 流程实例id
|
||||
String PROCESS_INSTANCE_ID_KEY = "processInstanceId";
|
||||
// 表单数据前辍
|
||||
String FORM_DATA_PREFIX = "f_";
|
||||
// 表单数据
|
||||
String FORM_DATA = FORM_DATA_PREFIX + "data";
|
||||
// 任务表单数据前辍
|
||||
String TASK_FORM_DATA_PREFIX = "tf_";
|
||||
// 审批意见
|
||||
String APPROVAL_COMMENT = "tf_approvalComment";
|
||||
// 审批提交附件
|
||||
String APPROVAL_ATTACHMENT = "tf_approvalAttachment";
|
||||
// 下一节点执行人
|
||||
String NEXT_NODE_OPERATOR = "tf_nextNodeOperator";
|
||||
// 抄送人
|
||||
String CC_ACTORS = "tf_ccActors";
|
||||
// 用户ID
|
||||
String USER_USER_ID = "u_userId";
|
||||
// 用户姓名
|
||||
String USER_REAL_NAME = "u_realName";
|
||||
// 用户所属部门ID
|
||||
String USER_DEPT_ID = "u_deptId";
|
||||
// 用户所属部门名称
|
||||
String USER_DEPT_NAME = "u_deptName";
|
||||
// 用户所属岗位id
|
||||
String USER_POST_ID = "u_postId";
|
||||
// 用户所属岗位名称
|
||||
String USER_POST_NAME = "u_postName";
|
||||
// 提交类型
|
||||
String SUBMIT_TYPE = "submitType";
|
||||
// 自动生成的标题
|
||||
String AUTO_GEN_TITLE = "autoGenTitle";
|
||||
// 节点名称
|
||||
String TASK_NAME = "taskName";
|
||||
// 是否第一个任务节点
|
||||
String IS_FIRST_TASK_NODE = "isFirstTaskNode";
|
||||
// 会签变量前辍
|
||||
String COUNTERSIGN_VARIABLE_PREFIX = "csv_";
|
||||
// 活跃的会签操作人数
|
||||
String NR_OF_ACTIVATE_INSTANCES = "nrOfActivateInstances";
|
||||
// 循环计数器,办理人在列表中的索引
|
||||
String LOOP_COUNTER = "loopCounter";
|
||||
// 会签总实例数
|
||||
String NR_OF_INSTANCES = "nrOfInstances";
|
||||
// 会签已完成实例数
|
||||
String NR_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
|
||||
|
||||
// 会签已完成并且已通过实例数
|
||||
String NR_OF_COMPLETED_AGREE_INSTANCES = "nrOfCompletedAgreeInstances";
|
||||
|
||||
// 会签操作人列表
|
||||
String COUNTERSIGN_OPERATOR_LIST = "operatorList";
|
||||
// 会签类型 PARALLEL表示并行会签,SEQUENTIAL表示串行会签
|
||||
String COUNTERSIGN_TYPE = "countersignType";
|
||||
// 会签不同意标识
|
||||
String COUNTERSIGN_DISAGREE_FLAG = "countersignDisagreeFlag";
|
||||
String ACTOR_IDS_KEY = "actorIds";
|
||||
// 自定义节点默认返回值变量
|
||||
String CUSTOM_RETURN_VAL = "custom_return_val";
|
||||
|
||||
|
||||
//============================================流程发起人==================================================
|
||||
|
||||
// 流程发起人
|
||||
String INITIATOR = "initiator";
|
||||
|
||||
// 流程发起人姓名
|
||||
String INITIATOR_NAME = "initiatorName";
|
||||
|
||||
// 流程发起人工号
|
||||
String INITIATOR_ACCOUNT = "initiatorAccount";
|
||||
|
||||
// 流程发起人部门ID
|
||||
String INITIATOR_UNIT_ID = "initiatorUnitId";
|
||||
|
||||
// 流程发起人部门名称
|
||||
String INITIATOR_UNIT_NAME = "initiatorUnitName";
|
||||
|
||||
// 流程发起人分工会ID
|
||||
String INITIATOR_UNIT_UNION_ID = "initiatorUnionId";
|
||||
|
||||
// 流程发起人分工会名称
|
||||
String INITIATOR_UNIT_UNION_NAME = "initiatorUnionName";
|
||||
|
||||
|
||||
//============================================流程额外==================================================
|
||||
|
||||
// 流程实例名称
|
||||
String PROCESS_INSTANCE_NAME = "instanceName";
|
||||
|
||||
//============================================扩展==================================================
|
||||
// 开启之后不会校验任务执行所属权
|
||||
String EXT_ENABLE_OPERATOR = "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.flow.entity.ProcessCategory;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
import com.budwk.app.flow.entity.ProcessDesign;
|
||||
import com.budwk.app.flow.service.ProcessDefineService;
|
||||
import com.budwk.app.zhgh.club.model.SysClubRule;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/category")
|
||||
@Ok("json:full")
|
||||
@Api("流程分类")
|
||||
public class FlowCategoryController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ProcessDefineService defineService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/flow/category/index.html")
|
||||
@SaCheckLogin
|
||||
public void index() {}
|
||||
|
||||
@At
|
||||
@ApiOperation("分页查询")
|
||||
@SaCheckPermission("flow.category")
|
||||
public Result pageData(PageForm pageForm, String name) {
|
||||
Sql sql = Sqls.create("""
|
||||
select
|
||||
*,
|
||||
(select count(1) from wf_process_design where category = pc.id) as categoryCount
|
||||
from
|
||||
wf_process_category pc
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and(Cnd.likeEX(ProcessCategory::getName, name));
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = defineService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("flow.category")
|
||||
@SLog(tag = "流程管理-流程分类", msg = "新增/编辑流程分类")
|
||||
public Result submit(ProcessCategory category) {
|
||||
dao.insertOrUpdate(category);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("flow.category")
|
||||
@SLog(tag = "流程管理-流程分类", msg = "删除流程分类")
|
||||
public Result delete(String id) {
|
||||
int count = dao.count(ProcessDesign.class, Cnd.where(ProcessDesign::getCategory, "=", id));
|
||||
if(count > 0) {
|
||||
return Result.error("此分类已关联%s个流程,无法删除".formatted(count));
|
||||
}
|
||||
dao.delete(ProcessCategory.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result list() {
|
||||
List<ProcessCategory> list = dao.query(ProcessCategory.class, Cnd.NEW());
|
||||
return Result.success(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.vo.LabelValueVO;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessPublisher;
|
||||
import com.budwk.app.flow.engine.model.ProcessModel;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.entity.Candidate;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
import com.budwk.app.flow.service.FlowCommonService;
|
||||
import com.budwk.app.flow.vo.HighLightVO;
|
||||
import com.budwk.app.flow.vo.ProcessInstanceVO;
|
||||
import com.budwk.app.flow.vo.ProcessTaskVO;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/common")
|
||||
@Api("流程通用")
|
||||
@Ok("json:full")
|
||||
public class FlowCommonController {
|
||||
|
||||
@Inject
|
||||
private FlowEngine flowEngine;
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private FlowCommonService flowCommonService;
|
||||
|
||||
@At("/approval/form")
|
||||
@SaCheckLogin
|
||||
@Ok("beetl:/platform/flow/common/approval/form.html")
|
||||
public void approval(Long taskId, Long instanceId, String businessId) {
|
||||
|
||||
}
|
||||
|
||||
@At("/instanceInfo")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("流程信息")
|
||||
public Result info(Long instanceId) {
|
||||
// 流程实例信息
|
||||
ProcessInstanceVO instanceVO = flowEngine.processInstanceService().findById(instanceId);
|
||||
// 当前办理环节 可能会有并行网关
|
||||
List<ProcessTaskVO> todoTaskVos = new ArrayList<>();
|
||||
List<ProcessTask> todoTasks = flowEngine.processTaskService().getDoingTaskList(instanceId, null);
|
||||
for (ProcessTask todoTask : todoTasks) {
|
||||
ProcessTaskVO taskVO = flowEngine.processTaskService().findById(todoTask.getId());
|
||||
todoTaskVos.add(taskVO);
|
||||
}
|
||||
|
||||
HashMap<String, Object> result = new HashMap<>();
|
||||
result.put("processInstance", instanceVO);
|
||||
result.put("todoTasks", todoTaskVos);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("已办任务")
|
||||
public Result doneTasks(Long instanceId, String bizId) {
|
||||
if (instanceId == null && StrUtil.isBlank(bizId)) {
|
||||
return Result.error("参数错误");
|
||||
}
|
||||
if (instanceId == null) {
|
||||
ProcessInstance instance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", bizId));
|
||||
if (instance == null) {
|
||||
return Result.success(Collections.emptyList());
|
||||
}
|
||||
instanceId = instance.getId();
|
||||
}
|
||||
List<ProcessTaskVO> doneTaskVos = new ArrayList<>();
|
||||
List<ProcessTask> doneTaskList = flowEngine.processTaskService().getDoneTaskList(instanceId, null);
|
||||
for (ProcessTask doneTask : doneTaskList) {
|
||||
ProcessTaskVO taskVO = flowEngine.processTaskService().findById(doneTask.getId());
|
||||
doneTaskVos.add(taskVO);
|
||||
}
|
||||
return Result.success().addData(doneTaskVos);
|
||||
}
|
||||
|
||||
|
||||
@At("/taskInfo")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("任务详情")
|
||||
public Result taskInfo(Long taskId) {
|
||||
ProcessTaskVO currentTask = flowEngine.processTaskService().findById(taskId);
|
||||
return Result.success(currentTask);
|
||||
}
|
||||
|
||||
@At("/defineInfo")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("流程定义详情")
|
||||
public Result defineInfo(String defineKey, Long instanceId) {
|
||||
ProcessDefine define = null;
|
||||
if (StrUtil.isNotBlank(defineKey)) {
|
||||
define = flowEngine.processDefineService().getLastByName(defineKey);
|
||||
}
|
||||
if (instanceId != null) {
|
||||
ProcessInstance instance = dao.fetch(ProcessInstance.class, instanceId);
|
||||
define = flowEngine.processDefineService().getById(instanceId);
|
||||
}
|
||||
|
||||
if (define == null) {
|
||||
return Result.error("没有流程定义");
|
||||
}
|
||||
|
||||
return Result.success(define);
|
||||
}
|
||||
|
||||
@At("/instanceUrl")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取流程定义的第一个任务节点")
|
||||
public Result firstTaskNode(Long processDefineId) {
|
||||
ProcessModel processModel = flowEngine.processDefineService().getProcessModel(processDefineId);
|
||||
return Result.success().addData(processModel.getInstanceUrl());
|
||||
}
|
||||
|
||||
|
||||
@At("/approvalRecord")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取流程定义的审批记录")
|
||||
public Result approvalRecord(Long instanceId) {
|
||||
List<ProcessTask> processTaskList = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", instanceId)
|
||||
.and(ProcessTask::getTaskState, "not in", List.of(ProcessTaskStateEnum.DOING, ProcessTaskStateEnum.WITHDRAW, ProcessTaskStateEnum.ABANDON))
|
||||
.asc(ProcessTask::getUpdatedAt));
|
||||
List<ProcessTaskVO> res = new ArrayList<>();
|
||||
processTaskList.forEach(processTask -> {
|
||||
ProcessTaskVO vo = BeanUtil.toBean(processTask, ProcessTaskVO.class);
|
||||
if (vo.getTaskState().equals(ProcessTaskStateEnum.FINISHED.getCode()) && StrUtil.isNotBlank(vo.getOperator())) {
|
||||
|
||||
}
|
||||
|
||||
res.add(vo);
|
||||
});
|
||||
return Result.success(res);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("流程图高亮")
|
||||
public Result instanceHighLight(@Param("instanceId") Long instanceId) {
|
||||
HighLightVO highLightVO = flowEngine.processInstanceService().highLight(instanceId);
|
||||
return Result.success(highLightVO);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("启动流程实例并自动完成")
|
||||
public Result startInstanceAndExecute(@Param("defineKey") String defineKey, @Param("bizData") String bizData, @Param("submitType") Integer submitType) {
|
||||
Dict args = Dict.create();
|
||||
args.set(FlowConst.SUBMIT_TYPE, submitType);
|
||||
args.set(FlowConst.FORM_DATA, bizData);
|
||||
ProcessInstance instance = flowEngine.startProcessInstanceByKey(defineKey, null, SecurityUtil.getUserId(), args);
|
||||
|
||||
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
|
||||
for (ProcessTask task : doingTaskList) {
|
||||
flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args);
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("启动流程实例并自动完成")
|
||||
public Result startInstance(@Param("defineKey") String defineKey, @Param("bizData") String bizData, @Param("submitType") Integer submitType) {
|
||||
Dict args = Dict.create();
|
||||
args.set(FlowConst.SUBMIT_TYPE, submitType);
|
||||
args.set(FlowConst.FORM_DATA, bizData);
|
||||
flowEngine.startProcessInstanceByKey(defineKey, null, SecurityUtil.getUserId(), args);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("重新发起流程实例")
|
||||
public Result reissueInstance(@Param("instanceId") Long instanceId, @Param("bizData") String bizData) {
|
||||
// // 把已撤销流程修改为进行中
|
||||
// dao.update(ProcessInstance.class, Chain.make("state", ProcessInstanceStateEnum.DOING.getCode()), Cnd.where(ProcessInstance::getId, "=", instanceId));
|
||||
// Dict args = Dict.create();
|
||||
// args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.RE_APPLY.getCode());
|
||||
// args.set(FlowConst.FORM_DATA, bizData);
|
||||
//
|
||||
// // 查找第一个提交任务节点
|
||||
// ProcessInstance instance = dao.fetch(ProcessInstance.class, instanceId);
|
||||
// ProcessModel processModel = flowEngine.processDefineService().getProcessModel(instance.getProcessDefineId());
|
||||
//
|
||||
// args.set(FlowConst.PROCESS_INSTANCE_ID_KEY, instanceId);
|
||||
// args.set(FlowConst.INITIATOR,instance.getCreatedBy());
|
||||
//
|
||||
// // 构建执行参数对象
|
||||
// Execution execution = new Execution();
|
||||
// execution.setProcessModel(processModel);
|
||||
// execution.setProcessInstance(instance);
|
||||
// execution.setProcessInstanceId(instance.getId());
|
||||
// execution.setEngine(flowEngine);
|
||||
// execution.setArgs(args);
|
||||
// execution.setOperator(SecurityUtil.getUserId());
|
||||
// // 拿到开始节点模型,调用其execute方法
|
||||
// processModel.getStart().execute(execution);
|
||||
//
|
||||
// return Result.success();
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
@At("/executeTask")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("执行任务")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result executeTask(@Param("data") String param) {
|
||||
Dict args = Json.fromJson(Dict.class, param);
|
||||
flowCommonService.executeTask(args);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("撤销任务")
|
||||
public Result revokeTask(@Param("taskId") Long taskId) {
|
||||
// 自己任务
|
||||
ProcessTask selfTask = dao.fetch(ProcessTask.class, taskId);
|
||||
selfTask.setTaskState(ProcessTaskStateEnum.DOING.getCode());
|
||||
|
||||
// 撤销任务
|
||||
List<ProcessTask> taskList = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskParentId, "=", taskId));
|
||||
for (ProcessTask task : taskList) {
|
||||
task.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
|
||||
dao.update(task);
|
||||
}
|
||||
// 会签并行任务 撤销后续任务
|
||||
if (selfTask.getPerformType().equals(ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode())) {
|
||||
List<ProcessTask> doingTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode())
|
||||
.and(ProcessTask::getProcessInstanceId, "=", selfTask.getProcessInstanceId()));
|
||||
for (ProcessTask doingTask : doingTasks) {
|
||||
doingTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
|
||||
dao.update(doingTask);
|
||||
}
|
||||
}
|
||||
|
||||
// 激活
|
||||
dao.update(selfTask);
|
||||
|
||||
// 流程激活
|
||||
dao.update(ProcessInstance.class, Chain.make("state", ProcessInstanceStateEnum.DOING.getCode()),Cnd.where(ProcessInstance::getId, "=", selfTask.getProcessInstanceId()));
|
||||
|
||||
// 发送任务撤回事件 确保上面执行成功
|
||||
for (ProcessTask task : taskList) {
|
||||
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build());
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At("/candidate")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取候选用户")
|
||||
public Result candidate(@Param("taskId") Long taskId) {
|
||||
ProcessTask task = flowEngine.processTaskService().fetch(taskId);
|
||||
ProcessInstance instance = flowEngine.processInstanceService().fetch(task.getProcessInstanceId());
|
||||
ProcessModel processModel = flowEngine.processDefineService().getProcessModel(instance.getProcessDefineId());
|
||||
// 是否存在候选处理
|
||||
List<TaskModel> nextTaskModels = processModel.getNextTaskModels(task.getTaskName());
|
||||
boolean b = nextTaskModels.stream().anyMatch(item -> item.getCandidateHandler() != null);
|
||||
List<Candidate> candidates = processModel.getNextTaskModelCandidates(task.getTaskName());
|
||||
return Result.success(Map.of("candidates", candidates, "support", b));
|
||||
}
|
||||
|
||||
@At("/jumpTaskNames")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取可跳转的任务节点")
|
||||
public Result jumpAbleTaskNames(@Param("instanceId") Long instanceId) {
|
||||
List<LabelValueVO> vos = flowEngine.processTaskService().jumpAbleTaskNameList(instanceId);
|
||||
return Result.success(vos);
|
||||
}
|
||||
|
||||
@At("/surrogate")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("委托")
|
||||
public Result surrogate(@Param("taskId") Long taskId, @Param("actorIds") String[] actorIds) {
|
||||
flowEngine.processTaskService().addTaskActor(taskId, Arrays.asList(actorIds));
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At("/addCandidate")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("加签")
|
||||
public Result addCandidate(@Param("taskId") Long taskId, @Param("actorIds") String[] actorIds) {
|
||||
flowEngine.processTaskService().addCandidateActor(taskId, Arrays.asList(actorIds));
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
import com.budwk.app.flow.entity.ProcessDesign;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.service.ProcessDefineService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/define")
|
||||
@Ok("json:full")
|
||||
@Api("流程定义")
|
||||
public class FlowDefineController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ProcessDefineService processDefineService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/flow/define/index.html")
|
||||
@SaCheckPermission("flow.define")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/preview")
|
||||
@Ok("beetl:/platform/flow/define/preview.html")
|
||||
@SaCheckLogin
|
||||
public void preview(HttpServletRequest request) {
|
||||
request.setAttribute("defineId", request.getParameter("defineId"));
|
||||
request.setAttribute("instanceId", request.getParameter("instanceId"));
|
||||
|
||||
String defineKey = request.getParameter("defineKey");
|
||||
if (StrUtil.isNotBlank(defineKey)) {
|
||||
ProcessDefine define = dao.fetch(ProcessDefine.class, Cnd.where(ProcessDefine::getName, "=", defineKey).desc(ProcessDefine::getVersion));
|
||||
if (define != null) {
|
||||
request.setAttribute("defineId", define.getId());
|
||||
request.setAttribute("showImg", true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("删除流程定义")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("flow.define")
|
||||
public Result delete(@Param("id") Long id) {
|
||||
int count = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getProcessDefineId, "=", id));
|
||||
if (count > 0) {
|
||||
throw new BaseException("该流程定义正在使用中,请勿删除!");
|
||||
}
|
||||
dao.delete(ProcessDefine.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("流程定义详情")
|
||||
public Result detail(@Param("id") Long id) {
|
||||
ProcessDefine define = dao.fetch(ProcessDefine.class, id);
|
||||
return Result.success(define);
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("获取流程定义分页列表")
|
||||
@SaCheckPermission("flow.define")
|
||||
public Result pageData(PageForm pageForm, String displayName, String name, String category) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
*,
|
||||
ROW_NUMBER() OVER (ORDER BY name ) AS rowNum
|
||||
FROM
|
||||
( SELECT *, ROW_NUMBER() OVER ( PARTITION BY NAME ORDER BY version DESC ) AS rn FROM wf_process_define ) t
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
|
||||
cnd.and("rn", "=", 1);
|
||||
cnd.and(Cnd.likeEX(ProcessDesign::getDisplayName, displayName));
|
||||
cnd.and(Cnd.likeEX(ProcessDesign::getName, name));
|
||||
cnd.andEX(ProcessDesign::getCategory, "=", category);
|
||||
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = processDefineService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
|
||||
List<NutMap> list = pagination.getList(NutMap.class);
|
||||
for (NutMap nutMap : list) {
|
||||
List<ProcessDefine> defineList = dao.query(
|
||||
ProcessDefine.class,
|
||||
Cnd.where(ProcessDefine::getName, "=", nutMap.getString("name"))
|
||||
.and("id", "!=", nutMap.getString("id"))
|
||||
.desc(ProcessDefine::getVersion)
|
||||
);
|
||||
nutMap.put("children", defineList);
|
||||
}
|
||||
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("启用流程定义")
|
||||
@SaCheckPermission("flow.define")
|
||||
public Result enable(@Param("id") Long id) {
|
||||
ProcessDefine define = new ProcessDefine();
|
||||
define.setId(id);
|
||||
define.setState(1);
|
||||
dao.updateIgnoreNull(define);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("禁用流程定义")
|
||||
@SaCheckPermission("flow.define")
|
||||
public Result disable(@Param("id") Long id) {
|
||||
ProcessDefine define = new ProcessDefine();
|
||||
define.setId(id);
|
||||
define.setState(0);
|
||||
dao.updateIgnoreNull(define);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.lang.ClassScanner;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.CandidateHandler;
|
||||
import com.budwk.app.flow.entity.ProcessDesign;
|
||||
import com.budwk.app.flow.service.ProcessDesignService;
|
||||
import com.budwk.app.sys.services.SysUserService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/design")
|
||||
@Ok("json:full")
|
||||
@Api("流程设计")
|
||||
@Slf4j
|
||||
public class FlowDesignController {
|
||||
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
@Inject
|
||||
private ProcessDesignService processDesignService;
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
// 获取所有任务参与者处理类
|
||||
public static final List<JSONObject> ASSIGMENT_HANDLER_LIST;
|
||||
// 获取所有候选用户处理类
|
||||
public static final List<JSONObject> CANDIDATE_HANDLER_LIST;
|
||||
|
||||
static {
|
||||
Set<Class<?>> classes = ClassScanner.scanPackageBySuper("com.budwk.app", AssignmentHandler.class);
|
||||
ArrayList<JSONObject> list = new ArrayList<>(classes.size());
|
||||
for (Class<?> aClass : classes) {
|
||||
try {
|
||||
AssignmentHandler handler = (AssignmentHandler) ReflectUtil.newInstance(aClass);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.set("value", handler.getClass().getName());
|
||||
jsonObject.set("order", handler.getOrder());
|
||||
jsonObject.set("name", handler.getMessage());
|
||||
list.add(jsonObject);
|
||||
} catch (Exception e) {
|
||||
log.error("初始化AssignmentHandler失败: {}", aClass.getName());
|
||||
}
|
||||
}
|
||||
// 排序 按order
|
||||
list.sort(Comparator.comparingInt(o -> o.getInt("order")));
|
||||
ASSIGMENT_HANDLER_LIST = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
static {
|
||||
Set<Class<?>> classes = ClassScanner.scanPackageBySuper("com.budwk.app", CandidateHandler.class);
|
||||
ArrayList<JSONObject> list = new ArrayList<>(classes.size());
|
||||
for (Class<?> aClass : classes) {
|
||||
try {
|
||||
CandidateHandler handler = (CandidateHandler) ReflectUtil.newInstance(aClass);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.set("value", handler.getClass().getName());
|
||||
jsonObject.set("order", handler.getOrder());
|
||||
jsonObject.set("name", handler.getMessage());
|
||||
list.add(jsonObject);
|
||||
} catch (Exception e) {
|
||||
log.error("初始化CandidateHandler失败: {}", aClass.getName());
|
||||
}
|
||||
}
|
||||
// 排序 按order
|
||||
list.sort(Comparator.comparingInt(o -> o.getInt("order")));
|
||||
CANDIDATE_HANDLER_LIST = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/flow/design/index.html")
|
||||
@SaCheckPermission("flow.design")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("beetl:/platform/flow/design/designer.html")
|
||||
@SaCheckLogin
|
||||
public void designer(HttpServletRequest request) {
|
||||
request.setAttribute("id", request.getParameter("id"));
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("获取流程设计分页列表")
|
||||
@SaCheckPermission("flow.design")
|
||||
public Result pageData(PageForm pageForm, String displayName, String name, String category, Boolean deployed) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
|
||||
cnd.and(Cnd.likeEX(ProcessDesign::getDisplayName, displayName));
|
||||
cnd.and(Cnd.likeEX(ProcessDesign::getName, name));
|
||||
cnd.andEX(ProcessDesign::getCategory, "=", category);
|
||||
cnd.andEX(ProcessDesign::getIsDeployed, "=", deployed);
|
||||
|
||||
Pagination pagination = processDesignService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("保存流程设计")
|
||||
@SaCheckPermission("flow.design")
|
||||
public Result insert(@Param("design") ProcessDesign processDesign) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.set("name", processDesign.getName());
|
||||
jsonObject.set("displayName", processDesign.getDisplayName());
|
||||
jsonObject.set("category", processDesign.getCategory());
|
||||
jsonObject.set("instanceUrl", processDesign.getInstanceUrl());
|
||||
jsonObject.set("h5InstanceUrl", processDesign.getH5InstanceUrl());
|
||||
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
|
||||
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
|
||||
jsonObject.set("icon", processDesign.getIcon());
|
||||
jsonObject.set("picIcon", processDesign.getPicIcon());
|
||||
jsonObject.set("description", processDesign.getDescription());
|
||||
processDesign.setContent(jsonObject);
|
||||
dao.insert(processDesign);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("修改流程设计")
|
||||
@SaCheckPermission("flow.design")
|
||||
public Result update(@Param("design") ProcessDesign processDesign) {
|
||||
JSONObject jsonObject = processDesign.getContent();
|
||||
jsonObject.set("name", processDesign.getName());
|
||||
jsonObject.set("displayName", processDesign.getDisplayName());
|
||||
jsonObject.set("category", processDesign.getCategory());
|
||||
jsonObject.set("instanceUrl", processDesign.getInstanceUrl());
|
||||
jsonObject.set("h5InstanceUrl", processDesign.getH5InstanceUrl());
|
||||
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
|
||||
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
|
||||
jsonObject.set("icon", processDesign.getIcon());
|
||||
jsonObject.set("picIcon", processDesign.getPicIcon());
|
||||
jsonObject.set("description", processDesign.getDescription());
|
||||
processDesign.setContent(jsonObject);
|
||||
processDesignService.update(processDesign);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("修改流程设计")
|
||||
@SaCheckPermission("flow.design")
|
||||
public Result updateContent(@Param("design") ProcessDesign processDesign) {
|
||||
JSONObject jsonObject = processDesign.getContent();
|
||||
jsonObject.set("name", processDesign.getName());
|
||||
jsonObject.set("displayName", processDesign.getDisplayName());
|
||||
jsonObject.set("category", processDesign.getCategory());
|
||||
jsonObject.set("instanceUrl", processDesign.getInstanceUrl());
|
||||
jsonObject.set("h5InstanceUrl", processDesign.getH5InstanceUrl());
|
||||
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
|
||||
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
|
||||
jsonObject.set("icon", processDesign.getIcon());
|
||||
jsonObject.set("picIcon", processDesign.getPicIcon());
|
||||
jsonObject.set("description", processDesign.getDescription());
|
||||
processDesign.setContent(jsonObject);
|
||||
processDesignService.update(processDesign);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("删除流程设计")
|
||||
@SaCheckPermission("flow.design")
|
||||
public Result delete(@Param("id") Long id) {
|
||||
dao.delete(ProcessDesign.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("流程设计详情")
|
||||
public Result detail(@Param("id") Long id) {
|
||||
ProcessDesign design = dao.fetch(ProcessDesign.class, id);
|
||||
return Result.success(design);
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("发布流程设计")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("flow.design")
|
||||
public Result deploy(@Param("id") Long id) {
|
||||
processDesignService.deploy(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取流程设计任务参与者处理类")
|
||||
public Result assigmentHandlerClass() {
|
||||
return Result.success(ASSIGMENT_HANDLER_LIST);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取流程设计任务参与者处理类")
|
||||
public Result candidateHandlerClass() {
|
||||
return Result.success(CANDIDATE_HANDLER_LIST);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取流程设计任务参与者分页数据")
|
||||
public Result assigneePage(Integer pageNumber, Integer pageSize, String searchKeyword, @Param("userIds") String[] userIds) {
|
||||
Sql sql = Sqls.create("select id,loginname,username,unitName from vw_user $condition");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if(StrUtil.isNotBlank(searchKeyword)) {
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.orLike("username", searchKeyword);
|
||||
group.orLike("loginname", searchKeyword);
|
||||
cnd.and(group);
|
||||
}
|
||||
sql.setCondition(cnd);
|
||||
// cnd.andEX("id", "in", userIds);
|
||||
Pagination pagination = sysUserService.listPageMap(pageNumber, pageSize, sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取流程设计任务参与者分页数据回显")
|
||||
public Result assigneeEcho(@Param("userIds") String[] userIds) {
|
||||
Sql sql = Sqls.create("select id,loginname,username,unitName from vw_user where id in (@userIds)");
|
||||
sql.setParam("userIds", userIds);
|
||||
List<NutMap> list = sysUserService.listMap(sql);
|
||||
return Result.success(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.entity.ProcessDesign;
|
||||
import com.budwk.app.flow.entity.ProcessDesignHis;
|
||||
import com.budwk.app.flow.service.ProcessDesignService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/designer")
|
||||
@Ok("json:full")
|
||||
@Api("流程设计")
|
||||
public class FlowDesignerController {
|
||||
|
||||
@Inject
|
||||
private ProcessDesignService processDesignService;
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("保存流程设计")
|
||||
public Result insert(ProcessDesign processDesign){
|
||||
dao.insert(processDesign);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("修改流程设计")
|
||||
public Result update(ProcessDesign processDesign){
|
||||
processDesignService.update(processDesign);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("删除流程设计")
|
||||
public Result delete(@Param("id")Long id){
|
||||
dao.delete(ProcessDesign.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.service.ProcessInstanceService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/instance")
|
||||
@Ok("json:full")
|
||||
@Api("流程实例")
|
||||
public class FlowInstanceController {
|
||||
|
||||
@Inject
|
||||
private ProcessInstanceService processInstanceService;
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("启动并执行流程实例")
|
||||
public Result startAndExecute(Dict args) {
|
||||
Long processDefineId = args.getLong(FlowConst.PROCESS_DEFINE_ID_KEY);
|
||||
args.remove(FlowConst.PROCESS_DEFINE_ID_KEY);
|
||||
processInstanceService.startAndExecute(processDefineId,args);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package com.budwk.app.flow.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
import com.budwk.app.sys.services.SysUserService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@IocBean
|
||||
@At("/flow/todoCenter")
|
||||
@Ok("json:full")
|
||||
public class FlowTodoCenterController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@At("")
|
||||
@SaCheckLogin
|
||||
@Ok("beetl:/platform/flow/todoCenter/index.html")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result todo(Integer pageNumber, Integer pageSize, String searchKeyword, String category) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.id AS taskId,
|
||||
t.taskName AS taskKey,
|
||||
t.displayName AS taskName,
|
||||
t.taskState,
|
||||
t.formKey,
|
||||
t.h5FormKey,
|
||||
t.createdAt,
|
||||
t.finishTime,
|
||||
t.variable,
|
||||
ins.state AS instanceState,
|
||||
ins.id AS instanceId,
|
||||
ins.businessNo,
|
||||
cat.`name` categoryName,
|
||||
ins.variable ->> '$.instanceName' AS instanceName
|
||||
FROM
|
||||
wf_process_task t
|
||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
|
||||
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
|
||||
LEFT JOIN wf_process_category cat ON cat.id = def.category
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
|
||||
cnd.andEX("def.category", "=", category);
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
cnd.where().andLike("ins.variable ->> '$.instanceName'", searchKeyword);
|
||||
}
|
||||
cnd.desc("t.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = sysUserService.listPageMap(pageNumber, pageSize, sql);
|
||||
List<NutMap> list = pagination.getList();
|
||||
for (NutMap row : list) {
|
||||
NutMap variable = Json.fromJson(NutMap.class, row.getString("variable"));
|
||||
row.put("variable", variable);
|
||||
}
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result done(Integer pageNumber, Integer pageSize, String searchKeyword, String category) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.id AS taskId,
|
||||
t.taskName AS taskKey,
|
||||
t.displayName AS taskName,
|
||||
t.taskState,
|
||||
t.formKey,
|
||||
t.h5FormKey,
|
||||
t.createdAt,
|
||||
t.finishTime,
|
||||
t.variable,
|
||||
ins.state AS instanceState,
|
||||
ins.id AS instanceId,
|
||||
ins.businessNo,
|
||||
cat.`name` categoryName,
|
||||
ins.variable ->> '$.instanceName' AS instanceName
|
||||
FROM
|
||||
wf_process_task t
|
||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
|
||||
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
|
||||
LEFT JOIN wf_process_category cat ON cat.id = def.category
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.FINISHED.getCode());
|
||||
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
|
||||
cnd.andEX("def.category", "=", category);
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
cnd.where().andLike("ins.variable ->> '$.instanceName'", searchKeyword);
|
||||
}
|
||||
cnd.desc("t.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = sysUserService.listPageMap(pageNumber, pageSize, sql);
|
||||
List<NutMap> list = pagination.getList();
|
||||
for (NutMap row : list) {
|
||||
NutMap variable = Json.fromJson(NutMap.class, row.getString("variable"));
|
||||
row.put("variable", variable);
|
||||
}
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result started(Integer pageNumber, Integer pageSize, String searchKeyword, String category) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
ins.id AS instanceId,
|
||||
ins.processDefineId,
|
||||
ins.state,
|
||||
ins.businessNo,
|
||||
ins.operator,
|
||||
ins.variable,
|
||||
ins.createdAt,
|
||||
ins.variable ->> '$.instanceName' AS instanceName,
|
||||
GROUP_CONCAT(DISTINCT t.displayName) as taskName,
|
||||
cat.`name` categoryName,
|
||||
t.formKey,
|
||||
t.h5FormKey
|
||||
FROM
|
||||
wf_process_instance ins
|
||||
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id and t.taskState = 20
|
||||
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
|
||||
LEFT JOIN wf_process_category cat ON cat.id = def.category
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("ins.operator", "=", SecurityUtil.getUserId());
|
||||
cnd.andEX("def.category", "=", category);
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
cnd.where().andLike("ins.variable ->> '$.instanceName'", searchKeyword);
|
||||
}
|
||||
cnd.groupBy("ins.id");
|
||||
cnd.desc("ins.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = sysUserService.listPageMap(pageNumber, pageSize, sql);
|
||||
List<NutMap> list = pagination.getList();
|
||||
for (NutMap row : list) {
|
||||
NutMap variable = Json.fromJson(NutMap.class, row.getString("variable"));
|
||||
row.put("variable", variable);
|
||||
}
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result completed(Integer pageNumber, Integer pageSize, String searchKeyword, String category) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
ins.id AS instanceId,
|
||||
ins.processDefineId,
|
||||
ins.state,
|
||||
ins.businessNo,
|
||||
ins.operator,
|
||||
ins.variable,
|
||||
ins.createdAt,
|
||||
ins.variable ->> '$.instanceName' AS instanceName,
|
||||
cat.`name` categoryName
|
||||
FROM
|
||||
wf_process_instance ins
|
||||
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
|
||||
LEFT JOIN wf_process_category cat ON cat.id = def.category
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("ins.state", "=", 20);
|
||||
cnd.and("ins.operator", "=", SecurityUtil.getUserId());
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
cnd.where().andLike("ins.variable ->> '$.instanceName'", searchKeyword);
|
||||
}
|
||||
cnd.groupBy("ins.id");
|
||||
cnd.desc("ins.createdAt");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = sysUserService.listPageMap(pageNumber, pageSize, sql);
|
||||
List<NutMap> list = pagination.getList();
|
||||
for (NutMap row : list) {
|
||||
NutMap variable = Json.fromJson(NutMap.class, row.getString("variable"));
|
||||
row.put("variable", variable);
|
||||
}
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result statistics() {
|
||||
// 查询我发起的流程
|
||||
int startedCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getOperator, "=", SecurityUtil.getUserId()));
|
||||
// 查询我的待办任务
|
||||
Sql todoSql = Sqls.create("""
|
||||
SELECT
|
||||
count(1)
|
||||
FROM
|
||||
wf_process_task t
|
||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||
WHERE
|
||||
ta.actorId = @userId
|
||||
AND t.taskState = 10
|
||||
""");
|
||||
todoSql.setParam("userId", SecurityUtil.getUserId());
|
||||
todoSql.setCallback(Sqls.callback.integer());
|
||||
dao.execute(todoSql);
|
||||
int todoCount = todoSql.getInt();
|
||||
|
||||
// 查询我的已办任务
|
||||
Sql doneSql = Sqls.create("""
|
||||
SELECT
|
||||
count(1)
|
||||
FROM
|
||||
wf_process_task t
|
||||
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
|
||||
WHERE
|
||||
ta.actorId = @userId
|
||||
AND t.taskState = 20
|
||||
""");
|
||||
doneSql.setParam("userId", SecurityUtil.getUserId());
|
||||
doneSql.setCallback(Sqls.callback.integer());
|
||||
dao.execute(doneSql);
|
||||
int doneCount = doneSql.getInt();
|
||||
|
||||
// 查询未读消息
|
||||
Sql msgSql = Sqls.create("""
|
||||
SELECT
|
||||
count(DISTINCT r.messageId)
|
||||
FROM
|
||||
global_message_receiver r
|
||||
LEFT JOIN global_message m ON m.id = r.messageId
|
||||
WHERE
|
||||
r.receiverId = @userId
|
||||
AND r.isRead = 0
|
||||
AND m.`status` = 2
|
||||
""");
|
||||
msgSql.setParam("userId", SecurityUtil.getUserId());
|
||||
msgSql.setCallback(Sqls.callback.integer());
|
||||
dao.execute(msgSql);
|
||||
int msgCount = msgSql.getInt();
|
||||
|
||||
return Result.success(Map.of("startedCount", startedCount, "todoCount", todoCount, "doneCount", doneCount, "notifications", msgCount));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
|
||||
/**
|
||||
*
|
||||
* 模型行为
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
public interface Action {
|
||||
public void execute(Execution execution);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 分配参与者的处理接口
|
||||
* @author mldong
|
||||
* @date 2023/6/15
|
||||
*/
|
||||
public interface AssignmentHandler {
|
||||
/**
|
||||
* 分配参与者方法,可获取到当前的执行对象
|
||||
* @param model 模型对象
|
||||
* @param execution 执行对象
|
||||
* @return Object 参与者对象
|
||||
*/
|
||||
List<String> assign(TaskModel model, Execution execution);
|
||||
default String getMessage() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
default int getOrder() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.entity.Candidate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 候选人处理接口
|
||||
* @author mldong
|
||||
* @date 2023/6/26
|
||||
*/
|
||||
public interface CandidateHandler {
|
||||
/**
|
||||
* 根据任务模型参数获取候选人信息
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
List<Candidate> handle(TaskModel model);
|
||||
|
||||
default String getMessage() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
default int getOrder() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 服务上下文接口,类似spring的ioc
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public interface Context {
|
||||
/**
|
||||
* 根据服务名称、实例向服务工厂注册
|
||||
* @param name 服务名称
|
||||
* @param object 服务实例
|
||||
*/
|
||||
void put(String name, Object object);
|
||||
|
||||
/**
|
||||
* 根据服务名称、类型向服务工厂注册
|
||||
* @param name 服务名称
|
||||
* @param clazz 类型
|
||||
*/
|
||||
void put(String name, Class<?> clazz);
|
||||
|
||||
/**
|
||||
* 判断是否存在给定的服务名称
|
||||
* @param name 服务名称
|
||||
* @return
|
||||
*/
|
||||
boolean exist(String name);
|
||||
|
||||
/**
|
||||
* 根据给定的类型查找服务实例
|
||||
* @param clazz 类型
|
||||
* @return
|
||||
*/
|
||||
<T> T find(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 根据给定的类型查找所有此类型的服务实例
|
||||
* @param clazz 类型
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> findList(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 根据给定的服务名称、类型查找服务实例
|
||||
* @param name 服务名称
|
||||
* @param clazz 类型
|
||||
* @return
|
||||
*/
|
||||
<T> T findByName(String name, Class<T> clazz);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
|
||||
/**
|
||||
*
|
||||
* 决策处理器接口
|
||||
* @author mldong
|
||||
* @date 2023/5/1
|
||||
*/
|
||||
public interface DecisionHandler {
|
||||
/**
|
||||
* 定义决策方法,实现类需要根据执行对象做处理,并返回后置流转的name
|
||||
* @param execution
|
||||
* @return String 后置流转的name
|
||||
*/
|
||||
String decide(Execution execution);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.cfg.Configuration;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.service.ProcessDefineService;
|
||||
import com.budwk.app.flow.service.ProcessInstanceService;
|
||||
import com.budwk.app.flow.service.ProcessTaskService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程引擎接口
|
||||
*
|
||||
* @author mldong
|
||||
* @date 2023/5/29
|
||||
*/
|
||||
public interface FlowEngine {
|
||||
/**
|
||||
* 根据Configuration对象配置实现类
|
||||
*
|
||||
* @param config 全局配置对象
|
||||
* @return FlowEngine 流程引擎
|
||||
*/
|
||||
FlowEngine configure(Configuration config);
|
||||
|
||||
/**
|
||||
* 获取流程定义服务
|
||||
*
|
||||
* @return ProcessDefineService
|
||||
*/
|
||||
ProcessDefineService processDefineService();
|
||||
|
||||
/**
|
||||
* 获取流程实例服务
|
||||
*
|
||||
* @return ProcessInstanceService
|
||||
*/
|
||||
ProcessInstanceService processInstanceService();
|
||||
|
||||
/**
|
||||
* 获取流程任务服务
|
||||
*
|
||||
* @return ProcessTaskService
|
||||
*/
|
||||
ProcessTaskService processTaskService();
|
||||
|
||||
/**
|
||||
* 根据流程定义KEY、操作人ID、启动流程参数启动流程实例 (会自动选择KEY相同的流程定义中最新版本的那个)
|
||||
*
|
||||
* @param key 流程定义KEY
|
||||
* @param businessKey 业务标识
|
||||
* @param operator 操作人ID
|
||||
* @param args 启动参数
|
||||
* @return
|
||||
*/
|
||||
ProcessInstance startProcessInstanceByKey(String key, String businessKey, String operator, Dict args);
|
||||
|
||||
/**
|
||||
* 根据流程定义ID、操作人ID、启动流程参数启动流程实例
|
||||
*
|
||||
* @param id 流程定义ID
|
||||
* @param businessKey 业务标识
|
||||
* @param operator 操作人ID
|
||||
* @param args 启动流程参数
|
||||
* @return ProcessInstance 流程实例
|
||||
*/
|
||||
ProcessInstance startProcessInstanceById(Long id, String businessKey, String operator, Dict args);
|
||||
|
||||
/**
|
||||
* 根据流程定义ID、操作人ID、启动流程参数启动流程实例
|
||||
*
|
||||
* @param id 流程定义ID
|
||||
* @param businessKey 业务标识
|
||||
* @param operator 操作人ID
|
||||
* @param args 启动流程参数
|
||||
* @param parentId
|
||||
* @param parentNodeName
|
||||
* @return ProcessInstance 流程实例
|
||||
*/
|
||||
ProcessInstance startProcessInstanceById(Long id, String businessKey, String operator, Dict args, Long parentId, String parentNodeName);
|
||||
|
||||
/**
|
||||
* 执行流程任务
|
||||
*
|
||||
* @param processTaskId
|
||||
* @param operator
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
List<ProcessTask> executeProcessTask(Long processTaskId, String operator, Dict args);
|
||||
|
||||
/**
|
||||
* 执行流程任务并跳转
|
||||
*
|
||||
* @param processTaskId
|
||||
* @param operator
|
||||
* @param args
|
||||
* @param nodeName
|
||||
* @return
|
||||
*/
|
||||
List<ProcessTask> executeAndJumpTask(Long processTaskId, String operator, Dict args, String nodeName);
|
||||
|
||||
/**
|
||||
* 执行流程任务并跳转到结束节点
|
||||
*
|
||||
* @param processTaskId
|
||||
* @param operator
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
List<ProcessTask> executeAndJumpToEnd(Long processTaskId, String operator, Dict args);
|
||||
|
||||
/**
|
||||
* 执行流程任务并跳转到第一个任务节点
|
||||
*
|
||||
* @param processTaskId
|
||||
* @param operator
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
List<ProcessTask> executeAndJumpToFirstTaskNode(Long processTaskId, String operator, Dict args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.budwk.app.flow.engine;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
|
||||
/**
|
||||
*
|
||||
* 流程节点拦截器
|
||||
* @author mldong
|
||||
* @date 2023/9/3
|
||||
*/
|
||||
public interface FlowInterceptor{
|
||||
/**
|
||||
* 拦截方法,参数为执行对象
|
||||
* @param execution 执行对象。可从中获取执行的数据
|
||||
*/
|
||||
void intercept(Execution execution);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.budwk.app.flow.engine.cfg;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.Context;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.impl.SimpleContext;
|
||||
import com.budwk.app.flow.engine.parser.impl.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* 流程引擎配置类
|
||||
*/
|
||||
public class Configuration {
|
||||
public Configuration() {
|
||||
this(new SimpleContext());
|
||||
}
|
||||
public Configuration(Context context) {
|
||||
ServiceContext.setContext(context);
|
||||
ServiceContext.put("decision", DecisionParser.class);
|
||||
ServiceContext.put("end", EndParser.class);
|
||||
ServiceContext.put("fork", ForkParser.class);
|
||||
ServiceContext.put("join", JoinParser.class);
|
||||
ServiceContext.put("start", StartParser.class);
|
||||
ServiceContext.put("task", TaskParser.class);
|
||||
ServiceContext.put("custom",CustomParser.class);
|
||||
ServiceContext.put("wfSubProcess", WfSubProcessParser.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.budwk.app.flow.engine.core;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.ProcessModel;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* 执行对象参数
|
||||
*/
|
||||
@Data
|
||||
public class Execution {
|
||||
// 流程实例ID
|
||||
private Long processInstanceId;
|
||||
// 当前流程任务ID
|
||||
private Long processTaskId;
|
||||
// 执行对象扩展参数
|
||||
private Dict args;
|
||||
// 当前流程模型
|
||||
private ProcessModel processModel;
|
||||
// 当前任务
|
||||
private ProcessTask processTask;
|
||||
// 当前流程实例
|
||||
private ProcessInstance processInstance;
|
||||
// 所有任务集合
|
||||
private List<ProcessTask> processTaskList = new ArrayList<>();
|
||||
// 是否可合并
|
||||
private boolean isMerged;
|
||||
// 流程引擎对象
|
||||
private FlowEngine engine;
|
||||
// 操作人
|
||||
private String operator;
|
||||
// 当前节点模型
|
||||
private NodeModel nodeModel;
|
||||
/**
|
||||
* 添加任务到任务集合
|
||||
* @param processTask
|
||||
*/
|
||||
public void addTask(ProcessTask processTask) {
|
||||
this.processTaskList.add(processTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加任务集合
|
||||
* @param processTasks
|
||||
*/
|
||||
public void addTasks(List<ProcessTask> processTasks) {
|
||||
this.processTaskList.addAll(processTasks);
|
||||
}
|
||||
/**
|
||||
* 获取正在进行中的任务列表
|
||||
* @return
|
||||
*/
|
||||
public List<ProcessTask> getDoingTaskList() {
|
||||
return this.processTaskList.stream().filter(item->{
|
||||
return ProcessTaskStateEnum.DOING.getCode().equals(item.getTaskState());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.budwk.app.flow.engine.core;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.engine.cfg.Configuration;
|
||||
import com.budwk.app.flow.engine.model.*;
|
||||
import com.budwk.app.flow.engine.parser.ModelParser;
|
||||
import com.budwk.app.flow.engine.util.FlowUtil;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||
import com.budwk.app.flow.service.ProcessDefineService;
|
||||
import com.budwk.app.flow.service.ProcessInstanceService;
|
||||
import com.budwk.app.flow.service.ProcessTaskService;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流引擎实现
|
||||
*/
|
||||
public class FlowEngineImpl implements FlowEngine {
|
||||
protected Configuration configuration;
|
||||
private ProcessDefineService processDefineService;
|
||||
private ProcessInstanceService processInstanceService;
|
||||
private ProcessTaskService processTaskService;
|
||||
|
||||
@Override
|
||||
public FlowEngine configure(Configuration config) {
|
||||
this.configuration = config;
|
||||
processDefineService = ServiceContext.find(ProcessDefineService.class);
|
||||
processInstanceService = ServiceContext.find(ProcessInstanceService.class);
|
||||
processTaskService = ServiceContext.find(ProcessTaskService.class);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefineService processDefineService() {
|
||||
return processDefineService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessInstanceService processInstanceService() {
|
||||
return processInstanceService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessTaskService processTaskService() {
|
||||
return processTaskService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessInstance startProcessInstanceByKey(String key, String businessKey, String operator, Dict args) {
|
||||
ProcessDefine define = processDefineService.getLastByName(key);
|
||||
return startProcessInstanceById(define.getId(), businessKey, operator, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public ProcessInstance startProcessInstanceById(Long id, String businessKey, String operator, Dict args) {
|
||||
return startProcessInstanceById(id, businessKey, operator, args, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public ProcessInstance startProcessInstanceById(Long id, String businessKey, String operator, Dict args, Long parentId, String parentNodeName) {
|
||||
if (StrUtil.isBlank(businessKey)) {
|
||||
// throw new RuntimeException("业务主键不能为空");
|
||||
}
|
||||
|
||||
if (args == null) args = Dict.create();
|
||||
|
||||
// 1. 根据流程定义ID查询流程定义文件
|
||||
ProcessDefine processDefine = processDefineService.getById(id);
|
||||
if (processDefine == null) {
|
||||
throw new BaseException("没有流程定义");
|
||||
}
|
||||
// 2. 将流程定义文件转成流程模型
|
||||
ProcessModel processModel = processDefineService.processDefineToModel(processDefine);
|
||||
|
||||
// 3. 根据流程定义对象创建流程实例
|
||||
ProcessInstance processInstance = processInstanceService.createProcessInstance(processDefine, businessKey, operator, args, parentId, parentNodeName);
|
||||
args.set(FlowConst.PROCESS_INSTANCE_ID_KEY, processInstance.getId());
|
||||
|
||||
// 4. 构建执行参数对象
|
||||
Execution execution = new Execution();
|
||||
execution.setProcessModel(processModel);
|
||||
execution.setProcessInstance(processInstance);
|
||||
execution.setProcessInstanceId(processInstance.getId());
|
||||
execution.setEngine(this);
|
||||
execution.setArgs(args);
|
||||
// 5. 拿到开始节点模型,调用其execute方法
|
||||
processModel.getStart().execute(execution);
|
||||
return processInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public List<ProcessTask> executeProcessTask(Long processTaskId, String operator, Dict args) {
|
||||
Execution execution = execute(processTaskId, operator, args);
|
||||
if (execution == null) return Collections.emptyList();
|
||||
ProcessModel processModel = execution.getProcessModel();
|
||||
// 7. 根据流程任务名称获取对应的任务节点模型
|
||||
NodeModel nodeModel = processModel.getNode(execution.getProcessTask().getTaskName());
|
||||
// 8. 调用节点模型执行方法
|
||||
nodeModel.execute(execution);
|
||||
return execution.getProcessTaskList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public List<ProcessTask> executeAndJumpTask(Long processTaskId, String operator, Dict args, String nodeName) {
|
||||
Execution execution = execute(processTaskId, operator, args);
|
||||
if (execution == null) return Collections.emptyList();
|
||||
ProcessModel model = execution.getProcessModel();
|
||||
if (StrUtil.isEmpty(nodeName)) {
|
||||
ProcessTask newTask = processTaskService.rejectTask(model, execution.getProcessTask());
|
||||
execution.addTask(newTask);
|
||||
} else {
|
||||
NodeModel nodeModel = model.getNode(nodeName);
|
||||
if (nodeModel == null) {
|
||||
throw new BaseException("根据节点名称[" + nodeName + "]无法找到节点模型");
|
||||
}
|
||||
// 判断是否为第一个任务节点
|
||||
if (nodeModel instanceof TaskModel) {
|
||||
TaskModel taskModel = (TaskModel) nodeModel;
|
||||
if (FlowUtil.isFistTaskName(model, taskModel.getName())) {
|
||||
// 第一个任务节点为申请节点,处理人等于流程发起人
|
||||
taskModel.setAssignee(execution.getProcessInstance().getOperator());
|
||||
}
|
||||
}
|
||||
//动态创建转移对象,由转移对象执行execution实例
|
||||
TransitionModel tm = new TransitionModel();
|
||||
tm.setTarget(nodeModel);
|
||||
tm.setEnabled(true);
|
||||
tm.execute(execution);
|
||||
}
|
||||
return execution.getProcessTaskList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public List<ProcessTask> executeAndJumpToEnd(Long processTaskId, String operator, Dict args) {
|
||||
Execution execution = execute(processTaskId, operator, args);
|
||||
if (execution == null) return Collections.emptyList();
|
||||
ProcessModel model = execution.getProcessModel();
|
||||
List<EndModel> endModelList = model.getModels(EndModel.class);
|
||||
endModelList.forEach(endModel -> {
|
||||
TransitionModel tm = new TransitionModel();
|
||||
tm.setTarget(endModel);
|
||||
tm.setEnabled(true);
|
||||
tm.execute(execution);
|
||||
});
|
||||
return execution.getProcessTaskList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessTask> executeAndJumpToFirstTaskNode(Long processTaskId, String operator, Dict args) {
|
||||
Execution execution = execute(processTaskId, operator, args);
|
||||
if (execution == null) return Collections.emptyList();
|
||||
ProcessModel model = execution.getProcessModel();
|
||||
StartModel startModel = model.getStart();
|
||||
startModel.getOutputs().forEach(transitionModel -> {
|
||||
transitionModel.setEnabled(true);
|
||||
// 调整参与者为流程发起人
|
||||
if (transitionModel.getTarget() instanceof TaskModel) {
|
||||
TaskModel taskModel = (TaskModel) transitionModel.getTarget();
|
||||
taskModel.setAssignee(execution.getProcessInstance().getOperator());
|
||||
}
|
||||
transitionModel.execute(execution);
|
||||
});
|
||||
return execution.getProcessTaskList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成执行对象
|
||||
*
|
||||
* @param processTaskId
|
||||
* @param operator
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
private Execution execute(Long processTaskId, String operator, Dict args) {
|
||||
// 1.1 根据id查询正在进行中的流程任务
|
||||
ProcessTask processTask = processTaskService.getById(processTaskId);
|
||||
if (processTask == null || !ProcessTaskStateEnum.DOING.getCode().equals(processTask.getTaskState())) {
|
||||
throw new BaseException("没有进行中的流程任务");
|
||||
}
|
||||
// 1.2 判断是否可以执行任务
|
||||
if (!processTaskService.isAllowed(processTask, operator)) {
|
||||
// 当前参与者不能执行该流程任务
|
||||
throw new BaseException("当前参与者不能执行该流程任务");
|
||||
}
|
||||
// 2. 根据流程任务查询流程实例
|
||||
ProcessInstance processInstance = processInstanceService.getById(processTask.getProcessInstanceId());
|
||||
// 3. 根据流程实例查询流程定义
|
||||
ProcessDefine processDefine = processDefineService.getById(processInstance.getProcessDefineId());
|
||||
// 4. 将流程定义文件转成流程模型
|
||||
ProcessModel processModel = ModelParser.parse(JSONUtil.toJsonStr(processDefine.getContent()));
|
||||
// 5. 将流程任务状态修改为已完成
|
||||
processTaskService.finishProcessTask(processTaskId, operator, args);
|
||||
processTask.setTaskState(ProcessTaskStateEnum.FINISHED.getCode());
|
||||
// 6. 根据流程定义、实例、任务构建执行参数对象
|
||||
Execution execution = new Execution();
|
||||
execution.setProcessModel(processModel);
|
||||
execution.setProcessInstance(processInstance);
|
||||
execution.setProcessInstanceId(processInstance.getId());
|
||||
execution.setProcessTask(processTask);
|
||||
execution.setProcessTaskId(processTaskId);
|
||||
execution.setOperator(operator);
|
||||
execution.setEngine(this);
|
||||
Dict processInstanceVariable = JSONUtil.toBean(processInstance.getVariable(), Dict.class);
|
||||
Dict newArgs = Dict.create();
|
||||
newArgs.putAll(processInstanceVariable);
|
||||
newArgs.putAll(args);
|
||||
execution.setArgs(newArgs);
|
||||
// 如果提交参数中存在f_前辍参数,则更新到流程实例变量中
|
||||
Dict addArgs = Dict.create();
|
||||
args.forEach((key, value) -> {
|
||||
if (key.startsWith(FlowConst.FORM_DATA_PREFIX)) {
|
||||
addArgs.put(key, value);
|
||||
}
|
||||
});
|
||||
if (ObjectUtil.isNotEmpty(addArgs)) {
|
||||
processInstanceService.addVariable(processInstance.getId(), addArgs);
|
||||
}
|
||||
return execution;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.budwk.app.flow.engine.core;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.budwk.app.flow.engine.Context;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 单例服务上下文
|
||||
*/
|
||||
public class ServiceContext {
|
||||
private static Context context;
|
||||
|
||||
|
||||
public static void setContext(Context context) {
|
||||
ServiceContext.context = context;
|
||||
}
|
||||
|
||||
public static void put(String name, Object object) {
|
||||
Assert.notNull(context,"未注册服务上下文");
|
||||
context.put(name, object);
|
||||
}
|
||||
|
||||
public static void put(String name, Class<?> clazz) {
|
||||
Assert.notNull(context,"未注册服务上下文");
|
||||
context.put(name, ReflectUtil.newInstance(clazz));
|
||||
}
|
||||
|
||||
public static boolean exist(String name) {
|
||||
Assert.notNull(context,"未注册服务上下文");
|
||||
return context.exist(name);
|
||||
}
|
||||
|
||||
public static <T> T find(Class<T> clazz) {
|
||||
Assert.notNull(context,"未注册服务上下文");
|
||||
return context.find(clazz);
|
||||
}
|
||||
|
||||
public static <T> List<T> findList(Class<T> clazz) {
|
||||
Assert.notNull(context,"未注册服务上下文");
|
||||
return context.findList(clazz);
|
||||
}
|
||||
|
||||
public static <T> T findByName(String name, Class<T> clazz) {
|
||||
Assert.notNull(context,"未注册服务上下文");
|
||||
return context.findByName(name, clazz);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.budwk.app.flow.engine.event;
|
||||
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 流程开始事件
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ProcessEvent {
|
||||
// 流程事件类型
|
||||
private ProcessEventTypeEnum eventType;
|
||||
// 执行源id(流程实例id,流程任务id)
|
||||
private Long sourceId;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.budwk.app.flow.engine.event;
|
||||
|
||||
/**
|
||||
* 流程事件监听接口
|
||||
*/
|
||||
public interface ProcessEventListener {
|
||||
void onEvent(ProcessEvent event);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.budwk.app.flow.engine.event;
|
||||
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程事件发布者类
|
||||
*/
|
||||
public class ProcessPublisher {
|
||||
// 事件通知方法
|
||||
public static void notify(ProcessEvent event) {
|
||||
// 这里直接从上下文中获取所有的监听器,并调用监听器的onEvent方法==>通知
|
||||
List<ProcessEventListener> processEventListenerList = ServiceContext.findList(ProcessEventListener.class);
|
||||
processEventListenerList.forEach(processEventListener -> {
|
||||
processEventListener.onEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.budwk.app.flow.engine.handlers;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
|
||||
/**
|
||||
* 流程各模型操作处理接口
|
||||
*/
|
||||
public interface IHandler {
|
||||
/**
|
||||
* 子类需要实现的方法,来处理具体的操作
|
||||
* @param execution 执行对象
|
||||
*/
|
||||
void handle(Execution execution);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.budwk.app.flow.engine.handlers.impl;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.expression.ExpressionUtil;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.CountersignTypeEnum;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 会签任务处理,用于判断会签任务是否可通过
|
||||
*
|
||||
*/
|
||||
public class CountersignHandler implements IHandler {
|
||||
private TaskModel taskModel;
|
||||
|
||||
public CountersignHandler(TaskModel taskModel) {
|
||||
this.taskModel = taskModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Execution execution) {
|
||||
boolean isMerged = false;
|
||||
String countersignType = taskModel.getExt().get("countersignType", "PARALLEL");
|
||||
String countersignCompletionCondition = taskModel.getExt().get("countersignCompletionCondition", "");
|
||||
countersignCompletionCondition = taskModel.getCountersignCompletionCondition();
|
||||
String prefix = FlowConst.COUNTERSIGN_VARIABLE_PREFIX + taskModel.getName() + "_";
|
||||
// 会签办理人列表
|
||||
List<String> operatorList = Convert.toList(String.class, execution.getArgs().get(prefix + FlowConst.COUNTERSIGN_OPERATOR_LIST));
|
||||
// 循环计数器,办理人在列表中的索引
|
||||
int loopCounter = operatorList.indexOf(execution.getOperator());
|
||||
// 追加计数器
|
||||
execution.getArgs().put(prefix + FlowConst.LOOP_COUNTER, loopCounter);
|
||||
// 追加已完成数量
|
||||
execution.getArgs().put(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES,
|
||||
execution.getArgs().get(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES, 0) + 1);
|
||||
|
||||
// 追加已完成并且同意数量
|
||||
if (!execution.getArgs().containsKey(FlowConst.COUNTERSIGN_DISAGREE_FLAG)) {
|
||||
execution.getArgs().put(prefix + FlowConst.NR_OF_COMPLETED_AGREE_INSTANCES,
|
||||
execution.getArgs().get(prefix + FlowConst.NR_OF_COMPLETED_AGREE_INSTANCES, 0) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* ● 全部通过:为空
|
||||
* ● 按数量通过:#nrOfCompletedInstances==n,这里表示n人完成任务,会签结束。
|
||||
* ● 按比例通过:#nrOfCompletedInstances/nrOfInstances==n,这里表示已完成会签数与总实例数达到一定比例时,会签结束
|
||||
* ● 一票通过:#nrOfCompletedInstances==1,这里表示1人完成任务,会签结束。
|
||||
* ● 一票否决:ONE_VOTE_VETO
|
||||
*/
|
||||
if ("ONE_VOTE_VETO".equalsIgnoreCase(countersignCompletionCondition)) {
|
||||
// 一票否决
|
||||
if (execution.getArgs().containsKey(FlowConst.COUNTERSIGN_DISAGREE_FLAG)) {
|
||||
// 存在拒绝标识,则直接通过
|
||||
isMerged = true;
|
||||
}
|
||||
} else if (!StrUtil.isBlank(countersignCompletionCondition)) {
|
||||
// 根据条件判断是否通过
|
||||
Dict dict = Dict.create();
|
||||
execution.getArgs().forEach((k, v) -> {
|
||||
dict.set(k.replace(prefix, ""), v);
|
||||
});
|
||||
isMerged = Convert.toBool(ExpressionUtil.eval(countersignCompletionCondition, dict));
|
||||
}
|
||||
if (!isMerged && CountersignTypeEnum.SEQUENTIAL.toString().equalsIgnoreCase(countersignType)) {
|
||||
// 串行未通过,则判断是否为最后一个
|
||||
if (loopCounter == operatorList.size() - 1) {
|
||||
isMerged = true;
|
||||
} else {
|
||||
// 非最后一个,则继续创建会签任务
|
||||
execution.getEngine().processTaskService().createCountersignTask(taskModel, execution);
|
||||
}
|
||||
}
|
||||
if (!isMerged && CountersignTypeEnum.PARALLEL.toString().equalsIgnoreCase(countersignType)) {
|
||||
// 是否所有会签任务已完成
|
||||
boolean finished = execution.getEngine().processTaskService().getDoingTaskList(execution.getProcessInstanceId(), new String[]{taskModel.getName()}).size() == 0;
|
||||
if (!isMerged) {
|
||||
// 未通过,更新已完成实例数量
|
||||
Dict addVariable = Dict.create();
|
||||
addVariable.put(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES, execution.getArgs().get(prefix + FlowConst.NR_OF_COMPLETED_INSTANCES));
|
||||
addVariable.put(prefix + FlowConst.NR_OF_COMPLETED_AGREE_INSTANCES, execution.getArgs().get(prefix + FlowConst.NR_OF_COMPLETED_AGREE_INSTANCES));
|
||||
execution.getEngine().processInstanceService().addVariable(execution.getProcessInstanceId(), addVariable);
|
||||
}
|
||||
if(finished) {
|
||||
execution.getEngine().processTaskService().rejectTask(execution.getProcessModel(), execution.getProcessTask());
|
||||
}
|
||||
}
|
||||
if (isMerged) {
|
||||
// 获取所有会签参数键值
|
||||
List<String> keys = execution.getArgs().keySet().stream().filter(k -> k.startsWith(prefix)).collect(Collectors.toList());
|
||||
keys.add(FlowConst.COUNTERSIGN_DISAGREE_FLAG);
|
||||
// 如果可以合并,则把流程实例中的会签参数清空
|
||||
execution.getEngine().processInstanceService().removeVariable(
|
||||
execution.getProcessInstanceId(),
|
||||
keys.toArray(new String[]{}));
|
||||
// 如果为并行会签,需将其他会签任务设置为废弃
|
||||
if (CountersignTypeEnum.PARALLEL.toString().equalsIgnoreCase(countersignType)) {
|
||||
execution.getEngine().processTaskService().getDoingTaskList(execution.getProcessInstanceId(), new String[]{taskModel.getName()}).stream().forEach(t -> {
|
||||
execution.getEngine().processTaskService().abandonProcessTask(t.getId(), FlowConst.AUTO_ID, execution.getArgs());
|
||||
});
|
||||
}
|
||||
}
|
||||
execution.setMerged(isMerged);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.budwk.app.flow.engine.handlers.impl;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.FlowInterceptor;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessPublisher;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建任务处理器
|
||||
*/
|
||||
public class CreateTaskHandler implements IHandler {
|
||||
private TaskModel taskModel;
|
||||
public CreateTaskHandler(TaskModel taskModel) {
|
||||
this.taskModel = taskModel;
|
||||
}
|
||||
@Override
|
||||
public void handle(Execution execution) {
|
||||
List<ProcessTask> processTaskList;
|
||||
if(ProcessTaskPerformTypeEnum.COUNTERSIGN.equals(taskModel.getPerformType())) {
|
||||
// 会签类型,创建会签任务
|
||||
processTaskList = execution.getEngine().processTaskService().createCountersignTask(taskModel, execution);
|
||||
} else {
|
||||
// 创建普通任务
|
||||
processTaskList = execution.getEngine().processTaskService().createTask(taskModel, execution);
|
||||
}
|
||||
// 将任务添加到执行对象中
|
||||
execution.addTasks(processTaskList);
|
||||
// 从服务上下文中获取拦截器执行
|
||||
ServiceContext.findList(FlowInterceptor.class).forEach(flowInterceptor -> {
|
||||
flowInterceptor.intercept(execution);
|
||||
});
|
||||
// 发布流程任务开始事件
|
||||
processTaskList.forEach(processTask -> {
|
||||
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_START).sourceId(processTask.getId()).build());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.budwk.app.flow.engine.handlers.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessPublisher;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import com.budwk.app.flow.engine.model.EndModel;
|
||||
import com.budwk.app.flow.engine.model.ProcessModel;
|
||||
import com.budwk.app.flow.engine.model.SubProcessModel;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
|
||||
|
||||
/**
|
||||
* 结束流程实例的处理器
|
||||
*/
|
||||
public class EndProcessHandler implements IHandler {
|
||||
private EndModel endModel;
|
||||
public EndProcessHandler(EndModel endModel) {
|
||||
this.endModel = endModel;
|
||||
}
|
||||
@Override
|
||||
public void handle(Execution execution) {
|
||||
Integer submitType = execution.getArgs().get(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.AGREE.getCode());
|
||||
if(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.REJECT.getCode())) {
|
||||
execution.getEngine().processInstanceService().rejectProcessInstance(execution.getProcessInstanceId());
|
||||
} else {
|
||||
execution.getEngine().processInstanceService().finishProcessInstance(execution.getProcessInstanceId());
|
||||
}
|
||||
// 发布流程实例结束事件
|
||||
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_INSTANCE_END).sourceId(execution.getProcessInstanceId()).build());
|
||||
ProcessInstance processInstance = execution.getProcessInstance();
|
||||
if(ObjectUtil.isNotNull(processInstance.getParentId())) {
|
||||
// 如果子流程存在父流程实例,则执行父流程的子流程节点模型方法
|
||||
ProcessInstance parentInstance = execution.getEngine().processInstanceService().getById(processInstance.getParentId());
|
||||
if(parentInstance == null) return;
|
||||
ProcessModel pm = execution.getEngine().processDefineService().getProcessModel(parentInstance.getProcessDefineId());
|
||||
if(pm == null) return;
|
||||
SubProcessModel spm = (SubProcessModel)pm.getNode(processInstance.getParentNodeName());
|
||||
Execution newExecution = new Execution();
|
||||
newExecution.setEngine(execution.getEngine());
|
||||
newExecution.setProcessModel(pm);
|
||||
newExecution.setProcessInstance(parentInstance);
|
||||
newExecution.setProcessInstanceId(parentInstance.getId());
|
||||
newExecution.setArgs(execution.getArgs());
|
||||
spm.execute(newExecution);
|
||||
execution.addTasks(newExecution.getProcessTaskList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.budwk.app.flow.engine.handlers.impl;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import com.budwk.app.flow.engine.model.*;
|
||||
import com.budwk.app.flow.service.ProcessTaskService;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 合并分支操作的处理器
|
||||
*/
|
||||
public class MergeBranchHandler implements IHandler {
|
||||
private JoinModel joinModel;
|
||||
|
||||
public MergeBranchHandler(JoinModel joinModel) {
|
||||
this.joinModel = joinModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Execution execution) {
|
||||
// 判断是否存在正在执行的任务,存在则不允许合并
|
||||
execution.setMerged(
|
||||
execution.getEngine()
|
||||
.processTaskService()
|
||||
.getDoingTaskList(execution.getProcessInstanceId(), findActiveNodes()).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
public static void findForkTaskNames(NodeModel node, StringBuilder buffer, Set<String> visitedNodes) {
|
||||
if (node == null) return;
|
||||
|
||||
String nodeId = node.getName();
|
||||
if (visitedNodes.contains(nodeId)) return;
|
||||
visitedNodes.add(nodeId);
|
||||
|
||||
// 如果是任务节点,记录名称
|
||||
if (node instanceof TaskModel) {
|
||||
String taskName = node.getName();
|
||||
if (buffer.indexOf(taskName) == -1) {
|
||||
if (!buffer.isEmpty()) buffer.append(",");
|
||||
buffer.append(taskName);
|
||||
}
|
||||
}
|
||||
|
||||
// 继续向上游递归(包括fork节点)
|
||||
List<TransitionModel> inputs = node.getInputs();
|
||||
for (TransitionModel tm : inputs) {
|
||||
findForkTaskNames(tm.getSource(), buffer, visitedNodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对join节点的所有输入变迁进行递归,查找join至fork节点的所有中间task元素
|
||||
*
|
||||
* @param node
|
||||
* @param buffer
|
||||
*/
|
||||
public static void findForkTaskNames(NodeModel node, StringBuilder buffer) {
|
||||
if (node instanceof ForkModel) return;
|
||||
List<TransitionModel> inputs = node.getInputs();
|
||||
for (TransitionModel tm : inputs) {
|
||||
if (tm.getSource() instanceof TaskModel) {
|
||||
buffer.append(tm.getSource().getName()).append(",");
|
||||
}
|
||||
findForkTaskNames(tm.getSource(), buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取join节点的输入变迁中,所有从fork节点开始的任务名称
|
||||
*
|
||||
* @param node 当前节点
|
||||
* @param buffer 存储中间task名称的StringBuilder对象
|
||||
* @param visited 存储已访问的节点名称的Set对象
|
||||
* @param foundFork 是否已经找到fork节点
|
||||
*/
|
||||
private static void findForkTaskNames(NodeModel node, StringBuilder buffer, Set<String> visited, boolean foundFork) {
|
||||
// 如果已经访问过这个节点,直接返回避免死循环
|
||||
if (visited.contains(node.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 标记当前节点为已访问
|
||||
visited.add(node.getName());
|
||||
|
||||
List<TransitionModel> inputs = node.getInputs();
|
||||
for (TransitionModel tm : inputs) {
|
||||
NodeModel sourceNode = tm.getSource();
|
||||
|
||||
// 如果源节点是fork节点,标记找到了fork,但不继续递归这个节点
|
||||
if (sourceNode instanceof ForkModel) {
|
||||
foundFork = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果源节点是task,记录下来
|
||||
if (sourceNode instanceof TaskModel) {
|
||||
buffer.append(sourceNode.getName()).append(",");
|
||||
}
|
||||
|
||||
// 继续递归(除非已经找到了fork节点且当前路径已经完成)
|
||||
findForkTaskNames(sourceNode, buffer, visited, foundFork);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对join节点的所有输入变迁进行递归,查找join至fork节点的所有中间task元素
|
||||
*
|
||||
* @see MergeBranchHandler#findActiveNodes()
|
||||
*/
|
||||
public String[] findActiveNodes() {
|
||||
StringBuilder buffer = new StringBuilder(20);
|
||||
|
||||
findForkTaskNames(joinModel, buffer, new HashSet<>(), false);
|
||||
|
||||
// findForkTaskNames(joinModel, buffer, visitedNodes);
|
||||
// findForkTaskNames(joinModel, buffer);
|
||||
String[] taskNames = buffer.toString().split(",");
|
||||
return taskNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断流程是否可合并
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @param nodeModel
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMerged(Long processInstanceId, NodeModel nodeModel) {
|
||||
// 合并节点
|
||||
StringBuilder buffer = new StringBuilder(20);
|
||||
MergeBranchHandler.findForkTaskNames(nodeModel, buffer);
|
||||
String[] taskNames = buffer.toString().split(",");
|
||||
ProcessTaskService processTaskService = ServiceContext.find(ProcessTaskService.class);
|
||||
boolean isMerged = processTaskService.getDoingTaskList(processInstanceId, taskNames).isEmpty();
|
||||
return isMerged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.budwk.app.flow.engine.handlers.impl;
|
||||
|
||||
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import com.budwk.app.flow.engine.model.SubProcessModel;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
|
||||
/**
|
||||
* 启动子流程处理类
|
||||
*/
|
||||
public class StartSubProcessHandler implements IHandler {
|
||||
private SubProcessModel model;
|
||||
|
||||
public StartSubProcessHandler(SubProcessModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Execution execution) {
|
||||
ProcessDefine processDefine = execution.getEngine().processDefineService().getProcessDefineByVersion(model.getName(), model.getVersion());
|
||||
if (processDefine == null) {
|
||||
throw new BaseException("子流程" + model.getName() + "定义不存在");
|
||||
}
|
||||
Long parentId = execution.getProcessInstanceId();
|
||||
String parentNodeName = model.getName();
|
||||
execution.getEngine().startProcessInstanceById(
|
||||
processDefine.getId(),
|
||||
null,
|
||||
execution.getOperator(),
|
||||
execution.getArgs(),
|
||||
parentId, parentNodeName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.budwk.app.flow.engine.impl;
|
||||
|
||||
import com.budwk.app.flow.engine.Context;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@Slf4j
|
||||
public class NutzContext implements Context {
|
||||
|
||||
@Inject("refer:$ioc")
|
||||
private Ioc ioc;
|
||||
|
||||
@Override
|
||||
public void put(String name, Object object) {
|
||||
ioc.addBean(name, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, Class<?> clazz) {
|
||||
try {
|
||||
Object o = clazz.getDeclaredConstructor().newInstance();
|
||||
ioc.addBean(name, o);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
||||
NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exist(String name) {
|
||||
return ioc.has(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T find(Class<T> clazz) {
|
||||
try {
|
||||
T bean = ioc.getByType(clazz);
|
||||
return bean;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findList(Class<T> clazz) {
|
||||
List<T> res = new ArrayList<>();
|
||||
String[] names = ioc.getNamesByType(clazz);
|
||||
for (String name : names) {
|
||||
res.add(ioc.get(clazz, name));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findByName(String name, Class<T> clazz) {
|
||||
return ioc.get(clazz, name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.budwk.app.flow.engine.impl;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.budwk.app.flow.engine.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* 简单的上下文发现实现类
|
||||
*/
|
||||
public class SimpleContext implements Context {
|
||||
private Dict dict = Dict.create();
|
||||
@Override
|
||||
public void put(String name, Object object) {
|
||||
dict.put(name, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, Class<?> clazz) {
|
||||
dict.put(name, ReflectUtil.newInstance(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exist(String name) {
|
||||
return ObjectUtil.isNotNull(dict.getObj(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T find(Class<T> clazz) {
|
||||
for (Map.Entry<String, Object> entry : dict.entrySet()) {
|
||||
if (clazz.isInstance(entry.getValue())) {
|
||||
return clazz.cast(entry.getValue());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findList(Class<T> clazz) {
|
||||
List<T> res = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : dict.entrySet()) {
|
||||
if (clazz.isInstance(entry.getValue())) {
|
||||
res.add(clazz.cast(entry.getValue()));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findByName(String name, Class<T> clazz) {
|
||||
for (Map.Entry<String, Object> entry : dict.entrySet()) {
|
||||
if (entry.getKey().equals(name) && clazz.isInstance(entry.getValue())) {
|
||||
return clazz.cast(entry.getValue());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//package com.budwk.app.flow.engine.impl;
|
||||
//
|
||||
//import cn.hutool.core.util.ObjectUtil;
|
||||
//import com.budwk.app.flow.engine.Context;
|
||||
//import org.springframework.beans.factory.config.BeanDefinition;
|
||||
//import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
//import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
//import org.springframework.context.ApplicationContext;
|
||||
//import org.springframework.context.ApplicationContextAware;
|
||||
//import org.springframework.context.ConfigurableApplicationContext;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//import java.util.Map;
|
||||
//
|
||||
///**
|
||||
// *
|
||||
// * spring的服务查找实现
|
||||
// * @author mldong
|
||||
// * @date 2023/5/31
|
||||
// */
|
||||
//@Component
|
||||
//public class SpringContext implements Context {
|
||||
//// private ApplicationContext applicationContext;
|
||||
//// DefaultListableBeanFactory beanFactory;
|
||||
//
|
||||
// @Override
|
||||
// public void put(String name, Object object) {
|
||||
// ConfigurableApplicationContext context = (ConfigurableApplicationContext)applicationContext;
|
||||
// context.getBeanFactory().registerSingleton(name, object);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void put(String name, Class<?> clazz) {
|
||||
// BeanDefinition definition = new RootBeanDefinition(clazz);
|
||||
// if(beanFactory==null) {
|
||||
// beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
|
||||
// }
|
||||
// beanFactory.registerBeanDefinition(name, definition);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean exist(String name) {
|
||||
// try{
|
||||
// return ObjectUtil.isNotNull(SpringUtil.getBean(name));
|
||||
// } catch (Exception e) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <T> T find(Class<T> clazz) {
|
||||
// return SpringUtil.getBean(clazz);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <T> List<T> findList(Class<T> clazz) {
|
||||
// List<T> res = new ArrayList<>();
|
||||
// Map<String,T> map = SpringUtil.getBeansOfType(clazz);
|
||||
// map.forEach((k,v)->{
|
||||
// res.add(v);
|
||||
// });
|
||||
// return res;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <T> T findByName(String name, Class<T> clazz) {
|
||||
// return SpringUtil.getBean(name, clazz);
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.budwk.app.flow.engine.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.flow.engine.FlowInterceptor;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.service.ProcessSurrogateService;
|
||||
import com.budwk.app.flow.service.ProcessTaskService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务拦截器,处理代理人
|
||||
*
|
||||
*/
|
||||
@IocBean
|
||||
public class SurrogateInterceptor implements FlowInterceptor {
|
||||
@Inject
|
||||
private ProcessSurrogateService processSurrogateService;
|
||||
@Inject
|
||||
private ProcessTaskService processTaskService;
|
||||
|
||||
@Override
|
||||
public void intercept(Execution execution) {
|
||||
execution.getProcessTaskList().forEach(processTask -> {
|
||||
List<String> actorList = processTaskService.getTaskActors(processTask.getId());
|
||||
actorList.forEach(actor -> {
|
||||
String agent = processSurrogateService.getSurrogate(actor, execution.getProcessModel().getName());
|
||||
if (StrUtil.isNotEmpty(agent)) {
|
||||
processTaskService.addTaskActor(processTask.getId(), CollectionUtil.newArrayList(agent));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import lombok.Data;
|
||||
/**
|
||||
*
|
||||
* 模型基类
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@Data
|
||||
public class BaseModel {
|
||||
private String name; // 唯一编码
|
||||
private String displayName; // 显示名称
|
||||
/**
|
||||
* 将执行对象execution交给具体的处理器处理
|
||||
* @param handler
|
||||
* @param execution
|
||||
*/
|
||||
protected void fire(IHandler handler, Execution execution) {
|
||||
handler.handle(execution);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.IHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 自定义节点模型
|
||||
* @author mldong
|
||||
* @date 2023/12/7
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class CustomModel extends NodeModel{
|
||||
private String clazz; // 类路径
|
||||
private String methodName; // 方法名
|
||||
private String args; // 入参
|
||||
private String var; // 执行返回值的变量
|
||||
/**
|
||||
* 加载模型时初始化的对象实例
|
||||
*/
|
||||
private Object invokeObject;
|
||||
|
||||
public void exec(Execution execution) {
|
||||
if(invokeObject == null) {
|
||||
invokeObject = ReflectUtil.newInstance(clazz);
|
||||
}
|
||||
if(invokeObject == null) {
|
||||
throw new BaseException("自定义模型[class=" + clazz + "]实例化对象失败");
|
||||
}
|
||||
|
||||
if(invokeObject instanceof IHandler) {
|
||||
IHandler handler = (IHandler)invokeObject;
|
||||
handler.handle(execution);
|
||||
} else {
|
||||
Object[] objects = getArgs(execution.getArgs(), args);
|
||||
Class<?> paramTypes[] = Arrays.stream(objects).map(Object::getClass).toArray(Class[]::new);
|
||||
Method method = ReflectUtil.getMethod(invokeObject.getClass(),methodName, paramTypes);
|
||||
if(method == null) {
|
||||
throw new BaseException("自定义模型[class=" + clazz + "]无法找到方法名称:" + methodName);
|
||||
}
|
||||
Object returnValue = ReflectUtil.invoke(invokeObject, method, objects);
|
||||
if(StrUtil.isNotEmpty(var)) {
|
||||
execution.getArgs().put(var, returnValue);
|
||||
}
|
||||
}
|
||||
execution.getEngine().processTaskService().history(execution, this);
|
||||
runOutTransition(execution);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传递的执行参数、模型的参数列表返回实际的参数对象数组
|
||||
* @param execArgs 运行时传递的参数数据
|
||||
* @param args 自定义节点需要的参数
|
||||
* @return 调用自定义节点类方法的参数数组
|
||||
*/
|
||||
private Object[] getArgs(Map<String, Object> execArgs, String args) {
|
||||
Object[] objects = null;
|
||||
if(StrUtil.isNotEmpty(args)) {
|
||||
String[] argArray = args.split(",");
|
||||
objects = new Object[argArray.length];
|
||||
for(int i = 0; i < argArray.length; i++) {
|
||||
objects[i] = execArgs.get(argArray[i]);
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.expression.ExpressionUtil;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.engine.DecisionHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 决策模型
|
||||
*
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class DecisionModel extends NodeModel {
|
||||
private String expr; // 决策表达式
|
||||
private String handleClass; // 决策处理类
|
||||
|
||||
@Override
|
||||
public void exec(Execution execution) {
|
||||
// 执行决策节点自定义执行逻辑
|
||||
boolean isFound = false;
|
||||
String nextNodeName = null;
|
||||
if (StrUtil.isNotEmpty(expr)) {
|
||||
Object obj = ExpressionUtil.eval(expr, execution.getArgs());
|
||||
nextNodeName = Convert.toStr(obj, "");
|
||||
} else if (StrUtil.isNotEmpty(handleClass)) {
|
||||
DecisionHandler decisionHandler = ReflectUtil.newInstance(handleClass);
|
||||
nextNodeName = decisionHandler.decide(execution);
|
||||
}
|
||||
for (TransitionModel transitionModel : getOutputs()) {
|
||||
if (StrUtil.isNotEmpty(transitionModel.getExpr()) && Convert.toBool(ExpressionUtil.eval(transitionModel.getExpr(), execution.getArgs()), false)) {
|
||||
// 决策节点输出边存在表达式,则使用输出边的表达式,true则执行
|
||||
isFound = true;
|
||||
transitionModel.setEnabled(true);
|
||||
transitionModel.execute(execution);
|
||||
} else if (transitionModel.getTo().equalsIgnoreCase(nextNodeName)) {
|
||||
// 找到对应的下一个节点
|
||||
isFound = true;
|
||||
transitionModel.setEnabled(true);
|
||||
transitionModel.execute(execution);
|
||||
}
|
||||
}
|
||||
if (!isFound) {
|
||||
// 找不到下一个可执行路线
|
||||
throw new BaseException("decision节点无法确定下一步执行路线");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.impl.EndProcessHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* 结束模型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class EndModel extends NodeModel {
|
||||
@Override
|
||||
public void exec(Execution execution) {
|
||||
// 执行结束节点自定义执行逻辑
|
||||
System.out.println(super.toString());
|
||||
fire(new EndProcessHandler(this), execution);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
|
||||
/**
|
||||
*
|
||||
* 分支模型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
public class ForkModel extends NodeModel {
|
||||
@Override
|
||||
public void exec(Execution execution) {
|
||||
// 执行分支节点自定义执行逻辑
|
||||
runOutTransition(execution);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.impl.MergeBranchHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* 合并模型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class JoinModel extends NodeModel {
|
||||
@Override
|
||||
public void exec(Execution execution) {
|
||||
// 执行合并节点自定义执行逻辑
|
||||
fire(new MergeBranchHandler(this),execution);
|
||||
if(execution.isMerged()) {
|
||||
runOutTransition(execution);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.flow.engine.Action;
|
||||
import com.budwk.app.flow.engine.*;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* 节点模型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true, exclude = {"inputs", "outputs"})
|
||||
@Data
|
||||
public abstract class NodeModel extends BaseModel implements Action {
|
||||
private String layout;// 布局属性(x,y,w,h)
|
||||
// 输入边集合
|
||||
@JsonIgnore
|
||||
private List<TransitionModel> inputs = new ArrayList<TransitionModel>();
|
||||
// 输出边集合
|
||||
@JsonIgnore
|
||||
private List<TransitionModel> outputs = new ArrayList<TransitionModel>();
|
||||
private String preInterceptors; // 节点前置拦截器
|
||||
private String postInterceptors; // 节点后置拦截器
|
||||
|
||||
/**
|
||||
* 由子类自定义执行方法
|
||||
* @param execution
|
||||
*/
|
||||
abstract void exec(Execution execution);
|
||||
@Override
|
||||
public void execute(Execution execution) {
|
||||
// 0.设置当前节点模型
|
||||
execution.setNodeModel(this);
|
||||
// 1. 调用前置拦截器
|
||||
execPreInterceptors(execution);
|
||||
// 2. 调用子类的exec方法
|
||||
exec(execution);
|
||||
// 3. 调用后置拦截器
|
||||
execPostInterceptors(execution);
|
||||
}
|
||||
/**
|
||||
* 执行输出边
|
||||
*/
|
||||
protected void runOutTransition(Execution execution) {
|
||||
outputs.forEach(tr->{
|
||||
tr.setEnabled(true);
|
||||
tr.execute(execution);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行节点前置拦截器
|
||||
* @param execution
|
||||
*/
|
||||
private void execPreInterceptors(Execution execution) {
|
||||
if(StrUtil.isEmpty(preInterceptors)) {
|
||||
preInterceptors = execution.getProcessModel().getPreInterceptors();
|
||||
}
|
||||
execInterceptors(preInterceptors,execution);
|
||||
}
|
||||
/**
|
||||
* 执行节点后置拦截器
|
||||
* @param execution
|
||||
*/
|
||||
private void execPostInterceptors(Execution execution) {
|
||||
if(StrUtil.isEmpty(postInterceptors)) {
|
||||
postInterceptors = execution.getProcessModel().getPostInterceptors();
|
||||
}
|
||||
execInterceptors(postInterceptors,execution);
|
||||
}
|
||||
/**
|
||||
* 执行节点拦截器
|
||||
* @param execution
|
||||
*/
|
||||
private void execInterceptors(String interceptors,Execution execution) {
|
||||
if(StrUtil.isEmpty(interceptors)) return;
|
||||
// 存在多个,英文逗号分割
|
||||
String [] interceptorArr = interceptors.split(",");
|
||||
for (int i = 0; i < interceptorArr.length; i++) {
|
||||
String interceptor = interceptorArr[i];
|
||||
// 反射实例化
|
||||
FlowInterceptor flowInterceptor = ReflectUtil.newInstance(interceptor);
|
||||
if(flowInterceptor!=null){
|
||||
// 调用拦截器方法
|
||||
flowInterceptor.intercept(execution);
|
||||
}
|
||||
}
|
||||
}
|
||||
public <T> List<T> getNextModels(Class<T> clazz) {
|
||||
List<T> models = new ArrayList<T>();
|
||||
// 记录已递归项,防止死循环
|
||||
Map<String,Object> temp = new HashMap();
|
||||
for(TransitionModel tm : this.getOutputs()) {
|
||||
addNextModels(models, tm, clazz, temp);
|
||||
}
|
||||
return models;
|
||||
}
|
||||
|
||||
protected <T> void addNextModels(List<T> models, TransitionModel tm, Class<T> clazz,Map<String,Object> temp) {
|
||||
if(temp.get(tm.getTo())!=null) {
|
||||
return;
|
||||
}
|
||||
if(clazz.isInstance(tm.getTarget())) {
|
||||
models.add((T)tm.getTarget());
|
||||
} else {
|
||||
for(TransitionModel tm2 : tm.getTarget().getOutputs()) {
|
||||
temp.put(tm.getTo(), tm.getTarget());
|
||||
addNextModels(models, tm2, clazz,temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据父节点模型、当前节点模型判断是否可退回。可退回条件:
|
||||
* 1、满足中间无fork、join、subprocess模型
|
||||
* 2、满足父节点模型如果为任务模型时,参与类型为any
|
||||
* @param parent 父节点模型
|
||||
* @return 是否可以退回
|
||||
*/
|
||||
public static boolean canRejected(NodeModel current, NodeModel parent) {
|
||||
boolean result = false;
|
||||
for(TransitionModel tm : current.getInputs()) {
|
||||
NodeModel source = tm.getSource();
|
||||
if(source == parent) {
|
||||
return true;
|
||||
}
|
||||
if(source instanceof ForkModel
|
||||
|| source instanceof JoinModel
|
||||
//|| source instanceof SubProcessModel
|
||||
|| source instanceof StartModel) {
|
||||
continue;
|
||||
}
|
||||
result = result || canRejected(source, parent);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return StrUtil.format("调用模型节点执行方法:model:{},name:{},displayName:{}", this.getClass().getSimpleName(), getName(),getDisplayName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.flow.engine.CandidateHandler;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.entity.Candidate;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程模型
|
||||
*
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProcessModel extends BaseModel {
|
||||
private String type; // 流程定义分类
|
||||
private String icon; // 流程定义图标
|
||||
private String category; // 流程定义分类
|
||||
private String remark; // 流程备注
|
||||
private String description; // 流程说明
|
||||
private String instanceUrl; // 启动实例要填写的表单key
|
||||
private String h5InstanceUrl; // 启动实例要填写的手机端表单key
|
||||
private String instanceViewUrl;
|
||||
private String h5InstanceViewUrl;
|
||||
private String expireTime; // 期待完成时间变量key
|
||||
private String instanceNoClass; // 实例编号生成器实现类
|
||||
private String preInterceptors; // 节点前置拦截器
|
||||
private String postInterceptors; // 节点后置拦截器
|
||||
// 流程定义的所有节点
|
||||
private List<NodeModel> nodes = new ArrayList<NodeModel>();
|
||||
// 流程定义的所有任务节点
|
||||
private List<TaskModel> tasks = new ArrayList<TaskModel>();
|
||||
|
||||
/**
|
||||
* 获取开始节点
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public StartModel getStart() {
|
||||
StartModel startModel = null;
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
NodeModel nodeModel = nodes.get(i);
|
||||
if (nodeModel instanceof StartModel) {
|
||||
startModel = (StartModel) nodeModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return startModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取process定义的指定节点名称的节点模型
|
||||
*
|
||||
* @param nodeName 节点名称
|
||||
* @return
|
||||
*/
|
||||
public NodeModel getNode(String nodeName) {
|
||||
for (NodeModel node : nodes) {
|
||||
if (node.getName().equals(nodeName)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个任务节点模型集合
|
||||
*
|
||||
* @param nodeName
|
||||
* @return
|
||||
*/
|
||||
public List<TaskModel> getNextTaskModels(String nodeName) {
|
||||
List<TaskModel> res = new ArrayList<>();
|
||||
NodeModel nodeModel = getNode(nodeName);
|
||||
if (nodeModel == null) return res;
|
||||
// 获取所有输出边的目标节点
|
||||
List<NodeModel> nextNodeModelList = nodeModel.getOutputs().stream().map(TransitionModel::getTarget).toList();
|
||||
nextNodeModelList.forEach(item -> {
|
||||
if (item instanceof TaskModel) {
|
||||
res.add((TaskModel) item);
|
||||
}
|
||||
});
|
||||
if (res.isEmpty()) {
|
||||
// 如果下一个节点不存在任务节点,递归往下找
|
||||
nextNodeModelList.forEach(item -> {
|
||||
List<TaskModel> taskModelList = getNextTaskModels(item.getName());
|
||||
res.addAll(taskModelList);
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个任务节点的候选人
|
||||
*
|
||||
* @param nodeName
|
||||
* @return
|
||||
*/
|
||||
public List<Candidate> getNextTaskModelCandidates(String nodeName) {
|
||||
List<Candidate> res = new ArrayList<>();
|
||||
List<TaskModel> nextTaskModels = getNextTaskModels(nodeName);
|
||||
nextTaskModels.forEach(item -> {
|
||||
res.addAll(getCandidates(item));
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务模型获取候选人
|
||||
*
|
||||
* @param taskModel
|
||||
* @return
|
||||
*/
|
||||
public List<Candidate> getCandidates(TaskModel taskModel) {
|
||||
List<Candidate> res = new ArrayList<>();
|
||||
// 从上下文中查找候选人处理人
|
||||
List<CandidateHandler> handlerList = ServiceContext.findList(CandidateHandler.class);
|
||||
handlerList.forEach(handler -> {
|
||||
// 通过候选从处理类获取候选人集合
|
||||
List<Candidate> candidateList = handler.handle(taskModel);
|
||||
if (candidateList != null) {
|
||||
res.addAll(candidateList);
|
||||
}
|
||||
});
|
||||
// 通过候选人处理类获取修选人
|
||||
String candidateHandler = taskModel.getCandidateHandler();
|
||||
if (StrUtil.isNotEmpty(candidateHandler)) {
|
||||
CandidateHandler candidateHandlerClass = ReflectUtil.newInstance(candidateHandler);
|
||||
List<Candidate> candidateList = candidateHandlerClass.handle(taskModel);
|
||||
if (candidateList != null) {
|
||||
res.addAll(candidateList);
|
||||
}
|
||||
}
|
||||
// 去重
|
||||
return res.stream().distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的节点类型返回流程定义中所有模型对象
|
||||
*
|
||||
* @param clazz 节点类型
|
||||
* @param <T> 泛型
|
||||
* @return 节点列表
|
||||
*/
|
||||
public <T> List<T> getModels(Class<T> clazz) {
|
||||
List<T> models = new ArrayList<T>();
|
||||
buildModels(models, getStart().getNextModels(clazz), clazz);
|
||||
return models;
|
||||
}
|
||||
|
||||
private <T> void buildModels(List<T> models, List<T> nextModels, Class<T> clazz) {
|
||||
for (T nextModel : nextModels) {
|
||||
if (!models.contains(nextModel)) {
|
||||
models.add(nextModel);
|
||||
buildModels(models, ((NodeModel) nextModel).getNextModels(clazz), clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessPublisher;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* 开始模型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class StartModel extends NodeModel {
|
||||
@Override
|
||||
public void exec(Execution execution) {
|
||||
// 执行开始节点自定义执行逻辑
|
||||
System.out.println(super.toString());
|
||||
// 发布流程实例开始事件
|
||||
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_INSTANCE_START).sourceId(execution.getProcessInstanceId()).build());
|
||||
runOutTransition(execution);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 子流程模型
|
||||
* @author mldong
|
||||
* @date 2023/12/7
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class SubProcessModel extends NodeModel{
|
||||
private String form;
|
||||
private Integer version;
|
||||
@Override
|
||||
void exec(Execution execution) {
|
||||
runOutTransition(execution);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.impl.CountersignHandler;
|
||||
import com.budwk.app.flow.enums.CountersignTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class TaskModel extends NodeModel {
|
||||
private String form; // 表单标识
|
||||
private String assignee; // 参与人
|
||||
private String assignmentHandler; // 参与人处理类
|
||||
private ProcessTaskTypeEnum taskType; // 任务类型(主办/协办)
|
||||
private ProcessTaskPerformTypeEnum performType; // 参与类型(普通参与/会签参与)
|
||||
private String reminderTime; // 提醒时间
|
||||
private String reminderRepeat; // 重复提醒间隔
|
||||
private String expireTime; // 期待任务完成时间变量key
|
||||
private String autoExecute; // 到期是否自动执行Y/N
|
||||
private String callback; // 自动执行回调类
|
||||
private Dict ext = Dict.create(); // 自定义扩展属性
|
||||
// 候选用户标识
|
||||
private String candidateUsers; // ext.getStr("candidateUsers");
|
||||
// 候选用户组标识
|
||||
private String candidateGroups; // ext.getStr("candidateGroups");
|
||||
// 候选用户处理类字符串
|
||||
private String candidateHandler; // ext.getStr("candidateHandler");
|
||||
// 会签类型 PARALLEL表示并行会签,SEQUENTIAL表示串行会签
|
||||
private CountersignTypeEnum countersignType;
|
||||
// 会签完成条件
|
||||
/**
|
||||
* ● 全部完成:为空
|
||||
* ● 按数量通过:#nrOfCompletedInstances==n,这里表示n人完成任务,会签结束。
|
||||
* ● 按比例通过:#nrOfCompletedInstances/nrOfInstances==n,这里表示已完成会签数与总实例数达到一定比例时,会签结束
|
||||
* ● 一票通过:#nrOfCompletedInstances==1,这里表示1人完成任务,会签结束。
|
||||
* ● 一票否决:ONE_VOTE_VETO
|
||||
*/
|
||||
private String countersignCompletionCondition;
|
||||
|
||||
private String h5form;
|
||||
|
||||
@Override
|
||||
public void exec(Execution execution) {
|
||||
// 执行任务节点自定义执行逻辑
|
||||
System.out.println(super.toString());
|
||||
if (ProcessTaskPerformTypeEnum.COUNTERSIGN.equals(performType)) {
|
||||
// 会签任务处理
|
||||
fire(new CountersignHandler(this), execution);
|
||||
if (execution.isMerged()) {
|
||||
runOutTransition(execution);
|
||||
}
|
||||
} else {
|
||||
runOutTransition(execution);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.budwk.app.flow.engine.model;
|
||||
|
||||
import com.budwk.app.flow.engine.Action;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.handlers.impl.CreateTaskHandler;
|
||||
import com.budwk.app.flow.engine.handlers.impl.StartSubProcessHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* 边模型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true, exclude = {"source", "target"})
|
||||
@Data
|
||||
public class TransitionModel extends BaseModel implements Action {
|
||||
private NodeModel source; // 边源节点引用
|
||||
private NodeModel target; // 边目标节点引用
|
||||
private String to; // 目标节点名称
|
||||
private String expr; // 边表达式
|
||||
private String g; // 边点坐标集合(x1,y1;x2,y2,x3,y3……)开始、拐角、结束
|
||||
private boolean enabled; // 是否可执行
|
||||
@Override
|
||||
public void execute(Execution execution) {
|
||||
if(!enabled) return;
|
||||
if(target instanceof TaskModel) {
|
||||
// 创建阻塞任务
|
||||
fire(new CreateTaskHandler((TaskModel) target),execution);
|
||||
} else if(target instanceof SubProcessModel){
|
||||
// 如果为子流程,则启动子流程
|
||||
fire(new StartSubProcessHandler((SubProcessModel) target), execution);
|
||||
} else {
|
||||
target.execute(execution);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.budwk.app.flow.engine.model.logicflow;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
/**
|
||||
*
|
||||
* logicFlow边
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
@Data
|
||||
public class LfEdge implements Serializable {
|
||||
private String id; // 边唯一id
|
||||
private String type; // 边类型
|
||||
private String sourceNodeId; // 源节点id
|
||||
private String targetNodeId; // 目标节点id
|
||||
private Dict properties; // 边属性
|
||||
private Dict text; // 边文本
|
||||
private LfPoint startPoint; // 边开始点坐标
|
||||
private LfPoint endPoint; // 边结束点坐标
|
||||
private List<LfPoint> pointsList; // 边所有点集合
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.budwk.app.flow.engine.model.logicflow;
|
||||
|
||||
import com.budwk.app.flow.engine.model.BaseModel;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* logicFlow模型
|
||||
*
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
@Data
|
||||
public class LfModel extends BaseModel {
|
||||
private String type; // 流程定义分类
|
||||
private String category;
|
||||
private String description; // 流程描述
|
||||
private String expireTime;// 过期时间(常量或变量)
|
||||
private String instanceUrl; // 启动实例的url,前后端分离后,定义为路由名或或路由地址
|
||||
private String h5InstanceUrl;
|
||||
private String instanceViewUrl;
|
||||
private String h5InstanceViewUrl;
|
||||
private String instanceNoClass; // 启动流程时,流程实例的流水号生成类
|
||||
private String preInterceptors; // 节点前置拦截器
|
||||
private String postInterceptors; // 节点后置拦截器
|
||||
private List<LfNode> nodes; // 节点集合
|
||||
private List<LfEdge> edges; // 边集合
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.budwk.app.flow.engine.model.logicflow;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
*
|
||||
* logicFlow节点
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
@Data
|
||||
public class LfNode implements Serializable {
|
||||
private String id; // 节点唯一id
|
||||
private String type; // 节点类型
|
||||
private int x; // 节点中心点x轴坐标
|
||||
private int y; // 节点中心点y轴坐标
|
||||
Dict properties; // 节点属性
|
||||
Dict text; // 节点文本
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.budwk.app.flow.engine.model.logicflow;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
*
|
||||
* logicFlow坐标
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
@Data
|
||||
public class LfPoint implements Serializable {
|
||||
private int x; // x轴坐标
|
||||
private int y; // y轴坐标
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.budwk.app.flow.engine.parser;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.TransitionModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
/**
|
||||
*
|
||||
* 通用属性解析(基本属性和边)
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public abstract class AbstractNodeParser implements NodeParser {
|
||||
// 节点模型对象
|
||||
protected NodeModel nodeModel;
|
||||
@Override
|
||||
public void parse(LfNode lfNode, List<LfEdge> edges) {
|
||||
nodeModel = newModel();
|
||||
// 解析基本信息
|
||||
nodeModel.setName(lfNode.getId());
|
||||
if(ObjectUtil.isNotNull(lfNode.getText())) {
|
||||
nodeModel.setDisplayName(lfNode.getText().getStr(TEXT_VALUE_KEY));
|
||||
}
|
||||
Dict properties = lfNode.getProperties();
|
||||
// 解析布局属性
|
||||
int x = lfNode.getX();
|
||||
int y = lfNode.getY();
|
||||
int w = Convert.toInt(properties.get(WIDTH_KEY),0);
|
||||
int h = Convert.toInt(properties.get(HEIGHT_KEY),0);
|
||||
nodeModel.setLayout(StrUtil.format("{},{},{},{}",x,y,w,h));
|
||||
// 解析拦截器
|
||||
nodeModel.setPreInterceptors(properties.getStr(PRE_INTERCEPTORS_KEY));
|
||||
nodeModel.setPostInterceptors(properties.getStr(POST_INTERCEPTORS_KEY));
|
||||
// 解析输出边
|
||||
List<LfEdge> nodeEdges = getEdgeBySourceNodeId(lfNode.getId(), edges);
|
||||
nodeEdges.forEach(edge->{
|
||||
TransitionModel transitionModel = new TransitionModel();
|
||||
transitionModel.setName(edge.getId());
|
||||
transitionModel.setTo(edge.getTargetNodeId());
|
||||
transitionModel.setSource(nodeModel);
|
||||
transitionModel.setExpr(edge.getProperties().getStr(EXPR_KEY));
|
||||
if(CollectionUtil.isNotEmpty(edge.getPointsList())) {
|
||||
// x1,y1;x2,y2;x3,y3……
|
||||
transitionModel.setG(edge.getPointsList().stream().map(point->{
|
||||
return point.getX()+","+point.getY();
|
||||
}).collect(Collectors.joining(";")));
|
||||
} else {
|
||||
if(ObjectUtil.isNotNull(edge.getStartPoint()) && ObjectUtil.isNotNull(edge.getEndPoint())) {
|
||||
int startPointX = edge.getStartPoint().getX();
|
||||
int startPointY = edge.getStartPoint().getY();
|
||||
int endPointX = edge.getEndPoint().getX();
|
||||
int endPointY = edge.getEndPoint().getY();
|
||||
transitionModel.setG(StrUtil.format("{},{};{},{}", startPointX, startPointY, endPointX, endPointY));
|
||||
}
|
||||
}
|
||||
nodeModel.getOutputs().add(transitionModel);
|
||||
});
|
||||
// 调用子类特定解析方法
|
||||
parseNode(lfNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 子类实现此类完成特定解析
|
||||
* @param lfNode
|
||||
*/
|
||||
public abstract void parseNode(LfNode lfNode);
|
||||
|
||||
/**
|
||||
* 由子类各自创建节点模型对象
|
||||
* @return
|
||||
*/
|
||||
public abstract NodeModel newModel();
|
||||
@Override
|
||||
public NodeModel getModel() {
|
||||
return nodeModel;
|
||||
}
|
||||
/**
|
||||
* 获取节点输入
|
||||
* @param targetNodeId 目标节点id
|
||||
* @param edges
|
||||
* @return
|
||||
*/
|
||||
private List<LfEdge> getEdgeByTargetNodeId(String targetNodeId,List<LfEdge> edges) {
|
||||
return edges.stream().filter(edge->{
|
||||
return edge.getTargetNodeId().equals(targetNodeId);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
/**
|
||||
* 获取节点输出
|
||||
* @param sourceNodeId 源节点id
|
||||
* @param edges
|
||||
* @return
|
||||
*/
|
||||
private List<LfEdge> getEdgeBySourceNodeId(String sourceNodeId,List<LfEdge> edges) {
|
||||
return edges.stream().filter(edge->{
|
||||
return edge.getSourceNodeId().equals(sourceNodeId);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.budwk.app.flow.engine.parser;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.ProcessModel;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.engine.model.TransitionModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfEdge;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ModelParser {
|
||||
private ModelParser() {
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 将json定义文件解析成流程模型对象
|
||||
// * @param bytes
|
||||
// * @return
|
||||
// */
|
||||
// public static ProcessModel parse(byte [] bytes) {
|
||||
// String json = IoUtil.readUtf8(new ByteArrayInputStream(bytes));
|
||||
// LfModel lfModel = JSONUtil.parse(json).toBean(LfModel.class);
|
||||
// ProcessModel processModel = new ProcessModel();
|
||||
// List<LfNode> nodes = lfModel.getNodes();
|
||||
// List<LfEdge> edges = lfModel.getEdges();
|
||||
// if(CollectionUtil.isEmpty(nodes) || CollectionUtil.isEmpty(edges) ) {
|
||||
// return processModel;
|
||||
// }
|
||||
// // 流程定义基本信息
|
||||
// processModel.setName(lfModel.getName());
|
||||
// processModel.setDisplayName(lfModel.getDisplayName());
|
||||
// processModel.setType(lfModel.getType());
|
||||
// processModel.setInstanceUrl(lfModel.getInstanceUrl());
|
||||
// processModel.setInstanceNoClass(lfModel.getInstanceNoClass());
|
||||
// processModel.setPostInterceptors(lfModel.getPostInterceptors());
|
||||
// processModel.setPreInterceptors(lfModel.getPreInterceptors());
|
||||
// // 流程节点信息
|
||||
// nodes.forEach(node->{
|
||||
// String type = node.getType().replace(NodeParser.NODE_NAME_PREFIX,"");
|
||||
// NodeParser nodeParser = ServiceContext.findByName(type,NodeParser.class);
|
||||
// if(nodeParser!=null) {
|
||||
// nodeParser.parse(node, edges);
|
||||
// NodeModel nodeModel = nodeParser.getModel();
|
||||
// processModel.getNodes().add(nodeParser.getModel());
|
||||
// if (nodeModel instanceof TaskModel) {
|
||||
// processModel.getTasks().add((TaskModel) nodeModel);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// // 循环节点模型,构造输入边、输出边的source、target
|
||||
// for(NodeModel node : processModel.getNodes()) {
|
||||
// for(TransitionModel transition : node.getOutputs()) {
|
||||
// String to = transition.getTo();
|
||||
// for(NodeModel node2 : processModel.getNodes()) {
|
||||
// if(to.equalsIgnoreCase(node2.getName())) {
|
||||
// node2.getInputs().add(transition);
|
||||
// transition.setTarget(node2);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return processModel;
|
||||
// }
|
||||
|
||||
public static ProcessModel parse(String json) {
|
||||
LfModel lfModel = JSONUtil.parse(json).toBean(LfModel.class);
|
||||
ProcessModel processModel = new ProcessModel();
|
||||
List<LfNode> nodes = lfModel.getNodes();
|
||||
List<LfEdge> edges = lfModel.getEdges();
|
||||
if (CollectionUtil.isEmpty(nodes) || CollectionUtil.isEmpty(edges)) {
|
||||
return processModel;
|
||||
}
|
||||
// 流程定义基本信息
|
||||
processModel.setName(lfModel.getName());
|
||||
processModel.setDisplayName(lfModel.getDisplayName());
|
||||
processModel.setType(lfModel.getType());
|
||||
processModel.setCategory(lfModel.getCategory());
|
||||
processModel.setDescription(lfModel.getDescription());
|
||||
processModel.setInstanceUrl(lfModel.getInstanceUrl());
|
||||
processModel.setH5InstanceUrl(lfModel.getH5InstanceUrl());
|
||||
processModel.setInstanceViewUrl(lfModel.getInstanceViewUrl());
|
||||
processModel.setH5InstanceViewUrl(lfModel.getH5InstanceViewUrl());
|
||||
processModel.setInstanceNoClass(lfModel.getInstanceNoClass());
|
||||
processModel.setPostInterceptors(lfModel.getPostInterceptors());
|
||||
processModel.setPreInterceptors(lfModel.getPreInterceptors());
|
||||
// 流程节点信息
|
||||
nodes.forEach(node -> {
|
||||
String type = node.getType().replace(NodeParser.NODE_NAME_PREFIX, "");
|
||||
NodeParser nodeParser = ServiceContext.findByName(type, NodeParser.class);
|
||||
if (nodeParser != null) {
|
||||
nodeParser.parse(node, edges);
|
||||
NodeModel nodeModel = nodeParser.getModel();
|
||||
processModel.getNodes().add(nodeParser.getModel());
|
||||
if (nodeModel instanceof TaskModel) {
|
||||
processModel.getTasks().add((TaskModel) nodeModel);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 循环节点模型,构造输入边、输出边的source、target
|
||||
for (NodeModel node : processModel.getNodes()) {
|
||||
for (TransitionModel transition : node.getOutputs()) {
|
||||
String to = transition.getTo();
|
||||
for (NodeModel node2 : processModel.getNodes()) {
|
||||
if (to.equalsIgnoreCase(node2.getName())) {
|
||||
node2.getInputs().add(transition);
|
||||
transition.setTarget(node2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return processModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.budwk.app.flow.engine.parser;
|
||||
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfEdge;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
*
|
||||
* 节点解析接口
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public interface NodeParser {
|
||||
String NODE_NAME_PREFIX="snaker:"; // 节点名称前辍
|
||||
String TEXT_VALUE_KEY = "value"; // 文本值
|
||||
String WIDTH_KEY = "width"; // 节点宽度
|
||||
String HEIGHT_KEY = "height"; // 节点高度
|
||||
String PRE_INTERCEPTORS_KEY = "preInterceptors"; // 前置拦截器
|
||||
String POST_INTERCEPTORS_KEY = "postInterceptors"; // 后置拦截器
|
||||
String EXPR_KEY = "expr"; // 表达式key
|
||||
String HANDLE_CLASS_KEY = "handleClass"; // 表达式处理类
|
||||
String FORM_KEY = "form"; // 表单标识
|
||||
String H5_FORM_KEY = "h5form"; // 表单标识
|
||||
String ASSIGNEE_KEY = "assignee"; // 参与人
|
||||
String ASSIGNMENT_HANDLE_KEY = "assignmentHandler"; // 参与人处理类
|
||||
String TASK_TYPE_KEY = "taskType"; // 任务类型(主办/协办)
|
||||
String PERFORM_TYPE_KEY = "performType"; // 参与类型(普通参与/会签参与)
|
||||
String REMINDER_TIME_KEY = "reminderTime"; // 提醒时间
|
||||
String REMINDER_REPEAT_KEY = "reminderRepeat"; // 重复提醒间隔
|
||||
String EXPIRE_TIME_KEY = "expireTime"; // 期待任务完成时间变量key
|
||||
String AUTH_EXECUTE_KEY = "autoExecute"; // 到期是否自动执行Y/N
|
||||
String CALLBACK_KEY = "callback"; // 自动执行回调类
|
||||
String EXT_FIELD_KEY = "field"; // 自定义扩展属性
|
||||
String EXT_FIELD_CANDIDATE_USERS_KET = "candidateUsers";
|
||||
String EXT_FIELD_CANDIDATE_GROUPS_KEY = "candidateGroups";
|
||||
String EXT_FIELD_CANDIDATE_HANDLER_KEY = "candidateHandler";
|
||||
String EXT_FIELD_COUNTERSIGN_TYPE_KEY = "countersignType"; // 会签类型
|
||||
String EXT_FIELD_COUNTERSIGN_COMPLETION_CONDITION_KEY = "countersignCompletionCondition"; // 会签完成条件
|
||||
String CLASS_KEY = "clazz"; // 类路径
|
||||
String METHOD_NAME_KEY = "methodName"; // 方法名
|
||||
String ARGS_KEY = "args"; // 方法入参
|
||||
String RETURN_VAL_KEY = "val"; // 返回变量名
|
||||
String VERSION_KEY = "version"; // 版本号
|
||||
/**
|
||||
* 节点属性解析方法,由解析类完成解析
|
||||
* @param lfNode LogicFlow节点对象
|
||||
* @param edges 所有边对象
|
||||
*/
|
||||
void parse(LfNode lfNode, List<LfEdge> edges);
|
||||
|
||||
/**
|
||||
* 解析完成后,提供返回NodeModel对象
|
||||
* @return 节点模型
|
||||
*/
|
||||
NodeModel getModel();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.model.CustomModel;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
/**
|
||||
* 自定义节点解析类
|
||||
* @author mldong
|
||||
* @date 2023/12/7
|
||||
*/
|
||||
public class CustomParser extends AbstractNodeParser {
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
CustomModel customModel = (CustomModel) nodeModel;
|
||||
Dict properties = lfNode.getProperties();
|
||||
customModel.setClazz(properties.getStr(CLASS_KEY));
|
||||
customModel.setMethodName(properties.getStr(METHOD_NAME_KEY));
|
||||
customModel.setArgs(properties.getStr(ARGS_KEY));
|
||||
customModel.setVar(properties.get(RETURN_VAL_KEY, FlowConst.CUSTOM_RETURN_VAL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new CustomModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.model.DecisionModel;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* 决策节点解析类
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public class DecisionParser extends AbstractNodeParser {
|
||||
/**
|
||||
* 解析decision节点特有属性
|
||||
* @param lfNode
|
||||
*/
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
DecisionModel decisionModel = (DecisionModel) nodeModel;
|
||||
Dict properties = lfNode.getProperties();
|
||||
decisionModel.setExpr(properties.getStr(EXPR_KEY));
|
||||
decisionModel.setHandleClass(properties.getStr(HANDLE_CLASS_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new DecisionModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
import com.budwk.app.flow.engine.model.EndModel;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* 结束节点解析类
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public class EndParser extends AbstractNodeParser {
|
||||
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new EndModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.model.ForkModel;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* 分支节点解析类
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public class ForkParser extends AbstractNodeParser {
|
||||
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new ForkModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
|
||||
import com.budwk.app.flow.engine.model.JoinModel;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* 合并节点解析器
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public class JoinParser extends AbstractNodeParser {
|
||||
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new JoinModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.StartModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* 开始节点解析类
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public class StartParser extends AbstractNodeParser {
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new StartModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.*;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
import com.budwk.app.flow.enums.CountersignTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum;
|
||||
import com.budwk.app.flow.enums.ProcessTaskTypeEnum;
|
||||
|
||||
/**
|
||||
*
|
||||
* 任务节点解析类
|
||||
* @author mldong
|
||||
* @date 2023/4/26
|
||||
*/
|
||||
public class TaskParser extends AbstractNodeParser {
|
||||
|
||||
/**
|
||||
* 解析task节点特有属性
|
||||
* @param lfNode
|
||||
*/
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
TaskModel taskModel = (TaskModel)nodeModel;
|
||||
Dict properties = lfNode.getProperties();
|
||||
taskModel.setForm(properties.getStr(FORM_KEY));
|
||||
taskModel.setH5form(properties.getStr(H5_FORM_KEY));
|
||||
taskModel.setAssignee(properties.getStr(ASSIGNEE_KEY));
|
||||
taskModel.setAssignmentHandler(properties.getStr(ASSIGNMENT_HANDLE_KEY));
|
||||
taskModel.setTaskType(ProcessTaskTypeEnum.codeOf(properties.get(TASK_TYPE_KEY)));
|
||||
taskModel.setPerformType(ProcessTaskPerformTypeEnum.codeOf(properties.get(PERFORM_TYPE_KEY)));
|
||||
taskModel.setReminderTime(properties.getStr(REMINDER_TIME_KEY));
|
||||
taskModel.setReminderRepeat(properties.getStr(REMINDER_REPEAT_KEY));
|
||||
taskModel.setExpireTime(properties.getStr(EXPIRE_TIME_KEY));
|
||||
taskModel.setAutoExecute(properties.getStr(AUTH_EXECUTE_KEY));
|
||||
taskModel.setCallback(properties.getStr(CALLBACK_KEY));
|
||||
// 解析候选人属性
|
||||
taskModel.setCandidateUsers(properties.getStr(EXT_FIELD_CANDIDATE_USERS_KET));
|
||||
taskModel.setCandidateGroups(properties.getStr(EXT_FIELD_CANDIDATE_GROUPS_KEY));
|
||||
taskModel.setCandidateHandler(properties.getStr(EXT_FIELD_CANDIDATE_HANDLER_KEY));
|
||||
// 解析会签属性
|
||||
taskModel.setCountersignType(CountersignTypeEnum.codeOf(properties.getStr(EXT_FIELD_COUNTERSIGN_TYPE_KEY)));
|
||||
taskModel.setCountersignCompletionCondition(properties.getStr(EXT_FIELD_COUNTERSIGN_COMPLETION_CONDITION_KEY));
|
||||
// 自定义扩展属性
|
||||
Object field = properties.get(EXT_FIELD_KEY);
|
||||
if(field!=null) {
|
||||
Dict ext = Convert.convert(Dict.class, field);
|
||||
taskModel.setExt(ext);
|
||||
// 解析候选人属性
|
||||
taskModel.setCandidateUsers(properties.get(EXT_FIELD_CANDIDATE_USERS_KET,ext.getStr(EXT_FIELD_CANDIDATE_USERS_KET)));
|
||||
taskModel.setCandidateGroups(properties.get(EXT_FIELD_CANDIDATE_GROUPS_KEY,ext.getStr(EXT_FIELD_CANDIDATE_GROUPS_KEY)));
|
||||
taskModel.setCandidateHandler(properties.get(EXT_FIELD_CANDIDATE_HANDLER_KEY,ext.getStr(EXT_FIELD_CANDIDATE_HANDLER_KEY)));
|
||||
// 解析会签属性
|
||||
taskModel.setCountersignType(CountersignTypeEnum.codeOf(properties.get(EXT_FIELD_COUNTERSIGN_TYPE_KEY,ext.getStr(EXT_FIELD_COUNTERSIGN_TYPE_KEY))));
|
||||
taskModel.setCountersignCompletionCondition(properties.get(EXT_FIELD_COUNTERSIGN_COMPLETION_CONDITION_KEY,ext.getStr(EXT_FIELD_COUNTERSIGN_COMPLETION_CONDITION_KEY)));
|
||||
} else {
|
||||
taskModel.setExt(Dict.create());
|
||||
}
|
||||
// 将其他properties添加到ext中
|
||||
properties.forEach((k,v)->{
|
||||
if(!ReflectUtil.hasField(TaskModel.class,k)){
|
||||
taskModel.getExt().set(k,v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new TaskModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.budwk.app.flow.engine.parser.impl;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.model.NodeModel;
|
||||
import com.budwk.app.flow.engine.model.SubProcessModel;
|
||||
import com.budwk.app.flow.engine.model.logicflow.LfNode;
|
||||
import com.budwk.app.flow.engine.parser.AbstractNodeParser;
|
||||
|
||||
|
||||
/**
|
||||
* 子流程解析类
|
||||
* @author mldong
|
||||
* @date 2023/12/7
|
||||
*/
|
||||
public class WfSubProcessParser extends AbstractNodeParser {
|
||||
@Override
|
||||
public void parseNode(LfNode lfNode) {
|
||||
SubProcessModel subProcessModel = (SubProcessModel) nodeModel;
|
||||
Dict properties = lfNode.getProperties();
|
||||
subProcessModel.setForm(properties.getStr(FORM_KEY));
|
||||
subProcessModel.setVersion(properties.getInt(VERSION_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeModel newModel() {
|
||||
return new SubProcessModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.budwk.app.flow.engine.scheduling;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
|
||||
/**
|
||||
* 任务调度接口,增加、删除任务
|
||||
* @author mldong
|
||||
* @date 2023/12/4
|
||||
*/
|
||||
public interface IScheduler {
|
||||
String SOURCE_ID_KEY = "sourceId";
|
||||
String SOURCE_TYPE_KEY = "sourceType";
|
||||
/**
|
||||
* 添加作业到调度器
|
||||
* @param args
|
||||
*/
|
||||
void addJob(String jobId,Dict args);
|
||||
|
||||
/**
|
||||
* 从调度器中删除作业
|
||||
* @param jobId
|
||||
*/
|
||||
void removeJob(String jobId);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.budwk.app.flow.engine.scheduling;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessEventListener;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author mldong
|
||||
* @date 2023/12/5
|
||||
*/
|
||||
@IocBean
|
||||
public class SchedulerProcessEventListener implements ProcessEventListener {
|
||||
@Override
|
||||
public void onEvent(ProcessEvent event) {
|
||||
List<IScheduler> schedulerList = ServiceContext.findList(IScheduler.class);
|
||||
if(CollectionUtil.newArrayList(
|
||||
ProcessEventTypeEnum.PROCESS_INSTANCE_START,
|
||||
ProcessEventTypeEnum.PROCESS_TASK_START
|
||||
).contains(event.getEventType())) {
|
||||
// 流程实例开始事件、流程任务开始事件,添加作业到调度器
|
||||
schedulerList.forEach(scheduler->{
|
||||
scheduler.addJob(event.getEventType().name()+"_"+event.getSourceId(),
|
||||
Dict.of(
|
||||
IScheduler.SOURCE_ID_KEY,event.getSourceId(),
|
||||
IScheduler.SOURCE_TYPE_KEY,event.getEventType().getCode()
|
||||
));
|
||||
});
|
||||
} else if(CollectionUtil.newArrayList(
|
||||
ProcessEventTypeEnum.PROCESS_INSTANCE_END,
|
||||
ProcessEventTypeEnum.PROCESS_TASK_END
|
||||
).contains(event.getEventType())) {
|
||||
// 流程实例结束事件、流程任务结束事件,从调度器移除作业
|
||||
schedulerList.forEach(scheduler->{
|
||||
scheduler.removeJob(event.getEventType().name()+"_"+event.getSourceId());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.budwk.app.flow.engine.util;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.expression.ExpressionUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.ProcessModel;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.views.View_user;
|
||||
import org.dromara.warm.flow.core.exception.FlowException;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* 流程工具
|
||||
*
|
||||
*/
|
||||
public class FlowUtil {
|
||||
private FlowUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加用户信息到变量中
|
||||
*
|
||||
* @param operator
|
||||
* @param args
|
||||
*/
|
||||
public static void addUserInfoToArgs(String operator, Dict args) {
|
||||
Dao dao = ServiceContext.find(Dao.class);
|
||||
View_user user = dao.fetch(View_user.class, Cnd.where(View_user::getId, "=", operator));
|
||||
|
||||
if (user == null) {
|
||||
throw new FlowException("用户不存在:" + operator);
|
||||
}
|
||||
|
||||
args.put(FlowConst.USER_USER_ID, operator);
|
||||
args.put(FlowConst.INITIATOR, operator);
|
||||
args.put(FlowConst.INITIATOR_NAME, user.getUsername());
|
||||
args.put(FlowConst.INITIATOR_ACCOUNT, user.getLoginname());
|
||||
args.put(FlowConst.INITIATOR_UNIT_ID, user.getUnitId());
|
||||
args.put(FlowConst.INITIATOR_UNIT_NAME, user.getUnitName());
|
||||
args.put(FlowConst.INITIATOR_UNIT_UNION_ID, user.getUnionId());
|
||||
args.put(FlowConst.INITIATOR_UNIT_UNION_NAME, user.getUnionName());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 增加自动构造标题
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void addAutoGenTitle(String processDefineDisplayName, Dict args) {
|
||||
// 申请人的xx流程-日期
|
||||
// String format = "{}的{}-{}";
|
||||
// args.put(FlowConst.AUTO_GEN_TITLE, StrUtil.format(format, args.getStr(FlowConst.USER_REAL_NAME), processDefineDisplayName,
|
||||
// DateUtil.format(new Date(), "yyyy-MM-dd HH:mm")));
|
||||
|
||||
args.put(FlowConst.PROCESS_INSTANCE_NAME, args.getStr(FlowConst.INITIATOR_NAME) + "的" + processDefineDisplayName + "申请");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数转字典
|
||||
*
|
||||
* @param variable
|
||||
* @return
|
||||
*/
|
||||
public static Dict variableToDict(String variable) {
|
||||
if (JSONUtil.isTypeJSON(variable)) {
|
||||
return Dict.parse(JSONUtil.parseObj(variable));
|
||||
}
|
||||
return Dict.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数转字典
|
||||
*
|
||||
* @param variable
|
||||
* @return
|
||||
*/
|
||||
public static NutMap variableToNutMap(String variable) {
|
||||
if (JSONUtil.isTypeJSON(variable)) {
|
||||
return NutMap.WRAP(JSONUtil.parseObj(variable));
|
||||
}
|
||||
return NutMap.NEW();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为第一个任务节点
|
||||
*
|
||||
* @param processModel
|
||||
* @param taskName
|
||||
* @return
|
||||
*/
|
||||
public static boolean isFistTaskName(ProcessModel processModel, String taskName) {
|
||||
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
|
||||
processModel.getStart().getOutputs().forEach(nodeModel -> {
|
||||
if (nodeModel.getTo().equalsIgnoreCase(taskName)) {
|
||||
atomicBoolean.set(true);
|
||||
}
|
||||
});
|
||||
return atomicBoolean.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析日期
|
||||
*
|
||||
* @param expireTime
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public static Date processTime(String expireTime, Dict args) {
|
||||
// 如果变量中存在,则使用变量中的时间
|
||||
if (args.containsKey(expireTime)) {
|
||||
Object obj = args.get(expireTime);
|
||||
if (obj instanceof Date) {
|
||||
return Convert.toDate(obj);
|
||||
} else if (obj instanceof Long) {
|
||||
return new Date(Convert.toLong(obj));
|
||||
} else if (obj instanceof String) {
|
||||
return DateUtil.parseDateTime(Convert.toStr(obj));
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(expireTime)) {
|
||||
if (expireTime.contains("s")) {
|
||||
return DateUtil.offsetSecond(new Date(), Convert.toInt(expireTime.substring(0, expireTime.length() - 1)));
|
||||
} else if (expireTime.contains("m")) {
|
||||
return DateUtil.offsetMinute(new Date(), Convert.toInt(expireTime.substring(0, expireTime.length() - 1)));
|
||||
} else if (expireTime.contains("h")) {
|
||||
return DateUtil.offsetHour(new Date(), Convert.toInt(expireTime.substring(0, expireTime.length() - 1)));
|
||||
} else if (expireTime.contains("d")) {
|
||||
return DateUtil.offsetDay(new Date(), Convert.toInt(expireTime.substring(0, expireTime.length() - 1)));
|
||||
}
|
||||
return DateUtil.parseDateTime(expireTime);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* 候选人实体
|
||||
* @author mldong
|
||||
* @date 2023/6/26
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class Candidate {
|
||||
// 用户ID
|
||||
private String userId;
|
||||
// 用户名
|
||||
private String userName;
|
||||
// 扩展属性
|
||||
private Dict ext = Dict.create();
|
||||
|
||||
/**
|
||||
* 重写equals,方便去重
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Candidate) {
|
||||
Candidate tmp = (Candidate) obj;
|
||||
if (ObjectUtil.equals(this.getUserId(),tmp.getUserId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写hashCode,方便去重
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_category")
|
||||
@Comment("流程分类")
|
||||
public class ProcessCategory extends BaseModel {
|
||||
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@PrevInsert(uu32 = true)
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String id;
|
||||
|
||||
@Comment("名称")
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 10)
|
||||
private String name;
|
||||
|
||||
@Comment("图片图标")
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||
private String picIcon;
|
||||
|
||||
@Comment("图标")
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||
private String icon;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Id;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程实例抄送
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-12-05
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_cc_instance")
|
||||
public class ProcessCcInstance extends BaseModel {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment("流程实例ID")
|
||||
@Column
|
||||
private Long processInstanceId;
|
||||
|
||||
@Comment("被抄送人ID")
|
||||
@Column
|
||||
private String actorId;
|
||||
|
||||
@Comment("抄送状态(1:已读;0:未读)")
|
||||
@Column
|
||||
private Integer state;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import cn.hutool.extra.pinyin.PinyinUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程定义
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-09-26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_define")
|
||||
@Comment("流程定义")
|
||||
public class ProcessDefine extends BaseModel {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment("唯一编码")
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@Comment("显示名称")
|
||||
@Column
|
||||
private String displayName;
|
||||
|
||||
@Comment("流程类型")
|
||||
@Column
|
||||
private String type;
|
||||
|
||||
@Comment("流程分类")
|
||||
@Column
|
||||
private String category;
|
||||
|
||||
@Comment("电脑端发起地址")
|
||||
@Column
|
||||
private String instanceUrl;
|
||||
|
||||
@Comment("手机端发起地址")
|
||||
@Column
|
||||
private String h5InstanceUrl;
|
||||
|
||||
@Comment("电脑端发起地址")
|
||||
@Column
|
||||
private String instanceViewUrl;
|
||||
|
||||
@Comment("手机端发起地址")
|
||||
@Column
|
||||
private String h5InstanceViewUrl;
|
||||
|
||||
@Comment("图标")
|
||||
@Column
|
||||
private String icon;
|
||||
|
||||
@Comment("流程是否可用(1可用;0不可用)")
|
||||
@Column
|
||||
private Integer state;
|
||||
|
||||
@Comment("流程模型定义")
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR)
|
||||
private JSONObject content;
|
||||
|
||||
@Comment("版本")
|
||||
@Column
|
||||
private Integer version;
|
||||
|
||||
@Comment("首字母")
|
||||
@Column
|
||||
@ColDefine(type = ColType.CHAR)
|
||||
private Character pinyinName;
|
||||
|
||||
@Comment("流程描述")
|
||||
@Column
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String description;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程设计
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-10-12
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Table("wf_process_design")
|
||||
@Comment("流程设计")
|
||||
public class ProcessDesign extends BaseModel {
|
||||
|
||||
@Comment("主键")
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment("唯一编码")
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@Comment("显示名称")
|
||||
@Column
|
||||
private String displayName;
|
||||
|
||||
@Comment("流程分类")
|
||||
@Column
|
||||
private String type;
|
||||
|
||||
@Comment("流程分类")
|
||||
@Column
|
||||
private String category;
|
||||
|
||||
@Comment("电脑端发起地址")
|
||||
@Column
|
||||
private String instanceUrl;
|
||||
|
||||
@Comment("手机端发起地址")
|
||||
@Column
|
||||
private String h5InstanceUrl;
|
||||
|
||||
@Comment("电脑端发起地址")
|
||||
@Column
|
||||
private String instanceViewUrl;
|
||||
|
||||
@Comment("手机端发起地址")
|
||||
@Column
|
||||
private String h5InstanceViewUrl;
|
||||
|
||||
@Comment("图片图标")
|
||||
@Column
|
||||
private String picIcon;
|
||||
|
||||
@Comment("图标")
|
||||
@Column
|
||||
private String icon;
|
||||
|
||||
@Comment("是否已部署")
|
||||
@Column
|
||||
private Integer isDeployed;
|
||||
|
||||
@Comment("备注")
|
||||
@Column
|
||||
private String remark;
|
||||
|
||||
@Comment("流程描述")
|
||||
@Column
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String description;
|
||||
|
||||
@Comment("流程定义")
|
||||
@Column
|
||||
@ColDefine(type = ColType.MYSQL_JSON)
|
||||
private JSONObject content;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Id;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程设计历史
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-09-26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_design_his")
|
||||
@Comment("流程设计历史")
|
||||
public class ProcessDesignHis extends BaseModel {
|
||||
|
||||
@Comment("主键")
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment("流程设计ID")
|
||||
@Column
|
||||
private Long processDesignId;
|
||||
|
||||
@Comment("流程模型定义")
|
||||
@Column
|
||||
private byte[] content;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Id;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程实例
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-09-26
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_instance")
|
||||
@Comment("流程实例")
|
||||
public class ProcessInstance extends BaseModel {
|
||||
|
||||
@Comment( "主键")
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment( "父流程ID,子流程实例才有值")
|
||||
@Column
|
||||
private Long parentId;
|
||||
|
||||
@Comment( "流程定义ID")
|
||||
@Column
|
||||
private Long processDefineId;
|
||||
|
||||
@Comment( "流程实例状态(10:进行中;20:已完成;30:已撤回;40:强行中止;50:挂起;99:已废弃)")
|
||||
@Column
|
||||
private Integer state;
|
||||
|
||||
@Comment( "父流程依赖的节点名称")
|
||||
@Column
|
||||
private String parentNodeName;
|
||||
|
||||
@Comment( "业务编号")
|
||||
@Column
|
||||
private String businessNo;
|
||||
|
||||
@Comment( "流程发起人")
|
||||
@Column
|
||||
private String operator;
|
||||
|
||||
@Comment( "期望完成时间")
|
||||
@Column
|
||||
private Date expireTime;
|
||||
|
||||
@Comment( "附属变量json存储")
|
||||
@Column
|
||||
private String variable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Id;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程委托代理
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-12-06
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_surrogate")
|
||||
@Comment("流程委托代理")
|
||||
public class ProcessSurrogate extends BaseModel {
|
||||
|
||||
@Comment( "主键")
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment( "流程名称")
|
||||
@Column
|
||||
private String processName;
|
||||
|
||||
@Comment( "授权人")
|
||||
@Column
|
||||
private String operator;
|
||||
|
||||
@Comment( "代理人")
|
||||
@Column
|
||||
private String surrogate;
|
||||
|
||||
@Comment( "授权开始时间")
|
||||
@Column
|
||||
private Date startTime;
|
||||
|
||||
@Comment( "授权结束时间")
|
||||
@Column
|
||||
private Date endTime;
|
||||
|
||||
@Comment( "是否启用")
|
||||
@Column
|
||||
private Integer enabled;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Id;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程任务
|
||||
* </p>
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Table("wf_process_task")
|
||||
@Comment("流程任务")
|
||||
public class ProcessTask extends BaseModel {
|
||||
|
||||
@Comment("主键")
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment("流程实例ID")
|
||||
@Column
|
||||
private Long processInstanceId;
|
||||
|
||||
@Comment("任务名称编码")
|
||||
@Column
|
||||
private String taskName;
|
||||
|
||||
@Comment("任务显示名称")
|
||||
@Column
|
||||
private String displayName;
|
||||
|
||||
@Comment("任务类型(0:主办任务;1:协办任务)")
|
||||
@Column
|
||||
private Integer taskType;
|
||||
|
||||
@Comment("参与类型(0:普通参与;1:会签参与)")
|
||||
@Column
|
||||
private Integer performType;
|
||||
|
||||
@Comment("任务状态(10:进行中;20:已完成;30:已撤回;40:强行中止;50:挂起;99:已废弃)")
|
||||
@Column
|
||||
private Integer taskState;
|
||||
|
||||
@Comment("任务处理人")
|
||||
@Column
|
||||
private String operator;
|
||||
|
||||
@Comment("任务完成时间")
|
||||
@Column
|
||||
private Date finishTime;
|
||||
|
||||
@Comment("任务期待完成时间")
|
||||
@Column
|
||||
private Date expireTime;
|
||||
|
||||
@Comment("任务处理表单KEY")
|
||||
@Column
|
||||
private String formKey;
|
||||
|
||||
@Comment("h5任务处理表单KEY")
|
||||
@Column
|
||||
private String h5FormKey;
|
||||
|
||||
@Comment("父任务ID")
|
||||
@Column
|
||||
private Long taskParentId;
|
||||
|
||||
@Comment("附属变量json存储")
|
||||
@Column
|
||||
private String variable;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.budwk.app.flow.entity;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Id;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程任务和参与人关系
|
||||
* </p>
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Table("wf_process_task_actor")
|
||||
@Comment("流程任务和参与人关系")
|
||||
public class ProcessTaskActor extends BaseModel {
|
||||
|
||||
@Comment("主键")
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Comment("流程任务ID")
|
||||
@Column
|
||||
private Long processTaskId;
|
||||
|
||||
@Comment("参与者ID")
|
||||
@Column
|
||||
private String actorId;
|
||||
|
||||
@Comment("参与者账号")
|
||||
@Column
|
||||
private String actorAccount;
|
||||
|
||||
@Comment("参与者名称")
|
||||
@Column
|
||||
private String actorName;
|
||||
|
||||
@Comment("参与者单位ID")
|
||||
@Column
|
||||
private String actorUnitId;
|
||||
|
||||
@Comment("参与者单位")
|
||||
@Column
|
||||
private String actorUnitName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author mldong
|
||||
* @date 2023/11/24
|
||||
*/
|
||||
//@DictEnum(key = "wf_countersign_type", name = "会签类型")
|
||||
public enum CountersignTypeEnum {
|
||||
PARALLEL(0, "并行会签"),
|
||||
SEQUENTIAL(1, "串行会签"),
|
||||
;
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
public static CountersignTypeEnum codeOf(Object code) {
|
||||
return Arrays.stream(CountersignTypeEnum.class.getEnumConstants()).filter(e ->
|
||||
e.getCode().equals(Convert.toInt(code))
|
||||
|| e.name().equalsIgnoreCase(Convert.toStr(code))
|
||||
|| e.getMessage().equalsIgnoreCase(Convert.toStr(code))
|
||||
).findAny().orElse(CountersignTypeEnum.PARALLEL);
|
||||
}
|
||||
|
||||
CountersignTypeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
/**
|
||||
* @author mldong
|
||||
* @date 2023/12/5
|
||||
*/
|
||||
public enum ProcessEventTypeEnum{
|
||||
PROCESS_INSTANCE_START(1,"流程实例开始事件"),
|
||||
PROCESS_INSTANCE_END(2, "流程实例结束事件"),
|
||||
PROCESS_TASK_START(3, "流程任务开始事件"),
|
||||
PROCESS_TASK_END(4, "流程任务结束事件"),
|
||||
|
||||
PROCESS_TASK_REVOKE(5, "流程任务撤销事件"),
|
||||
|
||||
;
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
|
||||
ProcessEventTypeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
import com.budwk.app.base.annotation.DictEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 流程实例状态(10:进行中;20:已完成;30:已撤回;40:强行终止;50:挂起;99:已废弃)
|
||||
*/
|
||||
@Getter
|
||||
@DictEnum(key = "ProcessInstanceStateEnum", name = "流程实例状态")
|
||||
public enum ProcessInstanceStateEnum {
|
||||
DOING(10, "进行中", "primary"),
|
||||
FINISHED(20, "已完成", "success"),
|
||||
WITHDRAW(30, "已撤回", "danger"),
|
||||
INTERRUPT(40, "强行终止", "danger"),
|
||||
REJECT(45, "已拒绝", "danger"),
|
||||
PENDING(50, "挂起", "danger"),
|
||||
ABANDON(99, "已废弃", "danger");
|
||||
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
private final String type;
|
||||
|
||||
ProcessInstanceStateEnum(Integer code, String message, String type) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 流程提交类型(操作类型)
|
||||
*
|
||||
* @author mldong
|
||||
* @date 2023/10/7
|
||||
*/
|
||||
@Getter
|
||||
public enum ProcessSubmitTypeEnum {
|
||||
APPLY(0, "发起申请"),
|
||||
AGREE(1, "同意"),
|
||||
REJECT(2, "拒绝"),
|
||||
ROLLBACK(3, "退回上一步"),
|
||||
JUMP(4, "跳转"),
|
||||
RE_APPLY(5, "重新提交"),
|
||||
ROLLBACK_TO_OPERATOR(6, "退回发起人"),
|
||||
COUNTERSIGN_DISAGREE(20, "会签不同意");
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
|
||||
ProcessSubmitTypeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
/**
|
||||
*
|
||||
* 任务参与类型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
public enum ProcessTaskPerformTypeEnum {
|
||||
/**
|
||||
* 普通参与:一个或多个人同时参与一个任务,所有人只要其中一人执行完成,就能驱动任务节点往下一步执行
|
||||
*/
|
||||
NORMAL(0,"普通参与"),
|
||||
/**
|
||||
* 会签参与:给每个人都创建任务,满足一定条件时,才能驱动任务节点往下一步执行
|
||||
* 1. 所有人都执行完成
|
||||
* 2. 有一定比率执行完成时
|
||||
* 3. 某特殊人员执行完成时
|
||||
* ……等等
|
||||
*/
|
||||
COUNTERSIGN(1, "会签参与"),
|
||||
;
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
|
||||
ProcessTaskPerformTypeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* code转成枚举
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public static ProcessTaskPerformTypeEnum codeOf(Object code) {
|
||||
if("ALL".equalsIgnoreCase(Convert.toStr(code))
|
||||
|| COUNTERSIGN.toString().toUpperCase().equals(code)
|
||||
|| COUNTERSIGN.toString().toLowerCase().equals(code)
|
||||
|| COUNTERSIGN.code.equals(code)) {
|
||||
return COUNTERSIGN;
|
||||
}
|
||||
return NORMAL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 流程任务状态枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum ProcessTaskStateEnum {
|
||||
|
||||
DOING(10, "进行中"),
|
||||
FINISHED(20, "已完成"),
|
||||
WITHDRAW(30, "已撤回"),
|
||||
INTERRUPT(40, "强行终止"),
|
||||
PENDING(50, "挂起"),
|
||||
TRANSFER(60, "已转办"),
|
||||
ABANDON(99, "已废弃");
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
ProcessTaskStateEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.budwk.app.flow.enums;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
* 任务类型
|
||||
* @author mldong
|
||||
* @date 2023/4/25
|
||||
*/
|
||||
//@DictEnum(key = "wf_process_task_type", name = "流程任务类型")
|
||||
public enum ProcessTaskTypeEnum {
|
||||
MAJOR(0,"主办"),
|
||||
SECONDARY(1, "协办"),
|
||||
RECORD(2,"记录"),
|
||||
;
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
|
||||
ProcessTaskTypeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* code转成枚举
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public static ProcessTaskTypeEnum codeOf(Object code) {
|
||||
return Arrays.stream(ProcessTaskTypeEnum.class.getEnumConstants()).filter(e ->
|
||||
e.getCode().equals(Convert.toInt(code))
|
||||
|| e.name().equalsIgnoreCase(Convert.toStr(code))
|
||||
|| e.getMessage().equalsIgnoreCase(Convert.toStr(code))
|
||||
).findAny().orElse(ProcessTaskTypeEnum.MAJOR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.flow.engine.CandidateHandler;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.entity.Candidate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//@IocBean 此处加了会从容器里面获取 反之反射获取
|
||||
public class FLowDemoCandidateHandler implements CandidateHandler {
|
||||
@Override
|
||||
public List<Candidate> handle(TaskModel model) {
|
||||
Candidate build = Candidate.builder().userId("1").userName("测试1").ext(
|
||||
Dict.of("loginName", "superadmin", "unitName", "工会")
|
||||
).build();
|
||||
return List.of(build);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "候选人测试DEMO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhf
|
||||
* @date 2025/11/22 16:06
|
||||
* @description 指定用户
|
||||
*/
|
||||
public class FlowAssignUserHandler implements AssignmentHandler {
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
String userId = execution.getArgs().getStr("userId");
|
||||
if (Lang.isEmpty(userId)){
|
||||
throw new RuntimeException("流程参数错误!");
|
||||
}
|
||||
return List.of(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "指定下一个流程人(根据args中的userId))";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSON;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.sys.models.Sys_user_role;
|
||||
import com.budwk.app.web.controllers.open.commons.service.CommonService;
|
||||
import com.budwk.app.zhgh.club.model.SysClubManager;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 获取协会审批人根据流程变量参数(clubId) 必传
|
||||
*/
|
||||
public class FlowClubPresidentByArgsAssignmentHandler implements AssignmentHandler {
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
String clubId = execution.getArgs().getStr("clubId");
|
||||
|
||||
if (StrUtil.isBlank(clubId)) {
|
||||
throw new BaseException("参数 clubId 不能为空");
|
||||
}
|
||||
|
||||
CommonService commonService = ServiceContext.find(CommonService.class);
|
||||
List<Sys_user> users = commonService.findUserInfoByRoleCode(Sys_user_role::getClubId, clubId, RoleConstant.CLUB_PRESIDENT);
|
||||
|
||||
if(Lang.isEmpty(users)) {
|
||||
throw new BaseException("未查询到相关审批人");
|
||||
}
|
||||
|
||||
return users.stream().map(Sys_user::getId).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "获取协会会长(根据args中的clubId)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.models.Sys_role;
|
||||
import com.budwk.app.sys.models.Sys_user_role;
|
||||
import com.budwk.app.sys.services.SysRoleService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 获取申请人所在分工会的分工会主席
|
||||
*/
|
||||
public class FlowFghzxAssignmentHandler implements AssignmentHandler {
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
Dao dao = ServiceContext.find(Dao.class);
|
||||
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
|
||||
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.BRANCH_UNION_CHAIRMAN);
|
||||
String unionId = StrUtil.blankToDefault(execution.getArgs().getStr(FlowConst.INITIATOR_UNIT_UNION_ID), SecurityUtil.getUnionId());
|
||||
List<Sys_user_role> user_roles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnionId, "=", unionId));
|
||||
if (Lang.isEmpty(user_roles)) {
|
||||
throw new BaseException("分工会主席没有设置,请联系校工会!", sysRole.getCode());
|
||||
}
|
||||
return user_roles.stream().map(Sys_user_role::getUserId).distinct().toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "当前用户所属分工会主席";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.models.Sys_role;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.sys.models.Sys_user_role;
|
||||
import com.budwk.app.sys.services.SysRoleService;
|
||||
import com.budwk.app.web.controllers.open.commons.service.CommonService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FlowCommonDelegationByArgsAssignmentHandler
|
||||
* @Author JyuHsin
|
||||
* @Date 2025/9/13 11:13
|
||||
* @Version 1.0
|
||||
* @Description TODO
|
||||
*/
|
||||
public class FlowGeneralDelegationByArgsAssignmentHandler implements AssignmentHandler {
|
||||
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
String sessionId = execution.getArgs().getStr("sessionId");
|
||||
String delegationId = execution.getArgs().getStr("delegationId");
|
||||
|
||||
if (StrUtil.isBlank(sessionId)) {
|
||||
throw new BaseException("参数 sessionId 不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(delegationId)) {
|
||||
throw new BaseException("参数 delegationId 不能为空");
|
||||
}
|
||||
|
||||
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
|
||||
Sys_role role = sysRoleService.getByCode(RoleConstant.TEACHER_CONGRESS_DELEGATION_HEAD);
|
||||
|
||||
List<Sys_user_role> roles = ServiceContext.find(Dao.class).query(
|
||||
Sys_user_role.class,
|
||||
Cnd.where(Sys_user_role::getRoleId, "=", role.getId())
|
||||
.and(Sys_user_role::getTcDelegationId, "=", delegationId)
|
||||
.and(Sys_user_role::getTcSessionId, "=", sessionId)
|
||||
);
|
||||
|
||||
if (Lang.isEmpty(roles)) {
|
||||
throw new RuntimeException("您所在的代表团没有设置团长,无法流转到下一步,请联系校工会进行设置。");
|
||||
}
|
||||
return roles.stream().map(Sys_user_role::getUserId).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "获取代表团团长(通用版本,根据args中的sessionId和delegationId)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.models.Sys_role;
|
||||
import com.budwk.app.sys.models.Sys_user_role;
|
||||
import com.budwk.app.sys.services.SysRoleService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @version 1.0
|
||||
* @Author FKY
|
||||
* @name:FlowQualificationReviewGroupByArgsAssignmentHandler
|
||||
* @Date 2025/12/9 10:21
|
||||
* @注释 获取代表资格审查组成员
|
||||
*/
|
||||
public class FlowQualificationReviewGroupByArgsAssignmentHandler implements AssignmentHandler {
|
||||
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
String sessionId = execution.getArgs().getStr("sessionId");
|
||||
|
||||
if (StrUtil.isBlank(sessionId)) {
|
||||
throw new BaseException("参数 sessionId 不能为空");
|
||||
}
|
||||
|
||||
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
|
||||
Sys_role role = sysRoleService.getByCode(RoleConstant.TEACHER_CONGRESS_DELEGATION_QUALIFICATION);
|
||||
|
||||
List<Sys_user_role> roles = ServiceContext.find(Dao.class).query(
|
||||
Sys_user_role.class,
|
||||
Cnd.where(Sys_user_role::getRoleId, "=", role.getId())
|
||||
.and(Sys_user_role::getTcSessionId, "=", sessionId)
|
||||
);
|
||||
|
||||
if (Lang.isEmpty(roles)) {
|
||||
throw new RuntimeException("未获取到当前届次的代表资格审查小组,无法流转到下一步,请联系校工会进行设置。");
|
||||
}
|
||||
return roles.stream().map(Sys_user_role::getUserId).toList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "获取代表资格审查组成员(通用版本,根据args中的sessionId)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @version 1.0
|
||||
* @Author FKY
|
||||
* @name:FlowSchoolUnionAdminByArgsAssignmentHandler
|
||||
* @Date 2025/9/29 17:00
|
||||
* @注释
|
||||
*/
|
||||
public class FlowSchoolUnionAdminByArgsAssignmentHandler implements AssignmentHandler {
|
||||
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
Dao dao = ServiceContext.find(Dao.class);
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
userRole.userId
|
||||
FROM
|
||||
`sys_user_role` userRole
|
||||
LEFT JOIN sys_role role ON role.id = userRole.roleId
|
||||
WHERE
|
||||
role.`code` = @roleCode
|
||||
GROUP BY
|
||||
userRole.userId
|
||||
""").setParam("roleCode", RoleConstant.SCHOOL_UNION_ADMIN.name());
|
||||
sql.setCallback(Sqls.callback.strList());
|
||||
dao.execute(sql);
|
||||
return sql.getList(String.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "获取校工会管理员";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhf
|
||||
* @date 2026/1/12 17:33
|
||||
* @description 校工会副主席
|
||||
*/
|
||||
public class FlowSchoolUnionViceChairmanHandler implements AssignmentHandler {
|
||||
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
Dao dao = ServiceContext.find(Dao.class);
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
userRole.userId
|
||||
FROM
|
||||
`sys_user_role` userRole
|
||||
LEFT JOIN sys_role role ON role.id = userRole.roleId
|
||||
WHERE
|
||||
role.`code` = @roleCode
|
||||
GROUP BY
|
||||
userRole.userId
|
||||
""").setParam("roleCode", RoleConstant.SCHOOL_UNION_VICE_CHAIRMAN.name());
|
||||
sql.setCallback(Sqls.callback.strList());
|
||||
dao.execute(sql);
|
||||
return sql.getList(String.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "获取校工会副主席";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.models.Sys_role;
|
||||
import com.budwk.app.sys.models.Sys_union_group;
|
||||
import com.budwk.app.sys.models.Sys_user_role;
|
||||
import com.budwk.app.sys.services.SysRoleService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.util.cri.Static;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @version 1.0
|
||||
* @Author zzr
|
||||
* @name:FlowUnionGroupLeadersByArgsAssignmentHandler
|
||||
* @Date 2025/9/28 16:35
|
||||
* @注释 获取工会小组长
|
||||
*/
|
||||
public class FlowUnionGroupLeadersByArgsAssignmentHandler implements AssignmentHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
Dao dao = ServiceContext.find(Dao.class);
|
||||
String unitId = StrUtil.blankToDefault(execution.getArgs().getStr(FlowConst.INITIATOR_UNIT_ID), SecurityUtil.getUnitId());
|
||||
String unionId = StrUtil.blankToDefault(execution.getArgs().getStr(FlowConst.INITIATOR_UNIT_UNION_ID), SecurityUtil.getUnionId());
|
||||
Sys_union_group group = dao.fetch(Sys_union_group.class, Cnd.where(Sys_union_group::getUnionId, "=", unionId)
|
||||
.and(new Static(" JSON_CONTAINS(unitIds, '\"%s\"')".formatted(unitId))));
|
||||
return Lang.isNotEmpty(group) ? group.getLeaders() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "当前登录用户的工会小组长";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.budwk.app.flow.handler;
|
||||
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.flow.engine.AssignmentHandler;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.core.ServiceContext;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.sys.models.Sys_role;
|
||||
import com.budwk.app.sys.models.Sys_unit;
|
||||
import com.budwk.app.sys.models.Sys_user_role;
|
||||
import com.budwk.app.sys.services.SysRoleService;
|
||||
import com.budwk.app.sys.views.View_user;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FlowUnitPartySecretaryHandler
|
||||
* @Author JyuHsin
|
||||
* @Date 2025/10/15 10:55
|
||||
* @Version 1.0
|
||||
* @Description TODO
|
||||
*/
|
||||
public class FlowUnitPartySecretaryHandler implements AssignmentHandler {
|
||||
|
||||
@Override
|
||||
public List<String> assign(TaskModel model, Execution execution) {
|
||||
String unitId = SecurityUtil.getUnitId();
|
||||
|
||||
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
|
||||
Sys_role role = sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY);
|
||||
|
||||
View_user user = ServiceContext.find(Dao.class).fetch(View_user.class, Cnd.where(View_user::getId, "=", SecurityUtil.getUserId()));
|
||||
List<Sys_user_role> roles;
|
||||
if(user.getUnionName().contains("行政")){
|
||||
List<Sys_unit> unitList = ServiceContext.find(Dao.class).query(Sys_unit.class, Cnd.where(Sys_unit::getUnionId, "=", user.getUnionId()));
|
||||
List<String> unitIds = unitList.stream().map(Sys_unit::getId).toList();
|
||||
roles = ServiceContext.find(Dao.class).query(
|
||||
Sys_user_role.class,
|
||||
Cnd.where(Sys_user_role::getRoleId, "=", role.getId())
|
||||
.and(Sys_user_role::getUnitId, "in", unitIds)
|
||||
);
|
||||
}else{
|
||||
roles = ServiceContext.find(Dao.class).query(
|
||||
Sys_user_role.class,
|
||||
Cnd.where(Sys_user_role::getRoleId, "=", role.getId())
|
||||
.and(Sys_user_role::getUnitId, "=", unitId)
|
||||
);
|
||||
}
|
||||
|
||||
if (Lang.isEmpty(roles)) {
|
||||
throw new RuntimeException("当前登录用户所在单位未设置单位党委书记,请联系校工会进行设置。");
|
||||
}
|
||||
return roles.stream().map(Sys_user_role::getUserId).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "获取当前登录用户所在单位党委书记,若是行政就找出行政分下的党委书记。因为行政只有一个党委书记";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return AssignmentHandler.super.getOrder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.budwk.app.flow.listenter;
|
||||
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessEventListener;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
/**
|
||||
* 流程实例开始事件监听器
|
||||
*/
|
||||
@IocBean
|
||||
public class ProcessInstanceStartEventListener implements ProcessEventListener {
|
||||
@Override
|
||||
public void onEvent(ProcessEvent event) {
|
||||
// TODO: 可以对接到学校OA的流程实例进行同步
|
||||
if (event.getEventType() == ProcessEventTypeEnum.PROCESS_INSTANCE_START) {
|
||||
Long sourceId = event.getSourceId();
|
||||
System.out.println("流程实例开始事件:" + sourceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.budwk.app.flow.listenter;
|
||||
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessEventListener;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
/**
|
||||
* 流程任务完成事件监听器
|
||||
*/
|
||||
@IocBean
|
||||
public class ProcessTaskEndEventListener implements ProcessEventListener {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Override
|
||||
public void onEvent(ProcessEvent event) {
|
||||
if (event.getEventType() == ProcessEventTypeEnum.PROCESS_TASK_END) {
|
||||
Long sourceId = event.getSourceId();
|
||||
ProcessTask task = dao.fetch(ProcessTask.class, sourceId);
|
||||
System.out.println("流程任务完成事件:" + task.getDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.budwk.app.flow.listenter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessEventListener;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
/**
|
||||
* 流程任务撤销事件监听器
|
||||
*/
|
||||
@IocBean
|
||||
public class ProcessTaskRevokeEventListener implements ProcessEventListener {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Override
|
||||
public void onEvent(ProcessEvent event) {
|
||||
if(event.getEventType()== ProcessEventTypeEnum.PROCESS_TASK_REVOKE){
|
||||
Long taskId = event.getSourceId();
|
||||
ProcessTask task = dao.fetch(ProcessTask.class, taskId);
|
||||
|
||||
String taskDisplayName = task.getDisplayName();
|
||||
|
||||
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
|
||||
|
||||
JSONObject variable = JSONUtil.parseObj(instance.getVariable());
|
||||
String initiatorName = variable.getStr(FlowConst.INITIATOR_NAME);
|
||||
String instanceName = variable.getStr(FlowConst.PROCESS_INSTANCE_NAME);
|
||||
|
||||
|
||||
// 消息API提醒
|
||||
String msgTemplate = "{},您的{}已被撤销,无需审核。";
|
||||
String msg = StrUtil.format(msgTemplate, instanceName, taskDisplayName);
|
||||
|
||||
// 发送 go go go
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.budwk.app.flow.listenter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.event.ProcessEvent;
|
||||
import com.budwk.app.flow.engine.event.ProcessEventListener;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.entity.ProcessTask;
|
||||
import com.budwk.app.flow.entity.ProcessTaskActor;
|
||||
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
|
||||
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程任务开始监听器
|
||||
*/
|
||||
@IocBean
|
||||
@Slf4j
|
||||
public class ProcessTaskStartEventListener implements ProcessEventListener {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private GlobalMessageSendService globalMessageSendService;
|
||||
|
||||
@Override
|
||||
public void onEvent(ProcessEvent event) {
|
||||
if (event.getEventType() == ProcessEventTypeEnum.PROCESS_TASK_START) {
|
||||
Long sourceId = event.getSourceId();
|
||||
ProcessTask task = dao.fetch(ProcessTask.class, sourceId);
|
||||
sendMessage(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void sendMessage(ProcessTask task) {
|
||||
// 说明是流程开始 用户首次完成任务
|
||||
if (task.getTaskParentId() == 0 && task.getTaskName().equals("startTask")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 节点名称
|
||||
String taskDisplayName = task.getDisplayName();
|
||||
// 实例名称
|
||||
Long instanceId = task.getProcessInstanceId();
|
||||
ProcessInstance instance = dao.fetch(ProcessInstance.class, instanceId);
|
||||
String instanceName = Json.fromJson(NutMap.class, instance.getVariable()).getString(FlowConst.PROCESS_INSTANCE_NAME);
|
||||
|
||||
// 任务接收人
|
||||
List<ProcessTaskActor> taskActors = dao.query(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "=", task.getId()));
|
||||
|
||||
for (ProcessTaskActor taskActor : taskActors) {
|
||||
// 模拟发送消息
|
||||
String message = StrUtil.format("您有一条待办任务,实例:{},处理环节:{}", taskActor.getActorName(), instanceName, taskDisplayName);
|
||||
log.info(message);
|
||||
globalMessageSendService.sendMessage(taskDisplayName, message, 2, List.of(taskActor.getActorId()), null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.budwk.app.flow.service;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.budwk.app.base.event.flow.jumptofirsttask.FlowJumpToFirstTaskNodePublisher;
|
||||
import com.budwk.app.flow.constant.FlowConst;
|
||||
import com.budwk.app.flow.engine.FlowEngine;
|
||||
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@IocBean
|
||||
public class FlowCommonService {
|
||||
|
||||
@Inject
|
||||
private FlowEngine flowEngine;
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @param args
|
||||
*/
|
||||
public void executeTask(Dict args){
|
||||
Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
|
||||
String operator = SecurityUtil.getUserId();
|
||||
if (args.containsKey(FlowConst.EXT_ENABLE_OPERATOR)&&args.getBool(FlowConst.EXT_ENABLE_OPERATOR)){
|
||||
operator = FlowConst.ADMIN_ID;
|
||||
}
|
||||
args.remove(FlowConst.PROCESS_TASK_ID_KEY);
|
||||
Integer submitType = args.getInt(FlowConst.SUBMIT_TYPE);
|
||||
if (submitType == null) {
|
||||
submitType = ProcessSubmitTypeEnum.AGREE.getCode();
|
||||
}
|
||||
args.put(FlowConst.SUBMIT_TYPE, submitType);
|
||||
|
||||
// 设置办理人信息到表单参数
|
||||
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "userName", SecurityUtil.getUserUsername());
|
||||
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "loginName", SecurityUtil.getUserLoginname());
|
||||
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitId", SecurityUtil.getUnitId());
|
||||
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", SecurityUtil.getUnitId());
|
||||
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionId", SecurityUtil.getUnionId());
|
||||
|
||||
if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.ROLLBACK.getCode())) {
|
||||
// 退回上一个节点
|
||||
flowEngine.executeAndJumpTask(processTaskId, operator, args, null);
|
||||
} else if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.REJECT.getCode())) {
|
||||
// 拒绝,跳转到结束节点
|
||||
flowEngine.executeAndJumpToEnd(processTaskId, operator, args);
|
||||
} else if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.JUMP.getCode())) {
|
||||
// 跳转到指定节点
|
||||
String taskName = args.getStr(FlowConst.TASK_NAME);
|
||||
flowEngine.executeAndJumpTask(processTaskId, operator, args, taskName);
|
||||
} else if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.ROLLBACK_TO_OPERATOR.getCode())) {
|
||||
// 退回发起人
|
||||
flowEngine.executeAndJumpToFirstTaskNode(processTaskId, operator, args);
|
||||
|
||||
// 退回发起人发送订阅消息,执行相关操作
|
||||
FlowJumpToFirstTaskNodePublisher.broadcast(processTaskId);
|
||||
|
||||
} else if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.COUNTERSIGN_DISAGREE.getCode())) {
|
||||
// 会签不同意,追加不同意标识
|
||||
args.put(FlowConst.COUNTERSIGN_DISAGREE_FLAG, 1);
|
||||
flowEngine.executeProcessTask(processTaskId, operator, args);
|
||||
} else if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.RE_APPLY.getCode())) {
|
||||
// 重新提交
|
||||
flowEngine.executeProcessTask(processTaskId, operator, args);
|
||||
} else {
|
||||
// 默认执行
|
||||
flowEngine.executeProcessTask(processTaskId, operator, args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//package com.budwk.app.flow.service;
|
||||
//
|
||||
//import com.baomidou.mybatisplus.extension.service.IService;
|
||||
//import com.budwk.app.base.service.BaseService;
|
||||
//import com.budwk.app.flow.entity.ProcessCcInstance;
|
||||
//import com.mldong.base.CommonPage;
|
||||
//import com.mldong.modules.wf.dto.ProcessCcInstancePageParam;
|
||||
//import com.mldong.modules.wf.dto.ProcessCcInstanceParam;
|
||||
//import com.mldong.modules.wf.entity.ProcessCcInstance;
|
||||
//import com.mldong.modules.wf.vo.ProcessCcInstanceVO;
|
||||
///**
|
||||
// * <p>
|
||||
// * 流程实例抄送 服务类
|
||||
// * </p>
|
||||
// *
|
||||
// * @author mldong
|
||||
// * @since 2023-12-05
|
||||
// */
|
||||
//public interface ProcessCcInstanceService extends BaseService<ProcessCcInstance> {
|
||||
// /**
|
||||
// * 添加流程实例抄送
|
||||
// * @param param
|
||||
// * @return
|
||||
// */
|
||||
// boolean save(ProcessCcInstanceParam param);
|
||||
//
|
||||
// /**
|
||||
// * 更新流程实例抄送
|
||||
// * @param param
|
||||
// * @return
|
||||
// */
|
||||
// boolean update(ProcessCcInstanceParam param);
|
||||
//
|
||||
// /**
|
||||
// * 自定义分页查询流程实例抄送
|
||||
// * @param param
|
||||
// * @return
|
||||
// */
|
||||
// CommonPage<ProcessCcInstanceVO> page(ProcessCcInstancePageParam param);
|
||||
// /**
|
||||
// * 通过id查询
|
||||
// * @param id
|
||||
// * @return
|
||||
// */
|
||||
// ProcessCcInstanceVO findById(Long id);
|
||||
//}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.budwk.app.flow.service;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.enums.UpAndDownParam;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.flow.engine.model.ProcessModel;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
import com.budwk.app.flow.vo.ProcessDefineVO;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程定义 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-09-26
|
||||
*/
|
||||
public interface ProcessDefineService extends BaseService<ProcessDefine> {
|
||||
/**
|
||||
* 添加流程定义
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean save(ProcessDefine param);
|
||||
|
||||
/**
|
||||
* 更新流程定义
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean update(ProcessDefine param);
|
||||
|
||||
/**
|
||||
* 自定义分页查询流程定义
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
// CommonPage<ProcessDefineVO> page(ProcessDefinePageParam param);
|
||||
/**
|
||||
* 通过id查询
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
ProcessDefineVO findById(Long id);
|
||||
/**
|
||||
* 部署流程定义文件,同name存在多个版本
|
||||
* @param inputStream
|
||||
* @see #deploy(byte[])
|
||||
* @return Long
|
||||
* @see Long deploy(byte[] bytes);
|
||||
*/
|
||||
Long deploy(InputStream inputStream);
|
||||
/**
|
||||
* 部署流程定义文件,同name存在多个版本
|
||||
* @param bytes
|
||||
* @return Long
|
||||
*/
|
||||
Long deploy(byte[] bytes);
|
||||
/**
|
||||
* 部署流程定义文件,同name存在多个版本
|
||||
* @param defineJsonStr
|
||||
* @see #deploy(byte[])
|
||||
* @return Long
|
||||
* @see Long deploy(byte[] bytes);
|
||||
*/
|
||||
Long deploy(String defineJsonStr);
|
||||
/**
|
||||
* 重新部署定义文件,按id更新json
|
||||
* @param processDefineId
|
||||
* @param inputStream
|
||||
* @return Long
|
||||
*/
|
||||
void redeploy(Long processDefineId,InputStream inputStream);
|
||||
/**
|
||||
* 重新部署定义文件,按id更新json
|
||||
* @param processDefineId
|
||||
* @param bytes
|
||||
* @return Long
|
||||
*/
|
||||
void redeploy(Long processDefineId,byte[] bytes);
|
||||
/**
|
||||
* 重新部署定义文件,按id更新json
|
||||
* @param defineJsonStr
|
||||
* @return Long
|
||||
*/
|
||||
void redeploy(Long processDefineId,String defineJsonStr);
|
||||
|
||||
/**
|
||||
* 卸载流程
|
||||
* @param processDefineId
|
||||
*/
|
||||
void unDeploy(Long processDefineId);
|
||||
|
||||
/**
|
||||
* 回恢流程
|
||||
* @param processDefineId
|
||||
*/
|
||||
void reDeploy(Long processDefineId);
|
||||
/**
|
||||
* 只更新type
|
||||
* @param processDefineId
|
||||
* @param type
|
||||
*/
|
||||
void updateType(Long processDefineId, String type);
|
||||
|
||||
/**
|
||||
* 根据ID获取流程定义对象
|
||||
* @param processDefineId
|
||||
* @return
|
||||
*/
|
||||
ProcessDefine getById(Long processDefineId);
|
||||
|
||||
/**
|
||||
* 根据id获取流程模型
|
||||
* @param processDefineId
|
||||
* @return
|
||||
*/
|
||||
ProcessModel getProcessModel(Long processDefineId);
|
||||
|
||||
/**
|
||||
* 流程定义转成流程模型
|
||||
* @param processDefine
|
||||
* @return
|
||||
*/
|
||||
ProcessModel processDefineToModel(ProcessDefine processDefine);
|
||||
|
||||
/**
|
||||
* 根据id获取定义json字符串
|
||||
* @param processDefineId
|
||||
* @return
|
||||
*/
|
||||
String getDefineJsonStr(Long processDefineId);
|
||||
|
||||
/**
|
||||
* 获取流程定义json对象
|
||||
* @param processDefineId
|
||||
* @return
|
||||
*/
|
||||
JSONObject getDefineJsonObject(Long processDefineId);
|
||||
|
||||
/**
|
||||
* 启用/禁用
|
||||
* @param param
|
||||
*/
|
||||
void upAndDown(UpAndDownParam param);
|
||||
/**
|
||||
* 根据名称获取最新的流程定义
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
ProcessDefine getLastByName(String name);
|
||||
/**
|
||||
* 通过流程定义名称和版本获取流程定义
|
||||
* @param name
|
||||
* @param version
|
||||
* @return
|
||||
*/
|
||||
ProcessDefine getProcessDefineByVersion(String name,Integer version);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.budwk.app.flow.service;
|
||||
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.flow.entity.ProcessDesignHis;
|
||||
import com.budwk.app.flow.vo.ProcessDesignHisVO;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程设计历史 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-09-25
|
||||
*/
|
||||
public interface ProcessDesignHisService extends BaseService<ProcessDesignHis> {
|
||||
/**
|
||||
* 添加流程设计历史
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean save(ProcessDesignHis param);
|
||||
|
||||
/**
|
||||
* 更新流程设计历史
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean update(ProcessDesignHis param);
|
||||
|
||||
/**
|
||||
* 自定义分页查询流程设计历史
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
// CommonPage<ProcessDesignHisVO> page(ProcessDesignHisPageParam param);
|
||||
/**
|
||||
* 通过id查询
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
ProcessDesignHisVO findById(Long id);
|
||||
|
||||
/**
|
||||
* 获取最新的流程设计
|
||||
* @param processDesignId
|
||||
* @return
|
||||
*/
|
||||
ProcessDesignHis getLatestByProcessDesignId(Long processDesignId);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.budwk.app.flow.service;
|
||||
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.flow.entity.ProcessDesign;
|
||||
import com.budwk.app.flow.vo.ProcessDesignTypeVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程设计 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author mldong
|
||||
* @since 2023-09-25
|
||||
*/
|
||||
public interface ProcessDesignService extends BaseService<ProcessDesign> {
|
||||
/**
|
||||
* 添加流程设计
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean save(ProcessDesign param);
|
||||
|
||||
/**
|
||||
* 更新流程设计
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean update(ProcessDesign param);
|
||||
|
||||
/**
|
||||
* 更新流程定义信息
|
||||
* @param jsonObject
|
||||
* @return
|
||||
*/
|
||||
boolean updateDefine(JSONObject jsonObject);
|
||||
|
||||
/**
|
||||
* 流程定义部署
|
||||
* @param processDesignId
|
||||
*/
|
||||
void deploy(Long processDesignId);
|
||||
|
||||
/**
|
||||
* 重新部署流程定义文件,覆盖最新版本(即不生成新版本)
|
||||
* @param processDesignId
|
||||
*/
|
||||
void redeploy(Long processDesignId);
|
||||
|
||||
/**
|
||||
* 按流程分类给流程设计分组
|
||||
* @return
|
||||
*/
|
||||
List<ProcessDesignTypeVO> listByType();
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.budwk.app.flow.service;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.base.vo.LabelValueVO;
|
||||
import com.budwk.app.flow.engine.core.Execution;
|
||||
import com.budwk.app.flow.engine.model.TaskModel;
|
||||
import com.budwk.app.flow.entity.ProcessDefine;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import com.budwk.app.flow.vo.HighLightVO;
|
||||
import com.budwk.app.flow.vo.ProcessInstanceVO;
|
||||
import com.budwk.app.flow.vo.ProcessTaskVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 流程实例 服务类
|
||||
* </p>
|
||||
*/
|
||||
public interface ProcessInstanceService extends BaseService<ProcessInstance> {
|
||||
/**
|
||||
* 添加流程实例
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean save(ProcessInstance param);
|
||||
|
||||
/**
|
||||
* 更新流程实例
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean update(ProcessInstance param);
|
||||
|
||||
/**
|
||||
* 自定义分页查询流程实例
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
// CommonPage<ProcessInstanceVO> page(ProcessInstancePageParam param);
|
||||
|
||||
/**
|
||||
* 通过id查询
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
ProcessInstanceVO findById(Long id);
|
||||
|
||||
/**
|
||||
* 将流程实例修改为已完成
|
||||
*
|
||||
* @param processInstanceId
|
||||
*/
|
||||
void finishProcessInstance(Long processInstanceId);
|
||||
|
||||
/**
|
||||
* 将流程实例修改为拒绝
|
||||
*
|
||||
* @param processInstanceId
|
||||
*/
|
||||
void rejectProcessInstance(Long processInstanceId);
|
||||
|
||||
/**
|
||||
* 根据流程、操作人员、父流程实例ID创建流程实例
|
||||
*
|
||||
* @param processDefine 流程定义对象
|
||||
* @param businessKey 业务KEY
|
||||
* @param operator 操作人员ID
|
||||
* @param args 参数列表
|
||||
* @return ProcessInstance 活动流程实例对象
|
||||
*/
|
||||
ProcessInstance createProcessInstance(ProcessDefine processDefine, String businessKey, String operator, Dict args);
|
||||
|
||||
/**
|
||||
* 根据流程、操作人员、父流程实例ID创建流程实例
|
||||
*
|
||||
* @param processDefine 流程定义对象
|
||||
* @param businessKey 业务KEY
|
||||
* @param operator 操作人员ID
|
||||
* @param args 参数列表
|
||||
* @param parentId
|
||||
* @param parentNodeName
|
||||
* @return ProcessInstance 活动流程实例对象
|
||||
*/
|
||||
ProcessInstance createProcessInstance(ProcessDefine processDefine, String businessKey, String operator, Dict args, Long parentId, String parentNodeName);
|
||||
|
||||
/**
|
||||
* 向指定实例id添加全局变量数据
|
||||
*
|
||||
* @param processDefineId 实例id
|
||||
* @param args 变量数据
|
||||
*/
|
||||
void addVariable(Long processDefineId, Dict args);
|
||||
|
||||
/**
|
||||
* 移除指定实例id中的全局变量
|
||||
*
|
||||
* @param processDefineId 实例id
|
||||
* @param keys 移除变量keys
|
||||
*/
|
||||
void removeVariable(Long processDefineId, String... keys);
|
||||
|
||||
|
||||
/**
|
||||
* 保存流程实例
|
||||
*
|
||||
* @param processInstance 流程实例对象
|
||||
*/
|
||||
void saveProcessInstance(ProcessInstance processInstance);
|
||||
|
||||
/**
|
||||
* 流程实例强制终止与唤醒相对应
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param operator 处理人员
|
||||
*/
|
||||
void interrupt(Long processInstanceId, String operator);
|
||||
|
||||
/**
|
||||
* 唤醒历史流程实例与终止相对应
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param operator
|
||||
*/
|
||||
void resume(Long processInstanceId, String operator);
|
||||
|
||||
/**
|
||||
* 挂起流程与激活相对应
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param operator 处理人员
|
||||
*/
|
||||
void pending(Long processInstanceId, String operator);
|
||||
|
||||
/**
|
||||
* 激活流程与挂起相对应
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @param operator
|
||||
*/
|
||||
void activate(Long processInstanceId, String operator);
|
||||
|
||||
/**
|
||||
* 更新流程实例
|
||||
*
|
||||
* @param processInstance 流程实例对象
|
||||
*/
|
||||
void updateProcessInstance(ProcessInstance processInstance);
|
||||
|
||||
/**
|
||||
* 根据ID获取流程实例
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
ProcessInstance getById(Long id);
|
||||
|
||||
/**
|
||||
* 启动且执行流程(自动执行第一个节点)
|
||||
*
|
||||
* @param processDefineId
|
||||
* @param args
|
||||
*/
|
||||
Dict startAndExecute(Long processDefineId, Dict args);
|
||||
|
||||
/**
|
||||
* 获取流程实例高亮数据
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @return
|
||||
*/
|
||||
HighLightVO highLight(Long processInstanceId);
|
||||
|
||||
/**
|
||||
* 审批记录
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @return
|
||||
*/
|
||||
List<ProcessTaskVO> approvalRecord(Long processInstanceId);
|
||||
|
||||
/**
|
||||
* 撤回
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @param operator
|
||||
*/
|
||||
void withdraw(Long processInstanceId, String operator);
|
||||
|
||||
/**
|
||||
* 更新会签变量
|
||||
*
|
||||
* @param taskModel
|
||||
* @param execution
|
||||
* @param taskActors
|
||||
*/
|
||||
void updateCountersignVariable(TaskModel taskModel, Execution execution, List<String> taskActors);
|
||||
|
||||
/**
|
||||
* 创建实例抄送
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @param creator
|
||||
* @param actorIds
|
||||
*/
|
||||
void createCCInstance(Long processInstanceId, String creator, String... actorIds);
|
||||
|
||||
/**
|
||||
* 更新抄送状态
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @param actorId
|
||||
*/
|
||||
void updateCCStatus(Long processInstanceId, String actorId);
|
||||
|
||||
/**
|
||||
* 获取流程参与人回显文本数据
|
||||
*
|
||||
* @param processInstanceId
|
||||
* @return
|
||||
*/
|
||||
List<LabelValueVO> getAssigneeTextData(Long processInstanceId);
|
||||
|
||||
/**
|
||||
* 删除流程实例
|
||||
* @param processInstanceId
|
||||
*/
|
||||
void deleteProcessInstanceById(Long processInstanceId);
|
||||
|
||||
/**
|
||||
* 删除流程实例
|
||||
* @param businessKey
|
||||
*/
|
||||
void deleteProcessInstanceByBusinessKey(String businessKey);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user