Files
qipai/scripts/check-admin-m08-d.mjs
T
2026-08-10 19:20:47 +08:00

311 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = fileURLToPath(new URL('..', import.meta.url));
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'",
"activeModule = 'thirdParty'",
"activeModule = 'people'",
"activeModule = 'devices'",
"activeModule = 'platformApps'",
"activeModule = 'content'",
"activeModule = 'franchise'",
"activeModule = 'system'",
'平台运营总览',
'StoresRoomsPanel',
'OrdersPanel',
'PaymentsPanel',
'ThirdPartyPanel',
'MembersStaffPanel',
'DevicesPanel',
'PlatformAppsPanel',
'ContentManagementPanel',
'FranchisePanel',
'LogsSystemPanel',
'AdminLoginPanel',
'运营总览',
'savedToken',
'handleAuthenticated',
'handleLogout',
'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<ManagedStore\[]>\(session, '\/stores'\)/);
assert.match(api, /request<BusinessStatistics>\(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 thirdParty = read('admin/src/components/ThirdPartyPanel.vue');
for (const pattern of [
'listThirdPartyRecords',
'getThirdPartySetup',
'redeemGroupVoucher',
'redeemGroupVoucherManually',
'saveThirdPartyConfig',
'saveThirdPartyMapping',
'voucherCode',
'PENDING_MAPPING'
]) {
assert.match(thirdParty, new RegExp(pattern));
}
const thirdPartyRoutes = read('backend/src/routes/third-party.ts');
assert.match(thirdPartyRoutes, /'\/admin-api\/third-party\/setup'/);
assert.match(thirdPartyRoutes, /'\/admin-api\/group-vouchers\/redeem'/);
const people = read('admin/src/components/MembersStaffPanel.vue');
for (const pattern of [
'listMembers',
'getMember',
'listStaffUsers',
'createStaffUser',
'updateStaffUser',
'resetStaffSessions',
'memberDetail',
'staffForm'
]) {
assert.match(people, new RegExp(pattern));
}
const userRoutes = read('backend/src/routes/user-management.ts');
assert.match(userRoutes, /userType: z\.enum\(\['STAFF'\]\)\.optional\(\)/);
const devices = read('admin/src/components/DevicesPanel.vue');
for (const pattern of [
'getDeviceTopology',
'createDeviceAsset',
'bindDeviceChannel',
'bindSubDevice',
'addDeviceMaintenance',
'controlDevice',
'openAlerts',
'Sub1G'
]) {
assert.match(devices, new RegExp(pattern));
}
const platformApps = read('admin/src/components/PlatformAppsPanel.vue');
for (const pattern of ['listPlatformApplications', 'updatePlatformApplicationConfig', 'bindPlatformApplication', 'AppSecret', 'extraConfigText']) {
assert.match(platformApps, new RegExp(pattern));
}
const platformRoutes = read('backend/src/routes/platform-management.ts');
assert.match(platformRoutes, /'\/admin-api\/platform-apps'/);
assert.match(platformRoutes, /PLATFORM_MANAGEMENT_FORBIDDEN/);
const content = read('admin/src/components/ContentManagementPanel.vue');
for (const pattern of [
'listMediaAssets',
'uploadMediaImage',
'listDecorations',
'saveDecoration',
'publishDecoration',
'listAdvertisements',
'saveAdvertisement',
'updateAdvertisement',
'媒体素材',
'门店装修',
'广告投放'
]) {
assert.match(content, new RegExp(pattern));
}
const contentRoutes = read('backend/src/routes/content-management.ts');
assert.match(contentRoutes, /'\/admin-api\/media\/images'/);
assert.match(contentRoutes, /'\/admin-api\/decorations'/);
assert.match(contentRoutes, /'\/admin-api\/advertisements\/:id'/);
const franchise = read('admin/src/components/FranchisePanel.vue');
for (const pattern of ['listFranchiseApplications', 'getFranchiseApplication',
'assignFranchiseApplication', 'addFranchiseFollowUp', '加盟申请与跟进队列', '跟进历史']) {
assert.match(franchise, new RegExp(pattern));
}
const franchiseRoutes = read('backend/src/routes/franchise.ts');
assert.match(franchiseRoutes, /'\/app-api\/franchise-applications'/);
assert.match(franchiseRoutes, /'\/admin-api\/franchise-applications\/:id\/follow-ups'/);
const systemPanel = read('admin/src/components/LogsSystemPanel.vue');
for (const pattern of ['listAuditLogs', 'getSystemOverview', 'updateTenantSystemConfig',
'安全审计日志', '导出当前页', 'Schema 迁移', '脱敏元数据']) {
assert.match(systemPanel, new RegExp(pattern));
}
const systemRoutes = read('backend/src/routes/system-operations.ts');
assert.match(systemRoutes, /'\/admin-api\/audit-logs'/);
assert.match(systemRoutes, /'\/admin-api\/system\/overview'/);
assert.match(systemRoutes, /'\/admin-api\/system\/tenant'/);
const systemRepository = read('backend/src/operations/system-operations-repository.ts');
assert.match(systemRepository, /sanitizeMetadata/);
assert.match(systemRepository, /TENANT_SYSTEM_CONFIG_UPDATED/);
const adminLogin = read('admin/src/components/AdminLoginPanel.vue');
for (const pattern of ['adminPasswordLogin', '租户代码', '安全登录', '连续失败 5 次']) {
assert.match(adminLogin, new RegExp(pattern));
}
const adminAuthRoutes = read('backend/src/routes/admin-auth.ts');
for (const pattern of ['/admin-api/auth/login', '/admin-api/auth/refresh', '/admin-api/auth/me',
'/admin-api/auth/logout', '/admin-api/auth/credentials/:userId']) {
assert.match(adminAuthRoutes, new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
}
const adminAuthRepository = read('backend/src/auth/admin-auth-repository.ts');
assert.match(adminAuthRepository, /ADMIN_LOGIN_SUCCEEDED/);
assert.match(adminAuthRepository, /ADMIN_LOGIN_FAILED/);
assert.match(adminAuthRepository, /ADMIN_REFRESH_REUSED/);
const adminAuthMigration = read('database/migrations/2026081004_m08d_admin_password_auth.up.sql');
assert.match(adminAuthMigration, /qipai_admin_credentials/);
assert.match(adminAuthMigration, /refresh_token_hash/);
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',
'.third-party-metrics',
'.settings-preview',
'.people-metrics',
'.member-detail-grid',
'.device-metrics',
'.device-topology-grid',
'.platform-app-metrics',
'.content-metrics',
'.asset-gallery',
'.component-editor',
'.franchise-metrics',
'.system-metrics',
'.audit-filters',
'.login-shell',
'.session-box',
'@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.');