feat: align frontend document and rule management flows
This commit is contained in:
@@ -35,26 +35,34 @@ export const permissionRouteAliasGroups = [
|
||||
entries: [
|
||||
{
|
||||
source: '^/reviewsTest(?=/|$)',
|
||||
target: '/reviews',
|
||||
note: '旧版评查测试页复用正式评查页权限。',
|
||||
target: '/documents',
|
||||
note: '文档评查详情页复用文档模块权限,兼容从文档列表与上传页进入详情的场景。',
|
||||
examples: [
|
||||
{ input: '/reviewsTest', output: '/reviews' },
|
||||
{ input: '/reviewsTest', output: '/documents' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/reviews(?=/|$)',
|
||||
target: '/documents',
|
||||
note: '旧版评查详情页同样归入文档模块权限,避免历史链接访问被拒绝。',
|
||||
examples: [
|
||||
{ input: '/reviews', output: '/documents' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/rulesTest/list(?=/|$)',
|
||||
target: '/rules/list',
|
||||
note: '旧版规则列表页复用新版规则列表权限。',
|
||||
target: '/rules',
|
||||
note: '旧版规则列表页归入规则管理主菜单权限。',
|
||||
examples: [
|
||||
{ input: '/rulesTest/list', output: '/rules/list' },
|
||||
{ input: '/rulesTest/list', output: '/rules' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/rulesTest/detail(?=/|$)',
|
||||
target: '/rules/list',
|
||||
note: '旧版规则详情页归入规则列表权限体系。',
|
||||
target: '/rules',
|
||||
note: '旧版规则详情页归入规则管理主菜单权限。',
|
||||
examples: [
|
||||
{ input: '/rulesTest/detail', output: '/rules/list' },
|
||||
{ input: '/rulesTest/detail', output: '/rules' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -98,17 +106,41 @@ export const permissionRouteAliasGroups = [
|
||||
{
|
||||
source: '^/rule-groups/new(?=/|$)',
|
||||
target: '/rule-groups',
|
||||
note: '评查点分组新建/编辑页复用分组列表权限。',
|
||||
note: '旧分组新建/编辑入口已下线,统一回到规则导航页。',
|
||||
examples: [
|
||||
{ input: '/rule-groups/new', output: '/rule-groups' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/rules/new(?=/|$)',
|
||||
target: '/rules/list',
|
||||
note: '评查点新建/编辑/复制页复用评查点列表权限。',
|
||||
source: '^/rules/list(?=/|$)',
|
||||
target: '/rules',
|
||||
note: '评查点列表页优先复用规则管理主菜单权限,兼容后端尚未细分子路由授权的场景。',
|
||||
examples: [
|
||||
{ input: '/rules/new', output: '/rules/list' },
|
||||
{ input: '/rules/list', output: '/rules' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/rules/new(?=/|$)',
|
||||
target: '/rules',
|
||||
note: '评查点新建/编辑/复制页复用规则管理主菜单权限。',
|
||||
examples: [
|
||||
{ input: '/rules/new', output: '/rules' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/rules/sets(?=/|$)',
|
||||
target: '/rules',
|
||||
note: '旧版规则管理入口复用规则管理主菜单权限,并跳转到新版规则维护页。',
|
||||
examples: [
|
||||
{ input: '/rules/sets', output: '/rules' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/documents/list(?=/|$)',
|
||||
target: '/documents',
|
||||
note: '文档列表子页复用文档模块主菜单权限,兼容 Remix 嵌套路由地址。',
|
||||
examples: [
|
||||
{ input: '/documents/list', output: '/documents' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -135,18 +167,34 @@ export const permissionRouteAliasGroups = [
|
||||
},
|
||||
{
|
||||
source: '^/contract-template/detail(?=/|$)',
|
||||
target: '/contract-template',
|
||||
note: '合同模板详情页归属到合同模板模块。',
|
||||
target: '/contract-template/list',
|
||||
note: '合同模板详情页复用合同列表权限,兼容父菜单本身不直接授权的场景。',
|
||||
examples: [
|
||||
{ input: '/contract-template/detail/123', output: '/contract-template/123' },
|
||||
{ input: '/contract-template/detail/123', output: '/contract-template/list/123' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/contract-draft(?=/|$)',
|
||||
target: '/contract-template',
|
||||
note: '合同起草页作为合同模板模块的延伸能力。',
|
||||
target: '/contract-template/list',
|
||||
note: '合同起草页复用合同列表权限,保持与老入口的访问语义一致。',
|
||||
examples: [
|
||||
{ input: '/contract-draft/1', output: '/contract-template/1' },
|
||||
{ input: '/contract-draft/1', output: '/contract-template/list/1' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/chat-with-llm/chat(?=/|$)',
|
||||
target: '/chat-with-llm',
|
||||
note: 'AI 对话实际工作台复用 AI 对话主菜单权限。',
|
||||
examples: [
|
||||
{ input: '/chat-with-llm/chat', output: '/chat-with-llm' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '^/chat-with-llm/dataset-manager(?=/|$)',
|
||||
target: '/chat-with-llm',
|
||||
note: '知识库管理页归属 AI 对话模块,避免隐藏子页权限断层。',
|
||||
examples: [
|
||||
{ input: '/chat-with-llm/dataset-manager', output: '/chat-with-llm' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
export type RuleSummary = {
|
||||
id: string;
|
||||
ruleId: string;
|
||||
name: string;
|
||||
group: string;
|
||||
risk: string;
|
||||
score: string;
|
||||
type: string;
|
||||
checkTypes: string[];
|
||||
logic: string;
|
||||
subRules: Array<{
|
||||
id: string;
|
||||
check: string;
|
||||
content: string;
|
||||
}>;
|
||||
subRuleIds: string[];
|
||||
scope: string[];
|
||||
dependencies: string[];
|
||||
stageCount: number;
|
||||
appliesIn: string[];
|
||||
prompt: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
function getTopLevelSection(source: string, key: string): string {
|
||||
const lines = source.split("\n");
|
||||
const start = lines.findIndex((line) => line === `${key}:`);
|
||||
if (start === -1) return "";
|
||||
const end = lines.findIndex((line, index) => index > start && /^[a-zA-Z_][\w-]*:/.test(line));
|
||||
return lines.slice(start + 1, end === -1 ? undefined : end).join("\n");
|
||||
}
|
||||
|
||||
function stripYamlValue(value = ""): string {
|
||||
return value.trim().replace(/^['"]|['"]$/g, "").replace(/\u0000/g, "");
|
||||
}
|
||||
|
||||
function splitBlocks(section: string, marker: RegExp): string[] {
|
||||
const lines = section.split("\n");
|
||||
const starts = lines.reduce<number[]>((indexes, line, index) => {
|
||||
if (marker.test(line)) indexes.push(index);
|
||||
return indexes;
|
||||
}, []);
|
||||
|
||||
return starts.map((start, index) => lines.slice(start, starts[index + 1]).join("\n"));
|
||||
}
|
||||
|
||||
export function parseRuleSummariesFromYaml(source: string): RuleSummary[] {
|
||||
const section = getTopLevelSection(source, "rules");
|
||||
const groups = splitBlocks(section, /^-\s+group:\s*/);
|
||||
|
||||
const readExplicitDependencies = (block: string): string[] => {
|
||||
const lines = block.split("\n");
|
||||
const start = lines.findIndex((line) => /^\s{4}dependencies:\s*$/.test(line));
|
||||
if (start === -1) return [];
|
||||
|
||||
const dependencies: string[] = [];
|
||||
for (let index = start + 1; index < lines.length; index += 1) {
|
||||
const line = lines[index];
|
||||
if (/^\s{4}[a-zA-Z_][^:]*:\s*/.test(line)) break;
|
||||
const match = line.match(/^\s{4}-\s+(.+)$/);
|
||||
if (match) dependencies.push(stripYamlValue(match[1]));
|
||||
}
|
||||
return dependencies;
|
||||
};
|
||||
|
||||
const normalizeDependency = (value: string) => {
|
||||
const normalized = stripYamlValue(value);
|
||||
if (normalized === "cross_page_seal") return "骑缝章";
|
||||
if (normalized === "seal") return "印章";
|
||||
if (normalized === "signature") return "签名";
|
||||
return normalized;
|
||||
};
|
||||
|
||||
const readPrompts = (block: string): string[] => {
|
||||
const lines = block.split("\n");
|
||||
const prompts: string[] = [];
|
||||
for (let index = 0; index < lines.length; index += 1) {
|
||||
const match = lines[index].match(/^(\s*)prompt:\s*(.*)$/);
|
||||
if (!match) continue;
|
||||
|
||||
const indent = match[1].length;
|
||||
const parts = [match[2]];
|
||||
for (let nextIndex = index + 1; nextIndex < lines.length; nextIndex += 1) {
|
||||
const line = lines[nextIndex];
|
||||
const nextIndent = line.match(/^\s*/)?.[0].length || 0;
|
||||
const trimmed = line.trim();
|
||||
if (trimmed && nextIndent <= indent) break;
|
||||
if (trimmed && nextIndent === indent + 2 && /^[a-zA-Z_][\w-]*:\s*/.test(trimmed)) break;
|
||||
parts.push(line);
|
||||
}
|
||||
|
||||
prompts.push(
|
||||
parts
|
||||
.join("\n")
|
||||
.replace(/^['"]/, "")
|
||||
.replace(/['"]\s*$/, "")
|
||||
.split("\n")
|
||||
.map((line) => line.replace(/^\s{8}/, ""))
|
||||
.join("\n")
|
||||
.trim(),
|
||||
);
|
||||
}
|
||||
return prompts.filter(Boolean);
|
||||
};
|
||||
|
||||
const readList = (block: string, key: string, indent = 4): string[] => {
|
||||
const lines = block.split("\n");
|
||||
const start = lines.findIndex((line) => new RegExp(`^\\s{${indent}}${key}:\\s*$`).test(line));
|
||||
if (start === -1) return [];
|
||||
|
||||
const values: string[] = [];
|
||||
for (let index = start + 1; index < lines.length; index += 1) {
|
||||
const line = lines[index];
|
||||
if (new RegExp(`^\\s{${indent}}[a-zA-Z_][^:]*:\\s*`).test(line)) break;
|
||||
const match = line.match(new RegExp(`^\\s{${indent}}-\\s+(.+)$`));
|
||||
if (match) values.push(stripYamlValue(match[1]));
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
const readFlexibleList = (block: string, key: string): string[] => {
|
||||
const lines = block.split("\n");
|
||||
const start = lines.findIndex((line) => new RegExp(`^(\\s*)${key}:\\s*$`).test(line));
|
||||
if (start === -1) return [];
|
||||
const indent = lines[start].match(/^\s*/)?.[0].length || 0;
|
||||
const values: string[] = [];
|
||||
|
||||
for (let index = start + 1; index < lines.length; index += 1) {
|
||||
const line = lines[index];
|
||||
const lineIndent = line.match(/^\s*/)?.[0].length || 0;
|
||||
const match = line.match(/^\s*-\s+(.+)$/);
|
||||
if (match) {
|
||||
values.push(stripYamlValue(match[1]));
|
||||
continue;
|
||||
}
|
||||
if (line.trim() && lineIndent <= indent) break;
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
const readStageList = (block: string, key: string): string[] => {
|
||||
const lines = block.split("\n");
|
||||
const start = lines.findIndex((line) => new RegExp(`^\\s{6}${key}:\\s*$`).test(line));
|
||||
if (start === -1) return [];
|
||||
|
||||
const values: string[] = [];
|
||||
for (let index = start + 1; index < lines.length; index += 1) {
|
||||
const line = lines[index];
|
||||
if (/^\s{6}[a-zA-Z_][^:]*:\s*/.test(line)) break;
|
||||
const match = line.match(/^\s{6}-\s+(.+)$/);
|
||||
if (match) values.push(stripYamlValue(match[1]));
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
const readStageScalar = (block: string, key: string): string =>
|
||||
stripYamlValue(block.match(new RegExp(`^\\s{6}${key}:\\s*(.+)$`, "m"))?.[1] || "");
|
||||
|
||||
const summarizeStage = (stageBlock: string): string => {
|
||||
const fields = readStageList(stageBlock, "fields");
|
||||
const field = readStageScalar(stageBlock, "field");
|
||||
const left = readStageScalar(stageBlock, "left") || readStageScalar(stageBlock, "left_field");
|
||||
const op = readStageScalar(stageBlock, "op");
|
||||
const right = readStageScalar(stageBlock, "right") || readStageScalar(stageBlock, "right_field");
|
||||
const value = readStageScalar(stageBlock, "value");
|
||||
const prompt = readStageScalar(stageBlock, "prompt");
|
||||
const element = readStageScalar(stageBlock, "element") || readStageScalar(stageBlock, "seal_id") || readStageScalar(stageBlock, "signature_id");
|
||||
|
||||
if (fields.length > 0) return fields.join("、");
|
||||
if (left || right) return [left, op, right].filter(Boolean).join(" ");
|
||||
if (field && value) return `${field} = ${value}`;
|
||||
if (field) return field;
|
||||
if (element) return element;
|
||||
if (prompt) return prompt.slice(0, 80);
|
||||
return (
|
||||
stageBlock
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.slice(1, 4)
|
||||
.join(";") || "未配置内容"
|
||||
);
|
||||
};
|
||||
|
||||
const readSubRules = (block: string) =>
|
||||
splitBlocks(block, /^\s{4}-\s+id:\s*/)
|
||||
.map((stageBlock) => {
|
||||
const id = stripYamlValue(stageBlock.match(/^\s{4}-\s+id:\s*(.+)$/m)?.[1] || "");
|
||||
const check = readStageScalar(stageBlock, "check") || readStageScalar(stageBlock, "type") || "-";
|
||||
return {
|
||||
id,
|
||||
check,
|
||||
content: summarizeStage(stageBlock),
|
||||
};
|
||||
})
|
||||
.filter((stage) => stage.id);
|
||||
|
||||
return groups.flatMap((groupBlock) => {
|
||||
const group = stripYamlValue(groupBlock.match(/^-\s+group:\s*(.+)$/m)?.[1] || "未分组");
|
||||
return splitBlocks(groupBlock, /^\s{2}-\s+rule_id:\s*/).map((ruleBlock) => {
|
||||
const ruleId = stripYamlValue(ruleBlock.match(/^\s{2}-\s+rule_id:\s*(.+)$/m)?.[1] || "");
|
||||
const name = stripYamlValue(ruleBlock.match(/^\s{4}name:\s*(.+)$/m)?.[1] || "未命名规则");
|
||||
const checkTypes = Array.from(
|
||||
new Set(Array.from(ruleBlock.matchAll(/^\s{6,}(?:check|type):\s*(.+)$/gm)).map((match) => stripYamlValue(match[1]))),
|
||||
);
|
||||
const stageDependencies = Array.from(
|
||||
ruleBlock.matchAll(/^\s{6,}(?:field|number|chinese|left|right|left_field|right_field|target|element|seal_id|signature_id):\s*(.+)$/gm),
|
||||
).map((match) => normalizeDependency(match[1]));
|
||||
const dependencies = Array.from(new Set([...readExplicitDependencies(ruleBlock), ...stageDependencies]));
|
||||
const scope = Array.from(
|
||||
new Set(
|
||||
Array.from(ruleBlock.matchAll(/^\s{4,}-\s*([^:\n]+)$/gm))
|
||||
.map((match) => stripYamlValue(match[1]))
|
||||
.filter((value) => !/^\d+$/.test(value)),
|
||||
),
|
||||
);
|
||||
const prompts = readPrompts(ruleBlock);
|
||||
const subRules = readSubRules(ruleBlock);
|
||||
|
||||
return {
|
||||
id: ruleId || `${group}-${name}`,
|
||||
ruleId,
|
||||
name,
|
||||
group,
|
||||
risk: stripYamlValue(ruleBlock.match(/^\s{4}risk:\s*(.+)$/m)?.[1] || "medium"),
|
||||
score: stripYamlValue(ruleBlock.match(/^\s{4}score:\s*(.+)$/m)?.[1] || "-"),
|
||||
type: stripYamlValue(ruleBlock.match(/^\s{4}type:\s*(.+)$/m)?.[1] || "deterministic"),
|
||||
checkTypes,
|
||||
logic: stripYamlValue(ruleBlock.match(/^\s{4}logic:\s*(.+)$/m)?.[1] || ""),
|
||||
subRules,
|
||||
subRuleIds: readList(ruleBlock, "rules"),
|
||||
scope: scope.slice(0, 8),
|
||||
dependencies: dependencies.slice(0, 8),
|
||||
stageCount: subRules.length,
|
||||
appliesIn: readFlexibleList(ruleBlock, "applies_in"),
|
||||
prompt: prompts.join("\n\n"),
|
||||
description: stripYamlValue(ruleBlock.match(/^\s{4}desc:\s*(.+)$/m)?.[1] || ""),
|
||||
} satisfies RuleSummary;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
import {
|
||||
buildRuleYamlPack,
|
||||
EMPTY_RULE_YAML,
|
||||
type RuleYamlPack,
|
||||
} from './rules-yaml-mock.server';
|
||||
|
||||
type RuleConfigPackApi = {
|
||||
packId: number;
|
||||
groupId: number;
|
||||
rootGroupId?: number | null;
|
||||
bindingId?: number | null;
|
||||
ruleSetId?: number | null;
|
||||
ruleType?: string | null;
|
||||
ruleName?: string | null;
|
||||
currentVersionId?: number | null;
|
||||
fallbackVersionId?: number | null;
|
||||
resolvedVersionId?: number | null;
|
||||
hasUsableVersion?: boolean;
|
||||
usableRuleCount?: number;
|
||||
documentTypeId?: number | null;
|
||||
documentType?: string;
|
||||
moduleType?: string;
|
||||
mainType?: string;
|
||||
subtype?: string;
|
||||
yamlText?: string;
|
||||
sourceStatus?: 'ready' | 'empty' | 'missing';
|
||||
};
|
||||
|
||||
type ApiEnvelope<T> = {
|
||||
code?: number;
|
||||
message?: string;
|
||||
msg?: string;
|
||||
data?: T;
|
||||
};
|
||||
|
||||
export type RuleVersionItem = {
|
||||
id: number;
|
||||
ruleSetId: number;
|
||||
versionNo: string;
|
||||
status: string;
|
||||
ossUrl: string;
|
||||
changeNote?: string | null;
|
||||
publishedAt?: string | null;
|
||||
};
|
||||
|
||||
function getMessage(payload: unknown, fallback: string): string {
|
||||
if (!payload || typeof payload !== 'object') {
|
||||
return fallback;
|
||||
}
|
||||
return String((payload as ApiEnvelope<unknown>).message || (payload as ApiEnvelope<unknown>).msg || fallback);
|
||||
}
|
||||
|
||||
function mapApiPackToRuleYamlPack(item: RuleConfigPackApi): RuleYamlPack {
|
||||
const yamlSource = (item.yamlText || '').trim() ? String(item.yamlText) : EMPTY_RULE_YAML;
|
||||
const sourceStatus = item.sourceStatus || ((item.yamlText || '').trim() ? 'ready' : 'empty');
|
||||
|
||||
return buildRuleYamlPack(
|
||||
{
|
||||
id: String(item.packId),
|
||||
yamlPath: null,
|
||||
documentType: item.documentType || '',
|
||||
moduleType: item.moduleType || (item.documentType ? `${item.documentType}评查` : '规则配置'),
|
||||
mainType: item.mainType || item.documentType || '',
|
||||
subtype: item.subtype || '通用',
|
||||
},
|
||||
yamlSource,
|
||||
sourceStatus,
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchRuleConfigPayload<T>(request: Request, path: string): Promise<T> {
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
if (!frontendJWT) {
|
||||
throw new Response('未登录或会话已失效', { status: 401 });
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const payload = (await response.json()) as ApiEnvelope<T>;
|
||||
if (!response.ok || typeof payload.data === 'undefined' || payload.data === null) {
|
||||
throw new Response(getMessage(payload, '规则配置加载失败'), { status: response.status || 500 });
|
||||
}
|
||||
|
||||
return payload.data;
|
||||
}
|
||||
|
||||
export async function loadRuleConfigPacks(request: Request): Promise<RuleYamlPack[]> {
|
||||
const items = await fetchRuleConfigPayload<RuleConfigPackApi[]>(request, '/api/v3/rule-config-packs');
|
||||
return items.map(mapApiPackToRuleYamlPack);
|
||||
}
|
||||
|
||||
export async function loadRuleConfigPack(request: Request, packId: string): Promise<RuleYamlPack | undefined> {
|
||||
if (!packId) {
|
||||
const packs = await loadRuleConfigPacks(request);
|
||||
return packs[0];
|
||||
}
|
||||
|
||||
const item = await fetchRuleConfigPayload<RuleConfigPackApi>(request, `/api/v3/rule-config-packs/${encodeURIComponent(packId)}`);
|
||||
return mapApiPackToRuleYamlPack(item);
|
||||
}
|
||||
|
||||
export async function loadRuleConfigVersions(request: Request, ruleType: string): Promise<RuleVersionItem[]> {
|
||||
if (!ruleType) {
|
||||
return [];
|
||||
}
|
||||
return fetchRuleConfigPayload<RuleVersionItem[]>(request, `/api/rule-sets/${encodeURIComponent(ruleType)}/versions`);
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export type RuleYamlPack = RulePackScope & {
|
||||
}>;
|
||||
};
|
||||
|
||||
const EMPTY_YAML = `metadata:
|
||||
export const EMPTY_RULE_YAML = `metadata:
|
||||
type_id: pending.internal.document
|
||||
name: 内部公文规则配置
|
||||
version: '0.1'
|
||||
@@ -377,6 +377,10 @@ function parseRules(source: string): RuleSummary[] {
|
||||
});
|
||||
}
|
||||
|
||||
export function parseRuleSummariesFromYaml(source: string): RuleSummary[] {
|
||||
return parseRules(source);
|
||||
}
|
||||
|
||||
function parseTopLevelFields(source: string): ExtractFieldSummary[] {
|
||||
const section = getTopLevelSection(source, 'extract');
|
||||
const extractedFields = splitBlocks(section, /^-\s+group:\s*/).flatMap(groupBlock => {
|
||||
@@ -513,7 +517,11 @@ function parseVisualElements(source: string): RuleYamlPack['visualElements'] {
|
||||
}).filter(item => item.id);
|
||||
}
|
||||
|
||||
function buildPack(config: RulePackScope & { id: string; yamlPath: string | null }, yamlSource: string, sourceStatus: RuleYamlPack['sourceStatus']): RuleYamlPack {
|
||||
export function buildRuleYamlPack(
|
||||
config: RulePackScope & { id: string; yamlPath: string | null },
|
||||
yamlSource: string,
|
||||
sourceStatus: RuleYamlPack['sourceStatus']
|
||||
): RuleYamlPack {
|
||||
const metadata = parseMetadata(yamlSource);
|
||||
const fields = parseTopLevelFields(yamlSource);
|
||||
const subDocuments = parseSubDocuments(yamlSource);
|
||||
@@ -547,14 +555,14 @@ export async function loadRuleYamlPacks(): Promise<RuleYamlPack[]> {
|
||||
// 2. 后端读取 OSS YAML 正文并返回元数据和内容;
|
||||
// 3. 前端仍消费 buildPack 之后的结构化数据,页面不直接关心 OSS 实现。
|
||||
if (!config.yamlPath) {
|
||||
return buildPack(config, EMPTY_YAML, 'empty');
|
||||
return buildRuleYamlPack(config, EMPTY_RULE_YAML, 'empty');
|
||||
}
|
||||
|
||||
try {
|
||||
const yamlSource = await readFile(config.yamlPath, 'utf8');
|
||||
return buildPack(config, yamlSource, 'ready');
|
||||
return buildRuleYamlPack(config, yamlSource, 'ready');
|
||||
} catch {
|
||||
return buildPack(config, EMPTY_YAML, 'missing');
|
||||
return buildRuleYamlPack(config, EMPTY_RULE_YAML, 'missing');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user