feat(M10-B): 完成可复算经营报表与日汇总

This commit is contained in:
Codex
2026-08-11 09:50:05 +08:00
parent 5d70114bab
commit ee7b16c932
35 changed files with 1355 additions and 65 deletions
+2 -2
View File
@@ -24,11 +24,11 @@ if (!distOnly) {
assert.doesNotMatch(app, /import\s+(?!AdminLoginPanel\b)\w+Panel\s+from/);
const router = read('admin/src/router.ts');
for (const path of ['/overview', '/stores', '/orders', '/products', '/notifications', '/payments', '/third-party',
for (const path of ['/overview', '/stores', '/orders', '/products', '/notifications', '/reports', '/payments', '/third-party',
'/people', '/cleaning', '/devices', '/apps', '/content', '/franchise', '/system']) {
assert.match(read('admin/src/navigation.mjs'), new RegExp(`path: '${path.replace('/', '\\/')}'`));
}
assert.equal((router.match(/\(\) => import\('/g) || []).length, 14);
assert.equal((router.match(/\(\) => import\('/g) || []).length, 15);
assert.match(router, /router\.beforeEach/);
assert.match(router, /isNavigationAllowed/);
+2 -2
View File
@@ -24,11 +24,11 @@ for (const pattern of [
assert.doesNotMatch(app, /import\s+(?!AdminLoginPanel\b)\w+Panel\s+from/);
const router = read('admin/src/router.ts');
assert.equal((router.match(/\(\) => import\('/g) || []).length, 14);
assert.equal((router.match(/\(\) => import\('/g) || []).length, 15);
assert.match(router, /router\.beforeEach/);
const navigation = read('admin/src/navigation.mjs');
for (const path of ['/overview', '/stores', '/orders', '/products', '/payments', '/third-party', '/people',
'/cleaning', '/notifications', '/devices', '/apps', '/content', '/franchise', '/system']) assert.ok(navigation.includes(`path: '${path}'`));
'/cleaning', '/notifications', '/reports', '/devices', '/apps', '/content', '/franchise', '/system']) assert.ok(navigation.includes(`path: '${path}'`));
const cleaningWorkspace = read('admin/src/components/CleaningWorkspace.vue');
for (const pattern of ['CleaningTasksPanel', 'CleaningSettlementsPanel', 'CleaningStatisticsPanel',
+36
View File
@@ -0,0 +1,36 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = dirname(dirname(fileURLToPath(import.meta.url)));
const read = (path) => readFileSync(join(root, path), 'utf8');
const service = read('backend/src/operations/business-report-service.ts');
const routes = read('backend/src/routes/business-statistics.ts');
const worker = read('backend/src/tasks/worker.ts');
const migration = read('database/migrations/2026081111_m10b_business_reports.up.sql');
const panel = read('admin/src/components/BusinessReportsPanel.vue');
const navigation = read('admin/src/navigation.mjs');
for (const token of [
'roomGrossCents', 'roomRefundCents', 'productGrossCents', 'productRefundCents',
'cleaningCostCents', 'usedMinutes', 'capacityMinutes', 'utilizationBasisPoints',
'zonedDateStart', 'sourceChecksum', 'reconciliation', 'enqueueDueAggregations'
]) assert.ok(service.includes(token), `Missing report service contract: ${token}`);
assert.ok(service.includes("'PARTIALLY_REFUNDED', 'REFUNDED'"));
for (const channel of ['WECHAT', 'BALANCE', 'PACKAGE', 'GROUP_BUY', 'OTHER']) {
assert.ok(service.includes(`'${channel}'`), `Missing payment channel: ${channel}`);
}
for (const path of [
'/admin-api/reports/business', '/admin-api/reports/business/export',
'/admin-api/reports/business/rebuild'
]) assert.ok(routes.includes(path), `Missing report route: ${path}`);
assert.ok(routes.includes('text/csv; charset=utf-8'));
assert.ok(worker.includes("['statistics.aggregate'"));
assert.ok(worker.includes('reports.enqueueDueAggregations()'));
assert.ok(migration.includes('qipai_business_daily_summaries'));
for (const permission of ['report.read', 'report.export', 'report.manage']) assert.ok(migration.includes(permission));
for (const label of ['今日', '近 7 日', '全部授权门店', '重算日汇总', '经营明细']) assert.ok(panel.includes(label));
assert.ok(navigation.includes("menuKey: 'reports'"));
console.log('PASS: M10-B timezone reporting, refunds, usage, channels, daily reconciliation and export gates are present.');
+2
View File
@@ -50,6 +50,8 @@ node scripts/check-m09-regression.mjs
Assert-NativeSuccess "check-m09-regression"
node scripts/check-m10-a-notifications.mjs
Assert-NativeSuccess "check-m10-a-notifications"
node scripts/check-m10-b-business-reports.mjs
Assert-NativeSuccess "check-m10-b-business-reports"
if (Test-Path "admin/package.json") {
Push-Location admin
+2 -2
View File
@@ -107,6 +107,6 @@ export QIPAI_MYSQL_USER="${username}"
export QIPAI_MYSQL_PASSWORD="${password}"
export QIPAI_MYSQL_CONNECTION_LIMIT=2
echo "INFO: MySQL ${mysql_version}; running M01-B through M10-A integration roundtrip in a temporary database."
echo "INFO: MySQL ${mysql_version}; running M01-B through M10-B integration roundtrip in a temporary database."
npm --prefix backend run test:mysql:migration
echo "PASS: M01-B through M10-A live MySQL integration roundtrip completed."
echo "PASS: M01-B through M10-B live MySQL integration roundtrip completed."