旧接口字段更新BUG修复
This commit is contained in:
@@ -9,6 +9,7 @@ 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.domain.config.vo.RuleFourBlocksGraphVO;
|
||||
import com.solution.rule.domain.config.vo.RuleGraphVO;
|
||||
import com.solution.rule.service.IRuleService;
|
||||
import com.solution.rule.service.IRuleConfigService;
|
||||
@@ -88,6 +89,14 @@ import java.util.List;
|
||||
return success(graph);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:list')")
|
||||
@GetMapping("/config/graph/four-blocks")
|
||||
@ApiOperation("四块规则知识图谱(装备/目标/阵位/航迹;参数值与运行时 globalParams 一致)")
|
||||
public AjaxResult configGraphFourBlocks() {
|
||||
RuleFourBlocksGraphVO vo = ruleConfigService.buildFourBlocksKnowledgeGraph();
|
||||
return success(vo);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:rule:query')")
|
||||
@GetMapping("/config/{ruleCode}")
|
||||
@ApiOperation("查询规则聚合详情")
|
||||
|
||||
@@ -18,7 +18,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</delete>
|
||||
|
||||
<sql id="selectNodeparameterVo">
|
||||
select id, treeId, node_instance_id, param_def_id,`value`, group_index from nodeparameter
|
||||
select id, tree_id, node_instance_id, param_def_id,`value`, group_index from nodeparameter
|
||||
</sql>
|
||||
|
||||
<select id="selectNodeparameterList" parameterType="Nodeparameter" resultMap="NodeparameterResult">
|
||||
@@ -28,7 +28,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="treeId != null"> and tree_id = #{treeId}</if>
|
||||
<if test="nodeInstanceId != null "> and node_instance_id = #{nodeInstanceId}</if>
|
||||
<if test="paramDefId != null "> and param_def_id = #{paramDefId}</if>
|
||||
<if test="value != null and value != ''"> and value = #{value}</if>
|
||||
<if test="value != null and value != ''"> and `value` = #{value}</if>
|
||||
<if test="groupIndex != null"> and group_index = #{groupIndex}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
@@ -18,6 +18,12 @@ public class RuleGraphVO implements Serializable {
|
||||
@ApiModelProperty("边列表")
|
||||
private List<RuleGraphEdgeVO> edges;
|
||||
|
||||
@ApiModelProperty("前端布局提示:如 radial_hub 使用径向圆心布局")
|
||||
private String layoutHint;
|
||||
|
||||
@ApiModelProperty("径向布局时的中心节点 id(通常为 drools_facade)")
|
||||
private String focusNodeId;
|
||||
|
||||
public List<RuleGraphNodeVO> safeNodes() {
|
||||
if (nodes == null) {
|
||||
nodes = new ArrayList<>();
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 com.solution.rule.domain.config.vo.RuleFourBlocksGraphVO;
|
||||
import com.solution.rule.domain.config.vo.RuleGraphVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -31,4 +32,9 @@ public interface IRuleConfigService {
|
||||
* 根据当前页规则主数据构建知识图谱(节点与边),参数与任务类型从库批量加载。
|
||||
*/
|
||||
RuleGraphVO buildKnowledgeGraph(List<RuleConfig> ruleConfigs);
|
||||
|
||||
/**
|
||||
* 四块规则知识图谱(装备/目标/阵位/航迹):中枢 If/Then + 环形参数,参数值与 {@link #loadEnabledGlobalParams()} 一致。
|
||||
*/
|
||||
RuleFourBlocksGraphVO buildFourBlocksKnowledgeGraph();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ import com.solution.rule.domain.config.RuleConfigQuery;
|
||||
import com.solution.rule.domain.config.RuleConfigTaskTypeRow;
|
||||
import com.solution.rule.domain.config.RuleDictItem;
|
||||
import com.solution.rule.domain.config.RuleParamMeta;
|
||||
import com.solution.rule.domain.config.graph.RuleFourBlockDefinition;
|
||||
import com.solution.rule.domain.config.graph.RuleFourBlockRuleOutputCatalog;
|
||||
import com.solution.rule.domain.config.graph.RuleParamOutputHint;
|
||||
import com.solution.rule.domain.config.vo.RuleFourBlockClusterVO;
|
||||
import com.solution.rule.domain.config.vo.RuleFourBlockParamRowVO;
|
||||
import com.solution.rule.domain.config.vo.RuleFourBlocksGraphVO;
|
||||
import com.solution.rule.domain.config.vo.RuleGraphEdgeVO;
|
||||
import com.solution.rule.domain.config.vo.RuleGraphNodeVO;
|
||||
import com.solution.rule.domain.config.vo.RuleGraphVO;
|
||||
@@ -20,6 +26,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -29,18 +36,6 @@ 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;
|
||||
|
||||
private static final Map<String, String> MODULE_LABELS;
|
||||
|
||||
static {
|
||||
Map<String, String> m = new LinkedHashMap<>();
|
||||
m.put("equipment", "装备匹配");
|
||||
m.put("target", "目标分配");
|
||||
m.put("position", "阵位部署");
|
||||
m.put("track", "航迹生成");
|
||||
m.put("group", "编组");
|
||||
MODULE_LABELS = Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RuleConfigMapper ruleConfigMapper;
|
||||
@Autowired
|
||||
@@ -187,19 +182,20 @@ public class RuleConfigServiceImpl implements IRuleConfigService {
|
||||
Map<String, Object> levelPayload = new LinkedHashMap<>();
|
||||
levelPayload.put("dictType", "level");
|
||||
levelPayload.put("dictCode", levelCode);
|
||||
putNode(nodeById, levelId, node(levelId, dictLabel(levelDict, levelCode, levelCode), "level", levelPayload));
|
||||
levelPayload.put("dictName", dictLabel(levelDict, levelCode, null));
|
||||
putNode(nodeById, levelId, node(levelId, levelCode, "level", levelPayload));
|
||||
|
||||
String kindId = "kind:" + levelCode + ":" + kindCode;
|
||||
Map<String, Object> kindPayload = new LinkedHashMap<>();
|
||||
kindPayload.put("levelCode", levelCode);
|
||||
kindPayload.put("kindCode", kindCode);
|
||||
putNode(nodeById, kindId, node(kindId, dictLabel(kindDict, kindCode, kindCode), "kind", kindPayload));
|
||||
kindPayload.put("dictName", dictLabel(kindDict, kindCode, null));
|
||||
putNode(nodeById, kindId, node(kindId, kindCode, "kind", kindPayload));
|
||||
|
||||
String moduleId = "module:" + moduleCode;
|
||||
String moduleLabel = MODULE_LABELS.getOrDefault(moduleCode, moduleCode);
|
||||
Map<String, Object> modulePayload = new LinkedHashMap<>();
|
||||
modulePayload.put("moduleCode", moduleCode);
|
||||
putNode(nodeById, moduleId, node(moduleId, moduleLabel, "module", modulePayload));
|
||||
putNode(nodeById, moduleId, node(moduleId, moduleCode, "module", modulePayload));
|
||||
|
||||
String ruleId = "rule:" + ruleCode;
|
||||
String ruleLabel = ObjectUtil.defaultIfBlank(rule.getRuleName(), ruleCode);
|
||||
@@ -248,7 +244,8 @@ public class RuleConfigServiceImpl implements IRuleConfigService {
|
||||
String taskId = "task:" + tt;
|
||||
Map<String, Object> taskPayload = new LinkedHashMap<>();
|
||||
taskPayload.put("taskTypeCode", tt);
|
||||
putNode(nodeById, taskId, node(taskId, dictLabel(taskTypeDict, tt, tt), "taskType", taskPayload));
|
||||
taskPayload.put("dictName", dictLabel(taskTypeDict, tt, null));
|
||||
putNode(nodeById, taskId, node(taskId, tt, "taskType", taskPayload));
|
||||
addEdge(edges, edgeIds, "applies_task:" + ruleCode + ":" + tt, ruleId, taskId, "rule_applies_task", null);
|
||||
}
|
||||
}
|
||||
@@ -279,6 +276,281 @@ public class RuleConfigServiceImpl implements IRuleConfigService {
|
||||
return graph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuleFourBlocksGraphVO buildFourBlocksKnowledgeGraph() {
|
||||
RuleFourBlocksGraphVO out = new RuleFourBlocksGraphVO();
|
||||
Map<String, Object> globalPreview = loadEnabledGlobalParams();
|
||||
out.setGlobalParamsPreview(new LinkedHashMap<>(globalPreview));
|
||||
|
||||
RuleConfigQuery q = new RuleConfigQuery();
|
||||
q.setEnabled(1);
|
||||
List<RuleConfig> allEnabled = ruleConfigMapper.selectRuleConfigList(q);
|
||||
if (CollUtil.isEmpty(allEnabled)) {
|
||||
out.setBlocks(new ArrayList<>());
|
||||
return out;
|
||||
}
|
||||
Set<String> fourModules = new HashSet<>(Arrays.asList("equipment", "target", "position", "track"));
|
||||
Map<String, List<RuleConfig>> byModule = allEnabled.stream()
|
||||
.filter(r -> r != null && ObjectUtil.isNotEmpty(r.getModuleCode()) && fourModules.contains(r.getModuleCode()))
|
||||
.collect(Collectors.groupingBy(RuleConfig::getModuleCode, LinkedHashMap::new, Collectors.toList()));
|
||||
|
||||
List<RuleFourBlockClusterVO> blocks = new ArrayList<>();
|
||||
for (RuleFourBlockDefinition def : RuleFourBlockDefinition.ordered()) {
|
||||
List<RuleConfig> moduleRules = new ArrayList<>(byModule.getOrDefault(def.getModuleCode(), Collections.emptyList()));
|
||||
moduleRules.sort(Comparator.comparing(RuleConfig::getPriorityNo, Comparator.nullsLast(Integer::compareTo)));
|
||||
|
||||
List<String> ruleCodes = moduleRules.stream().map(RuleConfig::getRuleCode).filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());
|
||||
List<RuleConfigParam> plist = ruleCodes.isEmpty()
|
||||
? Collections.emptyList()
|
||||
: ruleConfigMapper.selectParamsByRuleCodes(ruleCodes);
|
||||
|
||||
Map<String, Integer> priByRule = moduleRules.stream()
|
||||
.filter(r -> ObjectUtil.isNotEmpty(r.getRuleCode()))
|
||||
.collect(Collectors.toMap(RuleConfig::getRuleCode, RuleConfig::getPriorityNo, (a, b) -> a));
|
||||
|
||||
plist.sort(Comparator
|
||||
.comparing((RuleConfigParam p) -> priByRule.getOrDefault(p.getRuleCode(), Integer.MAX_VALUE))
|
||||
.thenComparing(p -> p.getSortNo() == null ? Integer.MAX_VALUE : p.getSortNo())
|
||||
.thenComparing(RuleConfigParam::getParamKey, Comparator.nullsLast(String::compareTo)));
|
||||
|
||||
Map<String, RuleConfig> ruleByCode = moduleRules.stream()
|
||||
.filter(r -> r != null && ObjectUtil.isNotEmpty(r.getRuleCode()))
|
||||
.collect(Collectors.toMap(RuleConfig::getRuleCode, Function.identity(), (a, b) -> a));
|
||||
|
||||
RuleFourBlockClusterVO cluster = new RuleFourBlockClusterVO();
|
||||
cluster.setBlockId(def.getBlockId());
|
||||
cluster.setModuleCode(def.getModuleCode());
|
||||
cluster.setTitle(def.getDroolsRuleName());
|
||||
cluster.setDroolsRuleName(def.getDroolsRuleName());
|
||||
cluster.setSalience(def.getSalience());
|
||||
cluster.setWhenExpr(def.getWhenExpr());
|
||||
cluster.setThenAction(def.getThenAction());
|
||||
cluster.setParamRows(buildFourBlockParamRows(def, ruleByCode, plist, globalPreview));
|
||||
cluster.setGraph(buildFourBlockNeoStyleGraph(def, moduleRules, plist, globalPreview, ruleByCode));
|
||||
blocks.add(cluster);
|
||||
}
|
||||
out.setBlocks(blocks);
|
||||
return out;
|
||||
}
|
||||
|
||||
private List<RuleFourBlockParamRowVO> buildFourBlockParamRows(RuleFourBlockDefinition def,
|
||||
Map<String, RuleConfig> ruleByCode,
|
||||
List<RuleConfigParam> plist,
|
||||
Map<String, Object> globalPreview) {
|
||||
List<RuleFourBlockParamRowVO> rows = new ArrayList<>();
|
||||
if (CollUtil.isEmpty(plist)) {
|
||||
return rows;
|
||||
}
|
||||
String moduleCode = def.getModuleCode();
|
||||
for (RuleConfigParam p : plist) {
|
||||
if (p == null || ObjectUtil.isEmpty(p.getParamKey()) || ObjectUtil.isEmpty(p.getRuleCode())) {
|
||||
continue;
|
||||
}
|
||||
if (p.getEnabled() != null && p.getEnabled() == 0) {
|
||||
continue;
|
||||
}
|
||||
RuleConfig r = ruleByCode.get(p.getRuleCode());
|
||||
if (r == null) {
|
||||
continue;
|
||||
}
|
||||
RuleFourBlockParamRowVO row = new RuleFourBlockParamRowVO();
|
||||
row.setRuleCode(r.getRuleCode());
|
||||
row.setRuleName(r.getRuleName());
|
||||
row.setParamKey(p.getParamKey());
|
||||
row.setParamName(ObjectUtil.defaultIfBlank(p.getParamName(), p.getParamKey()));
|
||||
row.setWhenText(formatParamWhenForRow(r, def));
|
||||
row.setThenText(formatParamThenForRow(r, def));
|
||||
row.setOutputText(formatParamOutputForRow(p, globalPreview, moduleCode));
|
||||
rows.add(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static String formatParamWhenForRow(RuleConfig r, RuleFourBlockDefinition def) {
|
||||
if (r != null && ObjectUtil.isNotEmpty(r.getConditionExpr())) {
|
||||
return r.getConditionExpr();
|
||||
}
|
||||
return ObjectUtil.defaultIfBlank(def.getWhenExpr(), "");
|
||||
}
|
||||
|
||||
private static String formatParamThenForRow(RuleConfig r, RuleFourBlockDefinition def) {
|
||||
if (r != null && ObjectUtil.isNotEmpty(r.getActionExpr())) {
|
||||
return r.getActionExpr();
|
||||
}
|
||||
return ObjectUtil.defaultIfBlank(def.getThenAction(), "");
|
||||
}
|
||||
|
||||
private static String formatParamOutputForRow(RuleConfigParam p, Map<String, Object> globalPreview, String moduleCode) {
|
||||
Object ev = globalPreview != null ? globalPreview.get(p.getParamKey()) : null;
|
||||
String hint = RuleParamOutputHint.effectLine(p.getParamKey(), moduleCode);
|
||||
if (ev == null) {
|
||||
return hint;
|
||||
}
|
||||
return "生效值:" + String.valueOf(ev) + ";" + hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Neo4j 风格子图:门面节点、多条 rule_item、参数按归属规则展开,含 drools_contains / rule_priority_next / rule_has_param 边。
|
||||
*/
|
||||
private RuleGraphVO buildFourBlockNeoStyleGraph(RuleFourBlockDefinition def,
|
||||
List<RuleConfig> moduleRules,
|
||||
List<RuleConfigParam> plist,
|
||||
Map<String, Object> globalPreview,
|
||||
Map<String, RuleConfig> ruleByCode) {
|
||||
RuleGraphVO graph = new RuleGraphVO();
|
||||
List<RuleGraphNodeVO> nodes = graph.safeNodes();
|
||||
List<RuleGraphEdgeVO> edges = graph.safeEdges();
|
||||
Set<String> edgeIds = new LinkedHashSet<>();
|
||||
|
||||
String bid = def.getBlockId();
|
||||
String droolsId = "four:block:" + bid + ":drools";
|
||||
|
||||
List<String> stepLabels = def.getComputationSteps();
|
||||
String lastStepId = null;
|
||||
for (int i = 0; i < stepLabels.size(); i++) {
|
||||
String text = stepLabels.get(i);
|
||||
if (ObjectUtil.isEmpty(text)) {
|
||||
continue;
|
||||
}
|
||||
String sid = "four:block:" + bid + ":step:" + i;
|
||||
Map<String, Object> sp = new LinkedHashMap<>();
|
||||
sp.put("stepIndex", i);
|
||||
sp.put("blockId", bid);
|
||||
nodes.add(node(sid, text, "compute_step", sp));
|
||||
if (lastStepId != null) {
|
||||
addEdge(edges, edgeIds, "four:compute_flow:" + bid + ":" + (i - 1),
|
||||
lastStepId, sid, "compute_flow", "");
|
||||
}
|
||||
lastStepId = sid;
|
||||
}
|
||||
|
||||
Map<String, Object> droolsPayload = new LinkedHashMap<>();
|
||||
droolsPayload.put("blockId", bid);
|
||||
droolsPayload.put("salience", def.getSalience());
|
||||
droolsPayload.put("droolsRuleName", def.getDroolsRuleName());
|
||||
droolsPayload.put("computationHint", def.getDroolsRuleName() + ":业务步骤汇总后与库表规则项、参数一并展示");
|
||||
nodes.add(node(droolsId, def.getDroolsRuleName(), "drools_facade", droolsPayload));
|
||||
|
||||
if (lastStepId != null) {
|
||||
addEdge(edges, edgeIds, "four:compute_flow:to_drools:" + bid,
|
||||
lastStepId, droolsId, "compute_flow", "汇总");
|
||||
}
|
||||
|
||||
Set<String> ruleCodeSet = new HashSet<>();
|
||||
List<String> ruleNodeIds = new ArrayList<>();
|
||||
for (RuleConfig r : moduleRules) {
|
||||
if (r == null || ObjectUtil.isEmpty(r.getRuleCode())) {
|
||||
continue;
|
||||
}
|
||||
ruleCodeSet.add(r.getRuleCode());
|
||||
String rid = "four:block:" + bid + ":rule:" + r.getRuleCode();
|
||||
ruleNodeIds.add(rid);
|
||||
|
||||
String ruleLabel = ObjectUtil.defaultIfBlank(r.getRuleName(), r.getRuleCode());
|
||||
Map<String, Object> rp = new LinkedHashMap<>();
|
||||
rp.put("ruleCode", r.getRuleCode());
|
||||
rp.put("ruleName", r.getRuleName());
|
||||
rp.put("priorityNo", r.getPriorityNo());
|
||||
rp.put("levelCode", r.getLevelCode());
|
||||
rp.put("kindCode", r.getKindCode());
|
||||
rp.put("moduleCode", r.getModuleCode());
|
||||
if (ObjectUtil.isNotEmpty(r.getConditionExpr())) {
|
||||
rp.put("conditionExpr", r.getConditionExpr());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(r.getActionExpr())) {
|
||||
rp.put("actionExpr", r.getActionExpr());
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(r.getRemark())) {
|
||||
rp.put("remark", r.getRemark());
|
||||
}
|
||||
nodes.add(node(rid, ruleLabel, "rule_row", rp));
|
||||
|
||||
addEdge(edges, edgeIds, "four:drools_contains:" + bid + ":" + r.getRuleCode(),
|
||||
droolsId, rid, "drools_contains", "包含");
|
||||
}
|
||||
|
||||
for (int i = 0; i < ruleNodeIds.size() - 1; i++) {
|
||||
addEdge(edges, edgeIds, "four:priority_next:" + bid + ":" + i,
|
||||
ruleNodeIds.get(i), ruleNodeIds.get(i + 1), "rule_priority_next", "优先级顺序");
|
||||
}
|
||||
|
||||
for (RuleConfig r : moduleRules) {
|
||||
if (r == null || ObjectUtil.isEmpty(r.getRuleCode()) || !ruleCodeSet.contains(r.getRuleCode())) {
|
||||
continue;
|
||||
}
|
||||
String rid = "four:block:" + bid + ":rule:" + r.getRuleCode();
|
||||
List<String> outs = RuleFourBlockRuleOutputCatalog.outputsForRule(def.getModuleCode(), r.getRuleCode());
|
||||
for (int j = 0; j < outs.size(); j++) {
|
||||
String full = outs.get(j);
|
||||
if (ObjectUtil.isEmpty(full)) {
|
||||
continue;
|
||||
}
|
||||
String oid = "four:block:" + bid + ":out:" + r.getRuleCode() + ":" + j;
|
||||
Map<String, Object> op = new LinkedHashMap<>();
|
||||
op.put("detail", full);
|
||||
op.put("ruleCode", r.getRuleCode());
|
||||
op.put("outputIndex", j);
|
||||
String lab = full.length() > 42 ? full.substring(0, 42) + "…" : full;
|
||||
nodes.add(node(oid, lab, "rule_output", op));
|
||||
addEdge(edges, edgeIds, "four:produces:" + bid + ":" + r.getRuleCode() + ":" + j,
|
||||
rid, oid, "rule_produces", "产出");
|
||||
}
|
||||
}
|
||||
|
||||
for (RuleConfigParam p : plist) {
|
||||
if (p == null || ObjectUtil.isEmpty(p.getParamKey()) || ObjectUtil.isEmpty(p.getRuleCode())) {
|
||||
continue;
|
||||
}
|
||||
if (p.getEnabled() != null && p.getEnabled() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!ruleCodeSet.contains(p.getRuleCode())) {
|
||||
continue;
|
||||
}
|
||||
String pk = p.getParamKey();
|
||||
String pid = "four:block:" + bid + ":param:" + p.getRuleCode() + ":" + pk;
|
||||
RuleParamMeta pm = resolveMeta(pk);
|
||||
|
||||
String pLabel = ObjectUtil.defaultIfBlank(p.getParamName(), pk);
|
||||
Map<String, Object> pl = new LinkedHashMap<>();
|
||||
pl.put("paramKey", pk);
|
||||
pl.put("storedRaw", p.getParamVal());
|
||||
pl.put("effectiveValue", globalPreview != null ? globalPreview.get(pk) : null);
|
||||
pl.put("ruleCode", p.getRuleCode());
|
||||
if (pm != null) {
|
||||
pl.put("metaLabel", pm.getLabel());
|
||||
pl.put("valueType", pm.getValueType());
|
||||
pl.put("description", pm.getDescription());
|
||||
}
|
||||
nodes.add(node(pid, pLabel, "param", pl));
|
||||
|
||||
String rid = "four:block:" + bid + ":rule:" + p.getRuleCode();
|
||||
addEdge(edges, edgeIds, "four:has_param:" + bid + ":" + p.getRuleCode() + ":" + pk,
|
||||
rid, pid, "rule_has_param", "参数");
|
||||
|
||||
RuleConfig rr = ruleByCode.get(p.getRuleCode());
|
||||
String fullWhen = formatParamWhenForRow(rr, def);
|
||||
String cid = "four:block:" + bid + ":cond:" + p.getRuleCode() + ":" + pk;
|
||||
String shortLab;
|
||||
if (ObjectUtil.isEmpty(fullWhen)) {
|
||||
shortLab = "(无条件)";
|
||||
} else {
|
||||
shortLab = fullWhen.length() > 36 ? fullWhen.substring(0, 36) + "…" : fullWhen;
|
||||
}
|
||||
Map<String, Object> cp = new LinkedHashMap<>();
|
||||
cp.put("fullWhenText", fullWhen);
|
||||
cp.put("paramKey", pk);
|
||||
cp.put("ruleCode", p.getRuleCode());
|
||||
nodes.add(node(cid, shortLab, "param_condition", cp));
|
||||
addEdge(edges, edgeIds, "four:cond_to_param:" + bid + ":" + p.getRuleCode() + ":" + pk,
|
||||
cid, pid, "condition_applies", "条件");
|
||||
}
|
||||
|
||||
graph.setLayoutHint("radial_hub");
|
||||
graph.setFocusNodeId(droolsId);
|
||||
return graph;
|
||||
}
|
||||
|
||||
private List<RuleDictItem> safeDict(String dictType) {
|
||||
List<RuleDictItem> list = ruleConfigMapper.selectDictByType(dictType);
|
||||
return list != null ? list : Collections.emptyList();
|
||||
|
||||
@@ -7,42 +7,73 @@
|
||||
- that was distributed with this source code.
|
||||
-->
|
||||
<template>
|
||||
<div class="rule-knowledge-graph">
|
||||
<div
|
||||
ref="graphShellRef"
|
||||
class="rule-knowledge-graph"
|
||||
:class="{
|
||||
'rule-knowledge-graph--four-blocks': density === 'four-blocks',
|
||||
'rule-knowledge-graph--fullscreen': isFullscreen,
|
||||
}"
|
||||
>
|
||||
<div class="rule-knowledge-graph__toolbar">
|
||||
<a-radio-group v-model:value="density" size="small" button-style="solid">
|
||||
<a-radio-button value="overview">简要结构</a-radio-button>
|
||||
<a-radio-button value="full">完整</a-radio-button>
|
||||
<a-radio-button value="four-blocks">四块分区</a-radio-button>
|
||||
</a-radio-group>
|
||||
<span class="rule-knowledge-graph__hint">
|
||||
简要:仅层级→种类→模块→规则;完整:含参数、任务类型与执行顺序边
|
||||
<span v-if="density !== 'four-blocks'" class="rule-knowledge-graph__hint">
|
||||
简要:仅层级→种类→模块→规则;完整:含参数、任务类型与执行顺序边;四块:业务运算步骤 + 规则项 + 参数(与 globalParams 一致)
|
||||
</span>
|
||||
<span v-else class="rule-knowledge-graph__hint rule-knowledge-graph__hint--compact">
|
||||
四宫格 · 拖拽画布 / 滚轮缩放
|
||||
</span>
|
||||
<a-button type="default" size="small" class="rule-knowledge-graph__fullscreen-btn" @click="toggleFullscreen">
|
||||
<template #icon>
|
||||
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||
<FullscreenOutlined v-else />
|
||||
</template>
|
||||
{{ isFullscreen ? '退出全屏' : '全屏' }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-if="errorMsg" class="rule-knowledge-graph__banner rule-knowledge-graph__banner--error">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
<div v-else-if="emptyHint" class="rule-knowledge-graph__banner">
|
||||
{{ emptyHint }}
|
||||
</div>
|
||||
<div v-show="!errorMsg && !emptyHint" ref="hostRef" class="rule-knowledge-graph__host" />
|
||||
<RuleFourBlocksPanel v-if="density === 'four-blocks'" :refresh-key="refreshKey" />
|
||||
<template v-else>
|
||||
<div v-if="errorMsg" class="rule-knowledge-graph__banner rule-knowledge-graph__banner--error">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
<div v-else-if="emptyHint" class="rule-knowledge-graph__banner">
|
||||
{{ emptyHint }}
|
||||
</div>
|
||||
<div v-show="!errorMsg && !emptyHint" ref="hostRef" class="rule-knowledge-graph__host" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue';
|
||||
import { Graph } from '@antv/g6';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { nextTick, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { findRuleConfigGraph } from './api';
|
||||
import RuleFourBlocksPanel from './RuleFourBlocksPanel.vue';
|
||||
import type { RuleConfigRequest, RuleGraphEdge, RuleGraphNode, RuleGraphPayload } from './types';
|
||||
|
||||
type RuleGraphDensityMode = 'overview' | 'full' | 'four-blocks';
|
||||
|
||||
const emit = defineEmits<{
|
||||
densityChange: [RuleGraphDensityMode],
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
query: RuleConfigRequest,
|
||||
refreshKey: number,
|
||||
}>();
|
||||
|
||||
const graphShellRef = ref<HTMLElement | null>(null);
|
||||
const hostRef = ref<HTMLDivElement | null>(null);
|
||||
const isFullscreen = ref(false);
|
||||
const errorMsg = ref<string | null>(null);
|
||||
const emptyHint = ref<string | null>(null);
|
||||
const density = ref<'overview' | 'full'>('overview');
|
||||
const density = ref<RuleGraphDensityMode>('overview');
|
||||
const lastPayload = ref<RuleGraphPayload | null>(null);
|
||||
|
||||
let graph: Graph | null = null;
|
||||
@@ -127,6 +158,48 @@ const toGraphData = (payload: RuleGraphPayload) => ({
|
||||
})),
|
||||
});
|
||||
|
||||
const syncFullscreenState = () => {
|
||||
const el = graphShellRef.value;
|
||||
isFullscreen.value = Boolean(el && document.fullscreenElement === el);
|
||||
};
|
||||
|
||||
const refitMainGraphAfterLayout = () => {
|
||||
if (!graph || !hostRef.value) {
|
||||
return;
|
||||
}
|
||||
const w = Math.max(hostRef.value.clientWidth, 280);
|
||||
const h = Math.max(hostRef.value.clientHeight, 240);
|
||||
graph.setSize(w, h);
|
||||
void graph.fitView();
|
||||
};
|
||||
|
||||
const onFullscreenChange = () => {
|
||||
syncFullscreenState();
|
||||
void nextTick(() => {
|
||||
refitMainGraphAfterLayout();
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
window.setTimeout(() => {
|
||||
refitMainGraphAfterLayout();
|
||||
}, 120);
|
||||
});
|
||||
};
|
||||
|
||||
const toggleFullscreen = async () => {
|
||||
const el = graphShellRef.value;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (document.fullscreenElement === el) {
|
||||
await document.exitFullscreen();
|
||||
} else {
|
||||
await el.requestFullscreen();
|
||||
}
|
||||
} catch {
|
||||
message.warning('无法切换全屏,请检查浏览器权限或使用 Chrome / Edge');
|
||||
}
|
||||
};
|
||||
|
||||
const disposeGraph = async () => {
|
||||
resizeObserver?.disconnect();
|
||||
resizeObserver = null;
|
||||
@@ -229,6 +302,9 @@ const buildGraph = async (payload: RuleGraphPayload, mode: 'overview' | 'full')
|
||||
};
|
||||
|
||||
const renderFromCache = async () => {
|
||||
if (density.value === 'four-blocks') {
|
||||
return;
|
||||
}
|
||||
if (!lastPayload.value) {
|
||||
return;
|
||||
}
|
||||
@@ -236,7 +312,8 @@ const renderFromCache = async () => {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await buildGraph(lastPayload.value, density.value);
|
||||
const mode: 'overview' | 'full' = density.value === 'full' ? 'full' : 'overview';
|
||||
await buildGraph(lastPayload.value, mode);
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
@@ -278,16 +355,34 @@ const load = async () => {
|
||||
watch(
|
||||
() => [props.query.pageNum, props.query.pageSize, props.refreshKey],
|
||||
() => {
|
||||
if (density.value === 'four-blocks') {
|
||||
return;
|
||||
}
|
||||
void load();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(density, () => {
|
||||
void renderFromCache();
|
||||
watch(density, async (mode, prev) => {
|
||||
emit('densityChange', mode);
|
||||
if (mode === 'four-blocks') {
|
||||
await disposeGraph();
|
||||
return;
|
||||
}
|
||||
if (prev === 'four-blocks') {
|
||||
await load();
|
||||
return;
|
||||
}
|
||||
await renderFromCache();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
syncFullscreenState();
|
||||
document.addEventListener('fullscreenchange', onFullscreenChange);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
||||
void disposeGraph();
|
||||
});
|
||||
</script>
|
||||
@@ -311,6 +406,14 @@ onBeforeUnmount(() => {
|
||||
padding: 0 2px 4px;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__fullscreen-btn {
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
color: #b8ccd6;
|
||||
border-color: rgba(120, 170, 200, 0.45);
|
||||
background: rgba(10, 28, 40, 0.65);
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__hint {
|
||||
font-size: 11px;
|
||||
color: #7a8d96;
|
||||
@@ -318,6 +421,35 @@ onBeforeUnmount(() => {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__hint--compact {
|
||||
font-size: 10px;
|
||||
color: #6d8290;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--four-blocks {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--fullscreen {
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
padding: 10px 12px;
|
||||
gap: 10px;
|
||||
background: #0d1f2c;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--fullscreen .rule-knowledge-graph__host {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--fullscreen :deep(.rule-four-blocks) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__host {
|
||||
flex: 1;
|
||||
min-height: 200px;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import { HttpRequestClient } from '@/utils/request';
|
||||
import type { ApiDataResponse, BasicResponse } from '@/types';
|
||||
import type { RuleConfig, RuleConfigPageableResponse, RuleConfigRequest, RuleDictItem, RuleGraphPayload, RuleParamMeta } from './types';
|
||||
import type { RuleConfig, RuleConfigPageableResponse, RuleConfigRequest, RuleDictItem, RuleFourBlocksPayload, RuleGraphPayload, RuleParamMeta } from './types';
|
||||
|
||||
const req = HttpRequestClient.create<BasicResponse>({
|
||||
baseURL: '/api',
|
||||
@@ -46,3 +46,7 @@ export const findRuleParamMeta = (): Promise<ApiDataResponse<RuleParamMeta[]>> =
|
||||
export const findRuleConfigGraph = (query: Partial<RuleConfigRequest> = {}): Promise<ApiDataResponse<RuleGraphPayload>> => {
|
||||
return req.get('/system/rule/config/graph', query);
|
||||
};
|
||||
|
||||
export const findRuleFourBlocksGraph = (): Promise<ApiDataResponse<RuleFourBlocksPayload>> => {
|
||||
return req.get('/system/rule/config/graph/four-blocks');
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
maxWidth: '78%',
|
||||
}"
|
||||
>
|
||||
<RuleKnowledgeGraph :query="query" :refresh-key="graphRevision" />
|
||||
<RuleKnowledgeGraph :query="query" :refresh-key="graphRevision" @density-change="onRuleGraphDensityChange" />
|
||||
</div>
|
||||
<div
|
||||
class="rule-config-main-split-resizer"
|
||||
@@ -251,12 +251,27 @@ const GRAPH_PANE_STORAGE_KEY = 'rule-config-graph-pane-percent';
|
||||
const GRAPH_PANE_MIN = 18;
|
||||
const GRAPH_PANE_MAX = 70;
|
||||
const graphPanePercent = ref(32);
|
||||
const graphPanePercentBeforeFourBlocks = ref<number | null>(null);
|
||||
const splitRootRef = ref<HTMLElement | null>(null);
|
||||
const graphRevision = ref(0);
|
||||
|
||||
const clampGraphPercent = (n: number) =>
|
||||
Math.min(GRAPH_PANE_MAX, Math.max(GRAPH_PANE_MIN, Math.round(n)));
|
||||
|
||||
const onRuleGraphDensityChange = (mode: 'overview' | 'full' | 'four-blocks') => {
|
||||
if (mode === 'four-blocks') {
|
||||
if (graphPanePercentBeforeFourBlocks.value === null) {
|
||||
graphPanePercentBeforeFourBlocks.value = graphPanePercent.value;
|
||||
}
|
||||
graphPanePercent.value = clampGraphPercent(62);
|
||||
return;
|
||||
}
|
||||
if (graphPanePercentBeforeFourBlocks.value !== null) {
|
||||
graphPanePercent.value = clampGraphPercent(graphPanePercentBeforeFourBlocks.value);
|
||||
graphPanePercentBeforeFourBlocks.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onGraphSplitMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const root = splitRootRef.value;
|
||||
|
||||
@@ -105,4 +105,33 @@ export interface RuleGraphEdge {
|
||||
export interface RuleGraphPayload {
|
||||
nodes: RuleGraphNode[],
|
||||
edges: RuleGraphEdge[],
|
||||
layoutHint?: string | null,
|
||||
focusNodeId?: string | null,
|
||||
}
|
||||
|
||||
export interface RuleFourBlockParamRow {
|
||||
ruleCode: NullableString,
|
||||
ruleName: NullableString,
|
||||
paramKey: NullableString,
|
||||
paramName: NullableString,
|
||||
whenText: NullableString,
|
||||
thenText: NullableString,
|
||||
outputText: NullableString,
|
||||
}
|
||||
|
||||
export interface RuleFourBlockCluster {
|
||||
blockId: string,
|
||||
moduleCode: NullableString,
|
||||
title: NullableString,
|
||||
droolsRuleName: NullableString,
|
||||
salience: number | null,
|
||||
whenExpr: NullableString,
|
||||
thenAction: NullableString,
|
||||
graph: RuleGraphPayload,
|
||||
paramRows?: RuleFourBlockParamRow[] | null,
|
||||
}
|
||||
|
||||
export interface RuleFourBlocksPayload {
|
||||
globalParamsPreview: Record<string, unknown>,
|
||||
blocks: RuleFourBlockCluster[],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user