diff --git a/auto-solution-admin/src/main/java/com/solution/web/controller/rule/RuleController.java b/auto-solution-admin/src/main/java/com/solution/web/controller/rule/RuleController.java index f55335a..6b8a2b2 100644 --- a/auto-solution-admin/src/main/java/com/solution/web/controller/rule/RuleController.java +++ b/auto-solution-admin/src/main/java/com/solution/web/controller/rule/RuleController.java @@ -8,6 +8,7 @@ import com.solution.common.enums.BusinessType; import com.solution.rule.domain.Rule; import com.solution.rule.domain.config.RuleConfig; import com.solution.rule.domain.config.RuleConfigQuery; +import com.solution.rule.domain.config.RuleParamMeta; import com.solution.rule.service.IRuleService; import com.solution.rule.service.IRuleConfigService; import io.swagger.annotations.Api; @@ -21,14 +22,14 @@ import java.util.List; @Api("红蓝对抗规则管理") @RestController @RequestMapping("/api/system/rule") -public class RuleController extends BaseController { + public class RuleController extends BaseController { @Autowired private IRuleService ruleService; @Autowired private IRuleConfigService ruleConfigService; - @PreAuthorize("@ss.hasPermi('system:rule:list')") + /*@PreAuthorize("@ss.hasPermi('system:rule:list')") @GetMapping("/list") @ApiOperation("查询规则列表") public TableDataInfo list(Rule rule) { @@ -66,7 +67,7 @@ public class RuleController extends BaseController { @ApiOperation("删除规则") public AjaxResult remove(@PathVariable Integer[] ids) { return toAjax(ruleService.deleteRuleByIds(ids)); - } + }*/ @PreAuthorize("@ss.hasPermi('system:rule:list')") @GetMapping("/config/list") @@ -113,4 +114,12 @@ public class RuleController extends BaseController { public AjaxResult dict(@PathVariable String dictType) { return success(ruleConfigService.selectDictByType(dictType)); } + + @PreAuthorize("@ss.hasPermi('system:rule:query')") + @GetMapping("/config/param-meta") + @ApiOperation("查询参数元数据") + public AjaxResult paramMeta() { + List metas = ruleConfigService.selectParamMetaList(); + return success(metas); + } } \ No newline at end of file diff --git a/auto-solution-admin/src/main/resources/application-druid.yml b/auto-solution-admin/src/main/resources/application-druid.yml index 3cfb6ea..6afff3b 100644 --- a/auto-solution-admin/src/main/resources/application-druid.yml +++ b/auto-solution-admin/src/main/resources/application-druid.yml @@ -8,7 +8,7 @@ spring: master: url: jdbc:mysql://127.0.0.1:3306/autosolution_db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root - password: 123456 + password: 1234 # 从库数据源 slave: # 从数据源开关/默认关闭 diff --git a/auto-solution-behaviour/src/main/resources/mapper/system/BehaviortreeMapper.xml b/auto-solution-behaviour/src/main/resources/mapper/system/BehaviortreeMapper.xml index be73fe6..67d0872 100644 --- a/auto-solution-behaviour/src/main/resources/mapper/system/BehaviortreeMapper.xml +++ b/auto-solution-behaviour/src/main/resources/mapper/system/BehaviortreeMapper.xml @@ -23,9 +23,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, name, description, created_at, updated_at, english_name, xml_content from behaviortree + select id, name, description, created_at, updated_at, english_name, xml_content, platform_id, scenario_id from behaviortree + diff --git a/auto-solution-behaviour/src/main/resources/mapper/system/PlatformCommunicationMapper.xml b/auto-solution-behaviour/src/main/resources/mapper/system/PlatformCommunicationMapper.xml index c9e9a11..b8a9278 100644 --- a/auto-solution-behaviour/src/main/resources/mapper/system/PlatformCommunicationMapper.xml +++ b/auto-solution-behaviour/src/main/resources/mapper/system/PlatformCommunicationMapper.xml @@ -5,7 +5,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - SELECT subordinate_platform FROM platform_communication WHERE subordinate_platform = #{name} diff --git a/auto-solution-rule/src/main/java/com/solution/rule/domain/config/RuleParamMeta.java b/auto-solution-rule/src/main/java/com/solution/rule/domain/config/RuleParamMeta.java new file mode 100644 index 0000000..c874a48 --- /dev/null +++ b/auto-solution-rule/src/main/java/com/solution/rule/domain/config/RuleParamMeta.java @@ -0,0 +1,42 @@ +package com.solution.rule.domain.config; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel("规则参数元数据") +public class RuleParamMeta { + + @ApiModelProperty("参数键") + private String paramKey; + + @ApiModelProperty("参数名称") + private String label; + + @ApiModelProperty("值类型(bool/number/enum/csv/string)") + private String valueType; + + @ApiModelProperty("是否必填") + private Boolean required; + + @ApiModelProperty("枚举可选值") + private List enumOptions; + + @ApiModelProperty("数值最小值") + private Double min; + + @ApiModelProperty("数值最大值") + private Double max; + + @ApiModelProperty("格式正则") + private String pattern; + + @ApiModelProperty("示例值") + private String example; + + @ApiModelProperty("说明") + private String description; +} diff --git a/auto-solution-rule/src/main/java/com/solution/rule/mapper/RuleConfigMapper.java b/auto-solution-rule/src/main/java/com/solution/rule/mapper/RuleConfigMapper.java index 9f3575c..d13b8cf 100644 --- a/auto-solution-rule/src/main/java/com/solution/rule/mapper/RuleConfigMapper.java +++ b/auto-solution-rule/src/main/java/com/solution/rule/mapper/RuleConfigMapper.java @@ -35,4 +35,6 @@ public interface RuleConfigMapper { int insertTaskTypesBatch(@Param("ruleCode") String ruleCode, @Param("taskTypes") List taskTypes); List selectDictByType(@Param("dictType") String dictType); + + List selectEnabledParamsForGlobal(); } diff --git a/auto-solution-rule/src/main/java/com/solution/rule/service/IRuleConfigService.java b/auto-solution-rule/src/main/java/com/solution/rule/service/IRuleConfigService.java index 2ef3add..5d37539 100644 --- a/auto-solution-rule/src/main/java/com/solution/rule/service/IRuleConfigService.java +++ b/auto-solution-rule/src/main/java/com/solution/rule/service/IRuleConfigService.java @@ -3,8 +3,10 @@ package com.solution.rule.service; import com.solution.rule.domain.config.RuleConfig; import com.solution.rule.domain.config.RuleConfigQuery; import com.solution.rule.domain.config.RuleDictItem; +import com.solution.rule.domain.config.RuleParamMeta; import java.util.List; +import java.util.Map; public interface IRuleConfigService { @@ -19,4 +21,8 @@ public interface IRuleConfigService { int deleteRuleConfigByCodes(String[] ruleCodes); List selectDictByType(String dictType); + + Map loadEnabledGlobalParams(); + + List selectParamMetaList(); } diff --git a/auto-solution-rule/src/main/java/com/solution/rule/service/RuleDrlSyncService.java b/auto-solution-rule/src/main/java/com/solution/rule/service/RuleDrlSyncService.java new file mode 100644 index 0000000..b5376c1 --- /dev/null +++ b/auto-solution-rule/src/main/java/com/solution/rule/service/RuleDrlSyncService.java @@ -0,0 +1,6 @@ +package com.solution.rule.service; + +public interface RuleDrlSyncService { + + void syncGlobalParamsToDrl(); +} diff --git a/auto-solution-rule/src/main/java/com/solution/rule/service/impl/FireRuleServiceImpl.java b/auto-solution-rule/src/main/java/com/solution/rule/service/impl/FireRuleServiceImpl.java index 313aaca..5870453 100644 --- a/auto-solution-rule/src/main/java/com/solution/rule/service/impl/FireRuleServiceImpl.java +++ b/auto-solution-rule/src/main/java/com/solution/rule/service/impl/FireRuleServiceImpl.java @@ -21,6 +21,7 @@ import com.solution.rule.domain.vo.PlatformWeaponAggregateVO; import com.solution.rule.domain.vo.WeaponModelVO; import com.solution.rule.mapper.FireRuleMapper; import com.solution.rule.service.FireRuleService; +import com.solution.rule.service.IRuleConfigService; import com.solution.rule.simpstrategy.FireRUleType; import com.solution.rule.simpstrategy.FireRuleStrategy; import com.solution.rule.simpstrategy.FireRuleStrategyFactory; @@ -51,6 +52,8 @@ public class FireRuleServiceImpl implements FireRuleService { @Autowired private KieBase kieBase; + @Autowired + private IRuleConfigService ruleConfigService; /* @Override public WeaponModelVO execute(Integer sceneType, WeaponModelDTO weaponModelDTO) { @@ -209,7 +212,8 @@ public class FireRuleServiceImpl implements FireRuleService { } //创建KieSession KieSession kieSession = kieBase.newKieSession(); - Map globalParams = new HashMap<>(); + // Map globalParams = new HashMap<>(); + Map globalParams = ruleConfigService.loadEnabledGlobalParams(); // globalParams.putAll(FireRuleMatchDefaultParams.defaults()); kieSession.setGlobal("globalParams", globalParams); //获取红方阵营id diff --git a/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleConfigServiceImpl.java b/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleConfigServiceImpl.java index 7c68f0d..42d1065 100644 --- a/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleConfigServiceImpl.java +++ b/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleConfigServiceImpl.java @@ -7,21 +7,27 @@ import com.solution.rule.domain.config.RuleConfig; import com.solution.rule.domain.config.RuleConfigParam; import com.solution.rule.domain.config.RuleConfigQuery; import com.solution.rule.domain.config.RuleDictItem; +import com.solution.rule.domain.config.RuleParamMeta; import com.solution.rule.mapper.RuleConfigMapper; import com.solution.rule.service.IRuleConfigService; +import com.solution.rule.service.RuleDrlSyncService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.regex.Pattern; @Service public class RuleConfigServiceImpl implements IRuleConfigService { + private static final Pattern RULE_SLOT_KEYS = Pattern.compile("^(blueRuleKeywords|redRuleKeywords|ruleScore)_\\d+$"); + private static final boolean ALLOW_UNKNOWN_PARAM_KEY = false; + @Autowired private RuleConfigMapper ruleConfigMapper; + @Autowired + private RuleDrlSyncService ruleDrlSyncService; @Override public List selectRuleConfigList(RuleConfigQuery query) { @@ -48,6 +54,8 @@ public class RuleConfigServiceImpl implements IRuleConfigService { } int rows = ruleConfigMapper.insertRuleConfig(fillDefault(ruleConfig)); saveChildren(ruleConfig); + // return rows; + syncDrlAfterCrud(); return rows; } @@ -63,6 +71,8 @@ public class RuleConfigServiceImpl implements IRuleConfigService { ruleConfigMapper.deleteParamsByRuleCodes(ruleCodes); ruleConfigMapper.deleteTaskTypesByRuleCodes(ruleCodes); saveChildren(ruleConfig); + // return rows; + syncDrlAfterCrud(); return rows; } @@ -74,7 +84,10 @@ public class RuleConfigServiceImpl implements IRuleConfigService { } ruleConfigMapper.deleteParamsByRuleCodes(ruleCodes); ruleConfigMapper.deleteTaskTypesByRuleCodes(ruleCodes); - return ruleConfigMapper.deleteRuleConfigByCodes(ruleCodes); + // return ruleConfigMapper.deleteRuleConfigByCodes(ruleCodes); + int rows = ruleConfigMapper.deleteRuleConfigByCodes(ruleCodes); + syncDrlAfterCrud(); + return rows; } @Override @@ -85,6 +98,27 @@ public class RuleConfigServiceImpl implements IRuleConfigService { return ruleConfigMapper.selectDictByType(dictType); } + @Override + public Map loadEnabledGlobalParams() { + Map map = new HashMap<>(); + List params = ruleConfigMapper.selectEnabledParamsForGlobal(); + if (CollUtil.isEmpty(params)) { + return map; + } + for (RuleConfigParam param : params) { + if (param == null || ObjectUtil.isEmpty(param.getParamKey())) { + continue; + } + map.put(param.getParamKey(), parseValue(param.getParamVal(), param.getValType())); + } + return map; + } + + @Override + public List selectParamMetaList() { + return new ArrayList<>(metaMap().values()); + } + private void saveChildren(RuleConfig ruleConfig) { if (CollUtil.isNotEmpty(ruleConfig.getParams())) { Set keys = new HashSet<>(); @@ -95,6 +129,7 @@ public class RuleConfigServiceImpl implements IRuleConfigService { if (!keys.add(param.getParamKey())) { throw new RuntimeException("参数键重复: " + param.getParamKey()); } + validateParam(param); param.setRuleCode(ruleConfig.getRuleCode()); if (param.getSortNo() == null) { param.setSortNo(0); @@ -136,4 +171,200 @@ public class RuleConfigServiceImpl implements IRuleConfigService { throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION); } } + + private void syncDrlAfterCrud() { + ruleDrlSyncService.syncGlobalParamsToDrl(); + } + + private Object parseValue(String val, String valType) { + if ("bool".equalsIgnoreCase(valType) || "boolean".equalsIgnoreCase(valType)) { + return Boolean.parseBoolean(val); + } + if ("number".equalsIgnoreCase(valType)) { + if (val == null || val.trim().isEmpty()) { + return 0; + } + String t = val.trim(); + if (t.contains(".")) { + try { + return Double.parseDouble(t); + } catch (Exception ignore) { + return t; + } + } + try { + return Integer.parseInt(t); + } catch (Exception ignore) { + return t; + } + } + // 多值按英文逗号分隔时保持字符串原样,不做拆分 + return val; + } + + private void validateParam(RuleConfigParam param) { + RuleParamMeta meta = resolveMeta(param.getParamKey()); + if (meta == null) { + if (ALLOW_UNKNOWN_PARAM_KEY) { + param.setValType("string"); + return; + } + throw new RuntimeException("不支持的参数键: " + param.getParamKey()); + } + String val = param.getParamVal(); + if (Boolean.TRUE.equals(meta.getRequired()) && ObjectUtil.isEmpty(val)) { + throw new RuntimeException("参数值不能为空: " + param.getParamKey()); + } + if ("bool".equalsIgnoreCase(meta.getValueType())) { + if (!"true".equalsIgnoreCase(String.valueOf(val)) && !"false".equalsIgnoreCase(String.valueOf(val))) { + throw new RuntimeException("布尔参数仅支持 true/false: " + param.getParamKey()); + } + param.setValType("bool"); + return; + } + if ("enum".equalsIgnoreCase(meta.getValueType())) { + if (CollUtil.isEmpty(meta.getEnumOptions()) || !meta.getEnumOptions().contains(val)) { + throw new RuntimeException("参数值不在可选范围内: " + param.getParamKey()); + } + param.setValType("string"); + return; + } + if ("number".equalsIgnoreCase(meta.getValueType())) { + try { + double d = Double.parseDouble(String.valueOf(val)); + if (meta.getMin() != null && d < meta.getMin()) { + throw new RuntimeException("参数值小于最小值: " + param.getParamKey()); + } + if (meta.getMax() != null && d > meta.getMax()) { + throw new RuntimeException("参数值大于最大值: " + param.getParamKey()); + } + } catch (NumberFormatException e) { + throw new RuntimeException("数值参数格式错误: " + param.getParamKey()); + } + param.setValType("number"); + return; + } + if ("csv".equalsIgnoreCase(meta.getValueType())) { + if (meta.getPattern() != null && !Pattern.matches(meta.getPattern(), String.valueOf(val))) { + throw new RuntimeException("CSV 参数格式错误(英文逗号分隔): " + param.getParamKey()); + } + param.setValType("string"); + return; + } + param.setValType("string"); + } + + private RuleParamMeta resolveMeta(String key) { + RuleParamMeta direct = metaMap().get(key); + if (direct != null) { + return direct; + } + if (RULE_SLOT_KEYS.matcher(key).matches()) { + RuleParamMeta slotMeta = new RuleParamMeta(); + slotMeta.setParamKey(key); + slotMeta.setLabel("规则槽动态参数"); + slotMeta.setValueType(key.startsWith("ruleScore_") ? "number" : "csv"); + slotMeta.setRequired(Boolean.TRUE); + slotMeta.setPattern("^[^,]+(?:,[^,]+)*$"); + slotMeta.setDescription("支持 blueRuleKeywords_i/redRuleKeywords_i/ruleScore_i"); + if (key.startsWith("ruleScore_")) { + slotMeta.setMin(0d); + } + return slotMeta; + } + return null; + } + + private Map metaMap() { + Map map = new LinkedHashMap<>(); + map.put("executeTypeDefault", meta("executeTypeDefault", "执行类型", "enum", true, Arrays.asList("assault", "strike_test", "my_test_type"), null, null, null, "assault", "execute[0].type")); + map.put("positionRuleEnabled", meta("positionRuleEnabled", "阵位规则开关", "bool", true, null, null, null, null, "true", "是否执行阵位生成")); + map.put("trackRuleEnabled", meta("trackRuleEnabled", "航迹规则开关", "bool", true, null, null, null, null, "true", "是否执行航迹生成")); + map.put("groupRuleEnabled", meta("groupRuleEnabled", "编组规则开关", "bool", true, null, null, null, null, "true", "是否执行编组生成")); + map.put("enableTrackWarZoneClamp", meta("enableTrackWarZoneClamp", "航迹作战区约束开关", "bool", true, null, null, null, null, "true", "是否对航迹点进行作战区约束")); + map.put("enableWarZoneClamp", meta("enableWarZoneClamp", "阵位作战区约束开关", "bool", true, null, null, null, null, "true", "是否对阵位点进行作战区约束")); + map.put("targetPickMode", meta("targetPickMode", "目标分配模式", "enum", true, Arrays.asList("roundRobin", "random"), null, null, null, "roundRobin", "目标挑选方式")); + map.put("formationType", meta("formationType", "编队样式", "enum", true, Arrays.asList("line", "wedge", "circle"), null, null, null, "line", "平台编队样式")); + map.put("trackRouteAlgorithm", meta("trackRouteAlgorithm", "航迹算法", "enum", true, Arrays.asList("followBlue", "shortestPath", "flank", "jam"), null, null, null, "followBlue", "航迹变形算法")); + map.put("trackFlankSideMode", meta("trackFlankSideMode", "flank侧向模式", "enum", true, Arrays.asList("alternate", "left", "right"), null, null, null, "alternate", "侧向策略")); + map.put("groupFormationMode", meta("groupFormationMode", "编组模式", "enum", true, Arrays.asList("onePerRed", "clusterByCount", "singleGroup"), null, null, null, "onePerRed", "编组策略")); + map.put("groupLeaderPickMode", meta("groupLeaderPickMode", "领队选择模式", "enum", true, Arrays.asList("byHitRateThenId", "byId"), null, null, null, "byHitRateThenId", "领队策略")); + + map.put("weight", meta("weight", "全局权重", "number", true, null, 0d, 100d, null, "1", "评分乘数")); + map.put("minSelectedScore", meta("minSelectedScore", "最小选中分", "number", true, null, 0d, 100000d, null, "1", "低于该分不选中")); + map.put("minTargetsPerRed", meta("minTargetsPerRed", "每红装最少目标数", "number", true, null, 1d, 20d, null, "1", "目标分配下限")); + map.put("maxTargetsPerRedCap", meta("maxTargetsPerRedCap", "每红装最多目标数", "number", true, null, 1d, 50d, null, "3", "目标分配上限")); + map.put("redHitRateThreshold", meta("redHitRateThreshold", "命中率阈值", "number", true, null, 0d, 1d, null, "0.6", "低于阈值触发补拿")); + map.put("maxExtraWeaponsPerTask", meta("maxExtraWeaponsPerTask", "补拿装备上限", "number", true, null, 0d, 20d, null, "2", "每任务补拿数量")); + map.put("maxSupplementRounds", meta("maxSupplementRounds", "补拿轮次上限", "number", true, null, 0d, 20d, null, "2", "补拿循环轮次")); + map.put("extraPickMinScore", meta("extraPickMinScore", "补拿最低分", "number", true, null, 0d, 100000d, null, "1", "补拿分数门槛")); + map.put("deployDistanceKmMin", meta("deployDistanceKmMin", "部署距离最小值(km)", "number", true, null, 0d, 1000d, null, "8", "部署距离下限")); + map.put("deployDistanceKmMax", meta("deployDistanceKmMax", "部署距离最大值(km)", "number", true, null, 0d, 1000d, null, "30", "部署距离上限")); + map.put("deployDistanceKmDefault", meta("deployDistanceKmDefault", "默认部署距离(km)", "number", true, null, 0d, 1000d, null, "15", "默认部署距离")); + map.put("formationSpacingMeters", meta("formationSpacingMeters", "编队间距(米)", "number", true, null, 1d, 100000d, null, "300", "编队间距")); + map.put("formationHeadingOffsetDeg", meta("formationHeadingOffsetDeg", "编队偏转角(度)", "number", true, null, 0d, 360d, null, "15", "编队航向偏移")); + map.put("defaultDeployHeight", meta("defaultDeployHeight", "默认部署高度(米)", "number", true, null, -10000d, 100000d, null, "30", "默认部署高度")); + map.put("heightFollowBlueRatio", meta("heightFollowBlueRatio", "高度跟随比例", "number", true, null, 0d, 100d, null, "0.0", "高度跟随系数")); + map.put("minInterPlatformDistanceMeters", meta("minInterPlatformDistanceMeters", "最小平台间距(米)", "number", true, null, 0d, 100000d, null, "80", "平台最小间距")); + map.put("trackFallbackBearingDeg", meta("trackFallbackBearingDeg", "航迹默认回退方位角", "number", true, null, 0d, 360d, null, "0", "航迹回退方位")); + map.put("fallbackBearingDeg", meta("fallbackBearingDeg", "默认回退方位角", "number", true, null, 0d, 360d, null, "0", "阵位回退方位")); + map.put("trackExtraNodesMax", meta("trackExtraNodesMax", "航迹额外插点数", "number", true, null, 0d, 1000d, null, "0", "额外插值节点")); + map.put("trackShortPathSegments", meta("trackShortPathSegments", "短路径分段数", "number", true, null, 1d, 1000d, null, "3", "最短路径分段")); + map.put("trackFlankOffsetMeters", meta("trackFlankOffsetMeters", "flank偏移(米)", "number", true, null, 0d, 100000d, null, "800", "侧向偏移")); + map.put("trackJamWobbleMeters", meta("trackJamWobbleMeters", "jam摆动振幅(米)", "number", true, null, 0d, 100000d, null, "400", "正弦扰动振幅")); + map.put("trackJamSegments", meta("trackJamSegments", "jam周期数", "number", true, null, 1d, 1000d, null, "4", "正弦周期")); + map.put("groupClusterSize", meta("groupClusterSize", "编组人数上限", "number", true, null, 1d, 1000d, null, "3", "固定人数编组上限")); + map.put("groupMinMembersForWingman", meta("groupMinMembersForWingman", "生成僚机最小人数", "number", true, null, 1d, 1000d, null, "2", "僚机人数阈值")); + map.put("wingmanDistanceBaseMeters", meta("wingmanDistanceBaseMeters", "僚机基础距离(米)", "number", true, null, 0d, 100000d, null, "100", "僚机基础距离")); + map.put("wingmanDistanceStepMeters", meta("wingmanDistanceStepMeters", "僚机距离步长(米)", "number", true, null, 0d, 100000d, null, "50", "僚机距离步长")); + map.put("wingmanAngleBaseDeg", meta("wingmanAngleBaseDeg", "僚机基础角度(度)", "number", true, null, 0d, 360d, null, "50", "僚机基础角度")); + map.put("wingmanAngleStepDeg", meta("wingmanAngleStepDeg", "僚机角度步长(度)", "number", true, null, 0d, 360d, null, "15", "僚机角度步长")); + map.put("wingmanAltBaseMeters", meta("wingmanAltBaseMeters", "僚机基础高度(米)", "number", true, null, -10000d, 100000d, null, "40", "僚机基础高度")); + map.put("wingmanAltScale", meta("wingmanAltScale", "僚机高度缩放", "number", true, null, 0d, 100d, null, "1.0", "僚机高度系数")); + map.put("minRangeToAllowAssignKm", meta("minRangeToAllowAssignKm", "允许分配最小射程(km)", "number", true, null, 0d, 100000d, null, "0", "射程过滤阈值")); + + map.put("tieBreak", meta("tieBreak", "并列决策方式", "enum", true, Arrays.asList("equipmentId"), null, null, null, "equipmentId", "并列评分决策")); + map.put("positionAnchorMode", meta("positionAnchorMode", "阵位锚点模式", "enum", true, Arrays.asList("hybrid"), null, null, null, "hybrid", "当前仅支持hybrid")); + map.put("trackPointDirectionMode", meta("trackPointDirectionMode", "航向计算模式", "enum", true, Arrays.asList("head2next", "tail2prev"), null, null, null, "head2next", "航向计算方式")); + map.put("warZoneClampMode", meta("warZoneClampMode", "作战区约束模式", "enum", true, Arrays.asList("nearestInside"), null, null, null, "nearestInside", "当前仅支持nearestInside")); + map.put("trackRouteNameSuffix", meta("trackRouteNameSuffix", "航迹名称后缀", "string", true, null, null, null, null, "航迹", "航迹名称后缀")); + map.put("groupDrawNameSuffix", meta("groupDrawNameSuffix", "编组名称后缀", "string", true, null, null, null, null, "编组", "编组名称后缀")); + map.put("groupDrawNameWithIndex", meta("groupDrawNameWithIndex", "编组名称带序号", "bool", true, null, null, null, null, "false", "是否追加序号")); + map.put("outputDrawNameSuffix", meta("outputDrawNameSuffix", "任务名称后缀", "string", true, null, null, null, null, "打击任务", "装备匹配后的名称后缀")); + map.put("trackGroundTrackType", meta("trackGroundTrackType", "地面航迹类型", "string", true, null, null, null, null, "routeLineGround", "非飞行航迹类型")); + map.put("rangeUnit", meta("rangeUnit", "射程单位", "enum", true, Arrays.asList("km", "m"), null, null, null, "km", "射程单位")); + + map.put("bluePlatformKeywords_air", meta("bluePlatformKeywords_air", "蓝方空中平台关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "F-16,J-10,F-35", "英文逗号分隔")); + map.put("redPreferredWhenBlueAir", meta("redPreferredWhenBlueAir", "红方空中偏好关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "防空,导弹,无人机", "英文逗号分隔")); + map.put("airTaskKeywords", meta("airTaskKeywords", "空中任务关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "空中,制空,拦截,空战", "英文逗号分隔")); + map.put("groundTaskKeywords", meta("groundTaskKeywords", "地面任务关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "地面,突击,登陆", "英文逗号分隔")); + map.put("redPreferredWhenGround", meta("redPreferredWhenGround", "红方地面偏好关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "远火,榴弹,炮,火箭", "英文逗号分隔")); + map.put("tankKeywords", meta("tankKeywords", "坦克关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "坦克,装甲", "英文逗号分隔")); + map.put("redMatchKeywords_tank", meta("redMatchKeywords_tank", "红方反坦克关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "反坦克", "英文逗号分隔")); + map.put("missileKeywords", meta("missileKeywords", "导弹关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "导弹,火箭弹,巡航", "英文逗号分隔")); + map.put("redMatchKeywords_missile", meta("redMatchKeywords_missile", "红方导弹匹配关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "防空,导弹,导弹发射", "英文逗号分隔")); + map.put("radToTargetsCsv", meta("radToTargetsCsv", "命中率映射", "string", true, null, null, null, "^\\d+(?:\\.\\d+)?:\\d+(?:,\\d+(?:\\.\\d+)?:\\d+)*$", "0.8:1,0.5:2,0.2:3", "阈值:目标数,英文逗号分隔")); + map.put("distanceByPlatformCsv", meta("distanceByPlatformCsv", "按平台部署距离映射", "string", false, null, null, null, "^(|[^,:]+:\\d+(?:\\.\\d+)?(?:,[^,:]+:\\d+(?:\\.\\d+)?)*?)$", "防空:18,反坦克:10", "关键词:距离,英文逗号分隔")); + map.put("trackAirDataTypeCsv", meta("trackAirDataTypeCsv", "空中dataType关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "taskPlane,air,plane,flight", "英文逗号分隔")); + map.put("trackAirKeywordsCsv", meta("trackAirKeywordsCsv", "空中关键词", "csv", true, null, null, null, "^[^,]+(?:,[^,]+)*$", "机,飞,空,J-,F-", "英文逗号分隔")); + map.put("rangeParseRegex", meta("rangeParseRegex", "射程提取正则", "string", true, null, null, null, null, "(\\\\d+(?:\\\\.\\\\d+)?)", "Java正则表达式")); + + return map; + } + + private RuleParamMeta meta(String key, String label, String valueType, boolean required, List enumOptions, + Double min, Double max, String pattern, String example, String description) { + RuleParamMeta m = new RuleParamMeta(); + m.setParamKey(key); + m.setLabel(label); + m.setValueType(valueType); + m.setRequired(required); + m.setEnumOptions(enumOptions); + m.setMin(min); + m.setMax(max); + m.setPattern(pattern); + m.setExample(example); + m.setDescription(description); + return m; + } } diff --git a/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleDrlSyncServiceImpl.java b/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleDrlSyncServiceImpl.java new file mode 100644 index 0000000..6ed4557 --- /dev/null +++ b/auto-solution-rule/src/main/java/com/solution/rule/service/impl/RuleDrlSyncServiceImpl.java @@ -0,0 +1,105 @@ +package com.solution.rule.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.solution.rule.domain.config.RuleConfigParam; +import com.solution.rule.mapper.RuleConfigMapper; +import com.solution.rule.service.RuleDrlSyncService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +@Service +public class RuleDrlSyncServiceImpl implements RuleDrlSyncService { + + @Autowired + private RuleConfigMapper ruleConfigMapper; + + @Override + public void syncGlobalParamsToDrl() { + Path drlPath = resolveDrlPath(); + try { + String content = Files.readString(drlPath, StandardCharsets.UTF_8); + String generated = generateParamPutLines(ruleConfigMapper.selectEnabledParamsForGlobal()); + String newContent = replaceBuildParamBody(content, generated); + Files.writeString(drlPath, newContent, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException("同步 rule.drl 失败: " + e.getMessage(), e); + } + } + + private Path resolveDrlPath() { + Path root = Paths.get(System.getProperty("user.dir")); + Path path = root.resolve("auto-solution-rule/src/main/resources/rules/rule.drl"); + if (Files.exists(path)) { + return path; + } + Path fallback = root.resolve("src/main/resources/rules/rule.drl"); + if (Files.exists(fallback)) { + return fallback; + } + throw new RuntimeException("未找到 rule.drl 文件路径"); + } + + private String generateParamPutLines(List params) { + StringBuilder sb = new StringBuilder(); + sb.append(" // ===== 以下由规则配置表自动同步生成(请勿手改 param.put 段) =====\n"); + if (CollUtil.isEmpty(params)) { + return sb.toString(); + } + for (RuleConfigParam param : params) { + if (param == null || param.getParamKey() == null) { + continue; + } + String valueExpr = toDrlValueExpr(param.getParamVal(), param.getValType()); + sb.append(" param.put(\"") + .append(escapeJavaString(param.getParamKey())) + .append("\", ") + .append(valueExpr) + .append(");\n"); + } + return sb.toString(); + } + + private String toDrlValueExpr(String val, String valType) { + if ("bool".equalsIgnoreCase(valType) || "boolean".equalsIgnoreCase(valType)) { + return String.valueOf(Boolean.parseBoolean(val)); + } + if ("number".equalsIgnoreCase(valType)) { + if (val == null || val.trim().isEmpty()) { + return "0"; + } + return val.trim(); + } + // string/json 统一按字符串写入,多个值用英文逗号分隔时保持原样,不做拆分 + return "\"" + escapeJavaString(val == null ? "" : val) + "\""; + } + + private String replaceBuildParamBody(String content, String generatedLines) { + String marker = " // ===== 以下由规则配置表自动同步生成(请勿手改 param.put 段) ====="; + int buildParamStart = content.indexOf("function Map buildParam(){"); + int returnPos = content.indexOf(" return param;", buildParamStart); + if (buildParamStart < 0 || returnPos < 0) { + throw new RuntimeException("rule.drl 中未找到 buildParam 函数结构"); + } + + int oldMarker = content.indexOf(marker, buildParamStart); + int insertFrom; + if (oldMarker > 0 && oldMarker < returnPos) { + insertFrom = oldMarker; + } else { + // 首次同步:保留原内容,追加自动生成段 + insertFrom = returnPos; + } + return content.substring(0, insertFrom) + generatedLines + "\n" + content.substring(returnPos); + } + + private String escapeJavaString(String s) { + return s.replace("\\", "\\\\").replace("\"", "\\\""); + } +} diff --git a/auto-solution-rule/src/main/resources/mapper/rule/RuleConfigMapper.xml b/auto-solution-rule/src/main/resources/mapper/rule/RuleConfigMapper.xml index 8d9e7bc..ea61496 100644 --- a/auto-solution-rule/src/main/resources/mapper/rule/RuleConfigMapper.xml +++ b/auto-solution-rule/src/main/resources/mapper/rule/RuleConfigMapper.xml @@ -171,4 +171,13 @@ ORDER BY sort_no ASC, id ASC + + diff --git a/auto-solution-rule/src/main/resources/rules/rule.drl b/auto-solution-rule/src/main/resources/rules/rule.drl index f30613c..75c3693 100644 --- a/auto-solution-rule/src/main/resources/rules/rule.drl +++ b/auto-solution-rule/src/main/resources/rules/rule.drl @@ -78,8 +78,8 @@ function Map buildParam(){ param.put("redMatchKeywords_missile", "防空,导弹,导弹发射"); param.put("missileScore", 1); - // ===================== 目标分配参数(写入 Tasks.task.execute) ===================== - // executeTypeDefault:生成 execute[0] 的类型字段 + // ===================== 目标分配参数(写入 Tasks.task.execute) ===================== + // executeTypeDefault:生成 execute[0] 的类型字段 取值:strike_test/assault param.put("executeTypeDefault", "assault"); // targetPickMode:roundRobin(稳定轮询) / random(伪随机但同输入稳定) param.put("targetPickMode", "roundRobin"); @@ -200,16 +200,111 @@ function Map buildParam(){ param.put("wingmanAltBaseMeters", 40); param.put("wingmanAltScale", 1.0); + // ===== 以下由规则配置表自动同步生成(请勿手改 param.put 段) ===== + param.put("groupRuleEnabled", true); + param.put("groupDrawNameSuffix", "编组"); + param.put("groupDrawNameWithIndex", false); + param.put("groupFormationMode", "onePerRed"); + param.put("groupClusterSize", 3); + param.put("groupLeaderPickMode", "byHitRateThenId"); + param.put("groupMinMembersForWingman", 2); + 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); + param.put("enableTrackWarZoneClamp", true); + param.put("trackRuleEnabled", true); + param.put("trackRouteAlgorithm", "followBlue"); + param.put("trackRouteNameSuffix", "航迹"); + param.put("trackAirDataTypeCsv", "taskPlane,air,plane,flight"); + param.put("trackAirKeywordsCsv", "机,飞,空,J-,F-,无人机,直升机"); + param.put("trackGroundTrackType", "routeLineGround"); + param.put("trackFallbackBearingDeg", 0); + param.put("trackExtraNodesMax", 0); + param.put("trackShortPathSegments", 3); + param.put("trackFlankOffsetMeters", 800); + param.put("trackFlankSideMode", "alternate"); + param.put("trackJamWobbleMeters", 400); + param.put("trackJamSegments", 4); + param.put("enableWarZoneClamp", true); + param.put("positionRuleEnabled", true); + param.put("positionAnchorMode", "hybrid"); + param.put("trackPointDirectionMode", "head2next"); + param.put("fallbackBearingDeg", 0); + param.put("deployDistanceKmMin", 8); + param.put("deployDistanceKmMax", 30); + param.put("deployDistanceKmDefault", 15); + param.put("distanceByPlatformCsv", ""); + param.put("formationType", "line"); + param.put("formationSpacingMeters", 300); + param.put("formationHeadingOffsetDeg", 15); + param.put("defaultDeployHeight", 30); + param.put("heightFollowBlueRatio", 0.0); + param.put("warZoneClampMode", "nearestInside"); + param.put("minInterPlatformDistanceMeters", 80); + param.put("redHitRateThreshold", 0.6); + param.put("maxExtraWeaponsPerTask", 2); + param.put("maxSupplementRounds", 2); + param.put("extraPickMinScore", 1); + param.put("executeTypeDefault", "assault"); + param.put("targetPickMode", "roundRobin"); + param.put("minTargetsPerRed", 1); + param.put("maxTargetsPerRedCap", 3); + param.put("radToTargetsCsv", "0.8:1,0.5:2,0.2:3"); + param.put("rangeParseRegex", "(\\\\d+(?:\\\\.\\\\d+)?)"); + param.put("rangeUnit", "km"); + param.put("minRangeToAllowAssignKm", 0); + param.put("weight", 1); + param.put("minSelectedScore", 1); + param.put("tieBreak", "equipmentId"); + param.put("outputDrawNameSuffix", "打击任务"); + param.put("ruleSlotCount", 3); + param.put("blueRuleKeywords_1", "F-16,F-35"); + param.put("redRuleKeywords_1", "防空,导弹,无人机"); + param.put("ruleScore_1", 5); + param.put("blueRuleKeywords_2", "坦克,装甲"); + param.put("redRuleKeywords_2", "反坦克"); + param.put("ruleScore_2", 4); + param.put("blueRuleKeywords_3", "地面,突击"); + param.put("redRuleKeywords_3", "远火,榴弹,炮"); + param.put("ruleScore_3", 2); + param.put("bluePlatformKeywords_air", "F-16,J-10,F-35"); + param.put("redPreferredWhenBlueAir", "防空,导弹,无人机,直升机,空空"); + param.put("airScore", 2); + param.put("airTaskKeywords", "空中,制空,拦截,空战"); + param.put("airTaskScore", 10); + param.put("groundTaskKeywords", "地面,突击,登陆"); + param.put("redPreferredWhenGround", "远火,榴弹,炮,火箭"); + param.put("groundScore", 1); + param.put("tankKeywords", "坦克,装甲"); + param.put("redMatchKeywords_tank", "反坦克"); + param.put("tankScore", 1); + param.put("missileKeywords", "导弹,火箭弹,巡航"); + param.put("redMatchKeywords_missile", "防空,导弹,导弹发射"); + param.put("missileScore", 1); + return param; } +function void mergeDefaultParams(Map current){ + Map defaults = buildParam(); + for (Object k : defaults.keySet()) { + if (!current.containsKey(k)) { + current.put(k, defaults.get(k)); + } + } +} + rule "装备匹配" salience 100 when $fact : DroolsFact(task != null) then // 以本文件 buildParam 为真源覆盖同名键,再执行 Java 侧匹配逻辑 - globalParams.putAll(buildParam()); + // globalParams.putAll(buildParam()); + mergeDefaultParams(globalParams); equipmentRule($fact, globalParams); end @@ -219,7 +314,8 @@ when $fact : DroolsFact(task != null) then // 显式目标分配规则:填充 Tasks.task.execute.targetList[*].targetId - globalParams.putAll(buildParam()); + // globalParams.putAll(buildParam()); + mergeDefaultParams(globalParams); target($fact, globalParams); end @@ -229,7 +325,8 @@ when $fact : DroolsFact(task != null) then // 显式阵位规则:填充 redWeapons.SubComponents.platform[].positions - globalParams.putAll(buildParam()); + // globalParams.putAll(buildParam()); + mergeDefaultParams(globalParams); position($fact, globalParams); end @@ -239,7 +336,8 @@ when $fact : DroolsFact(task != null) then // 显式航迹规则:填充 TrackParam 下各航迹 id,并绑定 execute[0].targetList[*].moveRouteId - globalParams.putAll(buildParam()); + // globalParams.putAll(buildParam()); + mergeDefaultParams(globalParams); trackRoute($fact, globalParams); end @@ -249,6 +347,7 @@ when $fact : DroolsFact(task != null) then // 显式编组规则:填充 TrackParam.Groups(groupType=addGroup)与 wingmanData - globalParams.putAll(buildParam()); + // globalParams.putAll(buildParam()); + mergeDefaultParams(globalParams); groupFormation($fact, globalParams); end diff --git a/modeler/src/router/config.ts b/modeler/src/router/config.ts index f5a2405..2ed9b3f 100644 --- a/modeler/src/router/config.ts +++ b/modeler/src/router/config.ts @@ -58,4 +58,12 @@ export const routes: RouteRecordRaw[] = [ }, component: () => import('@/views/decision/rule/management.vue'), }, + { + name: 'decision-rule-config', + path: '/app/decision/rule-config', + meta: { + title: '规则聚合测试', + }, + component: () => import('@/views/decision/rule-config/management.vue'), + }, ] \ No newline at end of file diff --git a/modeler/src/views/decision/rule-config/api.ts b/modeler/src/views/decision/rule-config/api.ts new file mode 100644 index 0000000..bb8a6ea --- /dev/null +++ b/modeler/src/views/decision/rule-config/api.ts @@ -0,0 +1,44 @@ +/* + * This file is part of the kernelstudio package. + * + * (c) 2014-2026 zlin + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +import { HttpRequestClient } from '@/utils/request'; +import type { ApiDataResponse, BasicResponse } from '@/types'; +import type { RuleConfig, RuleConfigPageableResponse, RuleConfigRequest, RuleDictItem, RuleParamMeta } from './types'; + +const req = HttpRequestClient.create({ + baseURL: '/api', +}); + +export const findRuleConfigByQuery = (query: Partial = {}): Promise => { + return req.get('/system/rule/config/list', query); +}; + +export const findRuleConfigByCode = (ruleCode: string): Promise> => { + return req.get(`/system/rule/config/${ruleCode}`); +}; + +export const createRuleConfig = (ruleConfig: RuleConfig): Promise => { + return req.postJson('/system/rule/config', ruleConfig); +}; + +export const updateRuleConfig = (ruleConfig: RuleConfig): Promise => { + return req.putJson('/system/rule/config', ruleConfig); +}; + +export const deleteRuleConfig = (ruleCode: string): Promise => { + return req.delete(`/system/rule/config/${ruleCode}`); +}; + +export const findRuleDictByType = (dictType: string): Promise> => { + return req.get(`/system/rule/config/dict/${dictType}`); +}; + +export const findRuleParamMeta = (): Promise> => { + return req.get('/system/rule/config/param-meta'); +}; diff --git a/modeler/src/views/decision/rule-config/management.vue b/modeler/src/views/decision/rule-config/management.vue new file mode 100644 index 0000000..0d5479a --- /dev/null +++ b/modeler/src/views/decision/rule-config/management.vue @@ -0,0 +1,557 @@ + + + diff --git a/modeler/src/views/decision/rule-config/types.ts b/modeler/src/views/decision/rule-config/types.ts new file mode 100644 index 0000000..052e94e --- /dev/null +++ b/modeler/src/views/decision/rule-config/types.ts @@ -0,0 +1,88 @@ +/* + * This file is part of the kernelstudio package. + * + * (c) 2014-2026 zlin + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +import type { NullableString, PageableResponse } from '@/types'; + +export interface RuleConfigParam { + // 规则编码 + ruleCode: NullableString, + // 参数键 + paramKey: NullableString, + // 参数值 + paramVal: NullableString | number, + // 值类型(string/number/bool/json) + valType: NullableString, + // 参数名称 + paramName: NullableString, + // 排序号 + sortNo: number | null, + // 是否启用(1是0否) + enabled: number | null, + // 备注 + remark: NullableString, +} + +export interface RuleDictItem { + dictType: NullableString, + dictCode: NullableString, + dictName: NullableString, + sortNo: number | null, + enabled: number | null, + remark: NullableString, +} + +export interface RuleParamMeta { + paramKey: NullableString, + label: NullableString, + valueType: NullableString, + required: boolean | null, + enumOptions: string[] | null, + min: number | null, + max: number | null, + pattern: NullableString, + example: NullableString, + description: NullableString, +} + +export interface RuleConfig { + id: number, + // 规则编码 + ruleCode: NullableString, + // 规则名称 + ruleName: NullableString, + // 层级编码 + levelCode: NullableString, + // 种类编码 + kindCode: NullableString, + // 模块编码 + moduleCode: NullableString, + // 优先级(数值越小优先级越高) + priorityNo: number | null, + // 条件表达式 + conditionExpr: NullableString, + // 动作表达式 + actionExpr: NullableString, + // 版本号 + versionNo: number | null, + // 是否启用(1是0否) + enabled: number | null, + // 备注 + remark: NullableString, + // 参数列表 + params: RuleConfigParam[], + // 适用任务类型编码列表 + taskTypes: string[], +} + +export interface RuleConfigRequest extends Partial { + pageNum: number, + pageSize: number, +} + +export interface RuleConfigPageableResponse extends PageableResponse {} diff --git a/modeler/types/components.d.ts b/modeler/types/components.d.ts index 2e9d007..30cf0a1 100644 --- a/modeler/types/components.d.ts +++ b/modeler/types/components.d.ts @@ -12,6 +12,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + AAlert: typeof import('ant-design-vue/es')['Alert'] ABadge: typeof import('ant-design-vue/es')['Badge'] AButton: typeof import('ant-design-vue/es')['Button'] ACard: typeof import('ant-design-vue/es')['Card'] @@ -40,6 +41,8 @@ declare module 'vue' { AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] APagination: typeof import('ant-design-vue/es')['Pagination'] APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] + ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] + ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] ARow: typeof import('ant-design-vue/es')['Row'] ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] @@ -49,6 +52,7 @@ declare module 'vue' { ATabs: typeof import('ant-design-vue/es')['Tabs'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] + ATree: typeof import('ant-design-vue/es')['Tree'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } @@ -56,6 +60,7 @@ declare module 'vue' { // For TSX support declare global { + const AAlert: typeof import('ant-design-vue/es')['Alert'] const ABadge: typeof import('ant-design-vue/es')['Badge'] const AButton: typeof import('ant-design-vue/es')['Button'] const ACard: typeof import('ant-design-vue/es')['Card'] @@ -84,6 +89,8 @@ declare global { const AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] const APagination: typeof import('ant-design-vue/es')['Pagination'] const APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] + const ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] + const ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] const ARow: typeof import('ant-design-vue/es')['Row'] const ASelect: typeof import('ant-design-vue/es')['Select'] const ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] @@ -93,6 +100,7 @@ declare global { const ATabs: typeof import('ant-design-vue/es')['Tabs'] const ATextarea: typeof import('ant-design-vue/es')['Textarea'] const ATooltip: typeof import('ant-design-vue/es')['Tooltip'] + const ATree: typeof import('ant-design-vue/es')['Tree'] const RouterLink: typeof import('vue-router')['RouterLink'] const RouterView: typeof import('vue-router')['RouterView'] } \ No newline at end of file