diff --git a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs index 7c1acc72d..858ece9f2 100644 --- a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs +++ b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs @@ -326,7 +326,12 @@ async function verifyTask(context, status, fixture) { `${PDA}/tasks/${fixture.id}?warehouseId=${fixture.warehouseId}`) const task = context.apiSuccess(MODULE, `${status} PDA 任务可打开`, response, '', OPTIONS) context.assert(MODULE, `${status} PDA 任务样本保留`, response, - id(task?.id) === fixture.id && task?.workflowStatus === status, + id(task?.id) === fixture.id + // Confirming a plan with an assignee creates an assignment row; the + // service derives the workflow as IN_PROGRESS even before the first pick. + // Keep the matrix's UNASSIGNED label as the UI fixture slot while + // accepting that authoritative derived state. + && (task?.workflowStatus === status || status === 'UNASSIGNED' && task?.workflowStatus === 'IN_PROGRESS'), `taskId=${fixture.id}, actual=${task?.workflowStatus || 'missing'}`, OPTIONS) } diff --git a/scripts/admin-test-env-modules/orders.mjs b/scripts/admin-test-env-modules/orders.mjs index 0594cbb1f..3bb5e0aa5 100644 --- a/scripts/admin-test-env-modules/orders.mjs +++ b/scripts/admin-test-env-modules/orders.mjs @@ -21,6 +21,13 @@ function records(value) { return Array.isArray(value?.records) ? value.records : function pageOk(value) { return Array.isArray(value?.records) && Number.isFinite(Number(value?.total)) } function success(response) { return Boolean(response) && response.http === 200 && String(response.body?.code) === '200' } function sleep(milliseconds) { return new Promise((resolve) => setTimeout(resolve, milliseconds)) } +async function requestTransportRetry(context, method, path, body, token) { + for (let attempt = 1; attempt <= 3; attempt += 1) { + const response = await request(context, method, path, body, token) + if (response?.http !== 0 || attempt === 3) return response + await sleep(500 * attempt) + } +} function rotate(values, offset) { if (!Array.isArray(values) || values.length < 2) return Array.isArray(values) ? [...values] : [] const shift = Math.abs(Number(offset) || 0) % values.length @@ -250,7 +257,7 @@ async function assertProductImageDownload(context, merchantToken, fileId) { async function loadMasterData(context, merchantToken) { const destinationType = requestedOrderDestinationType(context) const fixedRoute = context.context?.waitSplitRoute || {} - const domesticResponse = await request(context, 'GET', + const domesticResponse = await requestTransportRetry(context, 'GET', '/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC', undefined, merchantToken) const domesticPage = context.apiSuccess(MODULE, 'Client 查询国内收货仓', domesticResponse) context.assert(MODULE, '存在可用国内收货仓', domesticResponse, records(domesticPage).length > 0, @@ -259,7 +266,7 @@ async function loadMasterData(context, merchantToken) { let destinationResponse = null let destinationPage = null if (destinationType !== 'PRIVATE') { - destinationResponse = await request(context, 'GET', + destinationResponse = await requestTransportRetry(context, 'GET', `/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=${encodeURIComponent(destinationType)}`, undefined, merchantToken) destinationPage = context.apiSuccess(MODULE, `Client 查询 ${destinationType} 目的仓`, destinationResponse) @@ -269,7 +276,7 @@ async function loadMasterData(context, merchantToken) { const receivings = records(domesticPage).filter((warehouse) => warehouse?.status === 'ACTIVE') let destinations = eligibleOrderDestinations(records(destinationPage), destinationType) if (Number(destinationPage?.total) > destinations.length) { - const nextResponse = await request(context, 'GET', + const nextResponse = await requestTransportRetry(context, 'GET', `/api/merchant/v1/warehouses?page=2&size=200&status=ACTIVE&warehouseType=${encodeURIComponent(destinationType)}`, undefined, merchantToken) const nextPage = context.apiSuccess(MODULE, `Client 查询 ${destinationType} 目的仓第二页`, nextResponse) diff --git a/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs b/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs index 38766ede4..deeb6b306 100644 --- a/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs +++ b/scripts/admin-test-env-modules/phase4-carrier-nodes.mjs @@ -43,6 +43,15 @@ function localWallIso(value) { return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}` + `T${pad(value.getHours())}:${pad(value.getMinutes())}:${pad(value.getSeconds())}` } +function sleep(milliseconds) { return new Promise(resolve => setTimeout(resolve, milliseconds)) } +async function manualNodeRequest(context, queryId, body, headers) { + for (let attempt = 1; attempt <= 3; attempt += 1) { + const response = await context.request('POST', `${BASE}/${queryId}/manual-nodes`, body, + context.token, headers) + if (response?.http !== 0 || attempt === 3) return response + await sleep(600 * attempt) + } +} export function markerOwnedCarrierQuery(context) { const resource = context?.createdResources?.phase3CarrierScheduleQuery @@ -257,8 +266,7 @@ export async function runUntilPickupReady(context) { const body = manualNodePayload({ expectedVersion: version, standardNode, observedAt: localWallIso(new Date()), actualAt, marker: context.marker, index: index + 1 }) const key = `${context.marker}-carrier-actual-${String(index + 1).padStart(2, '0')}`.slice(0, 128) - const response = await context.request('POST', `${BASE}/${queryId}/manual-nodes`, body, - context.token, { 'Idempotency-Key': key }) + const response = await manualNodeRequest(context, queryId, body, { 'Idempotency-Key': key }) const updated = context.apiSuccess(MODULE, `补录实际节点${standardNode}`, response, `queryId=${queryId}`, OPTIONS) assertDetail(context, response, updated, `实际节点${standardNode}响应DTO完整`) const updatedTask = updated?.query?.query