优化
This commit is contained in:
@@ -84,7 +84,7 @@ export async function run(context) {
|
||||
|
||||
表中的 `*-fixture.mjs` 是被业务模块复用的测试数据前置,不是新增的独立全量入口。执行 HTTP 时仍从 `../admin-test-env-acceptance.mjs` 进入;`*contract.test.mjs` 只做离线脚本结构和数据归属回归。
|
||||
|
||||
四期 HTTP 验收使用国内登录响应同时签发的短时海外 Token。总入口通过 `--gateway` 访问国内 Gateway,通过 `--overseas-gateway` 访问海外 Gateway。海外仓、海外业务和海外主数据模块只对带本轮 marker 的资源执行已确认的新增、编辑、状态与导出,并在结束前按契约停用;PDA 收货模块使用本轮临时 WAREHOUSE_PICKER 海外 Token,只调用 `/api/pda/v1/last-leg/receiving-containers/**` 柜号收货入口,不接受 `customerId`、`planNo` 或分配 ID。收货成功后由相邻的 `phase4-overseas-pda-operations` 消费仅存在于进程内存、不会进入 JSON 报告的 Token 与库存事实,执行盘点、整托移库、托盘操作和换标;新模块无论成功失败都在 finally 停用两个 picker 并清空临时凭据。两模块均不调用出库、退货出库、派送或内部接口;任何真实柜、箱唛、托盘、库存或换标前置缺失都必须形成 deployment blocker,不能按空场景通过;船节点禁止手工创建任务。
|
||||
四期 HTTP 验收使用国内登录响应同时签发的短时海外 Token。总入口通过 `--gateway` 访问国内 Gateway,通过 `--overseas-gateway` 访问海外 Gateway。海外仓、海外业务和海外主数据模块只对带本轮 marker 的资源执行已确认的新增、编辑、状态与导出,并在结束前按契约停用;PDA 收货模块使用本轮临时 OVERSEAS_WAREHOUSE_PDA_OPERATOR 海外 Token,只调用 `/api/pda/v1/last-leg/receiving-containers/**` 柜号收货入口,不接受 `customerId`、`planNo` 或分配 ID。收货成功后由相邻的 `phase4-overseas-pda-operations` 消费仅存在于进程内存、不会进入 JSON 报告的 Token 与库存事实,执行盘点、整托移库、托盘操作和换标;新模块无论成功失败都在 finally 停用两个 picker 并清空临时凭据。两模块均不调用出库、退货出库、派送或内部接口;任何真实柜、箱唛、托盘、库存或换标前置缺失都必须形成 deployment blocker,不能按空场景通过;船节点禁止手工创建任务。
|
||||
|
||||
节点全链定向入口为 `--modules phase4-overseas-pda`。依赖展开固定经过本轮 `LOADED` 柜次的 `DESTINATION_CLEARANCE`、`CUSTOMS_DECLARATION`、`DELIVERY_ORDER`、`PICKUP_ORDER` 四类资料生命周期,再确认国内出仓、核对自动创建的 `OUTBOUND_CONFIRM` 船节点任务、人工补录至实际 `PICKUP_READY`,最后验证海外 PDA 候选可见。四类资料模块只写本轮 marker 柜次,覆盖上传/完成重放、同键异载荷 `A0409`、冲突后零漂移和聚合回读;任一 fixture 或 Trace 缺失均为 deployment blocker。
|
||||
|
||||
|
||||
@@ -36,22 +36,30 @@ export async function run(context) {
|
||||
const rolesResponse = await context.request('GET', '/api/platform/v1/iam/roles')
|
||||
const roles = context.apiSuccess(MODULE, '查询三期仓库内置角色', rolesResponse, '', OPTIONS)
|
||||
const warehouseRoles = new Map((Array.isArray(roles) ? roles : [])
|
||||
.filter((item) => ['WAREHOUSE_OPERATOR', 'WAREHOUSE_PICKER', 'WAREHOUSE_TALLY'].includes(item.roleCode))
|
||||
.filter((item) => ['WAREHOUSE_OPERATOR', 'OVERSEAS_WAREHOUSE_PDA_OPERATOR', 'WAREHOUSE_TALLY', 'CN_FIRSTLEG_PDA_OPERATOR'].includes(item.roleCode))
|
||||
.map((item) => [item.roleCode, item]))
|
||||
context.assert(MODULE, '操作员、拣货员、理货员三个系统角色已启用', rolesResponse,
|
||||
warehouseRoles.size === 3 && [...warehouseRoles.values()]
|
||||
context.assert(MODULE, '国内后台、国内PDA、海外PDA、理货复核四个系统角色已启用', rolesResponse,
|
||||
warehouseRoles.size === 4 && [...warehouseRoles.values()]
|
||||
.every((role) => role.roleType === 'SYSTEM' && role.status === 'ACTIVE'),
|
||||
`roles=${[...warehouseRoles.keys()].join(',')}`, OPTIONS)
|
||||
|
||||
const roleCodes = (roleCode) => new Set((warehouseRoles.get(roleCode)?.permissionIds || [])
|
||||
.map((permissionId) => permissionById.get(id(permissionId))).filter(Boolean))
|
||||
const pickerCodes = roleCodes('WAREHOUSE_PICKER')
|
||||
context.assert(MODULE, '拣货员只具备国内PDA查询作业与海外PDA入仓操作权限', rolesResponse,
|
||||
pickerCodes.size === 3
|
||||
&& pickerCodes.has('platform:firstleg:pda:view')
|
||||
&& pickerCodes.has('platform:firstleg:pda:work')
|
||||
&& pickerCodes.has('lastleg:pda:operate'),
|
||||
`permissions=${[...pickerCodes].sort().join(',')}`, OPTIONS)
|
||||
const domesticPdaCodes = roleCodes('CN_FIRSTLEG_PDA_OPERATOR')
|
||||
context.assert(MODULE, '国内头程PDA角色只具备国内任务查询与作业权限', rolesResponse,
|
||||
domesticPdaCodes.size === 2
|
||||
&& domesticPdaCodes.has('platform:firstleg:pda:view')
|
||||
&& domesticPdaCodes.has('platform:firstleg:pda:work')
|
||||
&& !domesticPdaCodes.has('lastleg:pda:operate'),
|
||||
`permissions=${[...domesticPdaCodes].sort().join(',')}`, OPTIONS)
|
||||
|
||||
const overseasPdaCodes = roleCodes('OVERSEAS_WAREHOUSE_PDA_OPERATOR')
|
||||
context.assert(MODULE, '海外仓PDA角色只具备海外PDA作业权限', rolesResponse,
|
||||
overseasPdaCodes.size === 1
|
||||
&& overseasPdaCodes.has('lastleg:pda:operate')
|
||||
&& !overseasPdaCodes.has('platform:firstleg:pda:view')
|
||||
&& !overseasPdaCodes.has('platform:firstleg:pda:work'),
|
||||
`permissions=${[...overseasPdaCodes].sort().join(',')}`, OPTIONS)
|
||||
|
||||
const operatorCodes = roleCodes('WAREHOUSE_OPERATOR')
|
||||
context.assert(MODULE, '操作员具备国内入仓权限与四期十项只读权限', rolesResponse,
|
||||
@@ -88,7 +96,7 @@ export async function run(context) {
|
||||
`phase4ReadCodes=${PHASE4_READ_CODES.length}`, OPTIONS)
|
||||
|
||||
context.assert(MODULE, '仓库系统角色不包含任何客户侧权限', rolesResponse,
|
||||
[...operatorCodes, ...pickerCodes, ...tallyCodes]
|
||||
[...operatorCodes, ...domesticPdaCodes, ...overseasPdaCodes, ...tallyCodes]
|
||||
.every((code) => code.startsWith('platform:') || code === 'lastleg:pda:operate'),
|
||||
'warehouse roles may use platform:* plus the single overseas service authority lastleg:pda:operate', OPTIONS)
|
||||
|
||||
|
||||
@@ -32,10 +32,11 @@ test('warehouse operator and tally use the exact phase-four list/detail permissi
|
||||
assert.match(iamSql, /permission_code REGEXP ':\(create\|edit\|status\|export\|adjust\|delete\|review\)\$'/)
|
||||
})
|
||||
|
||||
test('picker stays isolated to the dedicated overseas PDA authority', () => {
|
||||
assert.match(acceptance, /pickerCodes\.size === 3/)
|
||||
assert.match(acceptance, /pickerCodes\.has\('lastleg:pda:operate'\)/)
|
||||
assert.match(iamSql, /role\.role_code='WAREHOUSE_PICKER'/)
|
||||
test('domestic and overseas PDA operators use isolated authorities', () => {
|
||||
assert.match(acceptance, /domesticPdaCodes\.size === 2/)
|
||||
assert.match(acceptance, /overseasPdaCodes\.size === 1/)
|
||||
assert.match(acceptance, /overseasPdaCodes\.has\('lastleg:pda:operate'\)/)
|
||||
assert.match(iamSql, /role\.role_code='OVERSEAS_WAREHOUSE_PDA_OPERATOR'/)
|
||||
assert.match(iamSql, /permission\.permission_code='lastleg:pda:operate'/)
|
||||
})
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ test('PDA module covers the container-number receiving contract and excludes out
|
||||
assert.match(source, /同键异载荷冲突/)
|
||||
assert.match(source, /跨柜切换锁返回409/)
|
||||
assert.match(source, /第二仓库操作员抢占同柜返回409/)
|
||||
assert.match(source, /WAREHOUSE_PICKER/)
|
||||
assert.match(source, /OVERSEAS_WAREHOUSE_PDA_OPERATOR/)
|
||||
assert.match(source, /lastleg:pda:operate/)
|
||||
assert.match(source, /安全投影已收敛/)
|
||||
assert.match(source, /projectionReady = success\(projectionResponse\) \|\| notFound\(projectionResponse\)/)
|
||||
@@ -157,7 +157,7 @@ test('PDA receiving lifecycle is ordered from candidate claim through photos, mo
|
||||
]) assert.ok(source.includes(token), token)
|
||||
})
|
||||
|
||||
test('missing WAREHOUSE_PICKER overseas authority is a deployment blocker', async () => {
|
||||
test('missing OVERSEAS_WAREHOUSE_PDA_OPERATOR overseas authority is a deployment blocker', async () => {
|
||||
const calls = []; const gaps = []; const steps = []
|
||||
const context = {
|
||||
marker: 'PDA-CONTRACT', context: {}, createdResources: {},
|
||||
@@ -192,7 +192,7 @@ test('empty candidate response cannot become a zero-business-request pass', asyn
|
||||
async request(method, requestPath, body) {
|
||||
calls.push({ channel: 'domestic', method, requestPath })
|
||||
if (method === 'GET' && requestPath.endsWith('/roles')) return response([
|
||||
{ id: roleId, roleCode: 'WAREHOUSE_PICKER', status: 'ACTIVE', permissionIds: [permissionId] },
|
||||
{ id: roleId, roleCode: 'OVERSEAS_WAREHOUSE_PDA_OPERATOR', status: 'ACTIVE', permissionIds: [permissionId] },
|
||||
], requestPath, method)
|
||||
if (method === 'GET' && requestPath.endsWith('/permissions')) return response([
|
||||
{ id: permissionId, permissionCode: 'lastleg:pda:operate' },
|
||||
|
||||
@@ -90,15 +90,15 @@ async function loginMerchantForNegativeProbe(context) {
|
||||
async function resolvePickerAccess(context) {
|
||||
const rolesResponse = await context.request('GET', '/api/platform/v1/iam/roles')
|
||||
const roles = context.apiSuccess(MODULE, '查询海外PDA仓库操作员角色', rolesResponse, '', OPTIONS)
|
||||
const role = (Array.isArray(roles) ? roles : []).find((item) => item?.roleCode === 'WAREHOUSE_PICKER' && item?.status === 'ACTIVE')
|
||||
const role = (Array.isArray(roles) ? roles : []).find((item) => item?.roleCode === 'OVERSEAS_WAREHOUSE_PDA_OPERATOR' && item?.status === 'ACTIVE')
|
||||
const permissionsResponse = await context.request('GET', '/api/platform/v1/iam/permissions')
|
||||
const permissions = context.apiSuccess(MODULE, '查询海外PDA权限资源', permissionsResponse, '', OPTIONS)
|
||||
const operate = (Array.isArray(permissions) ? permissions : []).find((item) => item?.permissionCode === 'lastleg:pda:operate')
|
||||
const assigned = Boolean(role && operate && (role.permissionIds || []).map(id).includes(id(operate.id)))
|
||||
context.assert(MODULE, 'WAREHOUSE_PICKER精确具备海外PDA权限', rolesResponse, assigned,
|
||||
context.assert(MODULE, 'OVERSEAS_WAREHOUSE_PDA_OPERATOR精确具备海外PDA权限', rolesResponse, assigned,
|
||||
`roleId=${id(role?.id) || 'missing'}, permissionId=${id(operate?.id) || 'missing'}`, OPTIONS)
|
||||
if (!assigned) {
|
||||
blockerGap(context, '海外PDA仓库操作员权限', 'WAREHOUSE_PICKER未获得lastleg:pda:operate;必须先执行最新国内统一增量SQL并部署IAM')
|
||||
blockerGap(context, '海外PDA仓库操作员权限', 'OVERSEAS_WAREHOUSE_PDA_OPERATOR未获得lastleg:pda:operate;必须先执行最新国内统一增量SQL并部署IAM')
|
||||
return null
|
||||
}
|
||||
return { role, operate }
|
||||
@@ -662,7 +662,7 @@ export async function run(context) {
|
||||
}
|
||||
try {
|
||||
if (!primary?.token || !secondary?.token) {
|
||||
blockerGap(context, '海外PDA双操作员账号', '未能创建并登录两个WAREHOUSE_PICKER平台账号,无法验证全局一柜锁')
|
||||
blockerGap(context, '海外PDA双操作员账号', '未能创建并登录两个OVERSEAS_WAREHOUSE_PDA_OPERATOR平台账号,无法验证全局一柜锁')
|
||||
return
|
||||
}
|
||||
const matrix = context.createdResources?.phase4InboundProducerFixtures
|
||||
|
||||
Reference in New Issue
Block a user