Files
qipai/scripts/check-m09-b-cleaning-rules.mjs
T

100 lines
3.7 KiB
JavaScript

import assert from 'node:assert/strict';
import { existsSync, 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');
for (const suffix of ['up', 'verify', 'down']) {
assert.ok(
existsSync(join(root, `database/migrations/2026081005_m09b_cleaning_rules.${suffix}.sql`)),
`M09-B ${suffix} migration is missing`
);
}
const migration = read('database/migrations/2026081005_m09b_cleaning_rules.up.sql');
for (const pattern of [
'qipai_cleaning_templates',
'cleaning_template_version',
'photo_required',
'min_photo_count',
'max_photo_count',
'exempt_policy',
'rework_count',
'qipai_cleaning_task_photos',
'retention_until',
'attached_revision',
'qipai_cleaning_task_submissions',
'uq_qipai_cleaning_submission_revision'
]) assert.ok(migration.includes(pattern), `migration is missing ${pattern}`);
const repository = read('backend/src/cleaning/cleaning-task-repository.ts');
for (const pattern of [
'listTemplates',
'upsertTemplate',
"'appliesToExistingTasks', FALSE",
'listSubmissions',
'CLEANING_PHOTO_OWNERSHIP_INVALID',
"status = 'ATTACHED'",
'attached_revision = ?',
'claimExpiredPhotoUploads',
'markPhotoUploadsDeleted',
'CLEANING_EXEMPT_POLICY_DENIED',
"ORDER BY FIELD(scope_type, 'ROOM', 'STORE', 'TENANT')"
]) assert.ok(repository.includes(pattern), `repository is missing ${pattern}`);
const routes = read('backend/src/routes/cleaning.ts');
for (const pattern of [
'/admin-api/cleaning/templates',
'/admin-api/cleaning/tasks/:taskId/submissions',
'/admin-api/cleaning/photos/cleanup',
'tenantId: actor.tenantId',
'storeId: context.storeId',
'deleteImage(image.storagePath)'
]) assert.ok(routes.includes(pattern), `cleaning routes are missing ${pattern}`);
const media = read('backend/src/content/media-storage.ts');
for (const pattern of [
'declaredFormat',
'declaredExtension',
'metadata.format',
'.webp({ quality: 82 })',
'deleteImage'
]) assert.ok(media.includes(pattern), `media storage is missing ${pattern}`);
const automation = read('backend/src/devices/order-device-automation-service.ts');
for (const pattern of [
'ROOM_HAS_ACTIVE_ORDER',
"status IN ('PAID', 'RESERVED', 'IN_PROGRESS')",
'room_id = ? AND id <> ?'
]) assert.ok(automation.includes(pattern), `device automation is missing ${pattern}`);
const miniapp = read('miniapp/pages/cleaner/tasks.js')
+ read('miniapp/pages/cleaner/tasks.wxml');
for (const pattern of [
'minPhotoCount', 'maxPhotoCount', "task.status === 'STARTED'", 'photoRequired'
]) assert.ok(miniapp.includes(pattern), `cleaner miniapp is missing ${pattern}`);
const admin = read('admin/src/components/CleaningRulesPanel.vue')
+ read('admin/src/components/CleaningTasksPanel.vue')
+ read('admin/src/api.ts');
for (const pattern of [
'配置变更不会静默改写在途任务',
'listCleaningTemplates',
'upsertCleaningTemplate',
'listCleaningTaskSubmissions',
'验收照片版本',
'submission.revision'
]) assert.ok(admin.includes(pattern), `admin cleaning workflow is missing ${pattern}`);
const liveTest = read('backend/tests/mysql-migration-roundtrip.test.mjs');
for (const pattern of [
'cleaning template snapshot remains stable after config version change',
'rejected and accepted cleaning photo revisions remain distinguishable',
'expired unattached cleaning photo retention cleanup',
'task-level cleaning exemption policy denial'
]) assert.ok(liveTest.includes(pattern), `live MySQL test is missing ${pattern}`);
console.log('PASS: M09-B cleaning templates, photo security, versioned review and safe device shutdown gates are present.');