优化
This commit is contained in:
@@ -20,6 +20,11 @@ function validLongId(value) { return typeof value === 'string' && /^\d{16,}$/.te
|
||||
function validNumericId(value) { return typeof value === 'string' && /^\d+$/.test(value) && value !== '0' }
|
||||
function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) }
|
||||
function rejected(response) { return response?.http === 200 && ['A0409', '409'].includes(codeOf(response)) }
|
||||
function monotonicRejected(response) {
|
||||
return rejected(response)
|
||||
&& String(response?.body?.message || '').includes('人工实际节点必须严格晚于当前最新实际节点')
|
||||
&& Boolean(response?.requestId) && Boolean(response?.traceId)
|
||||
}
|
||||
function localIso(value) { return value.toISOString().slice(0, 19) }
|
||||
|
||||
export function markerOwnedCarrierQuery(context) {
|
||||
@@ -82,6 +87,18 @@ function nodeSnapshot(detail) {
|
||||
}))
|
||||
}
|
||||
|
||||
function actualProgressSnapshot(detail) {
|
||||
const task = detail?.query?.query
|
||||
return {
|
||||
version: Number(task?.version), latestActualNode: task?.latestActualNode,
|
||||
latestActualAt: task?.latestActualAt, nodes: nodeSnapshot(detail),
|
||||
histories: (Array.isArray(detail?.histories) ? detail.histories : []).map((item) => ({
|
||||
id: id(item?.id), action: item?.action, fromVersion: item?.fromVersion,
|
||||
toVersion: item?.toVersion, reason: item?.reason, createdAt: item?.createdAt,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
function assertDetail(context, response, detail, name) {
|
||||
const nodes = Array.isArray(detail?.nodes) ? detail.nodes : []
|
||||
const exceptions = Array.isArray(detail?.exceptions) ? detail.exceptions : []
|
||||
@@ -189,7 +206,34 @@ export async function runUntilPickupReady(context) {
|
||||
: Date.parse(`${frozenLatestActual}Z`)
|
||||
const baseActual = (Number.isNaN(frozenLatestActualMs) ? Date.now() : frozenLatestActualMs) + 60 * 60_000
|
||||
const pickupReadyIndex = STANDARD_NODES.indexOf('PICKUP_READY')
|
||||
for (let index = 0; index <= pickupReadyIndex; index += 1) {
|
||||
const frozenLatestNodeIndex = STANDARD_NODES.indexOf(detail?.query?.query?.latestActualNode)
|
||||
context.assert(MODULE, '出仓自动实际节点作为人工补录单调起点', zeroDriftResponse,
|
||||
frozenLatestNodeIndex >= 0 && frozenLatestNodeIndex < pickupReadyIndex,
|
||||
`latest=${detail?.query?.query?.latestActualNode || 'missing'}`, OPTIONS)
|
||||
|
||||
const sameNodeSnapshot = actualProgressSnapshot(detail)
|
||||
const sameNodeBody = manualNodePayload({ expectedVersion: version,
|
||||
standardNode: detail.query.query.latestActualNode, observedAt: localIso(new Date()),
|
||||
actualAt: localIso(new Date(baseActual)), marker: context.marker, index: frozenLatestNodeIndex + 1 })
|
||||
const sameNodeResponse = await context.request('POST', `${BASE}/${queryId}/manual-nodes`, sameNodeBody,
|
||||
context.token, { 'Idempotency-Key': `${context.marker}-carrier-same-actual`.slice(0, 128) })
|
||||
context.addStep(MODULE, '重复当前实际节点精确返回A0409', sameNodeResponse, monotonicRejected(sameNodeResponse),
|
||||
`HTTP=${sameNodeResponse.http}, code=${codeOf(sameNodeResponse)}, message=${sameNodeResponse.body?.message || 'missing'}`, OPTIONS)
|
||||
const sameNodeReplay = await context.request('POST', `${BASE}/${queryId}/manual-nodes`, sameNodeBody,
|
||||
context.token, { 'Idempotency-Key': `${context.marker}-carrier-same-actual`.slice(0, 128) })
|
||||
context.addStep(MODULE, '同级actual失败请求同键重试仍为单调A0409且operation claim零残留',
|
||||
sameNodeReplay, monotonicRejected(sameNodeReplay),
|
||||
`HTTP=${sameNodeReplay.http}, code=${codeOf(sameNodeReplay)}, message=${sameNodeReplay.body?.message || 'missing'}`,
|
||||
OPTIONS)
|
||||
const sameNodeRead = await context.request('GET', `${BASE}/${queryId}`)
|
||||
detail = context.apiSuccess(MODULE, '重复当前实际节点后回查零漂移', sameNodeRead, '', OPTIONS)
|
||||
context.assert(MODULE, '重复当前实际节点A0409后task、node、history零漂移', sameNodeRead,
|
||||
JSON.stringify(actualProgressSnapshot(detail)) === JSON.stringify(sameNodeSnapshot),
|
||||
`version=${detail?.query?.query?.version ?? 'missing'}, latest=${detail?.query?.query?.latestActualNode || 'missing'}`,
|
||||
OPTIONS)
|
||||
|
||||
let earlierActualChecked = false
|
||||
for (let index = frozenLatestNodeIndex + 1; index <= pickupReadyIndex; index += 1) {
|
||||
const standardNode = STANDARD_NODES[index]
|
||||
const actualAt = localIso(new Date(baseActual + index * 60_000))
|
||||
const body = manualNodePayload({ expectedVersion: version, standardNode,
|
||||
@@ -213,6 +257,32 @@ export async function runUntilPickupReady(context) {
|
||||
`status=${updatedTask?.status || 'missing'}, latest=${updatedTask?.latestActualNode || 'none'}`, OPTIONS)
|
||||
version = Number(updatedTask.version)
|
||||
detail = updated
|
||||
|
||||
if (!earlierActualChecked) {
|
||||
const progressedSnapshot = actualProgressSnapshot(detail)
|
||||
const earlierNode = STANDARD_NODES[index - 1]
|
||||
const earlierBody = manualNodePayload({ expectedVersion: version, standardNode: earlierNode,
|
||||
observedAt: localIso(new Date()), actualAt: localIso(new Date(baseActual + (index + 1) * 60_000)),
|
||||
marker: context.marker, index })
|
||||
const earlierResponse = await context.request('POST', `${BASE}/${queryId}/manual-nodes`, earlierBody,
|
||||
context.token, { 'Idempotency-Key': `${context.marker}-carrier-earlier-actual`.slice(0, 128) })
|
||||
context.addStep(MODULE, '补录更早实际节点精确返回A0409', earlierResponse, monotonicRejected(earlierResponse),
|
||||
`HTTP=${earlierResponse.http}, code=${codeOf(earlierResponse)}, message=${earlierResponse.body?.message || 'missing'}`,
|
||||
OPTIONS)
|
||||
const earlierReplay = await context.request('POST', `${BASE}/${queryId}/manual-nodes`, earlierBody,
|
||||
context.token, { 'Idempotency-Key': `${context.marker}-carrier-earlier-actual`.slice(0, 128) })
|
||||
context.addStep(MODULE, '更早actual失败请求同键重试仍为单调A0409且operation claim零残留',
|
||||
earlierReplay, monotonicRejected(earlierReplay),
|
||||
`HTTP=${earlierReplay.http}, code=${codeOf(earlierReplay)}, message=${earlierReplay.body?.message || 'missing'}`,
|
||||
OPTIONS)
|
||||
const earlierRead = await context.request('GET', `${BASE}/${queryId}`)
|
||||
detail = context.apiSuccess(MODULE, '更早实际节点冲突后回查零漂移', earlierRead, '', OPTIONS)
|
||||
context.assert(MODULE, '更早实际节点A0409后task、node、history零漂移', earlierRead,
|
||||
JSON.stringify(actualProgressSnapshot(detail)) === JSON.stringify(progressedSnapshot),
|
||||
`version=${detail?.query?.query?.version ?? 'missing'}, latest=${detail?.query?.query?.latestActualNode || 'missing'}`,
|
||||
OPTIONS)
|
||||
earlierActualChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
const pickupTask = detail.query.query
|
||||
@@ -287,6 +357,6 @@ export async function run(context) {
|
||||
const history = context.apiSuccess(MODULE, '查询本轮任务不可变历史', historyResponse, `queryId=${queryId}`, OPTIONS)
|
||||
context.assert(MODULE, '历史只含业务动作且人工节点完整留痕', historyResponse,
|
||||
Array.isArray(history) && history.every(validHistory)
|
||||
&& history.filter((item) => item.action === 'MANUAL_NODE').length >= STANDARD_NODES.length + 1,
|
||||
&& history.filter((item) => item.action === 'MANUAL_NODE').length >= STANDARD_NODES.length,
|
||||
`history=${Array.isArray(history) ? history.length : 'missing'}`, OPTIONS)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user