feat: sync rule management and review ui fixes
This commit is contained in:
@@ -42,6 +42,7 @@ export type ExtractFieldSummary = {
|
||||
name: string;
|
||||
type: string;
|
||||
multipleEntities: boolean;
|
||||
allowed?: string[];
|
||||
requiredFrom: string;
|
||||
description: string;
|
||||
};
|
||||
@@ -89,9 +90,13 @@ export type RuleYamlPack = RulePackScope & {
|
||||
name: string;
|
||||
type: string;
|
||||
required: string;
|
||||
requiredFrom?: string;
|
||||
signerRoles?: string[];
|
||||
signatureTypes?: string[];
|
||||
privateSealRestricted?: boolean;
|
||||
expectedMatchField?: string;
|
||||
expectedMatchAlternatives?: string[];
|
||||
prompt?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -144,6 +149,13 @@ function stripYamlValue(value = ''): string {
|
||||
return value.trim().replace(/^['"]|['"]$/g, '').replace(/\u0000/g, '');
|
||||
}
|
||||
|
||||
function toStringList(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
return value.map((item) => String(item || '').trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
function parseScalar(section: string, key: string): string {
|
||||
const match = section.match(new RegExp(`^\\s{2}${key}:\\s*(.*)$`, 'm'));
|
||||
return stripYamlValue(match?.[1] || '');
|
||||
@@ -400,38 +412,58 @@ export function parseRuleSummariesFromYaml(source: string): RuleSummary[] {
|
||||
}
|
||||
|
||||
function parseTopLevelFields(source: string): ExtractFieldSummary[] {
|
||||
const section = getTopLevelSection(source, 'extract');
|
||||
const extractedFields = splitBlocks(section, /^\s*-\s+group:\s*/).flatMap(groupBlock => {
|
||||
const group = stripYamlValue(groupBlock.match(/^\s*-\s+group:\s*(.+)$/m)?.[1] || '未分组');
|
||||
return splitBlocks(groupBlock, /^\s*-\s+name:\s*/).flatMap(fieldBlock => {
|
||||
const name = stripYamlValue(fieldBlock.match(/^\s*-\s+name:\s*(.+)$/m)?.[1] || '');
|
||||
const rawType = stripYamlValue(fieldBlock.match(/^\s+type:\s*(.+)$/m)?.[1] || '-');
|
||||
const parentField = {
|
||||
const parsed = YAML.parse(source || '') as Record<string, unknown> | null;
|
||||
const extractGroups = Array.isArray(parsed?.extract) ? parsed.extract : [];
|
||||
const extractedFields = extractGroups.flatMap((groupNode) => {
|
||||
if (!groupNode || typeof groupNode !== 'object') {
|
||||
return [];
|
||||
}
|
||||
const groupObject = groupNode as Record<string, unknown>;
|
||||
const group = String(groupObject.group || '未分组').trim() || '未分组';
|
||||
const fields = Array.isArray(groupObject.fields) ? groupObject.fields : [];
|
||||
return fields.flatMap((fieldNode) => {
|
||||
if (!fieldNode || typeof fieldNode !== 'object') {
|
||||
return [];
|
||||
}
|
||||
const field = fieldNode as Record<string, unknown>;
|
||||
const name = String(field.name || '').trim();
|
||||
if (!name) {
|
||||
return [];
|
||||
}
|
||||
const rawType = String(field.type || '-').trim();
|
||||
const requiredFrom = String(field.required_from || '-').trim() || '-';
|
||||
const parentField: ExtractFieldSummary = {
|
||||
id: `${group}-${name}`,
|
||||
group,
|
||||
name,
|
||||
type: rawType === 'multi_entity' ? 'verbatim' : rawType,
|
||||
multipleEntities: rawType === 'multi_entity',
|
||||
requiredFrom: stripYamlValue(fieldBlock.match(/^\s+required_from:\s*(.+)$/m)?.[1] || '-'),
|
||||
description: stripYamlValue(fieldBlock.match(/^\s+desc:\s*(.+)$/m)?.[1] || '')
|
||||
allowed: toStringList(field.allowed),
|
||||
requiredFrom,
|
||||
description: String(field.desc || '').trim(),
|
||||
};
|
||||
const childFields = Array.from(fieldBlock.matchAll(/^\s{2,}-\s+name:\s*(.+)$/gm)).map(match => {
|
||||
const childName = stripYamlValue(match[1]);
|
||||
const start = fieldBlock.indexOf(match[0]);
|
||||
const next = fieldBlock.slice(start + match[0].length).search(/^\s{2,}-\s+name:\s*/m);
|
||||
const childBlock = next === -1 ? fieldBlock.slice(start) : fieldBlock.slice(start, start + match[0].length + next);
|
||||
const childType = stripYamlValue(childBlock.match(/^\s+type:\s*(.+)$/m)?.[1] || 'verbatim');
|
||||
return {
|
||||
const childFields = Array.isArray(field.fields) ? field.fields : [];
|
||||
const normalizedChildFields = childFields.flatMap((childNode) => {
|
||||
if (!childNode || typeof childNode !== 'object') {
|
||||
return [];
|
||||
}
|
||||
const childField = childNode as Record<string, unknown>;
|
||||
const childName = String(childField.name || '').trim();
|
||||
if (!childName) {
|
||||
return [];
|
||||
}
|
||||
return [{
|
||||
id: `${group}-${name}-${childName}`,
|
||||
group,
|
||||
name: `${name}[*].${childName}`,
|
||||
type: childType,
|
||||
type: String(childField.type || 'verbatim').trim() || 'verbatim',
|
||||
multipleEntities: false,
|
||||
requiredFrom: stripYamlValue(childBlock.match(/^\s+required_from:\s*(.+)$/m)?.[1] || parentField.requiredFrom),
|
||||
description: stripYamlValue(childBlock.match(/^\s+desc:\s*(.+)$/m)?.[1] || `${name}的子字段`)
|
||||
};
|
||||
allowed: toStringList(childField.allowed),
|
||||
requiredFrom: String(childField.required_from || requiredFrom).trim() || requiredFrom,
|
||||
description: String(childField.desc || `${name}的子字段`).trim(),
|
||||
}];
|
||||
});
|
||||
return [parentField, ...childFields];
|
||||
return [parentField, ...normalizedChildFields];
|
||||
});
|
||||
}).filter(field => field.name);
|
||||
|
||||
@@ -493,6 +525,7 @@ function parseSubDocuments(source: string): SubDocumentSummary[] {
|
||||
name,
|
||||
type: rawType === 'multi_entity' ? 'verbatim' : rawType,
|
||||
multipleEntities: rawType === 'multi_entity',
|
||||
allowed: toStringList(field.allowed),
|
||||
requiredFrom: '-',
|
||||
description: String(field.desc || '').trim(),
|
||||
}];
|
||||
@@ -553,14 +586,25 @@ function parseVisualElements(source: string): RuleYamlPack['visualElements'] {
|
||||
: []
|
||||
);
|
||||
|
||||
const expectedMatch = node.expected_text_match && typeof node.expected_text_match === 'object'
|
||||
? (node.expected_text_match as Record<string, unknown>)
|
||||
: null;
|
||||
const signatureTypes = key === 'seals'
|
||||
? toStringList(node.allowed_types)
|
||||
: toStringList(node.signature_types);
|
||||
|
||||
return [{
|
||||
id,
|
||||
name: String(node.name || id).trim(),
|
||||
type: label,
|
||||
required: String(node.required ?? '-').trim(),
|
||||
signerRoles: toStringList(node.signer_roles),
|
||||
signatureTypes: toStringList(node.signature_types),
|
||||
privateSealRestricted: Boolean(node.private_seal_restricted),
|
||||
requiredFrom: String(node.required_from ?? '').trim(),
|
||||
signerRoles: key === 'signatures' ? toStringList(node.signer_roles) : [],
|
||||
signatureTypes,
|
||||
privateSealRestricted: key === 'signatures' ? Boolean(node.private_seal_restricted) : false,
|
||||
expectedMatchField: String(expectedMatch?.field || '').trim(),
|
||||
expectedMatchAlternatives: toStringList(expectedMatch?.alternatives),
|
||||
prompt: String(node.prompt || '').trim(),
|
||||
}];
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user