From 045117011bfe9f495b841884b4ad755cc8f5a83c Mon Sep 17 00:00:00 2001 From: MHW Date: Sat, 11 Apr 2026 13:46:02 +0800 Subject: [PATCH 1/5] docker-compose --- .../FireRuleRedWeaponOutputFillHelper.java | 240 ++++++++++++++++++ .../com/solution/rule/utils/RuleFunction.java | 16 ++ .../src/main/resources/rules/rule.drl | 34 +++ docker-compose.yml | 4 +- 4 files changed, 292 insertions(+), 2 deletions(-) diff --git a/auto-solution-rule/src/main/java/com/solution/rule/utils/FireRuleRedWeaponOutputFillHelper.java b/auto-solution-rule/src/main/java/com/solution/rule/utils/FireRuleRedWeaponOutputFillHelper.java index 18be66b..4927258 100644 --- a/auto-solution-rule/src/main/java/com/solution/rule/utils/FireRuleRedWeaponOutputFillHelper.java +++ b/auto-solution-rule/src/main/java/com/solution/rule/utils/FireRuleRedWeaponOutputFillHelper.java @@ -20,16 +20,21 @@ import com.solution.rule.domain.ultimately.vo.FireRuleRedPlatformSlotVO; import com.solution.rule.domain.ultimately.vo.FireRuleRedWeaponSlotVO; import com.solution.rule.domain.ultimately.vo.FireRuleOutputVO; import com.solution.rule.domain.ultimately.vo.FireRuleRouteTrackPointVO; +import com.solution.rule.domain.ultimately.vo.FireRuleSceneGroupNodeVO; import com.solution.rule.domain.ultimately.vo.FireRuleSceneTaskNodeVO; import com.solution.rule.domain.ultimately.vo.FireRuleSceneTaskPayloadVO; import com.solution.rule.domain.ultimately.vo.FireRuleTrackParamVO; import com.solution.rule.domain.ultimately.vo.FireRuleTrackRouteVO; +import com.solution.rule.domain.ultimately.vo.FireRuleWingmanDataVO; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -231,6 +236,241 @@ public final class FireRuleRedWeaponOutputFillHelper { } } + /** + * 编组规则:按红方装备列表分组,写入 {@link FireRuleTrackParamVO#getGroups}(JSON 键 Groups), + * groupType=addGroup,drawName=领队装备名+后缀;僚机 wingmanData 中 name 为红方 equipmentId。 + * 多蓝方任务循环时与航迹一致:在已有 Groups 上追加,且 group id 与已有项冲突时自动加后缀。 + */ + @SuppressWarnings("rawtypes") + public static void fillTrackParamGroups(FireRuleOutputVO out, FireRuleTaskInputDTO blueTask, Map params) { + if (out == null || blueTask == null) { + return; + } + boolean enabled = Boolean.parseBoolean(String.valueOf(params.getOrDefault("groupRuleEnabled", true))); + if (!enabled) { + return; + } + List reds = out.getRedWeapons(); + if (reds == null || reds.isEmpty()) { + return; + } + ensureTrackParam(out); + + String dirMode = str(params, "trackPointDirectionMode", "head2next"); + double fallbackBrg = readDouble(params, "trackFallbackBearingDeg", readDouble(params, "fallbackBearingDeg", 0d)); + double mainBearing = computeTrackBearingDeg(blueTask, dirMode, fallbackBrg); + + String mode = str(params, "groupFormationMode", "onePerRed"); + int clusterSize = readInt(params, "groupClusterSize", 3); + if (clusterSize < 1) { + clusterSize = 1; + } + int minWing = readInt(params, "groupMinMembersForWingman", 2); + + List> buckets = splitRedsIntoGroups(reds, mode, clusterSize); + List batch = new ArrayList<>(); + String blueSeg = sanitizeRouteIdSegment(nz(blueTask.getId())); + int gidx = 0; + for (List bucket : buckets) { + if (bucket == null || bucket.isEmpty()) { + continue; + } + FireRuleRedWeaponEquipmentVO leader = pickGroupLeader(bucket, params); + if (leader == null) { + continue; + } + FireRuleSceneGroupNodeVO node = new FireRuleSceneGroupNodeVO(); + String baseId = "group_" + blueSeg + "_" + gidx; + node.setId(ensureUniqueGroupId(out.getTrackParam(), baseId)); + String suffix = str(params, "groupDrawNameSuffix", "编组"); + boolean withIndex = Boolean.parseBoolean(String.valueOf(params.getOrDefault("groupDrawNameWithIndex", false))); + String drawName = nz(leader.getName()) + suffix; + if (withIndex) { + drawName += (gidx + 1); + } + node.setDrawName(drawName); + node.setGroupType("addGroup"); + node.setName("addGroup"); + node.setLeader(nz(leader.getEquipmentId())); + node.setSort(System.currentTimeMillis() + gidx); + node.setWingmanData(buildWingmanData(bucket, leader, mainBearing, params, minWing)); + batch.add(node); + gidx++; + } + mergeGroupsIntoTrackParam(out.getTrackParam(), batch); + } + + private static List> splitRedsIntoGroups( + List reds, String mode, int clusterSize) { + List clean = new ArrayList<>(); + for (FireRuleRedWeaponEquipmentVO r : reds) { + if (r != null) { + clean.add(r); + } + } + List> out = new ArrayList<>(); + if (clean.isEmpty()) { + return out; + } + if ("singleGroup".equalsIgnoreCase(mode)) { + out.add(clean); + return out; + } + if ("clusterByCount".equalsIgnoreCase(mode)) { + for (int i = 0; i < clean.size(); i += clusterSize) { + int end = Math.min(i + clusterSize, clean.size()); + out.add(new ArrayList<>(clean.subList(i, end))); + } + return out; + } + for (FireRuleRedWeaponEquipmentVO r : clean) { + out.add(Collections.singletonList(r)); + } + return out; + } + + private static FireRuleRedWeaponEquipmentVO pickGroupLeader(List bucket, Map params) { + if (bucket == null || bucket.isEmpty()) { + return null; + } + String mode = str(params, "groupLeaderPickMode", "byHitRateThenId"); + FireRuleRedWeaponEquipmentVO best = bucket.get(0); + for (int i = 1; i < bucket.size(); i++) { + FireRuleRedWeaponEquipmentVO cur = bucket.get(i); + if ("byId".equalsIgnoreCase(mode)) { + if (compareEquipmentId(cur, best) < 0) { + best = cur; + } + } else { + if (isBetterLeaderByHitRateThenId(cur, best)) { + best = cur; + } + } + } + return best; + } + + private static boolean isBetterLeaderByHitRateThenId(FireRuleRedWeaponEquipmentVO cand, FireRuleRedWeaponEquipmentVO inc) { + double rc = leaderRad(cand); + double ri = leaderRad(inc); + int c = Double.compare(rc, ri); + if (c > 0) { + return true; + } + if (c < 0) { + return false; + } + return compareEquipmentId(cand, inc) < 0; + } + + private static double leaderRad(FireRuleRedWeaponEquipmentVO w) { + if (w == null || w.getSuccessTargetRad() == null) { + return -1d; + } + return w.getSuccessTargetRad().doubleValue(); + } + + private static int compareEquipmentId(FireRuleRedWeaponEquipmentVO a, FireRuleRedWeaponEquipmentVO b) { + String sa = a != null ? nz(a.getEquipmentId()) : ""; + String sb = b != null ? nz(b.getEquipmentId()) : ""; + return sa.compareTo(sb); + } + + private static List buildWingmanData( + List bucket, + FireRuleRedWeaponEquipmentVO leader, + double mainBearing, + Map params, + int minMembersForWingman) { + if (bucket == null || leader == null || bucket.size() < minMembersForWingman) { + return Collections.emptyList(); + } + double distBase = readDouble(params, "wingmanDistanceBaseMeters", 100); + double distStep = readDouble(params, "wingmanDistanceStepMeters", 50); + double angBase = readDouble(params, "wingmanAngleBaseDeg", 50); + double angStep = readDouble(params, "wingmanAngleStepDeg", 15); + double altBase = readDouble(params, "wingmanAltBaseMeters", 40); + double altScale = readDouble(params, "wingmanAltScale", 1.0); + + List list = new ArrayList<>(); + int wmIdx = 0; + for (FireRuleRedWeaponEquipmentVO r : bucket) { + if (r == null || r == leader) { + continue; + } + FireRuleWingmanDataVO w = new FireRuleWingmanDataVO(); + w.setKey(wmIdx); + w.setName(nz(r.getEquipmentId())); + w.setDistance((int) Math.round(distBase + wmIdx * distStep)); + double deg = normDeg(mainBearing + angBase + wmIdx * angStep); + w.setAngle(String.format(Locale.US, "%.1f", deg)); + Double lh = extractFirstPlatformHeight(leader); + Double wh = extractFirstPlatformHeight(r); + double delta = 0d; + if (lh != null && wh != null) { + delta = wh - lh; + } + w.setAlt((int) Math.round(altBase + altScale * delta)); + list.add(w); + wmIdx++; + } + return list; + } + + private static Double extractFirstPlatformHeight(FireRuleRedWeaponEquipmentVO red) { + if (red == null || red.getSubComponents() == null || red.getSubComponents().getPlatform() == null) { + return null; + } + for (FireRuleRedPlatformSlotVO pl : red.getSubComponents().getPlatform()) { + if (pl == null || pl.getPositions() == null || pl.getPositions().size() < 3) { + continue; + } + return pl.getPositions().get(2); + } + return null; + } + + private static double normDeg(double deg) { + double a = deg % 360d; + if (a < 0) { + a += 360d; + } + return a; + } + + private static String ensureUniqueGroupId(FireRuleTrackParamVO tp, String baseId) { + Set seen = new HashSet<>(); + if (tp.getGroups() != null) { + for (FireRuleSceneGroupNodeVO g : tp.getGroups()) { + if (g != null && !isBlank(g.getId())) { + seen.add(g.getId()); + } + } + } + if (!seen.contains(baseId)) { + return baseId; + } + int k = 1; + while (seen.contains(baseId + "_" + k)) { + k++; + } + return baseId + "_" + k; + } + + private static void mergeGroupsIntoTrackParam(FireRuleTrackParamVO tp, List batch) { + if (tp == null || batch == null || batch.isEmpty()) { + return; + } + List cur = tp.getGroups(); + if (cur == null) { + cur = new ArrayList<>(); + } else { + cur = new ArrayList<>(cur); + } + cur.addAll(batch); + tp.setGroups(cur); + } + private static void ensureTrackParam(FireRuleOutputVO out) { if (out.getTrackParam() == null) { out.setTrackParam(new FireRuleTrackParamVO()); diff --git a/auto-solution-rule/src/main/java/com/solution/rule/utils/RuleFunction.java b/auto-solution-rule/src/main/java/com/solution/rule/utils/RuleFunction.java index 4074a9f..59b3ecc 100644 --- a/auto-solution-rule/src/main/java/com/solution/rule/utils/RuleFunction.java +++ b/auto-solution-rule/src/main/java/com/solution/rule/utils/RuleFunction.java @@ -245,6 +245,22 @@ public final class RuleFunction { FireRuleRedWeaponOutputFillHelper.fillTrackParamAndBindMoveRoute(out, fact.getTask(), p); } + /** + * 编组规则:填充 {@link com.solution.rule.domain.ultimately.vo.FireRuleTrackParamVO#getGroups}(groupType=addGroup)与僚机 wingmanData。 + */ + @SuppressWarnings("rawtypes") + public static void groupFormation(DroolsFact fact, Map globalParams) { + if (fact == null || fact.getTask() == null || fact.getFireRuleOutputVO() == null) { + return; + } + Map p = castParams(globalParams); + boolean enabled = Boolean.parseBoolean(String.valueOf(p.getOrDefault("groupRuleEnabled", true))); + if (!enabled) { + return; + } + FireRuleRedWeaponOutputFillHelper.fillTrackParamGroups(fact.getFireRuleOutputVO(), fact.getTask(), p); + } + @SuppressWarnings("unchecked") private static Map castParams(Map raw) { return raw == null ? new java.util.HashMap<>() : (Map) raw; diff --git a/auto-solution-rule/src/main/resources/rules/rule.drl b/auto-solution-rule/src/main/resources/rules/rule.drl index b059f40..f30613c 100644 --- a/auto-solution-rule/src/main/resources/rules/rule.drl +++ b/auto-solution-rule/src/main/resources/rules/rule.drl @@ -7,6 +7,7 @@ import static com.solution.rule.utils.RuleFunction.equipmentRule; import static com.solution.rule.utils.RuleFunction.target; import static com.solution.rule.utils.RuleFunction.position; import static com.solution.rule.utils.RuleFunction.trackRoute; +import static com.solution.rule.utils.RuleFunction.groupFormation; global java.util.Map globalParams; @@ -176,6 +177,29 @@ function Map buildParam(){ param.put("trackJamWobbleMeters", 400); param.put("trackJamSegments", 4); + // ===================== 编组规则参数(写入 TrackParam.Groups + wingmanData) ===================== + // groupRuleEnabled:是否生成编组节点。 + param.put("groupRuleEnabled", true); + // groupDrawNameSuffix:drawName = 领队装备 Name + 此后缀(默认「编组」) + param.put("groupDrawNameSuffix", "编组"); + // groupDrawNameWithIndex:是否在 drawName 末尾追加序号(避免多组同名),如 J15编组1 + param.put("groupDrawNameWithIndex", false); + // groupFormationMode:onePerRed(每件红装一组) / clusterByCount(按固定人数分组) / singleGroup(全部一组) + param.put("groupFormationMode", "onePerRed"); + // groupClusterSize:clusterByCount 模式下每组人数上限(>=1) + param.put("groupClusterSize", 3); + // groupLeaderPickMode:byHitRateThenId(命中率高优先,平手 equipmentId 小) / byId(equipmentId 字典序最小) + param.put("groupLeaderPickMode", "byHitRateThenId"); + // groupMinMembersForWingman:组内人数达到该值才生成 wingmanData(否则仅 leader 壳,僚机列表为空) + param.put("groupMinMembersForWingman", 2); + // wingman 几何参数(相对蓝方主航向 mainBearing 叠加) + param.put("wingmanDistanceBaseMeters", 100); + param.put("wingmanDistanceStepMeters", 50); + param.put("wingmanAngleBaseDeg", 50); + param.put("wingmanAngleStepDeg", 15); + param.put("wingmanAltBaseMeters", 40); + param.put("wingmanAltScale", 1.0); + return param; } @@ -218,3 +242,13 @@ then globalParams.putAll(buildParam()); trackRoute($fact, globalParams); end + +rule "编组匹配" +salience 60 +when + $fact : DroolsFact(task != null) +then + // 显式编组规则:填充 TrackParam.Groups(groupType=addGroup)与 wingmanData + globalParams.putAll(buildParam()); + groupFormation($fact, globalParams); +end diff --git a/docker-compose.yml b/docker-compose.yml index 287a58f..f51a847 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: mysql: - image: mysql:8.0 + image: autosolution-mysql-preloaded:latest container_name: autosolution-mysql restart: unless-stopped environment: @@ -27,7 +27,7 @@ services: test: [ "CMD-SHELL", - "mysql -h 127.0.0.1 -uroot -p$${MYSQL_ROOT_PASSWORD:-root} -Nse \"SELECT 1 FROM information_schema.tables WHERE table_schema='$${MYSQL_DATABASE:-autosolution_db}' AND table_name='sys_config' LIMIT 1\" | grep -q 1", + "mysql -h 127.0.0.1 -uroot -p$MYSQL_ROOT_PASSWORD -Nse 'SELECT 1 FROM information_schema.tables WHERE table_schema=\"$MYSQL_DATABASE\" AND table_name=\"sys_config\" LIMIT 1' | grep -q 1", ] interval: 10s timeout: 5s From 1061020791daa068dca4058d18ba37c7829115c5 Mon Sep 17 00:00:00 2001 From: MHW Date: Mon, 13 Apr 2026 10:39:37 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=A4=8D=E5=88=B6=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../behaviour/BehaviortreeController.java | 12 +++++ .../web/core/BehaviortreeProcessor.java | 54 +------------------ .../system/service/IBehaviortreeService.java | 7 +++ .../service/impl/BehaviortreeServiceImpl.java | 20 +++++++ 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java b/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java index be0323a..9af1ee2 100644 --- a/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java +++ b/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java @@ -97,6 +97,18 @@ public class BehaviortreeController extends BaseController return toAjax(behaviortreeProcessor.create(behaviortree)); } + /** + * 复制行为树 + */ + @ApiOperation("复制行为树") + @PreAuthorize("@ss.hasPermi('system:behaviortree:add')") + @Log(title = "行为树主", businessType = BusinessType.INSERT) + @PostMapping("/copy") + public AjaxResult copy(@RequestBody Behaviortree behaviortree) + { + return toAjax(behaviortreeService.copy(behaviortree)); + } + /** * 修改行为树主 */ diff --git a/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java b/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java index 2c41d49..52f3bb2 100644 --- a/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java +++ b/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java @@ -20,6 +20,7 @@ import com.solution.web.core.graph.GraphEdge; import com.solution.web.core.graph.GraphNode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.ArrayList; @@ -56,6 +57,7 @@ public class BehaviortreeProcessor { return result; } + @Transactional(rollbackFor = Exception.class) public int update(Behaviortree behaviortree) { int result = behaviortreeService.updateBehaviortree(behaviortree); @@ -75,58 +77,6 @@ public class BehaviortreeProcessor { private void processGraph(Behaviortree behaviortree) { - Graph graph = null; - try { - graph = objectMapper.readValue(behaviortree.getXmlContent(), Graph.class); - } catch (Exception e) { - // skip - e.printStackTrace(); - } - - if (null == graph) { - return; - } - - // 插入节点 treenodeinstance - Map instanceKeyMap = new HashMap<>(); - Map nodeKeyIndexMap = new HashMap<>(); - Map nodesMap = new HashMap<>(); - if (graph.hasNodes()) { - Long index = 0L; - for (GraphNode node : graph.getNodes()) { - nodesMap.put(node.getKey(), node); - - Treenodeinstance instance = createNodeInstance(behaviortree, node); - treenodeinstanceService.insertTreenodeinstance(instance); - instanceKeyMap.put(node.getKey(), instance); - - if (node.hasParameters()) { - if (node.isMultiable()) { - List nodeparameters = createMultiableNodeparameter(behaviortree, node, instance); - for (Nodeparameter nodeparameter : nodeparameters) { - nodeparameterService.insertNodeparameter(nodeparameter); - } - } else { - // 插入parameter nodeparameter - for (Templateparameterdef parameter : node.getParameters()) { - Nodeparameter nodeparameter = createNodeParameter(behaviortree, parameter, instance); - nodeparameterService.insertNodeparameter(nodeparameter); - } - } - } - nodeKeyIndexMap.put(node.getKey(), index); - - index++; - } - } - - // 插入连线 nodeconnection - if (graph.hasEdges()) { - for (GraphEdge edge : graph.getEdges()) { - Nodeconnection connection = createConnection(behaviortree, edge, nodesMap, instanceKeyMap, nodeKeyIndexMap); - nodeconnectionService.insertNodeconnection(connection); - } - } } private Nodeconnection createConnection(Behaviortree behaviortree, GraphEdge edge, diff --git a/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java b/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java index 583124e..8e1479d 100644 --- a/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java +++ b/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java @@ -62,4 +62,11 @@ public interface IBehaviortreeService * @return 结果 */ public int deleteBehaviortreeById(Long id); + + /** + * 复制行为树 + * @param behaviortree + * @return + */ + int copy(Behaviortree behaviortree); } diff --git a/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java b/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java index 07b58ad..ebc3904 100644 --- a/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java +++ b/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java @@ -99,4 +99,24 @@ public class BehaviortreeServiceImpl implements IBehaviortreeService { return behaviortreeMapper.deleteBehaviortreeById(id); } + + /** + * 复制行为树 + * @param behaviortree + * @return + */ + @Override + public int copy(Behaviortree behaviortree) { + if(ObjectUtil.isEmpty(behaviortree)){ + throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION); + } + String name = behaviortree.getName(); + String newName = name + "_" + behaviortree.getId(); + + String englishName = behaviortree.getEnglishName(); + String newEnglishName = englishName + "_" + behaviortree.getId(); + behaviortree.setEnglishName(newEnglishName); + behaviortree.setName(newName); + return behaviortreeMapper.insertBehaviortree(behaviortree); + } } From c0da7bd0d9509fe708eec600be6853368b150bee Mon Sep 17 00:00:00 2001 From: yitaikarma Date: Mon, 13 Apr 2026 12:22:30 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E7=94=BB=E5=B8=83=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=B0=BA=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modeler/src/views/decision/graph/canvas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modeler/src/views/decision/graph/canvas.ts b/modeler/src/views/decision/graph/canvas.ts index 43b94fd..f737652 100644 --- a/modeler/src/views/decision/graph/canvas.ts +++ b/modeler/src/views/decision/graph/canvas.ts @@ -104,6 +104,7 @@ export const createGraphConnectingAttributes = (): Partial => { export const createGraphCanvas = (container: HTMLDivElement, readonly: boolean = false): Graph => { const graph = new Graph({ container: container, + autoResize: true, grid: { size: 20, visible: true, From 5276a6b16fdf0e345bd0a952abc998c1bf4e3725 Mon Sep 17 00:00:00 2001 From: MHW Date: Mon, 13 Apr 2026 14:19:18 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=A4=8D=E5=88=B6=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../behaviour/BehaviortreeController.java | 3 +- .../web/core/BehaviortreeProcessor.java | 73 +++++++++++++++++++ .../system/service/IBehaviortreeService.java | 7 -- .../service/impl/BehaviortreeServiceImpl.java | 20 ----- 4 files changed, 75 insertions(+), 28 deletions(-) diff --git a/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java b/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java index 9af1ee2..c664e37 100644 --- a/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java +++ b/auto-solution-admin/src/main/java/com/solution/web/controller/behaviour/BehaviortreeController.java @@ -106,7 +106,8 @@ public class BehaviortreeController extends BaseController @PostMapping("/copy") public AjaxResult copy(@RequestBody Behaviortree behaviortree) { - return toAjax(behaviortreeService.copy(behaviortree)); + //return toAjax(behaviortreeService.copy(behaviortree)); + return toAjax(behaviortreeProcessor.copy(behaviortree)); } /** diff --git a/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java b/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java index 52f3bb2..32185af 100644 --- a/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java +++ b/auto-solution-admin/src/main/java/com/solution/web/core/BehaviortreeProcessor.java @@ -8,8 +8,10 @@ package com.solution.web.core; * that was distributed with this source code. */ +import cn.hutool.core.util.ObjectUtil; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.solution.common.constant.ExceptionConstants; import com.solution.system.domain.*; import com.solution.system.service.IBehaviortreeService; import com.solution.system.service.INodeconnectionService; @@ -76,7 +78,59 @@ public class BehaviortreeProcessor { } private void processGraph(Behaviortree behaviortree) { + //代码丢失 原:libertyspy 改:MHW + Graph graph = null; + try { + graph = objectMapper.readValue(behaviortree.getXmlContent(), Graph.class); + } catch (Exception e) { + // skip + e.printStackTrace(); + } + if (null == graph) { + return; + } + + // 插入节点 treenodeinstance + Map instanceKeyMap = new HashMap<>(); + Map nodeKeyIndexMap = new HashMap<>(); + Map nodesMap = new HashMap<>(); + if (graph.hasNodes()) { + Long index = 0L; + for (GraphNode node : graph.getNodes()) { + nodesMap.put(node.getKey(), node); + + Treenodeinstance instance = createNodeInstance(behaviortree, node); + treenodeinstanceService.insertTreenodeinstance(instance); + instanceKeyMap.put(node.getKey(), instance); + + if (node.hasParameters()) { + if (node.isMultiable()) { + List nodeparameters = createMultiableNodeparameter(behaviortree, node, instance); + for (Nodeparameter nodeparameter : nodeparameters) { + nodeparameterService.insertNodeparameter(nodeparameter); + } + } else { + // 插入parameter nodeparameter + for (Templateparameterdef parameter : node.getParameters()) { + Nodeparameter nodeparameter = createNodeParameter(behaviortree, parameter, instance); + nodeparameterService.insertNodeparameter(nodeparameter); + } + } + } + nodeKeyIndexMap.put(node.getKey(), index); + + index++; + } + } + + // 插入连线 nodeconnection + if (graph.hasEdges()) { + for (GraphEdge edge : graph.getEdges()) { + Nodeconnection connection = createConnection(behaviortree, edge, nodesMap, instanceKeyMap, nodeKeyIndexMap); + nodeconnectionService.insertNodeconnection(connection); + } + } } private Nodeconnection createConnection(Behaviortree behaviortree, GraphEdge edge, @@ -173,4 +227,23 @@ public class BehaviortreeProcessor { } + /** + * 复制行为树 + * @param behaviortree + * @return + */ + public int copy(Behaviortree behaviortree) { + if(ObjectUtil.isEmpty(behaviortree)){ + throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION); + } + String name = behaviortree.getName(); + String newName = name + "_" + behaviortree.getId(); + + String englishName = behaviortree.getEnglishName(); + String newEnglishName = englishName + "_" + behaviortree.getId(); + behaviortree.setEnglishName(newEnglishName); + behaviortree.setName(newName); + + return this.create(behaviortree); + } } diff --git a/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java b/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java index 8e1479d..583124e 100644 --- a/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java +++ b/auto-solution-behaviour/src/main/java/com/solution/system/service/IBehaviortreeService.java @@ -62,11 +62,4 @@ public interface IBehaviortreeService * @return 结果 */ public int deleteBehaviortreeById(Long id); - - /** - * 复制行为树 - * @param behaviortree - * @return - */ - int copy(Behaviortree behaviortree); } diff --git a/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java b/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java index ebc3904..07b58ad 100644 --- a/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java +++ b/auto-solution-behaviour/src/main/java/com/solution/system/service/impl/BehaviortreeServiceImpl.java @@ -99,24 +99,4 @@ public class BehaviortreeServiceImpl implements IBehaviortreeService { return behaviortreeMapper.deleteBehaviortreeById(id); } - - /** - * 复制行为树 - * @param behaviortree - * @return - */ - @Override - public int copy(Behaviortree behaviortree) { - if(ObjectUtil.isEmpty(behaviortree)){ - throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION); - } - String name = behaviortree.getName(); - String newName = name + "_" + behaviortree.getId(); - - String englishName = behaviortree.getEnglishName(); - String newEnglishName = englishName + "_" + behaviortree.getId(); - behaviortree.setEnglishName(newEnglishName); - behaviortree.setName(newName); - return behaviortreeMapper.insertBehaviortree(behaviortree); - } } From 3946bc561a605898d326056035d6c88186eb58fd Mon Sep 17 00:00:00 2001 From: yitaikarma Date: Mon, 13 Apr 2026 14:25:50 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E6=A0=91=E5=A4=8D=E5=88=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/decision/designer/trees-card.vue | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modeler/src/views/decision/designer/trees-card.vue b/modeler/src/views/decision/designer/trees-card.vue index 6354bb1..2cced9e 100644 --- a/modeler/src/views/decision/designer/trees-card.vue +++ b/modeler/src/views/decision/designer/trees-card.vue @@ -29,6 +29,12 @@ + + + import { defineComponent, onMounted, ref } from 'vue'; -import { CheckOutlined, DeleteOutlined, EditFilled, PlusOutlined } from '@ant-design/icons-vue'; +import { CheckOutlined, CopyOutlined, DeleteOutlined, EditFilled, PlusOutlined } from '@ant-design/icons-vue'; import type { BehaviorTree, BehaviorTreeRequest } from './tree'; -import { deleteOneTreeById, findTreesByQuery } from './api'; +import { copyTree, deleteOneTreeById, findTreesByQuery } from './api'; import { substring } from '@/utils/strings'; export default defineComponent({ emits: ['select-tree', 'create-tree'], components: { CheckOutlined, + CopyOutlined, PlusOutlined, DeleteOutlined, EditFilled, @@ -95,6 +102,14 @@ export default defineComponent({ }); }; + const handleCopy = (item: BehaviorTree) => { + copyTree({ id: item.id }).then(r => { + if (r.code === 200) { + loadTress(); + } + }); + }; + const columns = [ { title: '名称', @@ -133,6 +148,7 @@ export default defineComponent({ handleSelect, handleChange, handleDelete, + handleCopy, }; }, });