多叉树+图实现简单业务流程

文章目录

    • 场景
    • 整体架构流程
    • 业务界面
    • 技术细节
    • 小结

场景

     这次遇到一个需求,大致就是任务组织成方案,方案组织成预案,预案可裁剪调整.预案关联事件等级配置,告警触发预案产生事件.然后任务执行是有先后的,也就是有流程概念.

整体架构流程

在这里插入图片描述

     方案管理、预案管理构成任务流程的基础条件,告警信息关联预案配置构成事件,也就是流程启动的入口信息.

业务界面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

技术细节

     其实也没有什么特殊的技术,也就用到了多叉树、图、最长路径计算(深搜)等.

  • 多叉树
import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** @author zwmac*/
@Data
public class MoreParentTreeNode<T> {/*** 节点的唯一标识符*/private Long id;/*** 节点的名称*/private String name;/*** 节点的索引*/private Integer index;/*** 子节点list*/private List<MoreParentTreeNode<T>> children;/*** 父节点list*/private List<MoreParentTreeNode<T>> parents;/*** 扩展信息(一般就存节点对应的原始数据)*/private T extendInfo;public MoreParentTreeNode(Long id, String name,Integer index) {this.id = id;this.name = name;this.index = index;this.children = new ArrayList<>();this.parents = new ArrayList<>();}public MoreParentTreeNode() {}/*** 添加子节点* @param child 子节点*/public void addChild(MoreParentTreeNode<T> child) {if (this.children.contains(child)) {return;}this.children.add(child);child.parents.add(this);}/*** 添加父节点* @param parent 父节点*/public void addParent(MoreParentTreeNode<T> parent) {if (this.parents.contains(parent)) {return;}this.parents.add(parent);parent.children.add(this);}public void traverse() {System.out.println(this.name); // 打印节点名称if (this.children.isEmpty()) {System.out.println("没有子节点");}for (MoreParentTreeNode<T> child : this.children) {child.traverse(); // 递归遍历子节点}}public static void main(String[] args) {MoreParentTreeNode root = new MoreParentTreeNode(1L, "root",1);MoreParentTreeNode node1 = new MoreParentTreeNode(2L, "node1",2);MoreParentTreeNode node2 = new MoreParentTreeNode(3L, "node2",3);MoreParentTreeNode node3 = new MoreParentTreeNode(4L, "node3",4);MoreParentTreeNode node4 = new MoreParentTreeNode(5L, "node4",5);MoreParentTreeNode node5 = new MoreParentTreeNode(5L, "node5",6);MoreParentTreeNode node6 = new MoreParentTreeNode(6L, "node6",7);root.addChild(node1);root.addChild(node2);node1.addChild(node6);node3.addParent(node2);node4.addParent(node2);node4.addParent(node1);node5.addParent(node4);root.traverse();}
}
  • 图对象与计算基本方法
import lombok.Data;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** 流程图对象,用于计算最长执行时间* @author zwmac*/
@Data
public class ProcessGraph {/*** 顶点个数*/private int numVertices;/*** 邻接表*/private List<List<Integer>> adjList;/*** 顶点执行时间(边长)*/private Double[] executionTimes;/*** 构造函数* @param numVertices 顶点个数* @param executionTimes 顶点执行时间(边长)*/public ProcessGraph(int numVertices, Double[] executionTimes) {this.numVertices = numVertices;this.executionTimes = executionTimes;adjList = new ArrayList<>(numVertices);for (int i = 0; i < numVertices; i++) {adjList.add(new ArrayList<>());}}/*** 添加边* @param src 源顶点* @param dest 目标顶点*/public void addEdge(int src, int dest) {adjList.get(src).add(dest);}/*** 计算最长执行时间* @return 最长执行时间*/public Double findMaxExecutionTime() {Double[] maxExecutionTimes = new Double[numVertices];Arrays.fill(maxExecutionTimes, -1d);Double maxExecutionTime = 0d;for (int i = 0; i < numVertices; i++) {Double currentExecutionTime = dfs(i, maxExecutionTimes);if (currentExecutionTime > maxExecutionTime) {maxExecutionTime = currentExecutionTime;}}return maxExecutionTime;}/*** 深度优先搜索* @param vertex 顶点* @param maxExecutionTimes 最长执行时间数组* @return 最长执行时间*/private Double dfs(int vertex, Double[] maxExecutionTimes) {if (maxExecutionTimes[vertex] != -1) {return maxExecutionTimes[vertex];}Double maxExecutionTime = executionTimes[vertex];for (int neighbor : adjList.get(vertex)) {Double neighborExecutionTime = dfs(neighbor, maxExecutionTimes);if (neighborExecutionTime + executionTimes[vertex] > maxExecutionTime) {maxExecutionTime = neighborExecutionTime + executionTimes[vertex];}}maxExecutionTimes[vertex] = maxExecutionTime;return maxExecutionTime;}public static void main(String[] args) {Double[] executionTimes = {0d,3d, 4d, 5d, 2d, 3d,0d};ProcessGraph graph = new ProcessGraph(7, executionTimes);graph.addEdge(0, 1);graph.addEdge(0, 2);graph.addEdge(1, 3);graph.addEdge(1, 4);graph.addEdge(2, 3);graph.addEdge(3, 5);graph.addEdge(3, 5);//graph.addEdge(3, 5);Double maxExecutionTime = graph.findMaxExecutionTime();System.out.println("Max execution time: " + maxExecutionTime);}
}
  • 多叉树工具类
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.smartPark.business.emergency.event.entity.EmergencyEventTask;
import com.smartPark.business.emergency.event.entity.dto.EmergencyEventTaskDTO;
import org.apache.commons.lang3.StringUtils;import java.util.*;/*** @author zwmac*/
public class EventTaskTreeUtil {/*** 转换流程树* @param eventTaskList 任务列表* @return 流程树根节点*/public static MoreParentTreeNode<?> convertTreeDto(List<EmergencyEventTaskDTO> eventTaskList) {MoreParentTreeNode<?> root = new MoreParentTreeNode<>(0L,"开始",0);EmergencyEventTaskDTO rootEventTaskDto = new EmergencyEventTaskDTO();rootEventTaskDto.setTaskDuration(0d);Map<Long, MoreParentTreeNode<?>> nodeMap = new HashMap<>();for (int i = 0;i < eventTaskList.size();i++) {EmergencyEventTaskDTO eventTask = eventTaskList.get(i);Long id = eventTask.getId();String name = eventTask.getTaskName();MoreParentTreeNode<EmergencyEventTaskDTO> node = new MoreParentTreeNode<>(id,name,(i+1));node.setExtendInfo(eventTask);nodeMap.put(eventTask.getId(), node);}//处理父子关系(正)for (EmergencyEventTaskDTO eventTask : eventTaskList) {transNodeRef(eventTask, nodeMap, root);}//处理父子关系(反)for (int i = eventTaskList.size() - 1;i > -1; i--){EmergencyEventTaskDTO eventTask = eventTaskList.get(i);transNodeRef(eventTask, nodeMap, root);}return root;}/*** 转换流程树* @param eventTaskList 任务列表* @return 流程树根节点*/public static MoreParentTreeNode<?> convertTree(List<EmergencyEventTask> eventTaskList) {MoreParentTreeNode<?> root = new MoreParentTreeNode<>(0L,"开始",0);EmergencyEventTask rootEventTask = new EmergencyEventTask();rootEventTask.setTaskDuration(0d);Map<Long, MoreParentTreeNode<?>> nodeMap = new HashMap<>();for (int i = 0;i < eventTaskList.size();i++) {EmergencyEventTask eventTask = eventTaskList.get(i);Long id = eventTask.getId();String name = eventTask.getTaskName();MoreParentTreeNode<EmergencyEventTask> node = new MoreParentTreeNode<>(id,name,(i+1));node.setExtendInfo(eventTask);nodeMap.put(eventTask.getId(), node);}//处理父子关系(正)for (EmergencyEventTask eventTask : eventTaskList) {EmergencyEventTaskDTO eventTaskDTO = new EmergencyEventTaskDTO();BeanUtil.copyProperties(eventTask, eventTaskDTO);transNodeRef(eventTaskDTO, nodeMap, root);}//处理父子关系(反)for (int i = eventTaskList.size() - 1;i > -1; i--){EmergencyEventTask eventTask = eventTaskList.get(i);EmergencyEventTaskDTO eventTaskDTO = new EmergencyEventTaskDTO();BeanUtil.copyProperties(eventTask,eventTaskDTO);transNodeRef(eventTaskDTO, nodeMap, root);}return root;}/*** 转换节点关系* @param eventTask 父级任务* @param nodeMap 节点map* @param root 根节点*/private static void transNodeRef(EmergencyEventTaskDTO eventTask, Map<Long, MoreParentTreeNode<?>> nodeMap, MoreParentTreeNode<?> root) {Long id = eventTask.getId();MoreParentTreeNode<?> node = nodeMap.get(id);if (node != null){EmergencyEventTaskDTO eventTaskDTO = new EmergencyEventTaskDTO();Object eventTaskObj = node.getExtendInfo();if(eventTaskObj instanceof EmergencyEventTask) {EmergencyEventTask nodeEventTask = (EmergencyEventTask) eventTaskObj;BeanUtil.copyProperties(nodeEventTask, eventTaskDTO);}String preIds = eventTaskDTO.getBeforeTaskIds();if (StringUtils.isEmpty(preIds)){node.addParent(root);root.addChild(node);}else {String[] preIdsArr = preIds.split(",");if (ArrayUtil.isNotEmpty(preIdsArr)){for (String preId : preIdsArr) {MoreParentTreeNode<?> preNode = nodeMap.get(Long.valueOf(preId));//前置任务不为空if (preNode != null){preNode.addChild(node);}}}}nodeMap.put(id, node);}}/*** 获取节点的子节点列表* @param treeNode 节点* @param eventTaskId 事件任务id* @return 子节点列表*/public static List<? extends MoreParentTreeNode<?>> getNodeChildrenList(MoreParentTreeNode<?> treeNode, Long eventTaskId) {List<? extends MoreParentTreeNode<?>> childrenList = itGetNodeChildrenList(treeNode, eventTaskId);if (CollectionUtil.isNotEmpty(childrenList)) {return childrenList;}childrenList = itGetNodeParentList(treeNode, eventTaskId);return childrenList;}/*** 迭代获取节点的父节点列表* @param moreParentTreeNode 父节点* @param eventTaskId 事件任务id* @return 父节点列表*/private static List<? extends MoreParentTreeNode<?>> itGetNodeParentList(MoreParentTreeNode<?> moreParentTreeNode, Long eventTaskId) {List<? extends MoreParentTreeNode<?>> children = null;if (moreParentTreeNode.getId().equals(eventTaskId)){children = moreParentTreeNode.getChildren();}else{List<? extends MoreParentTreeNode<?>> parentTreeNodes = moreParentTreeNode.getParents();if(CollectionUtil.isNotEmpty(parentTreeNodes)) {for (MoreParentTreeNode<?> parentTreeNode : parentTreeNodes) {children = itGetNodeParentList(parentTreeNode, eventTaskId);if (CollectionUtil.isNotEmpty(children)) {break;}}}}return children;}/*** 迭代获取节点的子节点列表* @param moreParentTreeNode 子节点* @param eventTaskId 事件任务id* @return 子节点列表*/private static List<? extends MoreParentTreeNode<?>> itGetNodeChildrenList(MoreParentTreeNode<?> moreParentTreeNode, Long eventTaskId) {List<? extends MoreParentTreeNode<?>> children = null;if (moreParentTreeNode.getId().equals(eventTaskId)){children = moreParentTreeNode.getChildren();}else{List<? extends MoreParentTreeNode<?>> childrenChild = moreParentTreeNode.getChildren();if(CollectionUtil.isNotEmpty(childrenChild)) {for (MoreParentTreeNode<?> parentTreeNode : childrenChild) {children = itGetNodeChildrenList(parentTreeNode, eventTaskId);if (CollectionUtil.isNotEmpty(children)) {break;}}}}return children;}public static void main(String[] args) {List<EmergencyEventTaskDTO> eventTaskList = new ArrayList<>();initTestData(eventTaskList,9);System.out.println(JSONUtil.toJsonStr(eventTaskList));MoreParentTreeNode<?> treeNode = convertTreeDto(eventTaskList);List<?> childrenList = getNodeChildrenList(treeNode, 3L);treeNode.traverse();}private static void initTestData(List<EmergencyEventTaskDTO> eventTaskList, int num) {for (int i = 0;i < num; i++){EmergencyEventTaskDTO eventTask = new EmergencyEventTaskDTO();eventTask.setId(Long.valueOf(i));eventTask.setTaskName("任务-" + (i + 1));eventTask.setTaskDuration(RandomUtil.randomDouble(1,10));if (i > 1){StringJoiner sj = new StringJoiner(",");int n = RandomUtil.randomInt(0,i);for(int j = 0;j < i - 1 - n;j++){sj.add(String.valueOf(j+1));}eventTask.setBeforeTaskIds(sj.toString());}eventTaskList.add(eventTask);}}}

     使用思路也特别简单,实际就是将配置任务时只选择了节点上级任务的所有任务构成一个多叉树,然后跟这个树每个节点设置唯一编号,然后转化为图,计算最长路径作为事件的预计完成时间,最后就是各个任务节点的流转了.

/*** 计算预计完成时间* @param event 事件* @return 预计完成时间*/private Date calPlanEndTime(EmergencyEvent event) {Date planStartTime = event.getPlanStartTime();Date finalNow = planStartTime;//先查询预案任务QueryWrapper<EmergencyEventPlan> eventPlanQw = new QueryWrapper<>();eventPlanQw.eq("event_id_", event.getId());List<EmergencyEventPlan> eventPlanList = eventPlanService.list(eventPlanQw);if (CollectionUtil.isNotEmpty(eventPlanList)){//最终预计完成时间//再查询预案任务for (int i = 0;i < eventPlanList.size();i++){QueryWrapper<EmergencyEventTask> eventTaskQw = new QueryWrapper<>();eventTaskQw.eq("event_id_", event.getId());eventTaskQw.eq("event_plan_id_", eventPlanList.get(i).getId());List<EmergencyEventTask> eventTaskList = eventPlanTaskService.list(eventTaskQw);if (CollectionUtil.isNotEmpty(eventTaskList)){//再组织流程树MoreParentTreeNode<?> processTree = EventTaskTreeUtil.convertTree(eventTaskList);//最后计算时间Date planTaskEndTime = calEndTime(processTree, planStartTime, eventTaskList);if (planTaskEndTime.after(finalNow)){finalNow = planTaskEndTime;}}}}return finalNow;}

小结

  • 后悔数据结构没有学好
  • 算法用的少,也有很大工具包直接提供,但是还是很有学的必要的
         就随便写写,希望能帮到大家,uping!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/90974.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx 多层代理 + k8s ingress 后端服务获取客户真实ip 配置

1.nginx http 七层代理 修改命令空间&#xff1a; namespace: nginx-ingress : configmap&#xff1a;nginx-configuration kubectl get cm nginx-configuration -n ingress-nginx -o yaml添加如上配置 compute-full-forwarded-for: “true” forwarded-for-header: X-Forwa…

.balckhoues-V-XXXXXXX勒索病毒数据怎么处理|数据解密恢复

引言&#xff1a; 随着网络犯罪的不断演进&#xff0c;勒索病毒已成为当前数字时代的威胁之一&#xff0c;其中包括.balckhoues-V-XXXXXXX勒索病毒。本文将深入介绍.balckhoues-V-XXXXXXX勒索病毒的特点、数据恢复方法以及预防措施&#xff0c;以帮助您更好地理解和应对这一威…

NPDP产品经理认证怎么报名?考试难度大吗?

PMDA&#xff08;Product Development and Management Association&#xff09;是美国产品开发与管理协会&#xff0c;在中国由中国人才交流基金会培训中心举办NPDP&#xff08;New Product Development Professional&#xff09;考试&#xff0c;该考试是产品经理国际资格认证…

Spring实例化源码解析之ComponentScanAnnotationParser(四)

上一章我们分析了ConfigurationClassParser&#xff0c;配置类的解析源码分析。在ComponentScans和ComponentScan注解修饰的候选配置类的解析过程中&#xff0c;我们需要深入的了解一下ComponentScanAnnotationParser的parse执行流程&#xff0c;SpringBoot启动类为什么这么写&…

WinPcap4.1.3安装失败解决方法,A newer version of winpcap...

WinPcap4.1.3安装失败解决方法&#xff0c;A newer version of winpcap… 如图所示&#xff0c;提示本地安装有更高版本的WinPcap时&#xff0c;按如下操作即可解决。 找到相应文件&#xff0c;扩展名修改成如下&#xff1a; C:\Windows\SysWOW64 的wpcap.dll改成 wpcap.dll.…

【Verilog 教程】6.5 Verilog避免Latch

关键词&#xff1a;触发器&#xff0c;锁存器 Latch 的含义 锁存器&#xff08;Latch&#xff09;&#xff0c;是电平触发的存储单元&#xff0c;数据存储的动作取决于输入时钟&#xff08;或者使能&#xff09;信号的电平值。仅当锁存器处于使能状态时&#xff0c;输出才会随着…

使用 Velocity 模板引擎的 Spring Boot 应用

使用 Velocity 模板引擎的 Spring Boot 应用 模板引擎是构建动态内容的重要工具&#xff0c;特别适用于生成HTML、邮件内容、报告和其他文本文档。Velocity是一个强大的模板引擎&#xff0c;它具有简单易用的语法和灵活性。本文将介绍如何在Spring Boot应用中使用Velocity模板…

C语言入门Day_27 开发环境

前言&#xff1a; 在线编译环境涉及到联网&#xff0c;如果在没有网的情况下&#xff0c;我们就不能写代码了&#xff0c;这一章节&#xff0c;我们将会给大家介绍一下如何搭建一个本地的C语言编译环境。 如果想要设置 C 语言环境&#xff0c;需要确保电脑上有以下两款可用的…

二十二,加上各种贴图

使用pbr的各种贴图&#xff0c;albedo,金属度&#xff0c;ao,法线&#xff0c;粗糙度&#xff0c;可以更好的控制各个片元 1&#xff0c;先加上纹理坐标 texCoords->push_back(osg::Vec2(xSegment, ySegment)); geom->setVertexAttribArray(3, texCoords, osg::Array::BI…

凹凸贴图如何提高物体的真实感

什么是凹凸贴图 凹凸贴图&#xff08;Bump Mapping&#xff09;是一种计算机图形学中的技术&#xff0c;用于在表面上模拟微小的凹凸形状&#xff0c;从而增加了物体的细节和真实感。它可以在不改变物体几何形状的情况下&#xff0c;通过修改光照的反应&#xff0c;使表面看起来…

Java编程技巧:文件上传、下载、预览

目录 1、上传文件1.1、代码1.2、postman测试截图 2、下载resources目录中的模板文件2.1、项目结构2.2、代码2.3、使用场景 3、预览文件3.1、项目结构3.2、代码3.3、使用场景 1、上传文件 1.1、代码 PostMapping("/uploadFile") public String uploadFile(Multipart…

idea Springboot 高校科研资源共享系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 高校科研资源共享系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c; 系统具有完整的源代码和数据…

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型 第八章 驱动设计的思想&#xff1a;面向对象/分层/分离8.1 面向对象8.2 分层8.3 分离8.4 写示例代码8.5 课后作业 第九章 驱动进化之路&#xff1a;总线设备驱动模型9.1 驱动编写的 3种方法9.1.1 传统写法9.1.2 总线设备…

BUUCTF reverse wp 21 - 30

[ACTF新生赛2020]rome 无壳, 直接拖进IDA32 y键把v2改成char[49], n键重命名为iuput int func() {int result; // eaxint v1[4]; // [esp14h] [ebp-44h]char input[49]; // [esp24h] [ebp-34h] BYREFstrcpy(&input[23], "Qsw3sj_lz4_Ujwl");printf("Please…

《Upload-Labs》01. Pass 1~13

Upload-Labs 索引前言Pass-01题解 Pass-02题解总结 Pass-03题解总结 Pass-04题解 Pass-05题解总结 Pass-06题解总结 Pass-07题解总结 Pass-08题解总结 Pass-09题解 Pass-10题解 Pass-11题解 Pass-12题解总结 Pass-13题解 靶场部署在 VMware - Win7。 靶场地址&#xff1a;https…

基于Java的旅游管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

FreeRTOS入门教程(任务优先级,Tick)

文章目录 前言一、什么是任务优先级二、FreeRTOS如何分辨出优先级最高可运行的任务三、FreeRTOS中的时钟节拍Tick四、什么是时间片五、相同优先级任务怎么进行切换六、任务优先级实验七、修改任务优先级总结 前言 本篇文章将带大家学习FreeRTOS中的任务优先级&#xff0c;并且…

GPT-4科研实践:数据可视化、统计分析、编程、机器学习数据挖掘、数据预处理、代码优化、科研方法论

查看原文>>>GPT4科研实践技术与AI绘图 GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。例如在科研编程、绘图领域&#xff1a;1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言&#x…

【SSL】用Certbot生成免费HTTPS证书

1. 实验背景 服务器&#xff1a;CentOS7.x 示例域名&#xff1a; www.example.com 域名对应的web站点目录&#xff1a; /usr/local/openresty/nginx/html 2. 安装docker # yum -y install yum-utils# yum-config-manager --add-repo https://download.docker.com/linux/ce…

web:[极客大挑战 2019]LoveSQL

题目 打开页面显示如下 查看源代码&#xff0c;查到一个check.php&#xff0c;还是get传参 尝试账号密码输入 题目名为sql&#xff0c;用万能密码 1or 11# 或 admin or 11 给了一段乱码&#xff0c;也不是flag 查看字段数 /check.php?usernameadmin order by 3%23&pass…