refactor(platform): remove safety management module

This commit is contained in:
2026-07-27 15:49:51 +08:00
parent 95fb27f596
commit 8739cfd4d6
25 changed files with 27 additions and 391 deletions

View File

@@ -73,15 +73,9 @@ export function auditPlatform({ manifest, resources, readOnlyPage, routeSources,
}
if (!/^[\u4e00-\u9fff]/.test(resource.title) || resource.fields.length === 0 || resource.fields.some((field) => field.key === 'id' || field.key.endsWith('_id')) || resource.fields.some((field) => !/^[\u4e00-\u9fff]/.test(field.label))) failures.push(`${label}: invalid frontend allowlist`);
for (const expected of requiredBackendRoutes(contract)) if (!manifest.routes.some((route) => route.method === expected.method && route.path === expected.path)) failures.push(`${label}: missing backend ${expected.method}`);
if (contract.mode === 'append_only') {
const event = resources.find((item) => item.name === 'safe_event');
if (!event?.detailActions?.some((action) => action.name === contract.name && action.resource === contract.path)) failures.push(`${label}: missing safe_event detail action`);
if ([...viewSources.keys()].some((file) => file.includes(`/${contract.name}/`))) failures.push(`${label}: independent page exposed`);
} else {
const coverage = routeCoverage(contract, routeSources, viewSources);
if (!coverage.hasPage) failures.push(`${label}: missing page`);
if (!coverage.hasMenu) failures.push(`${label}: missing menu route`);
}
const coverage = routeCoverage(contract, routeSources, viewSources);
if (!coverage.hasPage) failures.push(`${label}: missing page`);
if (!coverage.hasMenu) failures.push(`${label}: missing menu route`);
if (contract.mode === 'readonly') {
const statusCall = new RegExp(`resourceApi\\.updateStatus\\(\\s*['\"]${escapeRegExp(contract.path)}['\"]`);
for (const [file, source] of [...sourceEntries(routeSources, 'src/router'), ...sourceEntries(apiSources, 'src/api'), ...sourceEntries(viewSources, 'src/views')]) {

View File

@@ -7,11 +7,8 @@ const resourcesSource = readFileSync('src/api/resources.ts', 'utf8');
const legacySafetyPrefix = ['sa', 'f_'].join('');
const legacyAuditPrefix = ['au', 'd_'].join('');
test('资源定义使用 safe 前缀且不保留已删除审计资源', () => {
assert.match(resourcesSource, /define\(\s*'safe_rule',\s*'\/safety\/safe_rule'/);
assert.match(resourcesSource, /define\(\s*'safe_event',\s*'\/safety\/safe_event'/);
assert.match(resourcesSource, /define\(\s*'safe_inspection',\s*'\/safety\/safe_inspection'/);
assert.match(resourcesSource, /define\(\s*'safe_event_disposal',\s*'\/safety\/safe_event\/:identity\/disposals'/);
test('安全模块资源定义已完全删除', () => {
assert.doesNotMatch(resourcesSource, /safe_(?:rule|event|inspection|event_disposal)|\/safety\//);
assert.doesNotMatch(resourcesSource, new RegExp(`define\\('${legacySafetyPrefix}(?:rule|event|inspection|event_disposal)',`));
assert.doesNotMatch(resourcesSource, new RegExp(`action\\('${legacySafetyPrefix}event_disposal',`));
@@ -76,25 +73,6 @@ test('每个资源必须由带菜单元数据的路由实际加载对应页面',
assert.deepEqual(failures, ['gas/gas_basic: missing menu route']);
});
test('仅追加处置必须挂在安全事件详情动作且不得有独立页面', () => {
const failures = auditPlatform({
manifest: { resources: [{ domain: 'safety', name: 'safe_event_disposal', path: '/safety/safe_event/:identity/disposals', mode: 'append_only', pageKind: 'list' }], routes: [
{ method: 'GET', path: '/safety/safe_event/:identity/disposals' },
{ method: 'POST', path: '/safety/safe_event/:identity/disposals' },
] },
resources: [{ name: 'safe_event_disposal', resource: '/safety/safe_event/:identity/disposals', mode: 'append_only', pageKind: 'list', title: '事件处置', fields: [{ key: 'action', label: '处置动作' }] }],
readOnlyPage: '',
routeSources: [],
viewSources: new Map([['src/views/safety/safe_event_disposal/ListPage.vue', '<template />']]),
apiSources: new Map(),
});
assert.deepEqual(failures, [
'safety/safe_event_disposal: missing safe_event detail action',
'safety/safe_event_disposal: independent page exposed',
]);
});
test('只读资源拒绝 API 或路由中的状态写入', () => {
const failures = auditPlatform({
manifest: { resources: [{ domain: 'wallet', name: 'wallet', path: '/wallet/wallet', mode: 'readonly', pageKind: 'list' }], routes: [

View File

@@ -56,25 +56,11 @@ test('表单载荷构造器省略空的可选关系并转换字段类型', async
);
});
test('安全规则与订单明细文本字段允许任意文本提交', () => {
test('订单明细文本字段允许任意文本提交', () => {
const resources = loadResources();
const { buildResourcePayload } = loadResourceForm();
const fields = (name) => resources.find((item) => item.name === name).fields;
assert.deepEqual(
JSON.parse(JSON.stringify(buildResourcePayload(fields('safe_rule'), {
rule_code: 'pressure-limit',
threshold: '{invalid}',
action: 'close-valve',
gray_scope: 'north region only',
}))),
{
rule_code: 'pressure-limit',
threshold: '{invalid}',
action: 'close-valve',
gray_scope: 'north region only',
},
);
assert.deepEqual(
JSON.parse(JSON.stringify(buildResourcePayload(fields('ec_order_item'), {
ec_order_identity: 'order-a',