This commit is contained in:
@@ -214,9 +214,10 @@ public class FlowDefAdminService {
|
||||
}
|
||||
List<String> problems = new ArrayList<>();
|
||||
validateExpressionKeysAgainstSnapshot(model, snapshot, problems);
|
||||
validateExpressionTypesAndDomains(model, snapshot, problems);
|
||||
if (!problems.isEmpty()) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR,
|
||||
"发布校验失败(表达式键查①):\n" + String.join("\n", problems));
|
||||
"发布校验失败(表达式三查):\n" + String.join("\n", problems));
|
||||
}
|
||||
def.setContentJson(embedFormContract(def.getContentJson(), snapshot));
|
||||
}
|
||||
@@ -286,6 +287,101 @@ public class FlowDefAdminService {
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 三查②③(2026-10-20 C1 第一刀余量项) =====================
|
||||
|
||||
/** 数值桶:可与数值字面量比大小的快照字段类型 */
|
||||
private static final java.util.Set<String> NUMERIC_TYPES =
|
||||
new java.util.HashSet<>(java.util.Arrays.asList("number"));
|
||||
/** 值域字面量型(enum 域查③适用):options 收进快照 enum 的类型 */
|
||||
private static final java.util.Set<String> ENUM_TYPES =
|
||||
new java.util.HashSet<>(java.util.Arrays.asList("select", "radio"));
|
||||
|
||||
/** `字段 op '字面量'` / `字段 op 数值`(op 含两侧空格容忍) */
|
||||
private static final java.util.regex.Pattern FIELD_STR_EQ = java.util.regex.Pattern.compile(
|
||||
"([A-Za-z_][A-Za-z0-9_]*)\\s*(==|!=)\\s*'([^']*)'");
|
||||
private static final java.util.regex.Pattern FIELD_NUM_CMP = java.util.regex.Pattern.compile(
|
||||
"([A-Za-z_][A-Za-z0-9_]*)\\s*(>=|<=|>|<)\\s*(-?\\d+(\\.\\d+)?)");
|
||||
private static final java.util.regex.Pattern NUM_FIELD_CMP = java.util.regex.Pattern.compile(
|
||||
"(-?\\d+(\\.\\d+)?)\\s*(>=|<=|>|<)\\s*([A-Za-z_][A-Za-z0-9_]*)");
|
||||
|
||||
/**
|
||||
* 类型桶查②红牌 + 域查③红牌(<b>启发式实现</b>:正则模式匹配字面量比较形态,非 AST——
|
||||
* v2 §4.7 保守边界:不执行表达式、不做隐式转换,只拦"数值比较 × 非数值字段"与
|
||||
* "枚举等值 × 字面量不在值域"两类高发错配;不命中的形态留给运行期既有挂起纪律)。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void validateExpressionTypesAndDomains(FlowDefModel model, Map<String, Object> snapshot,
|
||||
List<String> problems) {
|
||||
List<Map<String, Object>> fields = (List<Map<String, Object>>) snapshot.get("fields");
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Map<String, Object>> fieldByKey = new HashMap<>();
|
||||
for (Map<String, Object> f : fields) {
|
||||
Object k = f.get("key");
|
||||
if (k != null) {
|
||||
fieldByKey.put(String.valueOf(k), f);
|
||||
}
|
||||
}
|
||||
for (FlowDefModel.Node n : model.nodes) {
|
||||
if (n == null || !n.expressionJudge()) {
|
||||
continue;
|
||||
}
|
||||
for (FlowDefModel.Edge e : model.outEdges(n.id)) {
|
||||
if (e.condition == null || e.condition.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String where = "节点「" + n.name + "」出口「" + e.name + "」";
|
||||
String expr = e.condition.trim();
|
||||
// ② 类型桶查:数值比较算子作用于非数值字段 → 红牌
|
||||
java.util.regex.Matcher m = FIELD_NUM_CMP.matcher(expr);
|
||||
while (m.find()) {
|
||||
checkNumericBucket(where, m.group(1), m.group(2), fieldByKey, problems);
|
||||
}
|
||||
m = NUM_FIELD_CMP.matcher(expr);
|
||||
while (m.find()) {
|
||||
checkNumericBucket(where, m.group(4), m.group(3), fieldByKey, problems);
|
||||
}
|
||||
// ② 类型桶查(字符串侧):文本字面量等值比较数值字段 → 红牌
|
||||
// ③ 域查:枚举字段等值比较的字面量 ∉ 快照 enum 值域 → 红牌
|
||||
m = FIELD_STR_EQ.matcher(expr);
|
||||
while (m.find()) {
|
||||
String key = m.group(1);
|
||||
String op = m.group(2);
|
||||
String literal = m.group(3);
|
||||
Map<String, Object> field = fieldByKey.get(key);
|
||||
if (field == null) {
|
||||
continue; // 内置变量或未知键:①已拦/不属本查
|
||||
}
|
||||
String type = field.get("type") == null ? null : String.valueOf(field.get("type"));
|
||||
if (NUMERIC_TYPES.contains(type)) {
|
||||
problems.add(where + ":字段 " + key + " 是 number 类型,不能与字符串字面量 '"
|
||||
+ literal + "' 做 " + op + " 比较(类型桶查②)");
|
||||
} else if (ENUM_TYPES.contains(type) && "==".equals(op)) {
|
||||
List<String> enums = (List<String>) field.get("enum");
|
||||
if (enums != null && !enums.isEmpty() && !enums.contains(literal)) {
|
||||
problems.add(where + ":字段 " + key + " 的值域是 " + enums
|
||||
+ ",字面量 '" + literal + "' 不在其中(域查③)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNumericBucket(String where, String key, String op,
|
||||
Map<String, Map<String, Object>> fieldByKey, List<String> problems) {
|
||||
Map<String, Object> field = fieldByKey.get(key);
|
||||
if (field == null) {
|
||||
return;
|
||||
}
|
||||
String type = field.get("type") == null ? null : String.valueOf(field.get("type"));
|
||||
if (type != null && !NUMERIC_TYPES.contains(type)) {
|
||||
problems.add(where + ":字段 " + key + " 是 " + type + " 类型,不能做数值比较 "
|
||||
+ key + " " + op + " 数字(类型桶查②)");
|
||||
}
|
||||
}
|
||||
|
||||
/** 停用:不可新发起,存量跑完 */
|
||||
@Transactional
|
||||
public void offline(Long schId, Long id) {
|
||||
|
||||
Reference in New Issue
Block a user