fix: stabilize rule editor yaml roundtrip

This commit is contained in:
wren
2026-05-07 18:58:55 +08:00
parent 5fc3a7a254
commit 050aa679be
2 changed files with 47 additions and 5 deletions
+44 -2
View File
@@ -330,7 +330,7 @@ function normalizeBooleanText(value: string | boolean | undefined): boolean {
}
function rewriteExtractNodes(fields: ExtractFieldSummary[]): Array<Record<string, unknown>> {
const topLevelFields = fields.filter((field) => !field.name.includes('[*].'));
const topLevelFields = fields.filter((field) => field.group !== '派生字段' && !field.name.includes('[*].'));
return Array.from(groupBy(topLevelFields, (field) => field.group || '未分组').entries()).map(([group, items]) => ({
group,
fields: items.map((field) => ({
@@ -343,6 +343,46 @@ function rewriteExtractNodes(fields: ExtractFieldSummary[]): Array<Record<string
}));
}
function rewriteDerivedFieldNodes(
fields: ExtractFieldSummary[],
existingNodes: unknown,
): Array<Record<string, unknown>> {
const derivedFields = fields.filter((field) => field.group === '派生字段');
if (derivedFields.length === 0) {
return [];
}
const existingMap = new Map<string, Record<string, unknown>>();
if (Array.isArray(existingNodes)) {
existingNodes.forEach((node) => {
if (!node || typeof node !== 'object') return;
const record = deepClone(node as Record<string, unknown>);
const name = String(record.name || '').trim();
if (name) {
existingMap.set(name, record);
}
});
}
return derivedFields.map((field) => {
const existing = existingMap.get(field.name) || {};
const nextNode: Record<string, unknown> = {
...existing,
name: field.name,
type: field.type || String(existing.type || 'computed'),
};
const nextCompute = field.description && field.description !== '由其他字段计算得出'
? field.description
: String(existing.compute || '').trim();
if (nextCompute) {
nextNode.compute = nextCompute;
} else {
delete nextNode.compute;
}
return nextNode;
});
}
function rewriteSubDocumentNodes(subDocuments: SubDocumentSummary[]): Array<Record<string, unknown>> {
return subDocuments.map((document) => ({
id: document.id,
@@ -474,7 +514,6 @@ function rewriteRuleNode(baseRule: Record<string, unknown> | undefined, rule: Ru
}
delete nextRule.rules;
delete nextRule.logic;
const existingStages = Array.isArray(nextRule.stages) ? (nextRule.stages as Array<Record<string, unknown>>) : [];
const stages = existingStages.length > 0 ? deepClone(existingStages) : (buildMinimalRuleNode(rule).stages as Array<Record<string, unknown>>);
@@ -494,6 +533,8 @@ function rewriteRuleNode(baseRule: Record<string, unknown> | undefined, rule: Ru
}
nextRule.stages = stages;
if (rule.logic?.trim()) nextRule.logic = rule.logic.trim();
else if (typeof nextRule.logic === 'string' && !String(nextRule.logic).trim()) delete nextRule.logic;
return nextRule;
}
@@ -512,6 +553,7 @@ export function serializeEditableRuleConfig(config: EditableRuleConfig): string
if (Array.isArray(config.metadata.inheritsFrom) && config.metadata.inheritsFrom.length > 0) metadata.inherits_from = [...config.metadata.inheritsFrom];
root.metadata = metadata;
root.extract = rewriteExtractNodes(config.fields);
root.derived_fields = rewriteDerivedFieldNodes(config.fields, root.derived_fields);
root.sub_documents = rewriteSubDocumentNodes(config.subDocuments);
root.visual_elements = rewriteVisualElementNodes(config.visualElements);
+3 -3
View File
@@ -415,9 +415,9 @@ function parseRules(source: string): RuleSummary[] {
group,
risk: stripYamlValue(ruleBlock.match(/^\s+risk:\s*(.+)$/m)?.[1] || 'medium'),
score: stripYamlValue(ruleBlock.match(/^\s+score:\s*(.+)$/m)?.[1] || '-'),
type: stripYamlValue(ruleBlock.match(/^\s+type:\s*(.+)$/m)?.[1] || 'deterministic'),
type: stripYamlValue(ruleBlock.match(/^\s{4}type:\s*(.+)$/m)?.[1] || 'deterministic'),
checkTypes,
logic: stripYamlValue(ruleBlock.match(/^\s+logic:\s*(.+)$/m)?.[1] || ''),
logic: stripYamlValue(ruleBlock.match(/^\s{4}logic:\s*(.+)$/m)?.[1] || ''),
subRules,
subRuleIds: readList(ruleBlock, 'rules'),
scope: scope.slice(0, 8),
@@ -425,7 +425,7 @@ function parseRules(source: string): RuleSummary[] {
stageCount: subRules.length,
appliesIn: readFlexibleList(ruleBlock, 'applies_in'),
prompt: prompts.join('\n\n'),
description: stripYamlValue(ruleBlock.match(/^\s+desc:\s*(.+)$/m)?.[1] || '')
description: stripYamlValue(ruleBlock.match(/^\s{4}desc:\s*(.+)$/m)?.[1] || '')
};
});
});