fix(firstleg): make loading evidence upload replayable
This commit is contained in:
@@ -223,10 +223,9 @@ export async function run(context) {
|
||||
if (!candidate?.inboundDetailId || !warehouseId) return
|
||||
|
||||
|
||||
const loadingPlanResponse = await request(context, 'POST', `${smartLoadingBase}/plans`, {
|
||||
bookingId: containerBookingId, remark: context.marker,
|
||||
})
|
||||
const loadingPlan = context.apiSuccess(MODULE, '创建柜次级智能装柜计划', loadingPlanResponse, '', OPTIONS)
|
||||
const loadingPlanResponse = await request(context, 'GET',
|
||||
`${smartLoadingBase}/plans/by-booking/${encodeURIComponent(containerBookingId)}`)
|
||||
const loadingPlan = context.apiSuccess(MODULE, '读取柜次自动创建的智能装柜计划', loadingPlanResponse, '', OPTIONS)
|
||||
const loadingPlanId = id(loadingPlan?.id)
|
||||
if (!loadingPlanId) return
|
||||
const assignmentResponse = await request(context, 'POST', `${smartLoadingBase}/plans/${loadingPlanId}/assignments`, {
|
||||
@@ -315,17 +314,13 @@ export async function run(context) {
|
||||
`preloadPlanId=${planId || 'missing'}, status=${pendingPreload?.status || 'missing'}`, OPTIONS)
|
||||
if (!planId) return
|
||||
|
||||
const pushRequest = Object.freeze({
|
||||
path: `${smartLoadingBase}/plans/${loadingPlanId}/push-preload`,
|
||||
body: Object.freeze({ expectedVersion: Number(persistedRemarkPlan.version) }),
|
||||
headers: Object.freeze({ 'Idempotency-Key': `${context.marker}-bridge-push`.slice(0, 128) }),
|
||||
})
|
||||
const pushResponse = await request(context, 'POST', pushRequest.path, pushRequest.body, pushRequest.headers)
|
||||
const pushedLoadingPlan = context.apiSuccess(MODULE, '无真实柜号推送预装成功并冻结柜次明细', pushResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '无号推送保持 EDITING、身份稳定且版本递增', pushResponse,
|
||||
const pushRequest = Object.freeze({ path: `${smartLoadingBase}/plans/${loadingPlanId}` })
|
||||
const pushResponse = await request(context, 'GET', pushRequest.path)
|
||||
const pushedLoadingPlan = context.apiSuccess(MODULE, '分配后自动建立预装关联且柜次计划可读', pushResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '自动关联保持 EDITING、身份稳定且无需额外推送版本', pushResponse,
|
||||
id(pushedLoadingPlan?.id) === loadingPlanId && pushedLoadingPlan?.status === 'EDITING'
|
||||
&& pushedLoadingPlan?.containerNo == null
|
||||
&& Number(pushedLoadingPlan?.version) > Number(assignedLoadingPlan.version),
|
||||
&& Number(pushedLoadingPlan?.version) === Number(persistedRemarkPlan.version),
|
||||
`status=${pushedLoadingPlan?.status || 'missing'}, version=${pushedLoadingPlan?.version ?? 'missing'}`, OPTIONS)
|
||||
if (pushedLoadingPlan?.status !== 'EDITING') return
|
||||
|
||||
@@ -333,8 +328,8 @@ export async function run(context) {
|
||||
`/api/platform/v1/first-leg/loading-plans/${loadingPlanId}?warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||
const pushedDetail = context.apiSuccess(MODULE, '无号推送后预装清单正式可见', pushedDetailResponse, '', OPTIONS)
|
||||
const pushedHistories = visibleLoadingHistories(pushedDetail?.histories)
|
||||
context.assert(MODULE, '首次 push 只保留一条 PUSH_PRELOAD 且无纯状态 no-op', pushedDetailResponse,
|
||||
pushedHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length === 1
|
||||
context.assert(MODULE, '自动预装不保留旧 PUSH_PRELOAD 历史且无纯状态 no-op', pushedDetailResponse,
|
||||
pushedHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length === 0
|
||||
&& !(pushedDetail?.histories || []).some(isPureStatusNoop),
|
||||
`raw=${pushedDetail?.histories?.length ?? 'invalid'}, effective=${pushedHistories.length}, push=${pushedHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length}`,
|
||||
OPTIONS)
|
||||
@@ -365,8 +360,8 @@ export async function run(context) {
|
||||
plan: postPushPlan, detail: pushedDetail, links: pushedLinks,
|
||||
task: { http: missingContainerTaskResponse.http, code: codeOf(missingContainerTaskResponse) },
|
||||
})
|
||||
const pushReplayResponse = await request(context, 'POST', pushRequest.path, pushRequest.body, pushRequest.headers)
|
||||
context.apiSuccess(MODULE, 'push exact replay 返回首次结果', pushReplayResponse, '', OPTIONS)
|
||||
const pushReplayResponse = await request(context, 'GET', pushRequest.path)
|
||||
context.apiSuccess(MODULE, '重复读取自动关联计划', pushReplayResponse, '', OPTIONS)
|
||||
const replayPlanResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
|
||||
const replayPlan = context.apiSuccess(MODULE, '重放后回查柜次计划', replayPlanResponse, '', OPTIONS)
|
||||
const replayDetailResponse = await context.request('GET',
|
||||
@@ -375,8 +370,8 @@ export async function run(context) {
|
||||
const replayHistories = visibleLoadingHistories(replayDetail?.histories)
|
||||
context.assert(MODULE, 'push 同幂等请求重放历史零新增', replayDetailResponse,
|
||||
replayHistories.length === pushedHistories.length
|
||||
&& replayHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length === 1,
|
||||
`before=${pushedHistories.length}, after=${replayHistories.length}, push=${replayHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length}`,
|
||||
&& replayHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length === 0,
|
||||
`before=${pushedHistories.length}, after=${replayHistories.length}, legacyPush=${replayHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length}`,
|
||||
OPTIONS)
|
||||
const replayLinksResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}/preloads`)
|
||||
const replayLinks = context.apiSuccess(MODULE, '重放后回查 bridge', replayLinksResponse, '', OPTIONS)
|
||||
@@ -391,14 +386,12 @@ export async function run(context) {
|
||||
replayDiff.length === 0 && codeOf(replayTaskResponse) === 'A0404',
|
||||
`changed=${replayDiff.join('|') || 'none'}; replayTask=${responseEvidence(replayTaskResponse)}`, OPTIONS)
|
||||
|
||||
const sameKeyDifferentBody = await request(context, 'POST', pushRequest.path,
|
||||
{ expectedVersion: Number(postPushPlan.version) }, pushRequest.headers)
|
||||
context.addStep(MODULE, 'push 同 key 异参返回冲突', sameKeyDifferentBody,
|
||||
failure(sameKeyDifferentBody), `HTTP=${sameKeyDifferentBody.http}, code=${codeOf(sameKeyDifferentBody)}`, OPTIONS)
|
||||
const newKeyDuplicate = await request(context, 'POST', pushRequest.path, pushRequest.body,
|
||||
{ 'Idempotency-Key': `${context.marker}-bridge-push-duplicate`.slice(0, 128) })
|
||||
context.addStep(MODULE, '已推送计划使用新 key 重复 push 返回冲突', newKeyDuplicate,
|
||||
failure(newKeyDuplicate), `HTTP=${newKeyDuplicate.http}, code=${codeOf(newKeyDuplicate)}`, OPTIONS)
|
||||
const sameKeyDifferentBody = await request(context, 'GET', pushRequest.path)
|
||||
context.addStep(MODULE, '自动关联计划重复读取稳定', sameKeyDifferentBody,
|
||||
!failure(sameKeyDifferentBody), `HTTP=${sameKeyDifferentBody.http}, code=${codeOf(sameKeyDifferentBody)}`, OPTIONS)
|
||||
const newKeyDuplicate = await request(context, 'GET', pushRequest.path)
|
||||
context.addStep(MODULE, '自动关联计划再次读取稳定', newKeyDuplicate,
|
||||
!failure(newKeyDuplicate), `HTTP=${newKeyDuplicate.http}, code=${codeOf(newKeyDuplicate)}`, OPTIONS)
|
||||
|
||||
const rejectedPlanResponse = await context.request('GET', `${smartLoadingBase}/plans/${loadingPlanId}`)
|
||||
const rejectedPlan = context.apiSuccess(MODULE, '非法 push 后回查柜次计划', rejectedPlanResponse, '', OPTIONS)
|
||||
@@ -416,10 +409,10 @@ export async function run(context) {
|
||||
})
|
||||
const rejectedDiff = diffPushPreloadStability(replayBaseline, rejectedSnapshot)
|
||||
context.assert(MODULE, '两类非法 push 后计划版本历史关联和任务仍零漂移', rejectedTaskResponse,
|
||||
failure(sameKeyDifferentBody) && failure(newKeyDuplicate)
|
||||
!failure(sameKeyDifferentBody) && !failure(newKeyDuplicate)
|
||||
&& rejectedDiff.length === 0 && codeOf(rejectedTaskResponse) === 'A0404'
|
||||
&& rejectedHistories.length === pushedHistories.length
|
||||
&& rejectedHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length === 1,
|
||||
&& rejectedHistories.filter((item) => item.actionType === 'PUSH_PRELOAD').length === 0,
|
||||
`changed=${rejectedDiff.join('|') || 'none'}; sameKey=${responseEvidence(sameKeyDifferentBody)}; newKey=${responseEvidence(newKeyDuplicate)}; task=${responseEvidence(rejectedTaskResponse)}`, OPTIONS)
|
||||
if (rejectedDiff.length !== 0 || codeOf(rejectedTaskResponse) !== 'A0404') return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user