import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; const root = new URL('..', import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1'); const read = (path) => readFileSync(join(root, path), 'utf8'); const app = read('admin/src/App.vue'); for (const pattern of [ 'OperationsOverviewPanel', "activeModule = 'overview'", "activeModule = 'cleaning'", '平台运营总览', '运营总览', 'savedToken', 'loadCleaningWorkspace' ]) { assert.match(app, new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); } const overview = read('admin/src/components/OperationsOverviewPanel.vue'); for (const pattern of [ 'listManagedStores', 'getBusinessStatistics', '近 7 日', '近 30 日', '近 90 日', '成功实收', '预订金额', '下单会员', '验券成功', '当前房态', '支付渠道', '每日收入趋势', '核心业务导航', 'open-cleaning', '后续增量', 'collectedAmountCents', 'availableRoomTotal', 'paymentChannels', 'dailyRevenue', 'errorMessage' ]) { assert.match(overview, new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); } const api = read('admin/src/api.ts'); assert.match(api, /request\(session, '\/stores'\)/); assert.match(api, /request\(session, `\/statistics\?\$\{params\}`\)/); const routes = read('backend/src/routes/business-statistics.ts'); assert.match(routes, /'\/app-api\/management\/statistics', '\/admin-api\/statistics'/); const routeTest = read('backend/tests/business-statistics.test.mjs'); assert.match(routeTest, /url: '\/admin-api\/statistics\?/); const styles = read('admin/src/styles.css'); for (const pattern of [ '.overview-page', '.overview-toolbar', '.overview-metrics', '.overview-grid', '.revenue-bars', '.module-grid', '@media (max-width: 980px)', '@media (max-width: 560px)' ]) { assert.match(styles, new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); } console.log('PASS: M08-D admin operations overview and scoped business statistics are present.');