diff --git a/scripts/admin-test-env-modules/phase3-order-splitting.mjs b/scripts/admin-test-env-modules/phase3-order-splitting.mjs index a2e104378..9a75a40c5 100644 --- a/scripts/admin-test-env-modules/phase3-order-splitting.mjs +++ b/scripts/admin-test-env-modules/phase3-order-splitting.mjs @@ -68,6 +68,23 @@ export async function run(context) { `parent=${id(tree?.parentOrderId) || 'missing'}, children=${tree?.children?.length ?? 'n/a'}, allocated=${tree?.allocatedPackageCount ?? 'n/a'}, remaining=${tree?.remainingPackageCount ?? 'n/a'}`, OPTIONS) if (!tree) return + // Re-query through the real merchant contract as well. The customer scope is + // taken from the token; no tenant/customer id is accepted by this endpoint. + const merchantLogin = await context.request('POST', '/api/auth/login', { + username: 'merchant_admin', password: 'ChangeMe123!', + }, '') + const merchantAuth = context.apiSuccess(MODULE, '客户账号登录以验证履约树隔离', merchantLogin, '', OPTIONS) + const merchantToken = merchantAuth?.accessToken + if (merchantToken) { + const merchantTree = await context.request('GET', + `/api/merchant/v1/orders/${encodeURIComponent(orderId)}/fulfillment-tree`, undefined, merchantToken) + const merchantTreeData = context.apiSuccess(MODULE, '客户查询本客户父子履约树', merchantTree, + `orderId=${orderId}`, OPTIONS) + context.assert(MODULE, '客户履约树不返回平台内部字段且归属当前客户', merchantTree, + treeShape(merchantTreeData, orderId), + `parent=${id(merchantTreeData?.parentOrderId) || 'missing'}, children=${merchantTreeData?.children?.length ?? 'n/a'}`, OPTIONS) + } + const slicesPath = `/api/platform/v1/orders/container-bookings/${encodeURIComponent(containerBookingId)}/fulfillment-slices?tenantId=${encodeURIComponent(tenantId)}` const slicesResponse = await context.request('GET', slicesPath) const slices = context.apiSuccess(MODULE, '平台按柜次查询真实履约分片聚合', slicesResponse, @@ -105,6 +122,16 @@ export async function run(context) { Array.isArray(wrongSlicesData) && wrongSlicesData.length === 0, `rows=${Array.isArray(wrongSlicesData) ? wrongSlicesData.length : 'n/a'}`, OPTIONS) + if (merchantToken) { + const merchantSlices = await context.request('GET', + `/api/merchant/v1/orders/container-bookings/${encodeURIComponent(containerBookingId)}/fulfillment-slices`, + undefined, merchantToken) + const merchantSlicesData = context.apiSuccess(MODULE, '客户查询本客户柜次履约分片', merchantSlices, '', OPTIONS) + context.assert(MODULE, '客户柜次分片仅返回当前客户数据', merchantSlices, + Array.isArray(merchantSlicesData) && merchantSlicesData.every((row) => sliceShape(row, containerBookingId, tenantId)), + `rows=${Array.isArray(merchantSlicesData) ? merchantSlicesData.length : 'n/a'}`, OPTIONS) + } + context.addCoverageGap(MODULE, SCENARIO, '父子订单拆分、装载数量守恒与剩余自动子订单', '真实 fulfillment-tree 已可查询父订单、子订单字段和数量摘要,但当前公开平台契约只有查询端点,没有受权限保护的拆分创建/消费 fixture;本轮不能证明自动生成余量子单、幂等重放、版本冲突及禁止嵌套拆分', 'contract-gap', { fixture: { available: true, marker: context.marker, inboundId, containerBookingId, orderId } })