Merge branch 'feature-transform-node-data'
This commit is contained in:
@@ -6,7 +6,10 @@ import com.solution.common.core.domain.AjaxResult;
|
||||
import com.solution.common.core.page.TableDataInfo;
|
||||
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.service.IRuleService;
|
||||
import com.solution.rule.service.IRuleConfigService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -22,6 +25,8 @@ public class RuleController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IRuleService ruleService;
|
||||
@Autowired
|
||||
private IRuleConfigService ruleConfigService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:list')")
|
||||
@GetMapping("/list")
|
||||
@@ -62,4 +67,50 @@ public class RuleController extends BaseController {
|
||||
public AjaxResult remove(@PathVariable Integer[] ids) {
|
||||
return toAjax(ruleService.deleteRuleByIds(ids));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:list')")
|
||||
@GetMapping("/config/list")
|
||||
@ApiOperation("查询规则聚合列表")
|
||||
public TableDataInfo configList(RuleConfigQuery query) {
|
||||
startPage();
|
||||
return getDataTable(ruleConfigService.selectRuleConfigList(query));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:query')")
|
||||
@GetMapping("/config/{ruleCode}")
|
||||
@ApiOperation("查询规则聚合详情")
|
||||
public AjaxResult configInfo(@PathVariable String ruleCode) {
|
||||
return success(ruleConfigService.selectRuleConfigByCode(ruleCode));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:add')")
|
||||
@Log(title = "规则聚合管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/config")
|
||||
@ApiOperation("新增规则聚合")
|
||||
public AjaxResult addConfig(@RequestBody RuleConfig ruleConfig) {
|
||||
return toAjax(ruleConfigService.insertRuleConfig(ruleConfig));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:edit')")
|
||||
@Log(title = "规则聚合管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/config")
|
||||
@ApiOperation("修改规则聚合")
|
||||
public AjaxResult editConfig(@RequestBody RuleConfig ruleConfig) {
|
||||
return toAjax(ruleConfigService.updateRuleConfig(ruleConfig));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:remove')")
|
||||
@Log(title = "规则聚合管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/config/{ruleCodes}")
|
||||
@ApiOperation("删除规则聚合")
|
||||
public AjaxResult removeConfig(@PathVariable String[] ruleCodes) {
|
||||
return toAjax(ruleConfigService.deleteRuleConfigByCodes(ruleCodes));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:query')")
|
||||
@GetMapping("/config/dict/{dictType}")
|
||||
@ApiOperation("按类型查询规则字典")
|
||||
public AjaxResult dict(@PathVariable String dictType) {
|
||||
return success(ruleConfigService.selectDictByType(dictType));
|
||||
}
|
||||
}
|
||||
@@ -64,4 +64,15 @@ public class SceneController extends BaseController {
|
||||
public AjaxResult getAllTree(@PathVariable Integer id){
|
||||
return success(sceneService.getAllTree(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据场景id获取场景下所有关系
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/getAllRelation/{id}")
|
||||
@ApiOperation("根据场景id获取场景下所有关系")
|
||||
public AjaxResult getAllRelation(@PathVariable Integer id){
|
||||
return success(sceneService.getAllRelation(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,9 @@ spring:
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
# url: jdbc:mysql://192.168.166.71:3306/behaviortreedb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://localhost:3306/autosolution_db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
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
|
||||
password: 123456
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.solution.rule.domain.config;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel("规则聚合对象")
|
||||
public class RuleConfig {
|
||||
|
||||
@ApiModelProperty("主键ID")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("规则编码")
|
||||
private String ruleCode;
|
||||
|
||||
@ApiModelProperty("规则名称")
|
||||
private String ruleName;
|
||||
|
||||
@ApiModelProperty("层级编码(task/action/platform)")
|
||||
private String levelCode;
|
||||
|
||||
@ApiModelProperty("种类编码(select/assign/deploy/config/mode/spacetime/relation/limit)")
|
||||
private String kindCode;
|
||||
|
||||
@ApiModelProperty("模块编码(equipment/target/position/track/group)")
|
||||
private String moduleCode;
|
||||
|
||||
@ApiModelProperty("优先级(数字越小越先执行)")
|
||||
private Integer priorityNo;
|
||||
|
||||
@ApiModelProperty("条件表达式")
|
||||
private String conditionExpr;
|
||||
|
||||
@ApiModelProperty("动作表达式")
|
||||
private String actionExpr;
|
||||
|
||||
@ApiModelProperty("版本号")
|
||||
private Integer versionNo;
|
||||
|
||||
@ApiModelProperty("是否启用(1是0否)")
|
||||
private Integer enabled;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
private Date createdAt;
|
||||
|
||||
@ApiModelProperty("更新时间")
|
||||
private Date updatedAt;
|
||||
|
||||
@ApiModelProperty("参数列表")
|
||||
private List<RuleConfigParam> params;
|
||||
|
||||
@ApiModelProperty("适用任务类型编码列表")
|
||||
private List<String> taskTypes;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.solution.rule.domain.config;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@ApiModel("规则参数对象")
|
||||
public class RuleConfigParam {
|
||||
|
||||
@ApiModelProperty("规则编码")
|
||||
private String ruleCode;
|
||||
|
||||
@ApiModelProperty("参数键")
|
||||
private String paramKey;
|
||||
|
||||
@ApiModelProperty("参数值")
|
||||
private String paramVal;
|
||||
|
||||
@ApiModelProperty("值类型(string/number/bool/json)")
|
||||
private String valType;
|
||||
|
||||
@ApiModelProperty("参数名称")
|
||||
private String paramName;
|
||||
|
||||
@ApiModelProperty("排序号")
|
||||
private Integer sortNo;
|
||||
|
||||
@ApiModelProperty("是否启用(1是0否)")
|
||||
private Integer enabled;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.solution.rule.domain.config;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@ApiModel("规则配置查询对象")
|
||||
public class RuleConfigQuery {
|
||||
|
||||
@ApiModelProperty("规则编码")
|
||||
private String ruleCode;
|
||||
|
||||
@ApiModelProperty("规则名称")
|
||||
private String ruleName;
|
||||
|
||||
@ApiModelProperty("层级编码(task/action/platform)")
|
||||
private String levelCode;
|
||||
|
||||
@ApiModelProperty("种类编码(select/assign/deploy/config/mode/spacetime/relation/limit)")
|
||||
private String kindCode;
|
||||
|
||||
@ApiModelProperty("模块编码(equipment/target/position/track/group)")
|
||||
private String moduleCode;
|
||||
|
||||
@ApiModelProperty("是否启用(1是0否)")
|
||||
private Integer enabled;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.solution.rule.domain.config;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@ApiModel("规则字典项")
|
||||
public class RuleDictItem {
|
||||
|
||||
@ApiModelProperty("字典类型")
|
||||
private String dictType;
|
||||
|
||||
@ApiModelProperty("字典编码")
|
||||
private String dictCode;
|
||||
|
||||
@ApiModelProperty("字典名称")
|
||||
private String dictName;
|
||||
|
||||
@ApiModelProperty("排序号")
|
||||
private Integer sortNo;
|
||||
|
||||
@ApiModelProperty("是否启用(1是0否)")
|
||||
private Integer enabled;
|
||||
|
||||
@ApiModelProperty("备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.solution.rule.mapper;
|
||||
|
||||
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 org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RuleConfigMapper {
|
||||
|
||||
List<RuleConfig> selectRuleConfigList(RuleConfigQuery query);
|
||||
|
||||
RuleConfig selectRuleConfigByCode(@Param("ruleCode") String ruleCode);
|
||||
|
||||
int countByRuleCode(@Param("ruleCode") String ruleCode);
|
||||
|
||||
int insertRuleConfig(RuleConfig ruleConfig);
|
||||
|
||||
int updateRuleConfig(RuleConfig ruleConfig);
|
||||
|
||||
int deleteRuleConfigByCodes(@Param("ruleCodes") String[] ruleCodes);
|
||||
|
||||
List<RuleConfigParam> selectParamsByRuleCode(@Param("ruleCode") String ruleCode);
|
||||
|
||||
int deleteParamsByRuleCodes(@Param("ruleCodes") String[] ruleCodes);
|
||||
|
||||
int insertParamsBatch(@Param("params") List<RuleConfigParam> params);
|
||||
|
||||
List<String> selectTaskTypesByRuleCode(@Param("ruleCode") String ruleCode);
|
||||
|
||||
int deleteTaskTypesByRuleCodes(@Param("ruleCodes") String[] ruleCodes);
|
||||
|
||||
int insertTaskTypesBatch(@Param("ruleCode") String ruleCode, @Param("taskTypes") List<String> taskTypes);
|
||||
|
||||
List<RuleDictItem> selectDictByType(@Param("dictType") String dictType);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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 java.util.List;
|
||||
|
||||
public interface IRuleConfigService {
|
||||
|
||||
List<RuleConfig> selectRuleConfigList(RuleConfigQuery query);
|
||||
|
||||
RuleConfig selectRuleConfigByCode(String ruleCode);
|
||||
|
||||
int insertRuleConfig(RuleConfig ruleConfig);
|
||||
|
||||
int updateRuleConfig(RuleConfig ruleConfig);
|
||||
|
||||
int deleteRuleConfigByCodes(String[] ruleCodes);
|
||||
|
||||
List<RuleDictItem> selectDictByType(String dictType);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.solution.rule.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.solution.common.constant.ExceptionConstants;
|
||||
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.mapper.RuleConfigMapper;
|
||||
import com.solution.rule.service.IRuleConfigService;
|
||||
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;
|
||||
|
||||
@Service
|
||||
public class RuleConfigServiceImpl implements IRuleConfigService {
|
||||
|
||||
@Autowired
|
||||
private RuleConfigMapper ruleConfigMapper;
|
||||
|
||||
@Override
|
||||
public List<RuleConfig> selectRuleConfigList(RuleConfigQuery query) {
|
||||
return ruleConfigMapper.selectRuleConfigList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuleConfig selectRuleConfigByCode(String ruleCode) {
|
||||
RuleConfig config = ruleConfigMapper.selectRuleConfigByCode(ruleCode);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
config.setParams(ruleConfigMapper.selectParamsByRuleCode(ruleCode));
|
||||
config.setTaskTypes(ruleConfigMapper.selectTaskTypesByRuleCode(ruleCode));
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int insertRuleConfig(RuleConfig ruleConfig) {
|
||||
validateBase(ruleConfig);
|
||||
if (ruleConfigMapper.countByRuleCode(ruleConfig.getRuleCode()) > 0) {
|
||||
throw new RuntimeException("规则编码已存在");
|
||||
}
|
||||
int rows = ruleConfigMapper.insertRuleConfig(fillDefault(ruleConfig));
|
||||
saveChildren(ruleConfig);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int updateRuleConfig(RuleConfig ruleConfig) {
|
||||
validateBase(ruleConfig);
|
||||
if (ruleConfigMapper.countByRuleCode(ruleConfig.getRuleCode()) <= 0) {
|
||||
throw new RuntimeException("规则编码不存在");
|
||||
}
|
||||
int rows = ruleConfigMapper.updateRuleConfig(fillDefault(ruleConfig));
|
||||
String[] ruleCodes = {ruleConfig.getRuleCode()};
|
||||
ruleConfigMapper.deleteParamsByRuleCodes(ruleCodes);
|
||||
ruleConfigMapper.deleteTaskTypesByRuleCodes(ruleCodes);
|
||||
saveChildren(ruleConfig);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int deleteRuleConfigByCodes(String[] ruleCodes) {
|
||||
if (ruleCodes == null || ruleCodes.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
ruleConfigMapper.deleteParamsByRuleCodes(ruleCodes);
|
||||
ruleConfigMapper.deleteTaskTypesByRuleCodes(ruleCodes);
|
||||
return ruleConfigMapper.deleteRuleConfigByCodes(ruleCodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RuleDictItem> selectDictByType(String dictType) {
|
||||
if (ObjectUtil.isEmpty(dictType)) {
|
||||
throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION);
|
||||
}
|
||||
return ruleConfigMapper.selectDictByType(dictType);
|
||||
}
|
||||
|
||||
private void saveChildren(RuleConfig ruleConfig) {
|
||||
if (CollUtil.isNotEmpty(ruleConfig.getParams())) {
|
||||
Set<String> keys = new HashSet<>();
|
||||
for (RuleConfigParam param : ruleConfig.getParams()) {
|
||||
if (param == null || ObjectUtil.isEmpty(param.getParamKey())) {
|
||||
throw new RuntimeException("参数键不能为空");
|
||||
}
|
||||
if (!keys.add(param.getParamKey())) {
|
||||
throw new RuntimeException("参数键重复: " + param.getParamKey());
|
||||
}
|
||||
param.setRuleCode(ruleConfig.getRuleCode());
|
||||
if (param.getSortNo() == null) {
|
||||
param.setSortNo(0);
|
||||
}
|
||||
if (param.getEnabled() == null) {
|
||||
param.setEnabled(1);
|
||||
}
|
||||
if (ObjectUtil.isEmpty(param.getValType())) {
|
||||
param.setValType("string");
|
||||
}
|
||||
}
|
||||
ruleConfigMapper.insertParamsBatch(ruleConfig.getParams());
|
||||
}
|
||||
if (CollUtil.isNotEmpty(ruleConfig.getTaskTypes())) {
|
||||
ruleConfigMapper.insertTaskTypesBatch(ruleConfig.getRuleCode(), ruleConfig.getTaskTypes());
|
||||
}
|
||||
}
|
||||
|
||||
private RuleConfig fillDefault(RuleConfig ruleConfig) {
|
||||
if (ruleConfig.getPriorityNo() == null) {
|
||||
ruleConfig.setPriorityNo(100);
|
||||
}
|
||||
if (ruleConfig.getVersionNo() == null) {
|
||||
ruleConfig.setVersionNo(1);
|
||||
}
|
||||
if (ruleConfig.getEnabled() == null) {
|
||||
ruleConfig.setEnabled(1);
|
||||
}
|
||||
return ruleConfig;
|
||||
}
|
||||
|
||||
private void validateBase(RuleConfig ruleConfig) {
|
||||
if (ruleConfig == null
|
||||
|| ObjectUtil.isEmpty(ruleConfig.getRuleCode())
|
||||
|| ObjectUtil.isEmpty(ruleConfig.getRuleName())
|
||||
|| ObjectUtil.isEmpty(ruleConfig.getLevelCode())
|
||||
|| ObjectUtil.isEmpty(ruleConfig.getKindCode())
|
||||
|| ObjectUtil.isEmpty(ruleConfig.getModuleCode())) {
|
||||
throw new RuntimeException(ExceptionConstants.PARAMETER_EXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@
|
||||
</resultMap>
|
||||
<select id="findComponentsByPlatformId" resultMap="VPlatformComponentMap">
|
||||
SELECT * FROM platform_component
|
||||
WHERE platform_id=#{platformId}
|
||||
WHERE platform_id=#{platformId} AND platform_component.type = "comm"
|
||||
</select>
|
||||
|
||||
<resultMap id="VPBasicPlatformMap" type="com.solution.rule.domain.BasicPlatform">
|
||||
@@ -112,13 +112,21 @@
|
||||
FROM platform p
|
||||
LEFT JOIN platform_component pc ON p.id = pc.platform_id
|
||||
WHERE pc.type = 'comm'
|
||||
AND p.scenario_id = #{scenarioId}
|
||||
AND p.scenario_id = #{scenarioId}
|
||||
ORDER BY p.name,pc.name
|
||||
</select>
|
||||
<!-- SELECT
|
||||
p.id,
|
||||
p.name,
|
||||
p.description
|
||||
FROM platform p
|
||||
LEFT JOIN platform_component pc ON p.id = pc.platform_id
|
||||
WHERE pc.type = 'comm'
|
||||
AND p.scenario_id = #{scenarioId}
|
||||
ORDER BY p.name,pc.name
|
||||
</select>
|
||||
|
||||
<select id="findAllPlatformComponents" resultMap="VPlatformMap">
|
||||
SELECT *
|
||||
FROM platform
|
||||
<select id="findAllPlatformComponents" resultMap="VPlatformMap">
|
||||
SELECT *
|
||||
FROM platform-->
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.solution.rule.mapper.RuleConfigMapper">
|
||||
|
||||
<resultMap id="RuleConfigMap" type="com.solution.rule.domain.config.RuleConfig">
|
||||
<id property="id" column="id"/>
|
||||
<result property="ruleCode" column="rule_code"/>
|
||||
<result property="ruleName" column="rule_name"/>
|
||||
<result property="levelCode" column="level_code"/>
|
||||
<result property="kindCode" column="kind_code"/>
|
||||
<result property="moduleCode" column="module_code"/>
|
||||
<result property="priorityNo" column="priority_no"/>
|
||||
<result property="conditionExpr" column="condition_expr"/>
|
||||
<result property="actionExpr" column="action_expr"/>
|
||||
<result property="versionNo" column="version_no"/>
|
||||
<result property="enabled" column="enabled"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createdAt" column="created_at"/>
|
||||
<result property="updatedAt" column="updated_at"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="RuleConfigParamMap" type="com.solution.rule.domain.config.RuleConfigParam">
|
||||
<result property="ruleCode" column="rule_code"/>
|
||||
<result property="paramKey" column="param_key"/>
|
||||
<result property="paramVal" column="param_val"/>
|
||||
<result property="valType" column="val_type"/>
|
||||
<result property="paramName" column="param_name"/>
|
||||
<result property="sortNo" column="sort_no"/>
|
||||
<result property="enabled" column="enabled"/>
|
||||
<result property="remark" column="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="RuleDictItemMap" type="com.solution.rule.domain.config.RuleDictItem">
|
||||
<result property="dictType" column="dict_type"/>
|
||||
<result property="dictCode" column="dict_code"/>
|
||||
<result property="dictName" column="dict_name"/>
|
||||
<result property="sortNo" column="sort_no"/>
|
||||
<result property="enabled" column="enabled"/>
|
||||
<result property="remark" column="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectRuleConfigList" resultMap="RuleConfigMap">
|
||||
SELECT id, rule_code, rule_name, level_code, kind_code, module_code, priority_no,
|
||||
condition_expr, action_expr, version_no, enabled, remark, created_at, updated_at
|
||||
FROM rule_item
|
||||
<where>
|
||||
<if test="ruleCode != null and ruleCode != ''">
|
||||
AND rule_code = #{ruleCode}
|
||||
</if>
|
||||
<if test="ruleName != null and ruleName != ''">
|
||||
AND rule_name LIKE CONCAT('%', #{ruleName}, '%')
|
||||
</if>
|
||||
<if test="levelCode != null and levelCode != ''">
|
||||
AND level_code = #{levelCode}
|
||||
</if>
|
||||
<if test="kindCode != null and kindCode != ''">
|
||||
AND kind_code = #{kindCode}
|
||||
</if>
|
||||
<if test="moduleCode != null and moduleCode != ''">
|
||||
AND module_code = #{moduleCode}
|
||||
</if>
|
||||
<if test="enabled != null">
|
||||
AND enabled = #{enabled}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY priority_no ASC, updated_at DESC
|
||||
</select>
|
||||
|
||||
<select id="selectRuleConfigByCode" resultMap="RuleConfigMap">
|
||||
SELECT id, rule_code, rule_name, level_code, kind_code, module_code, priority_no,
|
||||
condition_expr, action_expr, version_no, enabled, remark, created_at, updated_at
|
||||
FROM rule_item
|
||||
WHERE rule_code = #{ruleCode}
|
||||
</select>
|
||||
|
||||
<select id="countByRuleCode" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM rule_item
|
||||
WHERE rule_code = #{ruleCode}
|
||||
</select>
|
||||
|
||||
<insert id="insertRuleConfig" parameterType="com.solution.rule.domain.config.RuleConfig">
|
||||
INSERT INTO rule_item
|
||||
(rule_code, rule_name, level_code, kind_code, module_code, priority_no, condition_expr,
|
||||
action_expr, version_no, enabled, remark, created_at, updated_at)
|
||||
VALUES
|
||||
(#{ruleCode}, #{ruleName}, #{levelCode}, #{kindCode}, #{moduleCode}, #{priorityNo}, #{conditionExpr},
|
||||
#{actionExpr}, #{versionNo}, #{enabled}, #{remark}, NOW(), NOW())
|
||||
</insert>
|
||||
|
||||
<update id="updateRuleConfig" parameterType="com.solution.rule.domain.config.RuleConfig">
|
||||
UPDATE rule_item
|
||||
<set>
|
||||
<if test="ruleName != null">rule_name = #{ruleName},</if>
|
||||
<if test="levelCode != null">level_code = #{levelCode},</if>
|
||||
<if test="kindCode != null">kind_code = #{kindCode},</if>
|
||||
<if test="moduleCode != null">module_code = #{moduleCode},</if>
|
||||
<if test="priorityNo != null">priority_no = #{priorityNo},</if>
|
||||
<if test="conditionExpr != null">condition_expr = #{conditionExpr},</if>
|
||||
<if test="actionExpr != null">action_expr = #{actionExpr},</if>
|
||||
<if test="versionNo != null">version_no = #{versionNo},</if>
|
||||
<if test="enabled != null">enabled = #{enabled},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
updated_at = NOW()
|
||||
</set>
|
||||
WHERE rule_code = #{ruleCode}
|
||||
</update>
|
||||
|
||||
<delete id="deleteRuleConfigByCodes">
|
||||
DELETE FROM rule_item
|
||||
WHERE rule_code IN
|
||||
<foreach item="code" collection="ruleCodes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<select id="selectParamsByRuleCode" resultMap="RuleConfigParamMap">
|
||||
SELECT rule_code, param_key, param_val, val_type, param_name, sort_no, enabled, remark
|
||||
FROM rule_item_param
|
||||
WHERE rule_code = #{ruleCode}
|
||||
ORDER BY sort_no ASC, id ASC
|
||||
</select>
|
||||
|
||||
<delete id="deleteParamsByRuleCodes">
|
||||
DELETE FROM rule_item_param
|
||||
WHERE rule_code IN
|
||||
<foreach item="code" collection="ruleCodes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<insert id="insertParamsBatch">
|
||||
INSERT INTO rule_item_param
|
||||
(rule_code, param_key, param_val, val_type, param_name, sort_no, enabled, remark, created_at, updated_at)
|
||||
VALUES
|
||||
<foreach item="item" collection="params" separator=",">
|
||||
(#{item.ruleCode}, #{item.paramKey}, #{item.paramVal}, #{item.valType}, #{item.paramName},
|
||||
#{item.sortNo}, #{item.enabled}, #{item.remark}, NOW(), NOW())
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="selectTaskTypesByRuleCode" resultType="string">
|
||||
SELECT task_type_code
|
||||
FROM rule_item_task_type
|
||||
WHERE rule_code = #{ruleCode}
|
||||
ORDER BY id ASC
|
||||
</select>
|
||||
|
||||
<delete id="deleteTaskTypesByRuleCodes">
|
||||
DELETE FROM rule_item_task_type
|
||||
WHERE rule_code IN
|
||||
<foreach item="code" collection="ruleCodes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<insert id="insertTaskTypesBatch">
|
||||
INSERT INTO rule_item_task_type (rule_code, task_type_code, created_at)
|
||||
VALUES
|
||||
<foreach item="taskType" collection="taskTypes" separator=",">
|
||||
(#{ruleCode}, #{taskType}, NOW())
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="selectDictByType" resultMap="RuleDictItemMap">
|
||||
SELECT dict_type, dict_code, dict_name, sort_no, enabled, remark
|
||||
FROM rule_dict
|
||||
WHERE dict_type = #{dictType}
|
||||
ORDER BY sort_no ASC, id ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,67 @@
|
||||
-- 规则主数据表结构(MySQL 8+)
|
||||
-- 说明:用于前端按“层级->种类->规则项”进行展示与增删改查。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `rule_dict` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`dict_type` VARCHAR(32) NOT NULL COMMENT '字典类型(level/kind/task_type/status)',
|
||||
`dict_code` VARCHAR(64) NOT NULL COMMENT '字典编码',
|
||||
`dict_name` VARCHAR(64) NOT NULL COMMENT '字典名称',
|
||||
`sort_no` INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
`enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用(1是0否)',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_rule_dict_type_code` (`dict_type`, `dict_code`),
|
||||
KEY `idx_rule_dict_type_enabled` (`dict_type`, `enabled`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='规则字典表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `rule_item` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`rule_code` VARCHAR(64) NOT NULL COMMENT '规则编码',
|
||||
`rule_name` VARCHAR(128) NOT NULL COMMENT '规则名称',
|
||||
`level_code` VARCHAR(32) NOT NULL COMMENT '规则层级(task/action/platform)',
|
||||
`kind_code` VARCHAR(32) NOT NULL COMMENT '规则种类(select/assign/deploy/config/mode/spacetime/relation/limit)',
|
||||
`module_code` VARCHAR(32) NOT NULL COMMENT '规则模块(equipment/target/position/track/group)',
|
||||
`priority_no` INT NOT NULL DEFAULT 100 COMMENT '优先级(数字越小越先执行)',
|
||||
`condition_expr` VARCHAR(1024) DEFAULT NULL COMMENT '条件表达式(展示用)',
|
||||
`action_expr` VARCHAR(1024) DEFAULT NULL COMMENT '动作表达式(展示用)',
|
||||
`version_no` INT NOT NULL DEFAULT 1 COMMENT '版本号',
|
||||
`enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用(1是0否)',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_rule_item_code` (`rule_code`),
|
||||
KEY `idx_rule_item_level_kind` (`level_code`, `kind_code`),
|
||||
KEY `idx_rule_item_module_enabled` (`module_code`, `enabled`),
|
||||
KEY `idx_rule_item_priority` (`priority_no`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='规则主表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `rule_item_task_type` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`rule_code` VARCHAR(64) NOT NULL COMMENT '规则编码',
|
||||
`task_type_code` VARCHAR(32) NOT NULL COMMENT '任务类型编码',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_rule_task_type` (`rule_code`, `task_type_code`),
|
||||
KEY `idx_rule_task_type` (`task_type_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='规则适用任务类型关联表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `rule_item_param` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`rule_code` VARCHAR(64) NOT NULL COMMENT '规则编码',
|
||||
`param_key` VARCHAR(128) NOT NULL COMMENT '参数键',
|
||||
`param_val` TEXT NOT NULL COMMENT '参数值(统一文本存储)',
|
||||
`val_type` VARCHAR(16) NOT NULL DEFAULT 'string' COMMENT '值类型(string/number/bool/json)',
|
||||
`param_name` VARCHAR(128) DEFAULT NULL COMMENT '参数名称',
|
||||
`sort_no` INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
`enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用(1是0否)',
|
||||
`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_rule_param` (`rule_code`, `param_key`),
|
||||
KEY `idx_rule_param_key` (`param_key`),
|
||||
KEY `idx_rule_param_enabled` (`enabled`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='规则参数表';
|
||||
@@ -0,0 +1,211 @@
|
||||
-- 从 rules/rule.drl 提取的初始化数据
|
||||
-- 说明:本文件提供字典、规则主数据、规则参数与任务类型关联的初始记录。
|
||||
|
||||
-- 1) 字典数据
|
||||
INSERT INTO `rule_dict` (`dict_type`, `dict_code`, `dict_name`, `sort_no`, `enabled`, `remark`) VALUES
|
||||
('level', 'task', '任务级', 10, 1, '任务层面的选择/分配/限制'),
|
||||
('level', 'action', '行动级', 20, 1, '行动层面的航迹/编组/模式'),
|
||||
('level', 'platform', '平台级', 30, 1, '平台层面的部署/时空约束'),
|
||||
('kind', 'select', '选择', 10, 1, '对象选择与评分'),
|
||||
('kind', 'assign', '分配', 20, 1, '目标分配'),
|
||||
('kind', 'deploy', '部署', 30, 1, '阵位与部署'),
|
||||
('kind', 'config', '配置', 40, 1, '通用配置参数'),
|
||||
('kind', 'mode', '工作模式', 50, 1, '算法与模式选择'),
|
||||
('kind', 'spacetime', '时空约束', 60, 1, '空间和时间相关约束'),
|
||||
('kind', 'relation', '关联关系', 70, 1, '蓝红关键词关联'),
|
||||
('kind', 'limit', '限制条件', 80, 1, '阈值和边界'),
|
||||
('task_type', 'strike', '打击任务', 10, 1, '典型任务类型'),
|
||||
('task_type', 'recon', '侦察任务', 20, 1, '典型任务类型'),
|
||||
('task_type', 'intercept', '拦截任务', 30, 1, '典型任务类型'),
|
||||
('task_type', 'support', '支援任务', 40, 1, '典型任务类型'),
|
||||
('task_type', 'jamming', '干扰任务', 50, 1, '典型任务类型'),
|
||||
('status', 'enabled', '启用', 10, 1, '通用状态'),
|
||||
('status', 'disabled', '停用', 20, 1, '通用状态')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`dict_name` = VALUES(`dict_name`),
|
||||
`sort_no` = VALUES(`sort_no`),
|
||||
`enabled` = VALUES(`enabled`),
|
||||
`remark` = VALUES(`remark`);
|
||||
|
||||
-- 2) 规则主数据(层级->种类->规则项)
|
||||
INSERT INTO `rule_item`
|
||||
(`rule_code`, `rule_name`, `level_code`, `kind_code`, `module_code`, `priority_no`, `condition_expr`, `action_expr`, `version_no`, `enabled`, `remark`)
|
||||
VALUES
|
||||
('R_TASK_SELECT_BASE', '任务级-装备选择基础规则', 'task', 'select', 'equipment', 100, 'task!=null', 'equipmentRule(fact, params)', 1, 1, '来自装备匹配主流程'),
|
||||
('R_TASK_SELECT_SLOT_1', '任务级-规则槽1', 'task', 'relation', 'equipment', 101, 'containsAny(blue,slot1Blue)&&containsAny(red,slot1Red)', 'score += ruleScore_1*weight', 1, 1, '蓝红关键词槽位匹配'),
|
||||
('R_TASK_SELECT_SLOT_2', '任务级-规则槽2', 'task', 'relation', 'equipment', 102, 'containsAny(blue,slot2Blue)&&containsAny(red,slot2Red)', 'score += ruleScore_2*weight', 1, 1, '蓝红关键词槽位匹配'),
|
||||
('R_TASK_SELECT_SLOT_3', '任务级-规则槽3', 'task', 'relation', 'equipment', 103, 'containsAny(blue,slot3Blue)&&containsAny(red,slot3Red)', 'score += ruleScore_3*weight', 1, 1, '蓝红关键词槽位匹配'),
|
||||
('R_TASK_REL_AIR_PLATFORM', '任务级-关联关系-空中平台', 'task', 'relation', 'equipment', 104, 'bluePlatformKeywords_air && redPreferredWhenBlueAir', 'score += airScore*weight', 1, 1, '兼容层空中平台关联'),
|
||||
('R_TASK_REL_AIR_TASK', '任务级-关联关系-空中任务', 'task', 'relation', 'equipment', 105, 'airTaskKeywords && redPreferredWhenBlueAir', 'score += airTaskScore*weight', 1, 1, '兼容层空中任务关联'),
|
||||
('R_TASK_REL_GROUND_TASK', '任务级-关联关系-地面任务', 'task', 'relation', 'equipment', 106, 'groundTaskKeywords && redPreferredWhenGround', 'score += groundScore*weight', 1, 1, '兼容层地面任务关联'),
|
||||
('R_TASK_REL_TANK', '任务级-关联关系-坦克装甲', 'task', 'relation', 'equipment', 107, 'tankKeywords && redMatchKeywords_tank', 'score += tankScore*weight', 1, 1, '兼容层坦克关联'),
|
||||
('R_TASK_REL_MISSILE', '任务级-关联关系-导弹火箭', 'task', 'relation', 'equipment', 108, 'missileKeywords && redMatchKeywords_missile', 'score += missileScore*weight', 1, 1, '兼容层导弹关联'),
|
||||
('R_TASK_ASSIGN_TARGET', '任务级-目标分配规则', 'task', 'assign', 'target', 90, 'task!=null', 'target(fact, params)', 1, 1, '目标分配与execute填充'),
|
||||
('R_TASK_LIMIT_SUPPLEMENT', '任务级-低命中率补拿限制', 'task', 'limit', 'target', 89, 'hitRate<threshold', '补拿装备并更新任务', 1, 1, '补拿轮次和数量限制'),
|
||||
('R_PLATFORM_DEPLOY', '平台级-阵位部署规则', 'platform', 'deploy', 'position', 80, 'positionRuleEnabled=true', 'position(fact, params)', 1, 1, '平台部署与编队参数'),
|
||||
('R_PLATFORM_SPACETIME', '平台级-时空约束规则', 'platform', 'spacetime', 'position', 79, 'enableWarZoneClamp=true', '平台点位约束到作战区', 1, 1, '阵位空间约束'),
|
||||
('R_ACTION_TRACK_ROUTE', '行动级-航迹生成规则', 'action', 'mode', 'track', 70, 'trackRuleEnabled=true', 'trackRoute(fact, params)', 1, 1, '航迹算法与路由绑定'),
|
||||
('R_ACTION_TRACK_SPACETIME', '行动级-航迹时空约束', 'action', 'spacetime', 'track', 69, 'enableTrackWarZoneClamp=true', '航迹点约束到作战区', 1, 1, '航迹空间约束'),
|
||||
('R_ACTION_GROUP_FORMATION', '行动级-编组规则', 'action', 'mode', 'group', 60, 'groupRuleEnabled=true', 'groupFormation(fact, params)', 1, 1, '编组与wingman规则')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`rule_name` = VALUES(`rule_name`),
|
||||
`level_code` = VALUES(`level_code`),
|
||||
`kind_code` = VALUES(`kind_code`),
|
||||
`module_code` = VALUES(`module_code`),
|
||||
`priority_no` = VALUES(`priority_no`),
|
||||
`condition_expr` = VALUES(`condition_expr`),
|
||||
`action_expr` = VALUES(`action_expr`),
|
||||
`version_no` = VALUES(`version_no`),
|
||||
`enabled` = VALUES(`enabled`),
|
||||
`remark` = VALUES(`remark`);
|
||||
|
||||
-- 3) 规则参数(来自 rule.drl 的 buildParam)
|
||||
INSERT INTO `rule_item_param`
|
||||
(`rule_code`, `param_key`, `param_val`, `val_type`, `param_name`, `sort_no`, `enabled`, `remark`)
|
||||
VALUES
|
||||
-- R_TASK_SELECT_BASE
|
||||
('R_TASK_SELECT_BASE','weight','1','number','全局权重',10,1,'评分乘数'),
|
||||
('R_TASK_SELECT_BASE','minSelectedScore','1','number','最小选中分',20,1,'低于该分不选中'),
|
||||
('R_TASK_SELECT_BASE','tieBreak','equipmentId','string','并列决策方式',30,1,'equipmentId字典序'),
|
||||
('R_TASK_SELECT_BASE','outputDrawNameSuffix','打击任务','string','输出任务后缀',40,1,'匹配成功后缀'),
|
||||
('R_TASK_SELECT_BASE','ruleSlotCount','3','number','规则槽数量',50,1,'槽位匹配条数'),
|
||||
|
||||
-- R_TASK_SELECT_SLOT_1~3
|
||||
('R_TASK_SELECT_SLOT_1','blueRuleKeywords_1','F-16,F-35','string','蓝方关键词1',10,1,'规则槽1'),
|
||||
('R_TASK_SELECT_SLOT_1','redRuleKeywords_1','防空,导弹,无人机','string','红方关键词1',20,1,'规则槽1'),
|
||||
('R_TASK_SELECT_SLOT_1','ruleScore_1','5','number','规则槽1分值',30,1,'规则槽1'),
|
||||
('R_TASK_SELECT_SLOT_2','blueRuleKeywords_2','坦克,装甲','string','蓝方关键词2',10,1,'规则槽2'),
|
||||
('R_TASK_SELECT_SLOT_2','redRuleKeywords_2','反坦克','string','红方关键词2',20,1,'规则槽2'),
|
||||
('R_TASK_SELECT_SLOT_2','ruleScore_2','4','number','规则槽2分值',30,1,'规则槽2'),
|
||||
('R_TASK_SELECT_SLOT_3','blueRuleKeywords_3','地面,突击','string','蓝方关键词3',10,1,'规则槽3'),
|
||||
('R_TASK_SELECT_SLOT_3','redRuleKeywords_3','远火,榴弹,炮','string','红方关键词3',20,1,'规则槽3'),
|
||||
('R_TASK_SELECT_SLOT_3','ruleScore_3','2','number','规则槽3分值',30,1,'规则槽3'),
|
||||
|
||||
-- R_TASK_REL_AIR_PLATFORM
|
||||
('R_TASK_REL_AIR_PLATFORM','bluePlatformKeywords_air','F-16,J-10,F-35','string','蓝方空中平台关键词',10,1,'兼容层'),
|
||||
('R_TASK_REL_AIR_PLATFORM','redPreferredWhenBlueAir','防空,导弹,无人机,直升机,空空','string','红方空中偏好关键词',20,1,'兼容层'),
|
||||
('R_TASK_REL_AIR_PLATFORM','airScore','2','number','空中平台分值',30,1,'兼容层'),
|
||||
|
||||
-- R_TASK_REL_AIR_TASK
|
||||
('R_TASK_REL_AIR_TASK','airTaskKeywords','空中,制空,拦截,空战','string','空中任务关键词',10,1,'兼容层'),
|
||||
('R_TASK_REL_AIR_TASK','airTaskScore','10','number','空中任务分值',20,1,'兼容层'),
|
||||
|
||||
-- R_TASK_REL_GROUND_TASK
|
||||
('R_TASK_REL_GROUND_TASK','groundTaskKeywords','地面,突击,登陆','string','地面任务关键词',10,1,'兼容层'),
|
||||
('R_TASK_REL_GROUND_TASK','redPreferredWhenGround','远火,榴弹,炮,火箭','string','红方地面偏好关键词',20,1,'兼容层'),
|
||||
('R_TASK_REL_GROUND_TASK','groundScore','1','number','地面任务分值',30,1,'兼容层'),
|
||||
|
||||
-- R_TASK_REL_TANK
|
||||
('R_TASK_REL_TANK','tankKeywords','坦克,装甲','string','坦克关键词',10,1,'兼容层'),
|
||||
('R_TASK_REL_TANK','redMatchKeywords_tank','反坦克','string','红方反坦克关键词',20,1,'兼容层'),
|
||||
('R_TASK_REL_TANK','tankScore','1','number','坦克分值',30,1,'兼容层'),
|
||||
|
||||
-- R_TASK_REL_MISSILE
|
||||
('R_TASK_REL_MISSILE','missileKeywords','导弹,火箭弹,巡航','string','导弹关键词',10,1,'兼容层'),
|
||||
('R_TASK_REL_MISSILE','redMatchKeywords_missile','防空,导弹,导弹发射','string','红方导弹匹配关键词',20,1,'兼容层'),
|
||||
('R_TASK_REL_MISSILE','missileScore','1','number','导弹分值',30,1,'兼容层'),
|
||||
|
||||
-- R_TASK_ASSIGN_TARGET
|
||||
('R_TASK_ASSIGN_TARGET','executeTypeDefault','assault','string','执行类型默认值',10,1,'目标分配'),
|
||||
('R_TASK_ASSIGN_TARGET','targetPickMode','roundRobin','string','目标选择模式',20,1,'roundRobin/random'),
|
||||
('R_TASK_ASSIGN_TARGET','minTargetsPerRed','1','number','每红装最少目标数',30,1,'目标分配'),
|
||||
('R_TASK_ASSIGN_TARGET','maxTargetsPerRedCap','3','number','每红装最多目标数',40,1,'目标分配'),
|
||||
('R_TASK_ASSIGN_TARGET','radToTargetsCsv','0.8:1,0.5:2,0.2:3','string','命中率映射目标数',50,1,'阈值映射'),
|
||||
('R_TASK_ASSIGN_TARGET','rangeParseRegex','(\\\\d+(?:\\\\.\\\\d+)?)','string','射程提取正则',60,1,'提取首个数字'),
|
||||
('R_TASK_ASSIGN_TARGET','rangeUnit','km','string','射程单位',70,1,'km/m'),
|
||||
('R_TASK_ASSIGN_TARGET','minRangeToAllowAssignKm','0','number','允许分配最小射程',80,1,'射程过滤'),
|
||||
|
||||
-- R_TASK_LIMIT_SUPPLEMENT
|
||||
('R_TASK_LIMIT_SUPPLEMENT','redHitRateThreshold','0.6','number','红装命中率阈值',10,1,'低于阈值触发补拿'),
|
||||
('R_TASK_LIMIT_SUPPLEMENT','maxExtraWeaponsPerTask','2','number','每任务最大补拿装备数',20,1,'限制条件'),
|
||||
('R_TASK_LIMIT_SUPPLEMENT','maxSupplementRounds','2','number','补拿最大轮次',30,1,'防止死循环'),
|
||||
('R_TASK_LIMIT_SUPPLEMENT','extraPickMinScore','1','number','补拿最小匹配分',40,1,'限制条件'),
|
||||
|
||||
-- R_PLATFORM_DEPLOY
|
||||
('R_PLATFORM_DEPLOY','positionRuleEnabled','true','bool','是否启用阵位规则',10,1,'部署开关'),
|
||||
('R_PLATFORM_DEPLOY','positionAnchorMode','hybrid','string','阵位锚点模式',20,1,'当前使用hybrid'),
|
||||
('R_PLATFORM_DEPLOY','trackPointDirectionMode','head2next','string','航向计算模式',30,1,'head2next/tail2prev'),
|
||||
('R_PLATFORM_DEPLOY','fallbackBearingDeg','0','number','默认航向角',40,1,'无法计算时回退'),
|
||||
('R_PLATFORM_DEPLOY','deployDistanceKmMin','8','number','部署距离最小值(km)',50,1,'部署约束'),
|
||||
('R_PLATFORM_DEPLOY','deployDistanceKmMax','30','number','部署距离最大值(km)',60,1,'部署约束'),
|
||||
('R_PLATFORM_DEPLOY','deployDistanceKmDefault','15','number','默认部署距离(km)',70,1,'部署距离'),
|
||||
('R_PLATFORM_DEPLOY','distanceByPlatformCsv','','string','按平台覆盖部署距离',80,1,'关键词:距离'),
|
||||
('R_PLATFORM_DEPLOY','formationType','line','string','编队样式',90,1,'line/wedge/circle'),
|
||||
('R_PLATFORM_DEPLOY','formationSpacingMeters','300','number','编队间距(米)',100,1,'部署参数'),
|
||||
('R_PLATFORM_DEPLOY','formationHeadingOffsetDeg','15','number','编队偏转角(度)',110,1,'部署参数'),
|
||||
('R_PLATFORM_DEPLOY','defaultDeployHeight','30','number','默认部署高度(米)',120,1,'部署参数'),
|
||||
('R_PLATFORM_DEPLOY','heightFollowBlueRatio','0.0','number','高度跟随比例',130,1,'部署参数'),
|
||||
('R_PLATFORM_DEPLOY','warZoneClampMode','nearestInside','string','作战区约束模式',140,1,'部署约束'),
|
||||
('R_PLATFORM_DEPLOY','minInterPlatformDistanceMeters','80','number','平台最小间距(米)',150,1,'部署约束'),
|
||||
|
||||
-- R_PLATFORM_SPACETIME
|
||||
('R_PLATFORM_SPACETIME','enableWarZoneClamp','true','bool','是否启用作战区约束',10,1,'平台时空约束'),
|
||||
|
||||
-- R_ACTION_TRACK_ROUTE
|
||||
('R_ACTION_TRACK_ROUTE','trackRuleEnabled','true','bool','是否启用航迹规则',10,1,'航迹开关'),
|
||||
('R_ACTION_TRACK_ROUTE','trackRouteAlgorithm','followBlue','string','航迹算法',20,1,'followBlue/shortestPath/flank/jam'),
|
||||
('R_ACTION_TRACK_ROUTE','trackRouteNameSuffix','航迹','string','航迹名称后缀',30,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackAirDataTypeCsv','taskPlane,air,plane,flight','string','空中航迹dataType关键词',40,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackAirKeywordsCsv','机,飞,空,J-,F-,无人机,直升机','string','空中航迹关键词',50,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackGroundTrackType','routeLineGround','string','地面航迹类型',60,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackFallbackBearingDeg','0','number','航迹默认回退航向角',70,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackExtraNodesMax','0','number','航迹额外插点上限',80,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackShortPathSegments','3','number','最短路径插值分段',90,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackFlankOffsetMeters','800','number','flank偏移距离(米)',100,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackFlankSideMode','alternate','string','flank侧向模式',110,1,'alternate/left/right'),
|
||||
('R_ACTION_TRACK_ROUTE','trackJamWobbleMeters','400','number','jam摆动振幅(米)',120,1,'行动模式'),
|
||||
('R_ACTION_TRACK_ROUTE','trackJamSegments','4','number','jam摆动周期数',130,1,'行动模式'),
|
||||
|
||||
-- R_ACTION_TRACK_SPACETIME
|
||||
('R_ACTION_TRACK_SPACETIME','enableTrackWarZoneClamp','true','bool','是否启用航迹作战区约束',10,1,'行动时空约束'),
|
||||
|
||||
-- R_ACTION_GROUP_FORMATION
|
||||
('R_ACTION_GROUP_FORMATION','groupRuleEnabled','true','bool','是否启用编组规则',10,1,'编组开关'),
|
||||
('R_ACTION_GROUP_FORMATION','groupDrawNameSuffix','编组','string','编组名称后缀',20,1,'编组规则'),
|
||||
('R_ACTION_GROUP_FORMATION','groupDrawNameWithIndex','false','bool','编组名称是否加序号',30,1,'编组规则'),
|
||||
('R_ACTION_GROUP_FORMATION','groupFormationMode','onePerRed','string','编组模式',40,1,'onePerRed/clusterByCount/singleGroup'),
|
||||
('R_ACTION_GROUP_FORMATION','groupClusterSize','3','number','按人数编组的每组上限',50,1,'编组规则'),
|
||||
('R_ACTION_GROUP_FORMATION','groupLeaderPickMode','byHitRateThenId','string','领队选择模式',60,1,'byHitRateThenId/byId'),
|
||||
('R_ACTION_GROUP_FORMATION','groupMinMembersForWingman','2','number','生成僚机的最小人数',70,1,'编组规则'),
|
||||
('R_ACTION_GROUP_FORMATION','wingmanDistanceBaseMeters','100','number','僚机基础距离(米)',80,1,'编组几何'),
|
||||
('R_ACTION_GROUP_FORMATION','wingmanDistanceStepMeters','50','number','僚机距离步长(米)',90,1,'编组几何'),
|
||||
('R_ACTION_GROUP_FORMATION','wingmanAngleBaseDeg','50','number','僚机基础角度(度)',100,1,'编组几何'),
|
||||
('R_ACTION_GROUP_FORMATION','wingmanAngleStepDeg','15','number','僚机角度步长(度)',110,1,'编组几何'),
|
||||
('R_ACTION_GROUP_FORMATION','wingmanAltBaseMeters','40','number','僚机基础高度(米)',120,1,'编组几何'),
|
||||
('R_ACTION_GROUP_FORMATION','wingmanAltScale','1.0','number','僚机高度缩放系数',130,1,'编组几何')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`param_val` = VALUES(`param_val`),
|
||||
`val_type` = VALUES(`val_type`),
|
||||
`param_name` = VALUES(`param_name`),
|
||||
`sort_no` = VALUES(`sort_no`),
|
||||
`enabled` = VALUES(`enabled`),
|
||||
`remark` = VALUES(`remark`);
|
||||
|
||||
-- 4) 规则适用任务类型(默认全部规则覆盖五类任务,后续可在前端按需调整)
|
||||
INSERT IGNORE INTO `rule_item_task_type` (`rule_code`, `task_type_code`)
|
||||
SELECT r.rule_code, t.task_type_code
|
||||
FROM (
|
||||
SELECT 'R_TASK_SELECT_BASE' AS rule_code
|
||||
UNION ALL SELECT 'R_TASK_SELECT_SLOT_1'
|
||||
UNION ALL SELECT 'R_TASK_SELECT_SLOT_2'
|
||||
UNION ALL SELECT 'R_TASK_SELECT_SLOT_3'
|
||||
UNION ALL SELECT 'R_TASK_REL_AIR_PLATFORM'
|
||||
UNION ALL SELECT 'R_TASK_REL_AIR_TASK'
|
||||
UNION ALL SELECT 'R_TASK_REL_GROUND_TASK'
|
||||
UNION ALL SELECT 'R_TASK_REL_TANK'
|
||||
UNION ALL SELECT 'R_TASK_REL_MISSILE'
|
||||
UNION ALL SELECT 'R_TASK_ASSIGN_TARGET'
|
||||
UNION ALL SELECT 'R_TASK_LIMIT_SUPPLEMENT'
|
||||
UNION ALL SELECT 'R_PLATFORM_DEPLOY'
|
||||
UNION ALL SELECT 'R_PLATFORM_SPACETIME'
|
||||
UNION ALL SELECT 'R_ACTION_TRACK_ROUTE'
|
||||
UNION ALL SELECT 'R_ACTION_TRACK_SPACETIME'
|
||||
UNION ALL SELECT 'R_ACTION_GROUP_FORMATION'
|
||||
) r
|
||||
CROSS JOIN (
|
||||
SELECT 'strike' AS task_type_code
|
||||
UNION ALL SELECT 'recon'
|
||||
UNION ALL SELECT 'intercept'
|
||||
UNION ALL SELECT 'support'
|
||||
UNION ALL SELECT 'jamming'
|
||||
) t;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.solution.system.domain;
|
||||
package com.solution.scene.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.solution.scene.mapper;
|
||||
|
||||
import com.solution.scene.domain.AfsimScenario;
|
||||
import com.solution.scene.domain.AfsimScenarioForm;
|
||||
import com.solution.scene.domain.PlatformCommunication;
|
||||
import com.solution.system.domain.Behaviortree;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@@ -34,4 +35,11 @@ public interface SceneMapper {
|
||||
* @return
|
||||
*/
|
||||
List<Behaviortree> selectAllTreeBySceneId(Integer id);
|
||||
|
||||
/**
|
||||
* 根据场景id获取场景下所有关系
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
List<PlatformCommunication> selectAllRelationBySceneId(Integer id);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.solution.scene.service;
|
||||
|
||||
import com.solution.scene.domain.AfsimScenario;
|
||||
import com.solution.scene.domain.AfsimScenarioForm;
|
||||
import com.solution.scene.domain.PlatformCommunication;
|
||||
import com.solution.system.domain.Behaviortree;
|
||||
|
||||
import java.util.List;
|
||||
@@ -33,4 +34,11 @@ public interface SceneService {
|
||||
* @return
|
||||
*/
|
||||
List<Behaviortree> getAllTree(Integer id);
|
||||
|
||||
/**
|
||||
* 根据场景id获取场景下所有关系
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
List<PlatformCommunication> getAllRelation(Integer id);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import com.solution.common.constant.ExceptionConstants;
|
||||
import com.solution.scene.domain.AfsimScenario;
|
||||
import com.solution.scene.domain.AfsimScenarioForm;
|
||||
import com.solution.scene.domain.PlatformCommunication;
|
||||
import com.solution.scene.mapper.PlatFormCommunicationMapper;
|
||||
import com.solution.scene.mapper.SceneMapper;
|
||||
import com.solution.scene.service.SceneService;
|
||||
@@ -90,4 +91,18 @@ public class SceneServiceImpl implements SceneService {
|
||||
return allTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据场景id获取场景下所有关系
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<PlatformCommunication> getAllRelation(Integer id) {
|
||||
List<PlatformCommunication> result = sceneMapper.selectAllRelationBySceneId(id);
|
||||
if(CollUtil.isEmpty( result)){
|
||||
throw new RuntimeException("该场景下不存在关系");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
WHERE scenario_id=#{id}
|
||||
</select>
|
||||
|
||||
<resultMap id="PlatformCommunicationResultMap" type="com.solution.scene.domain.PlatformCommunication">
|
||||
<id property="id" column="id"/>
|
||||
<result property="commandPlatform" column="command_platform"/>
|
||||
<result property="subordinatePlatform" column="subordinate_platform"/>
|
||||
<result property="commandComm" column="command_comm"/>
|
||||
<result property="subordinateComm" column="subordinate_comm"/>
|
||||
<result property="scenaryId" column="scenary_id"/>
|
||||
</resultMap>
|
||||
<select id="selectAllRelationBySceneId" resultMap="PlatformCommunicationResultMap"
|
||||
parameterType="java.lang.Integer">
|
||||
SELECT id,command_platform,subordinate_platform,command_comm,subordinate_comm,scenary_id
|
||||
FROM platform_communication
|
||||
WHERE scenary_id = #{id}
|
||||
</select>
|
||||
|
||||
<insert id="update" parameterType="com.solution.scene.domain.AfsimScenario">
|
||||
update afsim_scenario
|
||||
set name=#{name},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { HttpRequestClient } from '@/utils/request';
|
||||
import type { Scenario, ScenarioDetailsResponse, ScenarioPageableResponse, ScenarioRequest } from './types';
|
||||
import type { Scenario, ScenarioDetailsResponse, ScenarioPageableResponse, ScenarioRequest, CommunicationRelationsResponse } from './types';
|
||||
import type { PlatformWithComponentsResponse } from '../types';
|
||||
import type { BasicResponse } from '@/types';
|
||||
import type { BehaviorTree } from '../designer/tree';
|
||||
@@ -33,6 +33,15 @@ export const findPlatformWithComponents = (id: number): Promise<PlatformWithComp
|
||||
return req.get<PlatformWithComponentsResponse>(`/system/firerule/platforms/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取场景的所有通信关系
|
||||
* @param id 场景ID
|
||||
* @returns 通信关系列表
|
||||
*/
|
||||
export const findRelations = (id: number): Promise<CommunicationRelationsResponse> => {
|
||||
return req.get<CommunicationRelationsResponse>(`/system/scene/getAllRelation/${id}`);
|
||||
};
|
||||
|
||||
export const saveScenario = (scenario: Scenario): Promise<BasicResponse> => {
|
||||
return req.postJson<BasicResponse>(`/system/scene/saveSceneConfig`,scenario);
|
||||
};
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
<div class="ks-model-builder-content" style="width: calc(100% - 250px);">
|
||||
<div class="ks-model-builder-actions">
|
||||
<a-space>
|
||||
<a-button v-if="graph && currentScenario" class="ks-model-builder-save" style="width: auto;" size="small" @click="handleGenerateRandom">
|
||||
<ThunderboltOutlined />
|
||||
<span>随机生成</span>
|
||||
</a-button>
|
||||
<a-button v-if="graph && currentScenario && currentScenario.id > 0" class="ks-model-builder-save" style="width: auto;" size="small" @click="handleLoadFromBackend">
|
||||
<DatabaseOutlined />
|
||||
<span>从后端加载</span>
|
||||
</a-button>
|
||||
<a-button v-if="graph && currentScenario" class="ks-model-builder-save" size="small" @click="handleSave">
|
||||
<CheckOutlined />
|
||||
<span>保存</span>
|
||||
@@ -50,22 +58,24 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getTeleport } from '@antv/x6-vue-shape';
|
||||
import { Graph, Node, type NodeProperties } from '@antv/x6';
|
||||
import { CheckCircleOutlined, CheckOutlined, RollbackOutlined, SaveOutlined } from '@ant-design/icons-vue';
|
||||
import { CheckCircleOutlined, CheckOutlined, DatabaseOutlined, RollbackOutlined, SaveOutlined, ThunderboltOutlined } from '@ant-design/icons-vue';
|
||||
import { Wrapper } from '@/components/wrapper';
|
||||
import { safePreventDefault, safeStopPropagation } from '@/utils/event';
|
||||
import Header from '../header.vue';
|
||||
|
||||
import type { Scenario } from './types';
|
||||
import type { PlatformWithComponents } from '../types';
|
||||
import { createLineOptions, type GraphContainer, type GraphTaskElement, resolveGraph, useGraphCanvas } from '../graph';
|
||||
import { createLineOptions, type GraphContainer, type GraphEdgeElement, type GraphTaskElement, resolveGraph, useGraphCanvas } from '../graph';
|
||||
|
||||
import { registerScenarioElement } from './register';
|
||||
import { createGraphScenarioElement, createGraphTaskElementFromScenario } from './utils';
|
||||
|
||||
import PlatformCard from './platform-card.vue';
|
||||
import NodesCard from './nodes-card.vue';
|
||||
import { findOneScenarioById, saveScenario, getAllBehaviorTreesBySceneId } from './api';
|
||||
import { findOneScenarioById, saveScenario, findRelations } from './api';
|
||||
import { resolveConnectionRelation } from './relation';
|
||||
import { generateRandomCommunicationData } from './random-data-generator';
|
||||
import { convertRecordsToGraphContainer, type CommunicationRecord } from './data-converter';
|
||||
|
||||
const TeleportContainer = defineComponent(getTeleport());
|
||||
|
||||
@@ -81,6 +91,8 @@ export default defineComponent({
|
||||
CheckCircleOutlined,
|
||||
CheckOutlined,
|
||||
RollbackOutlined,
|
||||
ThunderboltOutlined,
|
||||
DatabaseOutlined,
|
||||
TeleportContainer,
|
||||
},
|
||||
setup() {
|
||||
@@ -218,36 +230,67 @@ export default defineComponent({
|
||||
} catch (e: any) {
|
||||
console.error('parse error,cause:', e);
|
||||
}
|
||||
if (!nodeGraph) {
|
||||
nodeGraph = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
}
|
||||
|
||||
// 设置当前场景
|
||||
currentScenario.value = {
|
||||
...scenario,
|
||||
graph: nodeGraph,
|
||||
graph: nodeGraph || { nodes: [], edges: [] },
|
||||
relations: []
|
||||
};
|
||||
console.log('选中场景:', currentScenario.value);
|
||||
|
||||
currentScenarioEditing.value = true;
|
||||
|
||||
// 将场景ID存储到graph对象中,供子组件访问
|
||||
if (graph.value) {
|
||||
(graph.value as any).currentScenario = currentScenario.value;
|
||||
|
||||
// 加载该场景下的行为树列表
|
||||
// 如果场景有ID且没有已保存的图数据,尝试从后端加载通信关系
|
||||
if (scenario.id > 0 && !nodeGraph) {
|
||||
try {
|
||||
const response = await getAllBehaviorTreesBySceneId(scenario.id);
|
||||
if (response.code === 200 && response.data) {
|
||||
(graph.value as any).behaviorTrees = response.data;
|
||||
console.log(`加载场景${scenario.id}的行为树列表:`, response.data.length, '个');
|
||||
message.loading({ content: '正在加载通信关系...', key: 'loading-relations' });
|
||||
const response = await findRelations(scenario.id);
|
||||
|
||||
console.log('API完整响应:', response);
|
||||
|
||||
// 解析API响应(支持多种格式)
|
||||
let relations: any[] = [];
|
||||
if (Array.isArray(response.data)) {
|
||||
relations = response.data;
|
||||
} else if (response.data && Array.isArray((response.data as any).data)) {
|
||||
relations = (response.data as any).data;
|
||||
} else if (response.data && Array.isArray((response.data as any).rows)) {
|
||||
relations = (response.data as any).rows;
|
||||
} else if (response.data && Array.isArray((response.data as any).list)) {
|
||||
relations = (response.data as any).list;
|
||||
}
|
||||
|
||||
console.log('解析后的通信关系数量:', relations.length);
|
||||
|
||||
if (relations.length > 0) {
|
||||
// 字段名标准化(驼峰转下划线)
|
||||
const normalizedRelations = relations.map((item: any) => ({
|
||||
id: item.id,
|
||||
command_platform: item.commandPlatform || item.command_platform,
|
||||
subordinate_platform: item.subordinatePlatform || item.subordinate_platform,
|
||||
command_comm: item.commandComm || item.command_comm,
|
||||
subordinate_comm: item.subordinateComm || item.subordinate_comm,
|
||||
scenary_id: item.scenaryId || item.scenary_id,
|
||||
}));
|
||||
|
||||
console.log('标准化后的第一条记录:', normalizedRelations[0]);
|
||||
|
||||
// 转换为图数据
|
||||
const convertedGraph = convertRecordsToGraphContainer(normalizedRelations);
|
||||
console.log('转换后的图数据:', convertedGraph);
|
||||
|
||||
// 更新当前场景的图数据
|
||||
currentScenario.value.graph = convertedGraph;
|
||||
currentScenario.value.communicationGraph = JSON.stringify(convertedGraph);
|
||||
|
||||
message.success({ content: `成功加载 ${normalizedRelations.length} 条通信关系`, key: 'loading-relations' });
|
||||
} else {
|
||||
(graph.value as any).behaviorTrees = [];
|
||||
console.warn('获取行为树列表失败:', response.msg);
|
||||
message.warning({ content: '该场景暂无通信关系数据', key: 'loading-relations' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取行为树列表失败:', error);
|
||||
(graph.value as any).behaviorTrees = [];
|
||||
console.error('从后端加载通信关系失败:', error);
|
||||
message.error({ content: '加载通信关系失败,请手动点击"从后端加载"', key: 'loading-relations' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +325,7 @@ export default defineComponent({
|
||||
}, 100); // 延迟一会儿,免得连线错位
|
||||
}
|
||||
}
|
||||
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
@@ -442,6 +486,193 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
// 随机生成节点流图
|
||||
const handleGenerateRandom = () => {
|
||||
if (!graph.value) {
|
||||
message.error('画布未初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 生成随机数据
|
||||
const { records, graph: randomGraph } = generateRandomCommunicationData(30);
|
||||
|
||||
console.log('生成的随机数据:', records);
|
||||
console.log('转换后的图数据:', randomGraph);
|
||||
|
||||
// 清空现有内容
|
||||
graph.value.clearCells();
|
||||
|
||||
// 设置当前场景
|
||||
if (!currentScenario.value) {
|
||||
currentScenario.value = {
|
||||
id: 0,
|
||||
name: `随机场景_${Date.now()}`,
|
||||
description: '自动生成的测试场景',
|
||||
communicationGraph: null,
|
||||
relations: [],
|
||||
graph: randomGraph,
|
||||
};
|
||||
} else {
|
||||
currentScenario.value.graph = randomGraph;
|
||||
currentScenario.value.communicationGraph = JSON.stringify(randomGraph);
|
||||
}
|
||||
|
||||
// 渲染节点
|
||||
setTimeout(() => {
|
||||
if (randomGraph.nodes) {
|
||||
randomGraph.nodes.forEach(ele => {
|
||||
const node = createGraphScenarioElement(ele as GraphTaskElement);
|
||||
graph.value?.addNode(node as Node);
|
||||
});
|
||||
}
|
||||
|
||||
// 延迟添加边,确保节点已渲染
|
||||
setTimeout(() => {
|
||||
if (randomGraph.edges) {
|
||||
randomGraph.edges.forEach(edgeData => {
|
||||
graph.value?.addEdge({
|
||||
...edgeData,
|
||||
...createLineOptions(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 自动适应视图
|
||||
fitToScreen();
|
||||
|
||||
message.success(`已生成 ${randomGraph.nodes.length} 个节点和 ${randomGraph.edges.length} 条连接线`);
|
||||
}, 100);
|
||||
}, 50);
|
||||
|
||||
} catch (error) {
|
||||
console.error('随机生成时出错:', error);
|
||||
message.error('生成失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 从后端加载平台数据并转换为通信关系图(当前使用模拟数据)
|
||||
const handleLoadFromBackend = async () => {
|
||||
if (!graph.value || !currentScenario.value) {
|
||||
message.error('请先选择场景');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
message.loading({ content: '正在加载通信关系数据...', key: 'loading' });
|
||||
|
||||
// 调用真实API获取通信关系
|
||||
console.log(`正在从后端加载场景 ${currentScenario.value.id} 的通信关系...`);
|
||||
const response = await findRelations(currentScenario.value.id);
|
||||
|
||||
console.log('API完整响应:', response);
|
||||
console.log('response.data类型:', typeof response.data, Array.isArray(response.data) ? 'Array' : 'Object');
|
||||
|
||||
// API返回的是 CommunicationRelationRecord[],与 CommunicationRecord 结构兼容
|
||||
// 处理可能的多种返回格式
|
||||
let relations: any[] = [];
|
||||
if (Array.isArray(response.data)) {
|
||||
relations = response.data;
|
||||
} else if (response.data && Array.isArray((response.data as any).data)) {
|
||||
relations = (response.data as any).data;
|
||||
} else if (response.data && Array.isArray((response.data as any).rows)) {
|
||||
relations = (response.data as any).rows;
|
||||
} else if (response.data && Array.isArray((response.data as any).list)) {
|
||||
relations = (response.data as any).list;
|
||||
}
|
||||
|
||||
console.log('解析后的通信关系数量:', relations.length);
|
||||
if (relations.length > 0) {
|
||||
console.log('第一条记录:', JSON.stringify(relations[0], null, 2));
|
||||
}
|
||||
|
||||
// 后端返回的是驼峰命名,需要转换为下划线命名以匹配前端类型
|
||||
const normalizedRelations = relations.map((item: any) => ({
|
||||
id: item.id,
|
||||
command_platform: item.commandPlatform || item.command_platform,
|
||||
subordinate_platform: item.subordinatePlatform || item.subordinate_platform,
|
||||
command_comm: item.commandComm || item.command_comm,
|
||||
subordinate_comm: item.subordinateComm || item.subordinate_comm,
|
||||
scenary_id: item.scenaryId || item.scenary_id,
|
||||
}));
|
||||
|
||||
console.log('标准化后的第一条记录:', normalizedRelations[0]);
|
||||
|
||||
if (normalizedRelations.length === 0) {
|
||||
console.warn('API未返回任何通信关系数据,使用模拟数据作为fallback');
|
||||
// Fallback到模拟数据(保留以便测试)
|
||||
relations.push(
|
||||
{ id: 6, command_platform: 'chief', subordinate_platform: 'task1_commander', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 7, command_platform: 'chief', subordinate_platform: 'task2_commander', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 8, command_platform: 'chief', subordinate_platform: 'task3_commander', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 9, command_platform: 'task1_commander', subordinate_platform: 'platform1', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 10, command_platform: 'task1_commander', subordinate_platform: 'platform3', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 11, command_platform: 'task1_commander', subordinate_platform: 'platform4', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 12, command_platform: 'task1_commander', subordinate_platform: 'platform5', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 13, command_platform: 'task1_commander', subordinate_platform: 'platform6', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 14, command_platform: 'task2_commander', subordinate_platform: 'platform3', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 15, command_platform: 'task2_commander', subordinate_platform: 'platform5', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 16, command_platform: 'task2_commander', subordinate_platform: 'platform4', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 17, command_platform: 'task3_commander', subordinate_platform: 'platform6', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 18, command_platform: 'task3_commander', subordinate_platform: 'platform6', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 19, command_platform: 'task3_commander', subordinate_platform: 'platform7', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
{ id: 20, command_platform: 'task3_commander', subordinate_platform: 'platform8', command_comm: 'radio', subordinate_comm: 'radio', scenary_id: currentScenario.value.id },
|
||||
);
|
||||
}
|
||||
|
||||
console.log('最终使用的通信记录:', normalizedRelations);
|
||||
|
||||
// 使用数据进行转换
|
||||
const convertedGraph = convertRecordsToGraphContainer(normalizedRelations);
|
||||
|
||||
console.log('转换后的图数据:', convertedGraph);
|
||||
|
||||
// 清空现有内容
|
||||
graph.value.clearCells();
|
||||
|
||||
// 更新当前场景
|
||||
currentScenario.value.graph = convertedGraph;
|
||||
currentScenario.value.communicationGraph = JSON.stringify(convertedGraph);
|
||||
|
||||
// 渲染节点
|
||||
setTimeout(() => {
|
||||
if (convertedGraph.nodes) {
|
||||
convertedGraph.nodes.forEach(ele => {
|
||||
const node = createGraphScenarioElement(ele as GraphTaskElement);
|
||||
graph.value?.addNode(node as Node);
|
||||
});
|
||||
}
|
||||
|
||||
// 延迟添加边,确保节点已渲染
|
||||
setTimeout(() => {
|
||||
if (convertedGraph.edges) {
|
||||
convertedGraph.edges.forEach(edgeData => {
|
||||
graph.value?.addEdge({
|
||||
...edgeData,
|
||||
...createLineOptions(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 自动适应视图
|
||||
fitToScreen();
|
||||
|
||||
message.success({
|
||||
content: `已从后端加载 ${convertedGraph.nodes.length} 个平台和 ${convertedGraph.edges.length} 条连接关系`,
|
||||
key: 'loading'
|
||||
});
|
||||
}, 100);
|
||||
}, 50);
|
||||
|
||||
} catch (error) {
|
||||
console.error('从后端加载时出错:', error);
|
||||
message.error({
|
||||
content: error instanceof Error ? error.message : '加载失败,请重试',
|
||||
key: 'loading'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
init();
|
||||
@@ -471,6 +702,8 @@ export default defineComponent({
|
||||
handleDrop,
|
||||
isDraggingOver,
|
||||
handleSave,
|
||||
handleGenerateRandom,
|
||||
handleLoadFromBackend,
|
||||
handleUpdateElement,
|
||||
handleSelect,
|
||||
};
|
||||
|
||||
1049
modeler/src/views/decision/communication/data-converter.ts
Normal file
1049
modeler/src/views/decision/communication/data-converter.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import type { GraphContainer } from '../graph';
|
||||
import { convertRecordsToGraphContainer, type CommunicationRecord } from './data-converter';
|
||||
|
||||
/**
|
||||
* 生成随机的通信关系数据用于测试
|
||||
* @param nodeCount 节点数量(默认5-8个)
|
||||
* @param edgeDensity 边的密度(0.3-0.7之间,表示连接概率)
|
||||
* @returns 随机生成的通信关系记录和对应的GraphContainer
|
||||
*/
|
||||
export const generateRandomCommunicationData = (
|
||||
nodeCount: number = Math.floor(Math.random() * 4) + 5, // 5-8个节点
|
||||
edgeDensity: number = 0.5
|
||||
): { records: CommunicationRecord[]; graph: GraphContainer } => {
|
||||
// 生成随机平台名称
|
||||
const platformTypes = ['指挥中心', '雷达站', '导弹阵地', '预警机', '战斗机', '驱逐舰', '潜艇', '电子战飞机'];
|
||||
const platforms: string[] = [];
|
||||
|
||||
for (let i = 0; i < nodeCount; i++) {
|
||||
const baseName = platformTypes[i % platformTypes.length];
|
||||
const suffix = Math.floor(i / platformTypes.length) > 0 ? `-${Math.floor(i / platformTypes.length) + 1}` : '';
|
||||
platforms.push(`${baseName}${suffix}`);
|
||||
}
|
||||
|
||||
// 生成随机通信关系 - 改进版:更符合实际指挥层级
|
||||
const records: CommunicationRecord[] = [];
|
||||
let recordId = 1;
|
||||
|
||||
// 确定根节点(指挥中心)
|
||||
const rootPlatformName = platforms.find(p => p.includes('指挥')) || platforms[0] || '默认平台';
|
||||
const rootIndex = platforms.indexOf(rootPlatformName);
|
||||
|
||||
if (rootIndex === -1) {
|
||||
console.warn('未找到根节点,使用第一个平台');
|
||||
return { records: [], graph: { nodes: [], edges: [] } };
|
||||
}
|
||||
|
||||
// 第一层:指挥中心直接管理的单位(通常是主要作战单元)
|
||||
// 选择2-4个作为一级下属
|
||||
const firstLevelCount = Math.min(Math.max(2, Math.floor(platforms.length / 2)), 4);
|
||||
const firstLevelIndices: number[] = [];
|
||||
|
||||
for (let i = 0; i < platforms.length && firstLevelIndices.length < firstLevelCount; i++) {
|
||||
if (i !== rootIndex) {
|
||||
firstLevelIndices.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 建立第一层连接:指挥中心 -> 一级下属
|
||||
firstLevelIndices.forEach(idx => {
|
||||
const subordinatePlatform = platforms[idx];
|
||||
if (subordinatePlatform) {
|
||||
records.push({
|
||||
id: recordId++,
|
||||
command_platform: rootPlatformName,
|
||||
subordinate_platform: subordinatePlatform,
|
||||
command_comm: '加密指挥链路',
|
||||
subordinate_comm: '接收端',
|
||||
scenary_id: 1,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 第二层:一级下属可以有二级下属
|
||||
const remainingIndices = platforms.map((_, i) => i).filter(i =>
|
||||
i !== rootIndex && !firstLevelIndices.includes(i)
|
||||
);
|
||||
|
||||
remainingIndices.forEach(idx => {
|
||||
// 随机选择一个一级下属作为父节点
|
||||
const parentIdx = firstLevelIndices[Math.floor(Math.random() * firstLevelIndices.length)];
|
||||
|
||||
if (parentIdx !== undefined) {
|
||||
const parentPlatform = platforms[parentIdx];
|
||||
const childPlatform = platforms[idx];
|
||||
|
||||
if (parentPlatform && childPlatform) {
|
||||
records.push({
|
||||
id: recordId++,
|
||||
command_platform: parentPlatform,
|
||||
subordinate_platform: childPlatform,
|
||||
command_comm: Math.random() > 0.5 ? '战术数据链' : '无线通信',
|
||||
subordinate_comm: '双向通信',
|
||||
scenary_id: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 第三层:添加少量横向协同连接(不超过总边数的20%)
|
||||
const maxCrossLinks = Math.floor(records.length * 0.2);
|
||||
let crossLinkCount = 0;
|
||||
|
||||
if (platforms.length > 4 && crossLinkCount < maxCrossLinks) {
|
||||
// 在同级之间添加协同连接
|
||||
for (let i = 0; i < firstLevelIndices.length - 1 && crossLinkCount < maxCrossLinks; i++) {
|
||||
if (Math.random() < 0.4) { // 40%概率
|
||||
const j = i + 1 + Math.floor(Math.random() * 2);
|
||||
if (j < firstLevelIndices.length) {
|
||||
const idxI = firstLevelIndices[i];
|
||||
const idxJ = firstLevelIndices[j];
|
||||
|
||||
if (idxI !== undefined && idxJ !== undefined) {
|
||||
const platformI = platforms[idxI];
|
||||
const platformJ = platforms[idxJ];
|
||||
|
||||
if (platformI && platformJ) {
|
||||
const exists = records.some(r =>
|
||||
r.command_platform === platformI &&
|
||||
r.subordinate_platform === platformJ
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
records.push({
|
||||
id: recordId++,
|
||||
command_platform: platformI,
|
||||
subordinate_platform: platformJ,
|
||||
command_comm: '协同通信',
|
||||
subordinate_comm: '双向通信',
|
||||
scenary_id: 1,
|
||||
});
|
||||
crossLinkCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为GraphContainer
|
||||
const graph = convertRecordsToGraphContainer(records);
|
||||
|
||||
return { records, graph };
|
||||
};
|
||||
@@ -48,3 +48,22 @@ export interface ScenarioDetailsResponse extends ApiDataResponse<Scenario> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通信关系记录(对应数据库表结构)
|
||||
*/
|
||||
export interface CommunicationRelationRecord {
|
||||
id: number;
|
||||
command_platform: string; // 指挥平台名称
|
||||
subordinate_platform: string; // 下属平台名称
|
||||
command_comm?: string; // 指挥端通信方式
|
||||
subordinate_comm?: string; // 下属端通信方式
|
||||
scenary_id?: number; // 场景ID
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景所有通信关系的响应
|
||||
*/
|
||||
export interface CommunicationRelationsResponse extends ApiDataResponse<CommunicationRelationRecord[]> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user