feat(M09-D3): 完成商品寄存与安全取出闭环

This commit is contained in:
Codex
2026-08-11 07:39:46 +08:00
parent 1a3ea7bdf0
commit 60264cbd11
28 changed files with 2735 additions and 72 deletions
+86
View File
@@ -0,0 +1,86 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
const root = process.cwd();
const read = (path) => readFileSync(join(root, path), 'utf8');
for (const suffix of ['up', 'down', 'verify']) {
assert.ok(
existsSync(join(root,
`database/migrations/2026081109_m09d3_product_storage.${suffix}.sql`)),
`M09-D3 ${suffix} migration is missing`
);
}
const migration = read('database/migrations/2026081109_m09d3_product_storage.up.sql');
for (const pattern of [
'qipai_product_storage_records',
'qipai_product_storage_items',
'qipai_product_storage_events',
'qipai_product_storage_event_items',
'PARTIALLY_RETRIEVED',
'claim_credential_hash',
'remaining_quantity',
'uq_qipai_product_storage_source_order',
'PRODUCT_STORAGE_EVENT_IMMUTABLE',
'PRODUCT_STORAGE_EVENT_ITEM_IMMUTABLE',
'goods.storage.read',
'goods.storage.manage'
]) assert.ok(migration.includes(pattern), `M09-D3 migration is missing ${pattern}`);
const service = read('backend/src/products/product-storage-service.ts');
for (const pattern of [
'ProductStorageService',
'createFromOrder',
'createManual',
'rotateCredential',
'expireDueForManagement',
'timingSafeEqual',
'FOR UPDATE',
'FOR UPDATE SKIP LOCKED',
'qipai_product_storage_event_items',
'qipai_outbox_events',
'qipai_audit_logs',
'PRODUCT_STORAGE_SOURCE_ORDER_ALREADY_STORED'
]) assert.ok(service.includes(pattern), `product storage service is missing ${pattern}`);
assert.match(service, /createHash\('sha256'\)/);
assert.match(service, /beginTransaction\(\)[\s\S]*commit\(\)[\s\S]*rollback\(\)/);
assert.match(service, /claimCredential: null/);
const routes = read('backend/src/routes/product-storages.ts');
for (const pattern of [
'/app-api/product-storages',
'/admin-api',
'/app-api/management',
'/credential/rotate',
'/expire-due',
'goods.storage.read',
'goods.storage.manage'
]) assert.ok(routes.includes(pattern), `product storage routes are missing ${pattern}`);
const app = read('backend/src/app.ts');
const server = read('backend/src/server.ts');
assert.match(app, /registerProductStorageRoutes/);
assert.match(app, /productStorages\?: ProductStorageRouteOptions/);
assert.match(server, /new ProductStorageService\(pool\)/);
const rbac = read('backend/src/auth/rbac-repository.ts');
assert.match(rbac, /'goods\.storage\.read', 'goods\.storage\.manage'/);
const packageJson = read('backend/package.json');
assert.match(packageJson, /product-storage-service\.test\.mjs/);
assert.match(packageJson, /product-storage-route\.test\.mjs/);
const mysqlTest = read('backend/tests/mysql-migration-roundtrip.test.mjs');
for (const pattern of [
'assertProductStorageLifecycle',
'only one concurrent full retrieval may succeed',
'PRODUCT_STORAGE_CREDENTIAL_INVALID',
'PRODUCT_STORAGE_EVENT_IMMUTABLE',
'PRODUCT_STORAGE_EVENT_ITEM_IMMUTABLE',
'expireDueForManagement'
]) assert.ok(mysqlTest.includes(pattern), `live MySQL test is missing ${pattern}`);
console.log(
'PASS: M09-D3 storage ownership, one-time credential, partial/concurrent retrieval and immutable-ledger gates are present.'
);
+2
View File
@@ -42,6 +42,8 @@ node scripts/check-m09-d1-product-inventory.mjs
Assert-NativeSuccess "check-m09-d1-product-inventory"
node scripts/check-m09-d2-product-orders.mjs
Assert-NativeSuccess "check-m09-d2-product-orders"
node scripts/check-m09-d3-product-storage.mjs
Assert-NativeSuccess "check-m09-d3-product-storage"
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 M09-D2 integration roundtrip in a temporary database."
echo "INFO: MySQL ${mysql_version}; running M01-B through M09-D3 integration roundtrip in a temporary database."
npm --prefix backend run test:mysql:migration
echo "PASS: M01-B through M09-D2 live MySQL integration roundtrip completed."
echo "PASS: M01-B through M09-D3 live MySQL integration roundtrip completed."