优化
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { openFixedActor, requireFixedActors, COST_ACTOR_SLOTS } from './admin-test-env-modules/fixed-test-actors.mjs'
|
||||
import { mkdirSync, writeFileSync, openSync, closeSync, unlinkSync, readFileSync, fsyncSync } from 'node:fs'
|
||||
import { resolve, dirname, basename, relative, isAbsolute, join } from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
@@ -15,7 +16,7 @@ import { verifyFleetExternalDispatch } from './admin-test-env-modules/fleet-exte
|
||||
import { verifyFleetHandover, cleanupFleetHandoverResources } from './admin-test-env-modules/fleet-handover-acceptance.mjs'
|
||||
import { verifyFleetTripCosts } from './admin-test-env-modules/fleet-trip-cost-acceptance.mjs'
|
||||
import { verifyFleetReturnRedispatch, waitForRedispatchSignatureAudit, selectReturnSources } from './admin-test-env-modules/fleet-return-redispatch-acceptance.mjs'
|
||||
import { resolvePickerAccess, createPicker, disablePicker, pickerFixtureSuffix } from './admin-test-env-modules/phase4-overseas-pda.mjs'
|
||||
import { resolvePickerAccess, createPicker, disablePicker, restoreFixedPicker } from './admin-test-env-modules/phase4-overseas-pda.mjs'
|
||||
import { saveReceiptAndComplete } from './admin-test-env-modules/phase5-overseas-fulfillment.mjs'
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
@@ -266,7 +267,7 @@ export function reviewedFullCheckpoint(report, stage = 'FULL_COVERAGE') {
|
||||
|| row.path !== `/api/platform/v1/last-leg/delivery-orders/${original.deliveryNo}?customerId=${proof.customerId}`
|
||||
|| JSON.stringify(row.result.order) !== JSON.stringify(original)
|
||||
}) || new Set(attempt.requests.map(row => row.result?.order?.deliveryNo)).size !== 3
|
||||
|| resources.fleetCostActors?.status !== 'DISABLED' || failures?.length !== 1 || failures[0].name !== '模块执行'
|
||||
|| resources.fleetCostActors?.status !== 'SESSIONS_CLOSED' || failures?.length !== 1 || failures[0].name !== '模块执行'
|
||||
|| failures[0].evidence !== attempt.failure)
|
||||
throw Error('Return source recovery requires the exact native candidate failure, three already-delivering documents and no return/signature writes')
|
||||
return { resources, marker: proof.marker, returnSourceSelection: true }
|
||||
@@ -295,7 +296,7 @@ export function reviewedFullCheckpoint(report, stage = 'FULL_COVERAGE') {
|
||||
|| handover.requests.some(row => (row.method !== 'GET' && row !== creation?.[0]) || row.status !== 'RESPONDED' || row.http !== 200 || id(row.code) !== '200')
|
||||
|| !costs.final?.page || costs.final.page.records?.length !== 2 : trip.handover)
|
||||
|| trip.closure || proof.receipts || proof.closure || trip.returnRedispatch
|
||||
|| resources.fleetCostActors?.status !== 'DISABLED' || !proof.picker?.membershipId
|
||||
|| resources.fleetCostActors?.status !== 'SESSIONS_CLOSED' || !proof.picker?.membershipId
|
||||
|| failures?.length !== 2 || !failures.some(row => row.name === failureName)
|
||||
|| failures.some(row => ![failureName, '模块执行'].includes(row.name)))
|
||||
throw Error('Departed checkpoint must be the reviewed read-only cost assertion failure, with no later business writes')
|
||||
@@ -637,73 +638,40 @@ export function fleetCostActorJournalData(value) {
|
||||
export async function prepareFleetCostActors(context) {
|
||||
if (!context.marker || context.createdResources.fleetCostActors || typeof context.checkpoint !== 'function')
|
||||
throw Error('Cost actors require fresh current-run ownership and durable checkpoints')
|
||||
const proof = context.createdResources.fleetCostActors = { marker: context.marker, status: 'CREATING', actors: [], requests: [] }
|
||||
const base = '/api/platform/v1/iam', sessions = []
|
||||
async function request(method, path, body, token) {
|
||||
const entry = { method, path, body: fleetCostActorJournalData(body), status: 'SENDING' }
|
||||
requireFixedActors(context.options, COST_ACTOR_SLOTS)
|
||||
const proof = context.createdResources.fleetCostActors = { marker: context.marker, status: 'CHECKING', actors: [], requests: [] }
|
||||
const sessions = []
|
||||
const io = Object.create(context)
|
||||
io.request = async (...args) => {
|
||||
const entry = { method: args[0], path: args[1], body: fleetCostActorJournalData(args[2]), status: 'SENDING' }
|
||||
proof.requests.push(entry); await context.checkpoint()
|
||||
try {
|
||||
const response = await context.request(method, path, body, token)
|
||||
Object.assign(entry, { status: 'RESPONDED', http: response?.http, code: response?.body?.code,
|
||||
requestId: response?.requestId, traceId: response?.traceId, result: fleetCostActorJournalData(response?.body?.data) })
|
||||
const response = await context.request(...args)
|
||||
Object.assign(entry, { status: 'RESPONDED', http: response?.http, code: response?.body?.code, requestId: response?.requestId, traceId: response?.traceId })
|
||||
await context.checkpoint()
|
||||
if (!success(response) || !response.requestId || !response.traceId) throw Error(`Cost actor request failed: ${method} ${path}`)
|
||||
return response.body.data
|
||||
return response
|
||||
} catch (error) {
|
||||
if (entry.status === 'SENDING') { entry.status = 'UNKNOWN_RESULT'; await context.checkpoint() }
|
||||
throw error
|
||||
entry.status = 'UNKNOWN_RESULT'; await context.checkpoint(); throw error
|
||||
}
|
||||
}
|
||||
async function close() {
|
||||
const errors = []
|
||||
for (const actor of proof.actors) {
|
||||
try {
|
||||
if (actor.membershipId && !actor.userDisabled) {
|
||||
const result = await request('PUT', `${base}/users/${actor.membershipId}/status`, { status: 'DISABLED' })
|
||||
if (result?.status !== 'DISABLED') throw Error('Cost test account was not disabled')
|
||||
actor.userDisabled = true
|
||||
}
|
||||
if (actor.roleId && !actor.roleDisabled) {
|
||||
const result = await request('PUT', `${base}/roles/${actor.roleId}/status`, { status: 'DISABLED' })
|
||||
if (result?.status !== 'DISABLED') throw Error('Cost test role was not disabled')
|
||||
actor.roleDisabled = true
|
||||
}
|
||||
} catch (error) { errors.push(error) }
|
||||
}
|
||||
proof.status = errors.length ? 'CLEANUP_FAILED' : 'DISABLED'; await context.checkpoint()
|
||||
if (errors.length) throw new AggregateError(errors, 'Cost actor cleanup requires review; do not recreate accounts')
|
||||
const result = await Promise.allSettled(sessions.map(actor => actor.close()))
|
||||
proof.status = result.some(row => row.status === 'rejected') ? 'CLEANUP_FAILED' : 'SESSIONS_CLOSED'
|
||||
await context.checkpoint()
|
||||
if (proof.status === 'CLEANUP_FAILED') throw Error('固定车队权限账号会话退出未全部确认')
|
||||
}
|
||||
try {
|
||||
const permissions = await request('GET', `${base}/permissions`)
|
||||
const list = permissions.filter(row => row.permissionCode === 'platform:fleet:trip:list' && row.status === 'ACTIVE')
|
||||
if (list.length !== 1 || !list[0].id) throw Error('Active trip list permission missing or ambiguous')
|
||||
for (const kind of ['reader', 'denied']) {
|
||||
const suffix = randomUUID().replaceAll('-', '').slice(0, 16), username = `fleet_${kind}_${suffix}`
|
||||
const actor = { kind, username }; proof.actors.push(actor)
|
||||
const roleCode = `FLEET_${kind.toUpperCase()}_${suffix.toUpperCase()}`
|
||||
const role = await request('POST', `${base}/roles`, { roleCode, roleName: `${context.marker}-${kind}`, description: context.marker })
|
||||
if (!id(role?.id) || role.roleCode !== roleCode || role.roleType !== 'CUSTOM') throw Error('Fresh cost role ownership mismatch')
|
||||
actor.roleId = id(role.id)
|
||||
await request('PUT', `${base}/roles/permissions`, { roleId: actor.roleId, permissionIds: kind === 'reader' ? [id(list[0].id)] : [] })
|
||||
const password = `Fleet${randomUUID().replaceAll('-', '')}A9`
|
||||
const user = await request('POST', `${base}/users`, { username, displayName: `${context.marker}-${kind}`,
|
||||
phone: await findFleetActorPhone(context, suffix), email: `${username}@example.com`, tenantId: '0',
|
||||
roleIds: [actor.roleId], password, status: 'ACTIVE' })
|
||||
if (!id(user?.membershipId) || user.username !== username || id(user.tenantId) !== '0'
|
||||
|| user.roleIds?.length !== 1 || id(user.roleIds[0]) !== actor.roleId) throw Error('Fresh cost account ownership mismatch')
|
||||
actor.membershipId = id(user.membershipId)
|
||||
const login = await request('POST', '/api/auth/login', { loginId: username, password }, '')
|
||||
if (!login?.accessToken || login.accessToken === context.token || sessions.includes(login.accessToken)) throw Error('Independent cost session missing')
|
||||
sessions.push(login.accessToken)
|
||||
const actual = await request('GET', `${base}/authorization/permissions`, undefined, login.accessToken)
|
||||
const expected = kind === 'reader' ? ['platform:fleet:trip:list'] : []
|
||||
if (!Array.isArray(actual) || JSON.stringify([...actual].sort()) !== JSON.stringify(expected)) throw Error('Cost actor effective permissions differ from exact scope')
|
||||
const actor = await openFixedActor(io, `fleet-cost.${kind}`, kind === 'reader' ? ['platform:fleet:trip:list'] : [])
|
||||
sessions.push(actor)
|
||||
proof.actors.push({ kind, username: actor.username, membershipId: actor.membershipId, roleId: actor.roleId, reused: true })
|
||||
}
|
||||
if (sessions[0].token === sessions[1].token) throw Error('固定车队权限账号必须有独立会话')
|
||||
proof.status = 'READY'; await context.checkpoint()
|
||||
return { marker: context.marker, listOnlyToken: sessions[0], deniedToken: sessions[1], close }
|
||||
return { marker: context.marker, listOnlyToken: sessions[0].token, deniedToken: sessions[1].token, close }
|
||||
} catch (error) {
|
||||
proof.status = 'FAILED'; await context.checkpoint()
|
||||
try { await close() } catch (cleanupError) { throw new AggregateError([error, cleanupError], 'Cost actor creation and cleanup failed') }
|
||||
try { await close() } catch (cleanupError) { throw new AggregateError([error, cleanupError], '固定账号核验和退出均失败') }
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -881,17 +849,7 @@ export async function runFleetScenario(context, { wait = ms => new Promise(resol
|
||||
if (full.proof?.status !== 'VERIFIED' || proof.fullExecution?.closure?.status !== 'CARGO_COMPLETED_AND_RESOURCES_RELEASED'
|
||||
|| trip.reservation?.status !== 'VERIFIED' || partial.proof) throw Error('PARTIAL must retain its unpicked real main after independent FULL closure and reservation')
|
||||
if (resumePalletized || resumeReceiptConfirmed) {
|
||||
const child = Object.create(context); child.marker = proof.pickerMarker
|
||||
const suffix = pickerFixtureSuffix(child), username = `pda_overseas_${suffix.toLowerCase()}_71`.slice(0, 64)
|
||||
const users = await nativeRequest(context, 'GET', `/api/platform/v1/iam/users?${new URLSearchParams({page:'1',size:'200',keyword:username})}`)
|
||||
const user = users.records?.find(row => id(row.membershipId) === id(proof.picker?.membershipId) && row.username === username)
|
||||
if (!user || user.protectedAccount || id(user.tenantId) !== '0' || id(user.overseasWarehouseId) !== warehouseId
|
||||
|| user.roleIds?.length !== 1 || id(user.roleIds[0]) !== id(access?.role?.id)) throw Error('Reviewed PDA identity or warehouse changed')
|
||||
picker = { membershipId: user.membershipId, username, token: null }
|
||||
const active = await nativeRequest(context, 'PUT', `/api/platform/v1/iam/users/${encodeURIComponent(id(user.membershipId))}/status`, {status:'ACTIVE'})
|
||||
if (active.status !== 'ACTIVE') throw Error('Original owned PDA not active')
|
||||
const login = await nativeRequest(context, 'POST', '/api/auth/login', {loginId:username,password:`Pda${suffix}A719`}, '')
|
||||
picker.token = login.accessToken
|
||||
picker = access ? await restoreFixedPicker(context, proof.picker, 71, warehouseId, access) : null
|
||||
} else {
|
||||
const child = Object.create(context); proof.pickerMarker = `${context.marker}-FPDA-${randomUUID().slice(0,8)}`; child.marker = proof.pickerMarker
|
||||
picker = access ? await createPicker(child, 71, warehouseId, access) : null
|
||||
|
||||
Reference in New Issue
Block a user