feat(M09-D2): 完成商品订单与库存占用生命周期
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
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/2026081108_m09d2_product_order_payment_inventory.${suffix}.sql`)),
|
||||
`M09-D2 ${suffix} migration is missing`
|
||||
);
|
||||
}
|
||||
|
||||
const migration = read(
|
||||
'database/migrations/2026081108_m09d2_product_order_payment_inventory.up.sql'
|
||||
);
|
||||
for (const pattern of [
|
||||
'qipai_product_orders',
|
||||
'qipai_product_order_items',
|
||||
'qipai_product_order_events',
|
||||
'qipai_product_payments',
|
||||
'qipai_product_refunds',
|
||||
'qipai_product_payment_callbacks',
|
||||
'qipai_product_refund_events',
|
||||
'PENDING_PAYMENT',
|
||||
'READY_FOR_SELF_SERVICE',
|
||||
'COMPENSATION_REQUIRED',
|
||||
'inventory_request_id',
|
||||
'request_fingerprint',
|
||||
'PRODUCT_ORDER_ITEM_IMMUTABLE',
|
||||
'PRODUCT_ORDER_EVENT_IMMUTABLE',
|
||||
'PRODUCT_REFUND_EVENT_IMMUTABLE',
|
||||
'goods.order.read',
|
||||
'goods.order.manage'
|
||||
]) assert.ok(migration.includes(pattern), `M09-D2 migration is missing ${pattern}`);
|
||||
assert.doesNotMatch(migration, /\bDECIMAL\b/i);
|
||||
assert.match(migration, /subtotal_cents = unit_price_cents \* quantity/);
|
||||
assert.match(migration, /fulfillment_mode = 'DELIVERY' AND room_order_id IS NOT NULL/);
|
||||
assert.match(migration, /fulfillment_mode = 'SELF_SERVICE' AND room_order_id IS NULL/);
|
||||
|
||||
const service = read('backend/src/products/product-order-service.ts');
|
||||
for (const pattern of [
|
||||
'ProductOrderService',
|
||||
'createPaymentForCustomer',
|
||||
'confirmPayment',
|
||||
'confirmRefund',
|
||||
'expirePendingOrders',
|
||||
'resolveProductOrderTransition',
|
||||
'lockMany',
|
||||
'deductMany',
|
||||
'releaseMany',
|
||||
'returnMany',
|
||||
'FOR UPDATE',
|
||||
'PRODUCT_ORDER_IDEMPOTENCY_CONFLICT',
|
||||
'PRODUCT_PAYMENT_CALLBACK_CONFLICT',
|
||||
'PRODUCT_REFUND_CALLBACK_CONFLICT'
|
||||
]) assert.ok(service.includes(pattern), `product order service is missing ${pattern}`);
|
||||
assert.match(service, /beginTransaction\(\)[\s\S]*commit\(\)[\s\S]*rollback\(\)/);
|
||||
assert.match(service, /order\.expiresAt <= this\.now\(\)/);
|
||||
|
||||
const routes = read('backend/src/routes/product-orders.ts');
|
||||
for (const pattern of [
|
||||
'/app-api/product-orders',
|
||||
'/admin-api',
|
||||
'/app-api/management',
|
||||
'goods.order.read',
|
||||
'goods.order.manage',
|
||||
'testAdapterEnabled',
|
||||
'completeTestRefundForManagement'
|
||||
]) assert.ok(routes.includes(pattern), `product order routes are missing ${pattern}`);
|
||||
|
||||
const app = read('backend/src/app.ts');
|
||||
const server = read('backend/src/server.ts');
|
||||
assert.match(app, /registerProductOrderRoutes/);
|
||||
assert.match(app, /productOrders\?: ProductOrderRouteOptions/);
|
||||
assert.match(server, /new ProductOrderService\(pool, inventoryService\)/);
|
||||
assert.match(server, /testAdapterEnabled: config\.payment\.testAdapterEnabled/);
|
||||
|
||||
const rbac = read('backend/src/auth/rbac-repository.ts');
|
||||
assert.match(rbac, /'goods\.order\.read', 'goods\.order\.manage'/);
|
||||
const packageJson = read('backend/package.json');
|
||||
assert.match(packageJson, /product-order-service\.test\.mjs/);
|
||||
assert.match(packageJson, /product-order-route\.test\.mjs/);
|
||||
|
||||
const mysqlTest = read('backend/tests/mysql-migration-roundtrip.test.mjs');
|
||||
for (const pattern of [
|
||||
'assertProductOrderPaymentInventory',
|
||||
'concurrent payment callbacks must deduct inventory once',
|
||||
'PRODUCT_ORDER_ITEM_IMMUTABLE',
|
||||
'PRODUCT_ORDER_EVENT_IMMUTABLE',
|
||||
'PRODUCT_REFUND_EVENT_IMMUTABLE',
|
||||
'expirePendingOrders'
|
||||
]) assert.ok(mysqlTest.includes(pattern), `live MySQL test is missing ${pattern}`);
|
||||
|
||||
console.log(
|
||||
'PASS: M09-D2 product order, payment callback, inventory compensation and immutable-event gates are present.'
|
||||
);
|
||||
Reference in New Issue
Block a user