/** * 功能描述:校验配送点资源的独立详情、新建、编辑页面与后端契约保持一致。 * 版本:v1.0.0。 */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const contract = JSON.parse(fs.readFileSync( path.join(projectRoot, 'src/contracts/delivery-resources.json'), 'utf8', )); const read = (relativePath) => fs.readFileSync(path.join(projectRoot, relativePath), 'utf8'); const routeKeys = new Set(contract.routes.map((route) => `${route.method} ${route.path}`)); const listResources = contract.resources.filter( (resource) => resource.pageKind === 'list' && resource.name !== 'delivery_profile', ); const creatable = new Set([ 'staff_account', 'staff_credential', 'user_account', 'user_address', 'gasorder_contract', 'gasorder_contract_product', 'gasorder_basic', 'wallet_recharge', 'wallet_apply_cash', ]); const editable = new Set([ 'staff_account', 'staff_credential', 'user_account', 'user_address', 'gasorder_contract', ]); /** 在条件不成立时中止检查,并给出可直接定位的原因。 */ function assert(condition, message) { if (!condition) throw new Error(message); } for (const resource of listResources) { assert( routeKeys.has(`GET ${resource.path}/:identity`), `${resource.name} 缺少详情接口`, ); if (creatable.has(resource.name)) { assert(routeKeys.has(`POST ${resource.path}`), `${resource.name} 缺少新建接口`); } if (editable.has(resource.name)) { assert(routeKeys.has(`PUT ${resource.path}/:identity`), `${resource.name} 缺少编辑接口`); } } assert(listResources.length === 17, `详情页面资源应为 17 个,当前为 ${listResources.length}`); assert(creatable.size === 9, `新建页面资源应为 9 个,当前为 ${creatable.size}`); assert(editable.size === 5, `编辑页面资源应为 5 个,当前为 ${editable.size}`); const builder = read('src/router/routes/modules/resource-route-builder.ts'); const platform = read('src/router/routes/modules/platform.ts'); const resourcePage = read('src/views/shared/ResourcePage.vue'); const definitions = read('src/api/resources.ts'); assert(builder.includes("recordRoute('detail', ':identity'"), '路由生成器未创建详情页'); assert(builder.includes("recordRoute('create', 'new'"), '路由生成器未创建新建页'); assert(builder.includes("recordRoute('edit', ':identity/edit'"), '路由生成器未创建编辑页'); assert(platform.includes("createChild('add'"), '工作人员正式新建路由必须保留 /staff/add'); assert(platform.includes("createChild('create'"), '配送订单正式新建路由必须保留 /gasorder/create'); assert(resourcePage.includes('ResourceListPage'), '标准资源列表尚未切换到独立页面导航'); assert(!resourcePage.includes('CrudListPage'), '活动列表仍依赖抽屉式 CrudListPage'); assert(definitions.includes("define('payment_order'"), '支付资源必须使用后端名称 payment_order'); assert(definitions.includes("define('payment_refund'"), '退款资源必须使用后端名称 payment_refund'); assert(routeKeys.has('GET /staff_account/:identity/avatar'), '配送人员缺少受保护头像读取接口'); assert(routeKeys.has('GET /user_account/:identity/avatar'), '用户账户缺少受保护头像读取接口'); const recordPage = read('src/views/resource/ResourceRecordPage.vue'); assert(recordPage.includes('ResourceAccountSummary'), '账户资源页尚未接入 5173 头像摘要卡片'); assert(recordPage.includes("field.key !== 'avatar'"), '头像字段仍可能显示为普通文本框'); console.log(`独立资源页面契约通过:详情 ${listResources.length},新建 ${creatable.size},编辑 ${editable.size}`);