fix: resolve platform relation identities

This commit is contained in:
2026-07-27 10:11:07 +08:00
parent 98995d6968
commit 2e62238416
11 changed files with 437 additions and 80 deletions

View File

@@ -1195,6 +1195,8 @@ const contracts = [
}
];
const source = fs.readFileSync('src/api/resources.ts', 'utf8');
const backendContractSource = fs.readFileSync('../../backend/api/internal/logic/platform/resource.go', 'utf8');
const backendRouterSource = fs.readFileSync('../../backend/api/internal/routers/platform.go', 'utf8');
const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText;
const resourceModule = { exports: {} };
vm.runInNewContext(compiled, { module: resourceModule, exports: resourceModule.exports });
@@ -1208,6 +1210,8 @@ function checkContracts() {
const definition = resources.find((resource) => resource.name === contract.name);
const label = `${contract.domain}/${contract.name}`;
if (!definition || definition.resource !== contract.resource || definition.mode !== contract.mode || definition.pageKind !== contract.pageKind || !sameFields(definition.fields, contract.fields)) failures.push(`${label}: mismatched contract or allowlist`);
if (!backendContractSource.includes(`Domain: "${contract.domain}"`) || !backendContractSource.includes(`Name: "${contract.name}"`)) failures.push(`${label}: absent from backend ExpectedResources`);
if (contract.name !== 'saf_event_disposal' && !backendRouterSource.includes(contract.resource)) failures.push(`${label}: absent from backend router`);
const requiredIdentity = contract.fields.filter((field) => field.required && field.key.endsWith('_identity')).map((field) => field.key);
if (definition && JSON.stringify(definition.requiredIdentities ?? []) !== JSON.stringify(requiredIdentity)) failures.push(`${label}: required identity fields do not match allowlist`);
if (contract.name === 'saf_event_disposal') continue;
@@ -1224,7 +1228,7 @@ function checkContracts() {
const readonlyPage = fs.readFileSync('src/views/shared/ReadOnlyListPage.vue', 'utf8');
if (/(CrudListPage|resourceApi\\.(create|update|archive|updateStatus))/.test(readonlyPage)) failures.push('readonly shared page: contains write surface');
const treePage = fs.readFileSync('src/views/shared/TreePage.vue', 'utf8');
if (!treePage.includes('<a-tree') || !/parent_identity|parent_id/.test(treePage) || treePage.includes('CrudListPage')) failures.push('tree shared page: missing tree semantics');
if (!treePage.includes('<a-tree') || !treePage.includes('parent_identity') || /\bparent_id\b|\bid\b/.test(treePage) || treePage.includes('CrudListPage')) failures.push('tree shared page: must be identity-first');
for (const name of ['ec_category', 'platform_menu']) { const view = path.join('src/views', name === 'ec_category' ? 'ec' : 'platform', name, 'TreePage.vue'); if (!fs.existsSync(view)) failures.push(`${name}: must use TreePage`); }
const safetyEvent = resources.find((resource) => resource.name === 'saf_event');
if (!safetyEvent?.detailActions?.some((item) => item.name === 'saf_event_disposal' && item.resource === '/safety/saf_event/:identity/disposals')) failures.push('safety event: missing disposal detail action');