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'", "activeModule = 'stores'", "activeModule = 'orders'", "activeModule = 'payments'", '平台运营总览', 'StoresRoomsPanel', 'OrdersPanel', 'PaymentsPanel', '运营总览', '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\}`\)/); for (const pattern of [ 'createManagedStore', 'updateManagedStore', 'archiveManagedStore', 'listManagedRooms', 'createManagedRoom', 'updateManagedRoom', 'updateManagedRoomStatus', 'addManagedRoomDisabledPeriod' ]) { assert.match(api, new RegExp(pattern)); } const stores = read('admin/src/components/StoresRoomsPanel.vue'); for (const pattern of [ '门店与房间管理', '搜索门店、城市或地址', '每周营业时间', 'wifiPassword', 'listManagedStores', 'listManagedRooms', 'updateManagedRoomStatus', '新增房间', '停用时段', 'configurationStatus', 'operationalStatus', 'archiveManagedRoom' ]) { assert.match(stores, new RegExp(pattern)); } const orders = read('admin/src/components/OrdersPanel.vue'); for (const pattern of [ '订单筛选与处置', 'listManagedOrders', 'getManagedOrder', 'listManagedOrderHistory', 'executeManagedOrderAction', 'addManagedOrderNote', 'adjustManagedOrderTime', 'renewManagedOrder', 'changeManagedOrderRoom', '导出当前页', '状态流水', 'allowedActions' ]) { assert.match(orders, new RegExp(pattern)); } const payments = read('admin/src/components/PaymentsPanel.vue'); for (const pattern of [ '支付、退款与分账', 'getProfitSharingSnapshot', 'saveCollectionAccount', 'saveProfitShareReceiver', 'saveProfitSharePolicy', 'createWechatRefund', 'requestWechatReconciliation', 'executeProfitSharing', '凭据只保存环境引用', 'clientRequestId', '生产 API', '非生产 Mock' ]) { assert.match(payments, new RegExp(pattern)); } 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 storeRepository = read('backend/src/stores/store-room-repository.ts'); assert.match(storeRepository, /wifi_password = COALESCE\(\?, wifi_password\)/); assert.match(storeRepository, /FROM qipai_store_business_hours/); assert.match(storeRepository, /wifiConfigured/); const orderQueryRoutes = read('backend/src/routes/order-query.ts'); assert.match(orderQueryRoutes, /'\/admin-api\/orders'/); const orderStateRoutes = read('backend/src/routes/order-state.ts'); assert.match(orderStateRoutes, /'\/admin-api\/orders\/:orderId\/history'/); const styles = read('admin/src/styles.css'); for (const pattern of [ '.overview-page', '.overview-toolbar', '.overview-metrics', '.overview-grid', '.revenue-bars', '.module-grid', '.store-layout', '.store-card', '.hours-editor', '.order-page-summary', '.history-card', '.payment-metrics', '.payment-tools-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.');