001/*
002 *    Copyright 2024-2025, Warm-Flow (290631660@qq.com).
003 *
004 *    Licensed under the Apache License, Version 2.0 (the "License");
005 *    you may not use this file except in compliance with the License.
006 *    You may obtain a copy of the License at
007 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
010 *    Unless required by applicable law or agreed to in writing, software
011 *    distributed under the License is distributed on an "AS IS" BASIS,
012 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *    See the License for the specific language governing permissions and
014 *    limitations under the License.
015 */
016package org.dromara.warm.flow.core.service.impl;
017
018import org.dom4j.Document;
019import org.dromara.warm.flow.core.FlowFactory;
020import org.dromara.warm.flow.core.chart.*;
021import org.dromara.warm.flow.core.constant.ExceptionCons;
022import org.dromara.warm.flow.core.dao.FlowDefinitionDao;
023import org.dromara.warm.flow.core.dto.FlowCombine;
024import org.dromara.warm.flow.core.entity.*;
025import org.dromara.warm.flow.core.enums.ActivityStatus;
026import org.dromara.warm.flow.core.enums.NodeType;
027import org.dromara.warm.flow.core.enums.PublishStatus;
028import org.dromara.warm.flow.core.enums.SkipType;
029import org.dromara.warm.flow.core.exception.FlowException;
030import org.dromara.warm.flow.core.orm.service.impl.WarmServiceImpl;
031import org.dromara.warm.flow.core.service.DefService;
032import org.dromara.warm.flow.core.utils.Base64;
033import org.dromara.warm.flow.core.utils.*;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import javax.imageio.ImageIO;
038import java.awt.*;
039import java.awt.image.BufferedImage;
040import java.io.ByteArrayInputStream;
041import java.io.ByteArrayOutputStream;
042import java.io.IOException;
043import java.io.InputStream;
044import java.nio.charset.StandardCharsets;
045import java.util.List;
046import java.util.*;
047import java.util.stream.Collectors;
048
049/**
050 * 流程定义Service业务层处理
051 *
052 * @author warm
053 * @since 2023-03-29
054 */
055public class DefServiceImpl extends WarmServiceImpl<FlowDefinitionDao<Definition>, Definition> implements DefService {
056
057    private static final Logger log = LoggerFactory.getLogger(DefServiceImpl.class);
058
059    @Override
060    public DefService setDao(FlowDefinitionDao<Definition> warmDao) {
061        this.warmDao = warmDao;
062        return this;
063    }
064
065    @Override
066    public Definition importXml(InputStream is) throws Exception {
067        if (ObjectUtil.isNull(is)) {
068            return null;
069        }
070        FlowCombine combine = FlowConfigUtil.readConfig(is);
071        // 流程定义
072        Definition definition = combine.getDefinition();
073        // 所有的流程节点
074        List<Node> allNodes = combine.getAllNodes();
075        // 所有的流程连线
076        List<Skip> allSkips = combine.getAllSkips();
077        // 根据不同策略进行新增
078        insertFlow(definition, allNodes, allSkips);
079        return definition;
080    }
081
082    @Override
083    public void saveXml(Definition def) throws Exception {
084        saveXml(def.getId(), def.getXmlString());
085    }
086
087    @Override
088    public void saveXml(Long id, String xmlString) throws Exception {
089        if (ObjectUtil.isNull(id) || StringUtils.isEmpty(xmlString)) {
090            return;
091        }
092        FlowCombine combine = FlowConfigUtil.readConfig(new ByteArrayInputStream
093                (xmlString.getBytes(StandardCharsets.UTF_8)));
094        // 所有的流程节点
095        List<Node> allNodes = combine.getAllNodes();
096        // 所有的流程连线
097        List<Skip> allSkips = combine.getAllSkips();
098
099        FlowFactory.nodeService().remove(FlowFactory.newNode().setDefinitionId(id));
100        FlowFactory.skipService().remove(FlowFactory.newSkip().setDefinitionId(id));
101        allNodes.forEach(node -> node.setDefinitionId(id));
102        allSkips.forEach(skip -> skip.setDefinitionId(id));
103        // 保存节点,流程连线,权利人
104        FlowFactory.nodeService().saveBatch(allNodes);
105        FlowFactory.skipService().saveBatch(allSkips);
106    }
107
108    @Override
109    public Document exportXml(Long id) {
110        Definition definition = getAllDataDefinition(id);
111        return FlowConfigUtil.createDocument(definition);
112    }
113
114    @Override
115    public String xmlString(Long id) {
116        Definition definition = getAllDataDefinition(id);
117        Document document = FlowConfigUtil.createDocument(definition);
118        return document.asXML();
119    }
120
121    @Override
122    public List<Definition> queryByCodeList(List<String> flowCodeList) {
123        return getDao().queryByCodeList(flowCodeList);
124    }
125
126    @Override
127    public void closeFlowByCodeList(List<String> flowCodeList) {
128        getDao().closeFlowByCodeList(flowCodeList);
129    }
130
131    @Override
132    public boolean checkAndSave(Definition definition) {
133        String version = getNewVersion(definition);
134        definition.setVersion(version);
135        return save(definition);
136    }
137
138    /**
139     * 删除流程定义
140     *
141     * @param ids
142     */
143    @Override
144    public boolean removeDef(List<Long> ids) {
145        FlowFactory.nodeService().deleteNodeByDefIds(ids);
146        FlowFactory.skipService().deleteSkipByDefIds(ids);
147        return removeByIds(ids);
148    }
149
150    @Override
151    public boolean publish(Long id) {
152        Definition definition = getById(id);
153        List<String> flowCodeList = Collections.singletonList(definition.getFlowCode());
154        // 把之前的流程定义改为已失效
155        closeFlowByCodeList(flowCodeList);
156
157        Definition flowDefinition = FlowFactory.newDef();
158        flowDefinition.setId(id);
159        flowDefinition.setIsPublish(PublishStatus.PUBLISHED.getKey());
160        return updateById(flowDefinition);
161    }
162
163    @Override
164    public boolean unPublish(Long id) {
165        List<Task> tasks = FlowFactory.taskService().list(FlowFactory.newTask().setDefinitionId(id));
166        AssertUtil.isNotEmpty(tasks, ExceptionCons.NOT_PUBLISH_TASK);
167        Definition definition = FlowFactory.newDef().setId(id);
168        definition.setIsPublish(PublishStatus.UNPUBLISHED.getKey());
169        return updateById(definition);
170    }
171
172    @Override
173    public boolean copyDef(Long id) {
174        Definition definition = getById(id).copy();
175        AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
176
177        List<Node> nodeList = FlowFactory.nodeService().list(FlowFactory.newNode().setDefinitionId(id))
178                .stream().map(Node::copy).collect(Collectors.toList());
179        List<Skip> skipList = FlowFactory.skipService().list(FlowFactory.newSkip().setDefinitionId(id))
180                .stream().map(Skip::copy).collect(Collectors.toList());
181        FlowFactory.dataFillHandler().idFill(definition.setId(null));
182        definition.setVersion(definition.getVersion() + "_copy")
183                .setIsPublish(PublishStatus.UNPUBLISHED.getKey())
184                .setCreateTime(null)
185                .setUpdateTime(null);
186
187        nodeList.forEach(node -> node.setId(null)
188                .setDefinitionId(definition.getId())
189                .setVersion(definition.getVersion())
190                .setCreateTime(null)
191                .setUpdateTime(null));
192        FlowFactory.nodeService().saveBatch(nodeList);
193
194        skipList.forEach(skip -> skip.setId(null)
195                .setDefinitionId(definition.getId())
196                .setCreateTime(null)
197                .setUpdateTime(null));
198        FlowFactory.skipService().saveBatch(skipList);
199        return save(definition);
200    }
201
202    @Override
203    public boolean active(Long id) {
204        Definition definition = getById(id);
205        AssertUtil.isTrue(definition.getActivityStatus().equals(ActivityStatus.ACTIVITY.getKey()), ExceptionCons.DEFINITION_ALREADY_ACTIVITY);
206        definition.setActivityStatus(ActivityStatus.ACTIVITY.getKey());
207        return updateById(definition);
208    }
209
210    @Override
211    public boolean unActive(Long id) {
212        Definition definition = getById(id);
213        AssertUtil.isTrue(definition.getActivityStatus().equals(ActivityStatus.SUSPENDED.getKey()), ExceptionCons.DEFINITION_ALREADY_SUSPENDED);
214        definition.setActivityStatus(ActivityStatus.SUSPENDED.getKey());
215        return updateById(definition);
216    }
217
218    @Override
219    public String flowChart(Long instanceId) {
220        Long definitionId = FlowFactory.insService().getById(instanceId).getDefinitionId();
221        return basicFlowChart(instanceId, definitionId);
222    }
223
224    @Override
225    public List<FlowChart> flowChartData(Long instanceId) {
226        Long definitionId = FlowFactory.insService().getById(instanceId).getDefinitionId();
227        FlowChartChain flowChartChain = new FlowChartChain();
228        basicFlowChart(instanceId, definitionId, flowChartChain);
229        return flowChartChain.getFlowChartList();
230    }
231
232    @Override
233    public String flowChartNoColor(Long definitionId) {
234        return basicFlowChart(null, definitionId);
235    }
236
237    @Override
238    public List<FlowChart> flowChartNoColorData(Long definitionId) {
239        FlowChartChain flowChartChain = new FlowChartChain();
240        basicFlowChart(null, definitionId, flowChartChain);
241        return flowChartChain.getFlowChartList();
242    }
243
244    /**
245     * DefService 根据流程实例ID获取流程图的图片流(渲染颜色)
246     * @param instanceId 实例id
247     * @param definitionId  流程定义id
248     * @return   流程图base64字符串
249     */
250    public String basicFlowChart(Long instanceId, Long definitionId) {
251
252        try {
253            FlowChartChain flowChartChain = new FlowChartChain();
254            Map<String, Integer> nodeXY = basicFlowChart(instanceId, definitionId, flowChartChain);
255
256            // 清晰度
257            int n = 2;
258            int width = (nodeXY.get("maxX") + nodeXY.get("minX")) * n;
259            int height = (nodeXY.get("maxY") + nodeXY.get("minY")) * n;
260            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
261
262            // 获取图形上下文,graphics想象成一个画笔
263            Graphics2D graphics = image.createGraphics();
264            graphics.setStroke(new BasicStroke((2 * n) + 1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
265            Font font = new Font("宋体", Font.BOLD, 12 * n);
266            graphics.setFont(font);
267            // 消除线条锯齿
268            graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
269            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
270            graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
271
272            // 对指定的矩形区域填充颜色: GREEN:绿色;  红色:RED;   灰色:GRAY
273            graphics.setColor(Color.WHITE);
274            graphics.fillRect(0, 0, width, height);
275
276            flowChartChain.draw(graphics, n);
277            graphics.setPaintMode();
278            graphics.dispose();// 释放此图形的上下文并释放它所使用的所有系统资源
279
280            ByteArrayOutputStream os = new ByteArrayOutputStream();
281            ImageIO.write(image, "png", os);
282            return Base64.encode(os.toByteArray());
283        } catch (IOException e) {
284            log.error("获取流程图异常", e);
285            throw new FlowException("获取流程图异常");
286        }
287
288    }
289
290
291    /**
292     * 获取流程图基本元数据
293     * @param instanceId 实例id
294     * @param definitionId  流程定义id
295     * @param flowChartChain 存储流程图基本元数据
296     * @return Map<String, Integer> 节点坐标信息
297     */
298    public Map<String, Integer> basicFlowChart(Long instanceId, Long definitionId, FlowChartChain flowChartChain) {
299        Instance instance;
300        if (ObjectUtil.isNotNull(instanceId)) {
301            instance = FlowFactory.insService().getById(instanceId);
302        } else {
303            instance = null;
304        }
305        Map<String, Color> colorMap = new HashMap<>();
306        Map<String, Integer> nodeXY = addNodeChart(colorMap, instance, definitionId, flowChartChain);
307        addSkipChart(colorMap, instance, definitionId, flowChartChain);
308
309        return nodeXY;
310    }
311    /**
312     * 添加跳转流程图
313     *
314     * @param colorMap 颜色映射
315     * @param instance 流程实例
316     * @param flowChartChain 流程图链
317     */
318    private void addSkipChart(Map<String, Color> colorMap, Instance instance, Long definitionId, FlowChartChain flowChartChain) {
319        List<Skip> skipList = FlowFactory.skipService().list(FlowFactory.newSkip().setDefinitionId(definitionId));
320        for (Skip skip : skipList) {
321            if (StringUtils.isNotEmpty(skip.getCoordinate())) {
322                String[] coordinateSplit = skip.getCoordinate().split("\\|");
323                String[] skipSplit = coordinateSplit[0].split(";");
324                int[] skipX = new int[skipSplit.length];
325                int[] skipY = new int[skipSplit.length];
326                TextChart textChart = null;
327                if (coordinateSplit.length > 1) {
328                    String[] textSplit = coordinateSplit[1].split(",");
329                    int textX = Integer.parseInt(textSplit[0].split("\\.")[0]);
330                    int textY = Integer.parseInt(textSplit[1].split("\\.")[0]);
331                    textChart = new TextChart(textX, textY, skip.getSkipName());
332                }
333
334                for (int i = 0; i < skipSplit.length; i++) {
335                    skipX[i] = Integer.parseInt(skipSplit[i].split(",")[0].split("\\.")[0]);
336                    skipY[i] = Integer.parseInt(skipSplit[i].split(",")[1].split("\\.")[0]);
337                }
338                Color c;
339                if (ObjectUtil.isNotNull(instance)) {
340                    c = colorGet(colorMap, "skip:" + skip.getId().toString());
341                } else {
342                    c = Color.BLACK;
343                }
344                flowChartChain.addFlowChart(new SkipChart(skipX, skipY, c, textChart));
345            }
346        }
347    }
348
349    /**
350     * 添加节点流程图
351     *
352     * @param instance 流程实例
353     * @param flowChartChain 流程图链
354     */
355    private Map<String, Integer> addNodeChart(Map<String, Color> colorMap, Instance instance, Long definitionId
356            , FlowChartChain flowChartChain) {
357        List<Node> nodeList = FlowFactory.nodeService().list(FlowFactory.newNode().setDefinitionId(definitionId));
358        List<Skip> allSkips = FlowFactory.skipService().list(FlowFactory.newSkip()
359                .setDefinitionId(definitionId).setSkipType(SkipType.PASS.getKey()));
360        if (ObjectUtil.isNotNull(instance)) {
361            // 流程图渲染,过滤掉所有后置节点
362            List<Node> needChartNodes = filterNodes(instance, allSkips, nodeList);
363            setColorMap(colorMap, instance, allSkips, needChartNodes);
364        }
365
366        Map<String, Integer> maxR = new HashMap<>();
367        maxR.put("minX", 5000);
368        maxR.put("minY", 5000);
369        maxR.put("maxX", 0);
370        maxR.put("maxY", 0);
371
372        for (Node node : nodeList) {
373            if (StringUtils.isNotEmpty(node.getCoordinate())) {
374                String[] coordinateSplit = node.getCoordinate().split("\\|");
375                String[] nodeSplit = coordinateSplit[0].split(",");
376                int nodeX = Integer.parseInt(nodeSplit[0].split("\\.")[0]);
377                int nodeY = Integer.parseInt(nodeSplit[1].split("\\.")[0]);
378                if (nodeX > maxR.get("maxX")) {
379                    maxR.put("maxX", nodeX);
380                }
381                if (nodeX < maxR.get("minX")) {
382                    maxR.put("minX", nodeX);
383                }
384                if (nodeY > maxR.get("maxY")) {
385                    maxR.put("maxY", nodeY);
386                }
387                if (nodeY < maxR.get("minY")) {
388                    maxR.put("minY", nodeY);
389                }
390                TextChart textChart = null;
391                if (coordinateSplit.length > 1) {
392                    String[] textSplit = coordinateSplit[1].split(",");
393                    int textX = Integer.parseInt(textSplit[0].split("\\.")[0]);
394                    int textY = Integer.parseInt(textSplit[1].split("\\.")[0]);
395                    textChart = new TextChart(textX, textY, node.getNodeName());
396                }
397                Color c;
398                if (ObjectUtil.isNotNull(instance)) {
399                    c = colorGet(colorMap, "node:" + node.getNodeCode());
400                } else {
401                    c = Color.BLACK;
402                }
403                if (NodeType.isStart(node.getNodeType())) {
404                    flowChartChain.addFlowChart(new OvalChart(nodeX, nodeY, ObjectUtil.isNotNull(instance) ? Color.GREEN : Color.BLACK, textChart));
405                } else if (NodeType.isBetween(node.getNodeType())) {
406                    flowChartChain.addFlowChart(new BetweenChart(nodeX, nodeY, c, textChart));
407                } else if (NodeType.isGateWaySerial(node.getNodeType())) {
408                    flowChartChain.addFlowChart(new SerialChart(nodeX, nodeY, c));
409                } else if (NodeType.isGateWayParallel(node.getNodeType())) {
410                    flowChartChain.addFlowChart(new ParallelChart(nodeX, nodeY, c));
411                } else if (NodeType.isEnd(node.getNodeType())) {
412                    flowChartChain.addFlowChart(new OvalChart(nodeX, nodeY, c, textChart));
413                }
414            }
415        }
416        return maxR;
417    }
418
419    /**
420     * 流程图渲染,过滤掉当前任务后的节点
421     *
422     * @param instance 流程实例
423     * @param allSkips 所有跳转
424     * @param nodeList 节点集合
425     * @return 过滤后的节点列表
426     */
427    private List<Node> filterNodes(Instance instance, List<Skip> allSkips, List<Node> nodeList) {
428        List<String> allNextNode = new ArrayList<>();
429        Map<String, List<Skip>> skipNextMap = StreamUtils.groupByKey(allSkips, Skip::getNowNodeCode);
430        if (NodeType.isEnd(instance.getNodeType())) {
431            return nodeList;
432        }
433        List<Task> curTasks = FlowFactory.taskService().list(FlowFactory.newTask().setInstanceId(instance.getId()));
434        for (Task curTask : curTasks) {
435            List<Skip> nextSkips = skipNextMap.get(curTask.getNodeCode());
436            getAllNextNode(nextSkips, allNextNode, skipNextMap);
437        }
438        return StreamUtils.filter(nodeList, node -> !allNextNode.contains(node.getNodeCode()));
439    }
440
441    /**
442     * 获取待办任务节点后的所有节点
443     *
444     * @param nextSkips 当前节点对应的所有跳转
445     * @param allNextNode 所有下个任务节点
446     */
447    private void getAllNextNode(List<Skip> nextSkips, List<String> allNextNode, Map<String, List<Skip>> skipMap) {
448        if (CollUtil.isNotEmpty(nextSkips)) {
449            for (Skip nextSkip : nextSkips) {
450                allNextNode.add(nextSkip.getNextNodeCode());
451                List<Skip> nextNextSkips = skipMap.get(nextSkip.getNextNodeCode());
452                getAllNextNode(nextNextSkips, allNextNode, skipMap);
453            }
454        }
455    }
456
457    /**
458     * 设置节点和跳转对应的颜色
459     *
460     * @param colorMap 颜色map
461     * @param instance 流程实例
462     * @param allSkips 所有跳转
463     * @param nodeList 节点集合
464     */
465    public void setColorMap(Map<String, Color> colorMap, Instance instance, List<Skip> allSkips
466            , List<Node> nodeList) {
467        final Color color = Color.PINK;
468        Map<String, List<Skip>> skipLastMap = StreamUtils.groupByKey(allSkips, Skip::getNextNodeCode);
469        Map<String, List<Skip>> skipNextMap = StreamUtils.groupByKey(allSkips, Skip::getNowNodeCode);
470        List<HisTask> hisTaskList = FlowFactory.hisTaskService().getNoReject(instance.getId());
471        for (Node node : nodeList) {
472            List<Skip> oneNextSkips = skipNextMap.get(node.getNodeCode());
473            List<Skip> oneLastSkips = skipLastMap.get(node.getNodeCode());
474            if (NodeType.isStart(node.getNodeType())) {
475                colorPut(colorMap, "node:" + node.getNodeCode(), Color.GREEN);
476                if (CollUtil.isNotEmpty(oneNextSkips)) {
477                    oneNextSkips.forEach(oneNextSkip -> colorPut(colorMap, "skip:" + oneNextSkip.getId().toString(), Color.GREEN));
478                }
479                continue;
480            }
481            if (NodeType.isGateWay(node.getNodeType())) {
482                continue;
483            }
484            Task task = FlowFactory.taskService()
485                    .getOne(FlowFactory.newTask().setNodeCode(node.getNodeCode()).setInstanceId(instance.getId()));
486            HisTask curHisTask = FlowFactory.hisTaskService()
487                    .getNoReject(node.getNodeCode(), null, hisTaskList);
488
489            if (CollUtil.isNotEmpty(oneLastSkips)) {
490                for (Skip oneLastSkip : oneLastSkips) {
491                    Color c = null;
492                    if (NodeType.isStart(oneLastSkip.getNowNodeType()) && task == null) {
493                        colorPut(colorMap, "node:" + node.getNodeCode(), Color.GREEN);
494                        setNextColorMap(colorMap, oneNextSkips, Color.GREEN);
495                    } else if (NodeType.isGateWay(oneLastSkip.getNowNodeType())) {
496                        // 如果前置节点是网关,那网关前任意一个任务完成就算完成
497                        List<Skip> twoLastSkips = skipLastMap.get(oneLastSkip.getNowNodeCode());
498                        for (Skip twoLastSkip : twoLastSkips) {
499                            HisTask twoLastHisTask = FlowFactory.hisTaskService()
500                                    .getNoReject(twoLastSkip.getNowNodeCode(), node.getNodeCode(), hisTaskList);
501
502                            // 前前置节点完成时间是否早于前置节点,如果是串行网关,那前前置节点必须只有一个完成,如果是并行网关都要完成
503                            if (task != null) {
504                                c = color;
505                                if (ObjectUtil.isNotNull(twoLastHisTask)) {
506                                    colorPut(colorMap, "skip:" + oneLastSkip.getId().toString(), Color.GREEN);
507                                    colorPut(colorMap, "node:" + oneLastSkip.getNowNodeCode(), Color.GREEN);
508                                }
509                            } else {
510                                if (NodeType.isEnd(node.getNodeType()) && NodeType.isEnd(instance.getNodeType())) {
511                                    HisTask curHisTaskN = FlowFactory.hisTaskService()
512                                            .getNoReject(null, node.getNodeCode(), hisTaskList);
513                                    if (ObjectUtil.isNotNull(curHisTaskN)) {
514                                        c = Color.GREEN;
515                                        curHisTaskN = FlowFactory.hisTaskService()
516                                                .getNoReject(twoLastSkip.getNowNodeCode(), node.getNodeCode(), hisTaskList);
517                                        if (ObjectUtil.isNotNull(curHisTaskN)) {
518                                            colorPut(colorMap, "skip:" + oneLastSkip.getId().toString(), c);
519                                            colorPut(colorMap, "node:" + oneLastSkip.getNowNodeCode(), c);
520                                        }
521                                    }
522                                } else if (curHisTask != null && ObjectUtil.isNotNull(twoLastHisTask) && (twoLastHisTask.getUpdateTime()
523                                        .before(curHisTask.getUpdateTime()) || twoLastHisTask.getUpdateTime()
524                                        .equals(curHisTask.getUpdateTime()))) {
525                                    c = Color.GREEN;
526                                    colorPut(colorMap, "node:" + oneLastSkip.getNowNodeCode(), c);
527                                } else {
528                                    c = Color.BLACK;
529                                }
530                                if (ObjectUtil.isNotNull(twoLastHisTask)) {
531                                    colorPut(colorMap, "skip:" + oneLastSkip.getId().toString(), c);
532                                }
533                            }
534                            colorPut(colorMap, "node:" + node.getNodeCode(), c);
535                            setNextColorMap(colorMap, oneNextSkips, c);
536                        }
537                    } else if (NodeType.isEnd(node.getNodeType()) && NodeType.isEnd(instance.getNodeType())) {
538                        HisTask curHisTaskN = FlowFactory.hisTaskService()
539                                .getNoReject(null, node.getNodeCode(), hisTaskList);
540                        if (ObjectUtil.isNotNull(curHisTaskN)) {
541                            colorPut(colorMap, "node:" + node.getNodeCode(), Color.GREEN);
542                            curHisTaskN = FlowFactory.hisTaskService()
543                                    .getNoReject(oneLastSkip.getNowNodeCode(), node.getNodeCode(), hisTaskList);
544                            if (ObjectUtil.isNotNull(curHisTaskN)) {
545                                colorPut(colorMap, "skip:" + oneLastSkip.getId().toString(), Color.GREEN);
546                            }
547                        }
548                    } else {
549                        HisTask oneLastHisTask = FlowFactory.hisTaskService()
550                                .getNoReject(oneLastSkip.getNowNodeCode(), node.getNodeCode(), hisTaskList);
551                        // 前前置节点完成时间是否早于前置节点,如果是串行网关,那前前置节点必须只有一个完成,如果是并行网关都要完成
552                        if (task != null) {
553                            c = color;
554                        } else if (curHisTask != null && ObjectUtil.isNotNull(oneLastHisTask) && (oneLastHisTask.getUpdateTime()
555                                .before(curHisTask.getUpdateTime()) || oneLastHisTask.getUpdateTime()
556                                .equals(curHisTask.getUpdateTime()))) {
557                            c = Color.GREEN;
558                        } else {
559                            c = Color.BLACK;
560                        }
561
562                        if (ObjectUtil.isNotNull(oneLastHisTask)) {
563                            colorPut(colorMap, "skip:" + oneLastSkip.getId().toString(), c);
564                        }
565                        colorPut(colorMap, "node:" + node.getNodeCode(), c);
566                        setNextColorMap(colorMap, oneNextSkips, c);
567                    }
568                }
569            }
570        }
571    }
572
573    /**
574     * 设置下个节点的颜色
575     *
576     * @param colorMap 颜色map
577     * @param oneNextSkips 下一个跳转
578     * @param c 颜色
579     */
580    private void setNextColorMap(Map<String, Color> colorMap, List<Skip> oneNextSkips, Color c) {
581        if (CollUtil.isNotEmpty(oneNextSkips)) {
582            oneNextSkips.forEach(oneNextSkip -> {
583                colorPut(colorMap, "skip:" + oneNextSkip.getId().toString(), c);
584                if (NodeType.isGateWay(oneNextSkip.getNextNodeType()) && (c == Color.GREEN || c == Color.BLACK)) {
585                    colorPut(colorMap, "node:" + oneNextSkip.getNextNodeCode(), c);
586                }
587            });
588        }
589    }
590
591    /**
592     * 优先绿色
593     *
594     * @param colorMap 颜色map
595     * @param key key
596     * @param c color
597     */
598    private void colorPut(Map<String, Color> colorMap, String key, Color c) {
599        Color color = colorMap.get(key);
600        if (c == Color.GREEN) {
601            colorMap.put(key, c);
602        } else if (color == null || color == Color.BLACK) {
603            colorMap.put(key, c);
604        }
605    }
606
607    private Color colorGet(Map<String, Color> colorMap, String key) {
608        Color color = colorMap.get(key);
609        if (color == null) {
610            color = Color.BLACK;
611        }
612        return color;
613    }
614
615    public Definition getAllDataDefinition(Long id) {
616        Definition definition = getDao().selectById(id);
617        List<Node> nodeList = FlowFactory.nodeService().list(FlowFactory.newNode().setDefinitionId(id));
618        definition.setNodeList(nodeList);
619        List<Skip> skips = FlowFactory.skipService().list(FlowFactory.newSkip().setDefinitionId(id));
620        Map<String, List<Skip>> flowSkipMap = skips.stream()
621                .collect(Collectors.groupingBy(Skip::getNowNodeCode));
622        nodeList.forEach(flowNode -> flowNode.setSkipList(flowSkipMap.get(flowNode.getNodeCode())));
623        return definition;
624    }
625
626    /**
627     * 每次只做新增操作,保证新增的flowCode+version是唯一的
628     *
629     * @param definition 流程定义
630     * @param allNodes 所有节点
631     * @param allSkips 所有跳转
632     */
633    private void insertFlow(Definition definition, List<Node> allNodes, List<Skip> allSkips) {
634        String version = getNewVersion(definition);
635        definition.setVersion(version);
636        for (Node node : allNodes) {
637            node.setVersion(version);
638        }
639        FlowFactory.defService().save(definition);
640        FlowFactory.nodeService().saveBatch(allNodes);
641        FlowFactory.skipService().saveBatch(allSkips);
642    }
643
644    private String getNewVersion(Definition definition) {
645        List<String> flowCodeList = Collections.singletonList(definition.getFlowCode());
646        List<Definition> definitions = getDao().queryByCodeList(flowCodeList);
647        int highestVersion = 0;
648        String latestNonPositiveVersion = null;
649        long latestTimestamp = Long.MIN_VALUE;
650
651        for (Definition otherDef : definitions) {
652            if (definition.getVersion() != null && definition.getFlowCode().equals(otherDef.getFlowCode())
653                    && definition.getVersion().equals(otherDef.getVersion())) {
654                throw new FlowException(definition.getFlowCode() + "(" + definition.getVersion() + ")" + ExceptionCons.ALREADY_EXIST);
655            }
656            if (definition.getFlowCode().equals(otherDef.getFlowCode())) {
657                try {
658                    int version = Integer.parseInt(otherDef.getVersion());
659                    if (version > highestVersion) {
660                        highestVersion = version;
661                    }
662                } catch (NumberFormatException e) {
663                    long timestamp = otherDef.getCreateTime().getTime();
664                    if (timestamp > latestTimestamp) {
665                        latestTimestamp = timestamp;
666                        latestNonPositiveVersion = otherDef.getVersion();
667                    }
668                }
669            }
670        }
671        String version = definition.getVersion();
672        if (version == null || version.isEmpty()) {
673            if (highestVersion > 0) {
674                version = String.valueOf(highestVersion + 1);
675            } else if (latestNonPositiveVersion != null) {
676                version = latestNonPositiveVersion + "_1";
677            } else {
678                version = "1";
679            }
680        }
681
682        return version;
683    }
684
685}