Files
platforms/frontend/platform_admin/scripts/check-resource-display-contracts.mjs

92 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 功能描述校验平台48类资源均具备中文展示字段并防止列表、详情退回数据库ID或动态英文列。
* 版本v1.0.0
*/
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
const currentDirectory = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(currentDirectory, '..');
function read(relativePath) {
return fs.readFileSync(path.join(root, relativePath), 'utf8');
}
function assertIncludes(source, expected, message) {
if (!source.includes(expected)) throw new Error(message);
}
const resources = read('src/api/resources.ts');
const display = read('src/api/resource-display.ts');
const detailContract = read('src/api/resource-detail-contract.ts');
const detailPage = read('src/views/resource/ResourceDetailContent.vue');
const listPage = read('src/views/shared/CrudListPage.vue');
const resourceNames = [...resources.matchAll(/\bdefine\('([^']+)'/g)].map(
(match) => match[1],
);
if (resourceNames.length !== 48 || new Set(resourceNames).size !== 48) {
throw new Error(`平台资源定义应为48类当前读取到${resourceNames.length}`);
}
if (/define\([^;]*?'readonly'\s*,\s*\[\s*\]/s.test(resources)) {
throw new Error('只读资源不得继续使用空字段契约');
}
const labelBlock = resources.match(
/const fieldLabels: Record<string, string> = \{([\s\S]*?)\n\};/,
)?.[1];
if (!labelBlock) throw new Error('无法读取全局中文字段字典');
const labels = new Set(
[...labelBlock.matchAll(/^\s{2}([A-Za-z_][A-Za-z0-9_]*):\s*'[^']+'/gm)].map(
(match) => match[1],
),
);
const usedKeys = new Set(
[...resources.matchAll(/\b(?:f|relation)\('([^']+)'/g)].map(
(match) => match[1],
),
);
const missingLabels = [...usedKeys].filter((key) => !labels.has(key));
if (missingLabels.length) {
throw new Error(`资源字段缺少中文名称:${missingLabels.join('、')}`);
}
if (/title="ID"|data-index="id"/.test(listPage)) {
throw new Error('标准资源列表不得把数据库ID作为业务列展示');
}
assertIncludes(listPage, 'title="系统唯一标识"', '列表必须中文标注系统唯一标识');
assertIncludes(
listPage,
'class="resource-table-shell"',
'列表必须使用内部横向滚动容器',
);
assertIncludes(
display,
'hasResourceFieldLabel',
'详情必须提供未知字段中文契约检查',
);
assertIncludes(
detailPage,
'!hasResourceFieldLabel(props.definition, actualKey)',
'详情不得直接回显未配置字段',
);
assertIncludes(
detailPage,
'resourceDetailContract(props.definition.name)',
'详情关联表必须使用资源专属固定列契约',
);
for (const collection of [
'products', 'revisions', 'items', 'assignments', 'statuses',
'tracks', 'confirmations', 'payments',
]) {
assertIncludes(
detailContract,
`${collection}: {`,
`缺少关联记录固定列契约:${collection}`,
);
}
process.stdout.write('平台48类资源中文展示契约检查通过\n');