This commit is contained in:
那些花儿
2025-07-31 10:36:02 +08:00
parent 73974dbdd3
commit 8118ea9687
21 changed files with 472 additions and 333 deletions
@@ -20,6 +20,7 @@ import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
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;
@@ -137,11 +138,23 @@ public class FlowCommonController {
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)
@@ -209,6 +222,7 @@ public class FlowCommonController {
@At("/executeTask")
@SaCheckLogin
@ApiOperation("执行任务")
@Aop(TransAop.READ_COMMITTED)
public Result executeTask(@Param("data") String param) {
Dict args = Json.fromJson(Dict.class, param);
Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
@@ -43,7 +43,8 @@ public class FlowDefineController {
@Ok("beetl:/platform/flow/define/preview.html")
@SaCheckLogin
public void preview(HttpServletRequest request) {
request.setAttribute("id", request.getParameter("id"));
request.setAttribute("defineId", request.getParameter("defineId"));
request.setAttribute("instanceId", request.getParameter("instanceId"));
}
@@ -125,6 +125,7 @@ public class FlowDesignController {
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
jsonObject.set("icon", processDesign.getIcon());
jsonObject.set("description", processDesign.getDescription());
processDesign.setContent(jsonObject);
dao.insert(processDesign);
return Result.success();
@@ -143,6 +144,7 @@ public class FlowDesignController {
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
jsonObject.set("icon", processDesign.getIcon());
jsonObject.set("description", processDesign.getDescription());
processDesign.setContent(jsonObject);
processDesignService.update(processDesign);
return Result.success();
@@ -161,12 +163,12 @@ public class FlowDesignController {
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
jsonObject.set("icon", processDesign.getIcon());
jsonObject.set("description", processDesign.getDescription());
processDesign.setContent(jsonObject);
processDesignService.update(processDesign);
return Result.success();
}
@At
@SaCheckLogin
@ApiOperation("删除流程设计")
@@ -13,8 +13,8 @@ import java.util.List;
import java.util.stream.Collectors;
/**
*
* 流程模型
*
* @author mldong
* @date 2023/4/25
*/
@@ -24,6 +24,8 @@ 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;
@@ -39,13 +41,14 @@ public class ProcessModel extends BaseModel {
/**
* 获取开始节点
*
* @return
*/
public StartModel getStart() {
StartModel startModel = null;
for (int i = 0; i < nodes.size(); i++) {
NodeModel nodeModel = nodes.get(i);
if(nodeModel instanceof StartModel) {
if (nodeModel instanceof StartModel) {
startModel = (StartModel) nodeModel;
break;
}
@@ -55,12 +58,13 @@ public class ProcessModel extends BaseModel {
/**
* 获取process定义的指定节点名称的节点模型
*
* @param nodeName 节点名称
* @return
*/
public NodeModel getNode(String nodeName) {
for(NodeModel node : nodes) {
if(node.getName().equals(nodeName)) {
for (NodeModel node : nodes) {
if (node.getName().equals(nodeName)) {
return node;
}
}
@@ -69,23 +73,24 @@ public class ProcessModel extends BaseModel {
/**
* 获取下一个任务节点模型集合
*
* @param nodeName
* @return
*/
public List<TaskModel> getNextTaskModels(String nodeName) {
List<TaskModel> res = new ArrayList<>();
NodeModel nodeModel = getNode(nodeName);
if(nodeModel == null) return res;
if (nodeModel == null) return res;
// 获取所有输出边的目标节点
List<NodeModel> nextNodeModelList = nodeModel.getOutputs().stream().map(TransitionModel::getTarget).toList();
nextNodeModelList.forEach(item->{
if(item instanceof TaskModel) {
nextNodeModelList.forEach(item -> {
if (item instanceof TaskModel) {
res.add((TaskModel) item);
}
});
if(res.isEmpty()) {
if (res.isEmpty()) {
// 如果下一个节点不存在任务节点,递归往下找
nextNodeModelList.forEach(item->{
nextNodeModelList.forEach(item -> {
List<TaskModel> taskModelList = getNextTaskModels(item.getName());
res.addAll(taskModelList);
});
@@ -95,19 +100,22 @@ public class ProcessModel extends BaseModel {
/**
* 获取下一个任务节点的候选人
*
* @param nodeName
* @return
*/
public List<Candidate> getNextTaskModelCandidates(String nodeName) {
List<Candidate> res = new ArrayList<>();
List<TaskModel> nextTaskModels = getNextTaskModels(nodeName);
nextTaskModels.forEach(item->{
nextTaskModels.forEach(item -> {
res.addAll(getCandidates(item));
});
return res;
}
/**
* 根据任务模型获取候选人
*
* @param taskModel
* @return
*/
@@ -115,29 +123,31 @@ public class ProcessModel extends BaseModel {
List<Candidate> res = new ArrayList<>();
// 从上下文中查找候选人处理人
List<CandidateHandler> handlerList = ServiceContext.findList(CandidateHandler.class);
handlerList.forEach(handler->{
handlerList.forEach(handler -> {
// 通过候选从处理类获取候选人集合
List<Candidate> candidateList = handler.handle(taskModel);
if(candidateList!=null) {
if (candidateList != null) {
res.addAll(candidateList);
}
});
// 通过候选人处理类获取修选人
String candidateHandler = taskModel.getCandidateHandler();
if(StrUtil.isNotEmpty(candidateHandler)) {
if (StrUtil.isNotEmpty(candidateHandler)) {
CandidateHandler candidateHandlerClass = ReflectUtil.newInstance(candidateHandler);
List<Candidate> candidateList = candidateHandlerClass.handle(taskModel);
if(candidateList!=null) {
if (candidateList != null) {
res.addAll(candidateList);
}
}
// 去重
return res.stream().distinct().collect(Collectors.toList());
}
/**
* 根据指定的节点类型返回流程定义中所有模型对象
*
* @param clazz 节点类型
* @param <T> 泛型
* @param <T> 泛型
* @return 节点列表
*/
public <T> List<T> getModels(Class<T> clazz) {
@@ -147,10 +157,10 @@ public class ProcessModel extends BaseModel {
}
private <T> void buildModels(List<T> models, List<T> nextModels, Class<T> clazz) {
for(T nextModel : nextModels) {
if(!models.contains(nextModel)) {
for (T nextModel : nextModels) {
if (!models.contains(nextModel)) {
models.add(nextModel);
buildModels(models, ((NodeModel)nextModel).getNextModels(clazz), clazz);
buildModels(models, ((NodeModel) nextModel).getNextModels(clazz), clazz);
}
}
}
@@ -15,6 +15,7 @@ import java.util.List;
public class LfModel extends BaseModel {
private String type; // 流程定义分类
private String category;
private String description; // 流程描述
private String expireTime;// 过期时间(常量或变量)
private String instanceUrl; // 启动实例的url,前后端分离后,定义为路由名或或路由地址
private String h5InstanceUrl;
@@ -1,20 +1,21 @@
package com.budwk.app.flow.engine.parser;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
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.*;
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.io.ByteArrayInputStream;
import java.util.List;
public class ModelParser {
private ModelParser(){}
private ModelParser() {
}
// /**
// * 将json定义文件解析成流程模型对象
@@ -71,7 +72,7 @@ public class ModelParser {
ProcessModel processModel = new ProcessModel();
List<LfNode> nodes = lfModel.getNodes();
List<LfEdge> edges = lfModel.getEdges();
if(CollectionUtil.isEmpty(nodes) || CollectionUtil.isEmpty(edges) ) {
if (CollectionUtil.isEmpty(nodes) || CollectionUtil.isEmpty(edges)) {
return processModel;
}
// 流程定义基本信息
@@ -79,6 +80,7 @@ public class ModelParser {
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());
@@ -87,10 +89,10 @@ public class ModelParser {
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) {
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());
@@ -100,11 +102,11 @@ public class ModelParser {
}
});
// 循环节点模型,构造输入边、输出边的source、target
for(NodeModel node : processModel.getNodes()) {
for(TransitionModel transition : node.getOutputs()) {
for (NodeModel node : processModel.getNodes()) {
for (TransitionModel transition : node.getOutputs()) {
String to = transition.getTo();
for(NodeModel node2 : processModel.getNodes()) {
if(to.equalsIgnoreCase(node2.getName())) {
for (NodeModel node2 : processModel.getNodes()) {
if (to.equalsIgnoreCase(node2.getName())) {
node2.getInputs().add(transition);
transition.setTarget(node2);
}
@@ -73,9 +73,14 @@ public class ProcessDefine extends BaseModel {
@Column
private Integer version;
@Comment
@Comment("首字母")
@Column
@ColDefine(type = ColType.CHAR)
private Character pinyinName;
@Comment("流程描述")
@Column
@ColDefine(type = ColType.TEXT)
private String description;
}
@@ -20,8 +20,6 @@ import org.nutz.dao.entity.annotation.*;
@Comment("流程设计")
public class ProcessDesign extends BaseModel {
private static final long serialVersionUID = 1L;
@Comment("主键")
@Id
private Long id;
@@ -70,6 +68,11 @@ public class ProcessDesign extends BaseModel {
@Column
private String remark;
@Comment("流程描述")
@Column
@ColDefine(type = ColType.TEXT)
private String description;
@Comment("流程定义")
@Column
@ColDefine(type = ColType.MYSQL_JSON)
@@ -219,12 +219,6 @@ public interface ProcessInstanceService extends BaseService<ProcessInstance> {
* @param actorId
*/
void updateCCStatus(Long processInstanceId, String actorId);
/**
* 自定义分页查询我的抄送
* @param param
* @return
*/
// CommonPage<ProcessInstanceVO> ccInstancePage(ProcessInstancePageParam param);
/**
* 获取流程参与人回显文本数据
@@ -117,6 +117,7 @@ public class ProcessDefineServiceImpl extends BaseServiceImpl<ProcessDefine> imp
define.setH5InstanceViewUrl(processModel.getH5InstanceViewUrl());
define.setIcon(processModel.getIcon());
define.setPinyinName(PinyinUtil.getFirstLetter(define.getDisplayName().charAt(0)));
define.setDescription(processModel.getDescription());
define.setState(1);
define.setContent(JSONUtil.parseObj(defineJsonStr));
@@ -175,8 +175,18 @@ public class ProcessTaskServiceImpl extends BaseServiceImpl<ProcessTask> impleme
processTask.setTaskType(taskModel.getTaskType().getCode());
processTask.setFormKey(taskModel.getForm());
processTask.setProcessInstanceId(execution.getProcessInstanceId());
execution.getArgs().put(FlowConst.IS_FIRST_TASK_NODE, FlowUtil.isFistTaskName(execution.getProcessModel(), taskModel.getName()));
// 增加是否为第一个任务节点标识
execution.getArgs().put(FlowConst.IS_FIRST_TASK_NODE, FlowUtil.isFistTaskName(execution.getProcessModel(), taskModel.getName()));
//过滤上个任务的表单数据 避免造成污染 每个任务只保存自己的表单数据
Dict cleanArgs = Dict.create();
execution.getArgs().forEach((k, v) -> {
if (!k.startsWith(FlowConst.TASK_FORM_DATA_PREFIX)) {
cleanArgs.set(k, v);
}
});
execution.setArgs(cleanArgs);
processTask.setVariable(JSONUtil.toJsonStr(execution.getArgs()));
processTask.setCreatedAt(now);
processTask.setUpdatedAt(now);
@@ -44,6 +44,9 @@ public class ProcessTaskVO extends ProcessTask {
private List<JSONObject> taskActorList;
// 任务参与者
private List<JSONObject> taskOperatorList;
// @Schema(description = "当前用户是否可执行")
private boolean executable;
// @Schema(description = "节点定义信息")