优化
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { testPlannedCapacity } from './fleet-booking-reservation-acceptance.mjs'
|
||||
import { bindLooseboardDemand } from './looseboard-pool-binding.mjs'
|
||||
|
||||
const API = '/api/platform/v1/last-leg'
|
||||
const MODULE = 'phase4-overseas-business'
|
||||
const OPTIONS = { scenario: 'looseboard-pool-preparation', deploymentBlocker: true }
|
||||
const validVersion = value => Number.isSafeInteger(value) && value >= 0
|
||||
|
||||
/** 复用本轮已审核且未扫码车次,发布可核验余位并衔接散板原单。 */
|
||||
export async function prepareLooseboardPool(context, { demand, tripProof, destinationKey, unitPrice }, bind = bindLooseboardDemand) {
|
||||
if (!context.createdResources?.fleetTrips?.includes(tripProof)
|
||||
|| tripProof.preparation !== 'VERIFIED' || !tripProof.tripNo || !tripProof.dispatchNo || tripProof.looseboardOffer
|
||||
|| context.createdResources?.phase4OverseasBusiness?.looseboard !== demand || demand?.marker !== context.marker
|
||||
|| demand.finalStatus !== 'PENDING_ACCEPTANCE' || demand.poolBinding || !destinationKey
|
||||
|| !Number.isFinite(unitPrice) || unitPrice <= 0 || typeof context.checkpoint !== 'function') {
|
||||
throw Error('散板余位准备缺少本轮原单、已审核未占用车次、约定目的地/价格或检查点')
|
||||
}
|
||||
const proof = tripProof.looseboardOffer = { marker: context.marker, status: 'PREPARING', requests: [] }
|
||||
const check = (name, condition) => {
|
||||
context.assert(MODULE, name, null, Boolean(condition), `tripNo=${tripProof.tripNo}`, OPTIONS)
|
||||
if (!condition) throw Error(name)
|
||||
}
|
||||
const request = async (method, path, body) => {
|
||||
const log = { method, path, ...(body ? { body: structuredClone(body) } : {}), status: 'PREPARED' }
|
||||
proof.requests.push(log)
|
||||
await context.checkpoint()
|
||||
let response
|
||||
try { log.status = 'SENDING'; response = await context.request(method, path, body) }
|
||||
catch (error) { log.status = 'UNKNOWN_RESULT'; throw error }
|
||||
Object.assign(log, { http: response?.http, code: response?.body?.code, requestId: response?.requestId, traceId: response?.traceId })
|
||||
const ok = response?.http === 200 && String(response?.body?.code) === '200'
|
||||
log.status = ok ? 'SUCCEEDED' : !response?.http || response.http >= 500 ? 'UNKNOWN_RESULT' : 'REJECTED'
|
||||
await context.checkpoint()
|
||||
context.apiSuccess(MODULE, `散板余位准备 ${method}`, response, JSON.stringify({ http: log.http, requestId: log.requestId, traceId: log.traceId }), OPTIONS)
|
||||
if (!ok) throw Error('散板余位准备失败;不得换键重造或自动重试')
|
||||
return response.body.data
|
||||
}
|
||||
const tripPath = `${API}/fleet/trips/${encodeURIComponent(tripProof.tripNo)}`
|
||||
const pool = `${API}/fleet/capacity-offers`
|
||||
try {
|
||||
const dispatch = await request('GET', `${API}/dispatches/${encodeURIComponent(tripProof.dispatchNo)}`)
|
||||
check('仅在原排车未扫码未发运时接入散板', dispatch?.dispatchNo === tripProof.dispatchNo && dispatch.status === 'ACTIVE'
|
||||
&& ['BEFORE_LOADING', 'LOADING'].includes(dispatch.stage)
|
||||
&& Number(dispatch.counts?.pickedPackageCount) === 0 && Number(dispatch.counts?.shippedPackageCount) === 0)
|
||||
let detail = await request('GET', tripPath)
|
||||
check('散板接车复用本轮同仓同车同司机', detail?.trip?.tripNo === tripProof.tripNo && detail.trip.status === 'PLANNED'
|
||||
&& String(detail.trip.warehouseId) === String(tripProof.warehouseId)
|
||||
&& String(detail.trip.vehicleId) === String(tripProof.vehicleId) && String(detail.trip.driverId) === String(tripProof.driverId)
|
||||
&& detail.dispatches?.some(row => row.dispatchNo === tripProof.dispatchNo)
|
||||
&& detail.trip.stops?.filter(stop => stop.destinationKey === destinationKey).length === 1)
|
||||
let plan = await request('GET', `${tripPath}/capacity-plan`)
|
||||
check('容量依据来自本轮原车真实货物', Array.isArray(plan?.cargo) && plan.cargo.length > 0
|
||||
&& plan.cargo.every(row => row.dispatchNo === tripProof.dispatchNo) && validVersion(plan.recordVersion)
|
||||
&& typeof plan.manifestHash === 'string' && /^[0-9a-f]{64}$/.test(plan.manifestHash))
|
||||
if (plan.planValid !== true) {
|
||||
const pallets = testPlannedCapacity(plan.cargo, detail.trip.stops)
|
||||
proof.planningBasis = { actual: false, kind: 'CONTROLLED_TEST_PLAN', pallets }
|
||||
plan = await request('POST', `${tripPath}/capacity-plan`, { recordVersion: plan.recordVersion, manifestHash: plan.manifestHash,
|
||||
pallets, reason: '仅本轮真实实托的受控预计占用;不得作为装车实测或回执' })
|
||||
}
|
||||
check('车次容量计划有效且无阻断', plan?.planValid === true && !plan.blockReason && plan.availableSegments?.length > 0)
|
||||
const loading = await request('GET', `${tripPath}/loading`)
|
||||
check('预计容量未冒充实载确认', loading?.confirmedLoad == null)
|
||||
detail = await request('GET', tripPath)
|
||||
check('发布前车次仍未发车且版本有效', detail?.trip?.status === 'PLANNED' && validVersion(detail.trip.recordVersion))
|
||||
const draftBody = { tripNo: tripProof.tripNo, tripRecordVersion: detail.trip.recordVersion,
|
||||
cutoffAt: new Date(Date.now() + 1800000).toISOString(), contactName: 'TEST调度', contactPhone: '19900000000',
|
||||
remark: context.marker, idempotencyKey: `${context.marker}-looseboard-offer` }
|
||||
const draft = await request('POST', pool, draftBody)
|
||||
check('余位草稿属于原车且版本完整', draft?.offer?.tripNo === tripProof.tripNo && draft.offer.status === 'DRAFT'
|
||||
&& typeof draft.offer.offerNo === 'string' && draft.offer.offerNo.length > 0
|
||||
&& validVersion(draft.offer.tripRecordVersion) && validVersion(draft.offer.recordVersion))
|
||||
proof.offerNo = draft.offer.offerNo
|
||||
const replay = await request('POST', pool, draftBody)
|
||||
check('余位发布前草稿重放不生成第二份', replay?.replayed === true && JSON.stringify(replay.offer) === JSON.stringify(draft.offer))
|
||||
const publishBody = { tripRecordVersion: draft.offer.tripRecordVersion, offerRecordVersion: draft.offer.recordVersion,
|
||||
idempotencyKey: `${context.marker}-looseboard-publish` }
|
||||
const publishPath = `${pool}/${encodeURIComponent(proof.offerNo)}/publish`
|
||||
const published = await request('POST', publishPath, publishBody)
|
||||
check('原车余位已开放且可登记承运', published?.offer?.offerNo === proof.offerNo && published.offer.tripNo === tripProof.tripNo
|
||||
&& published.offer.status === 'OPEN' && published.offer.capacityKnown && published.offer.canCreateInquiry && published.offer.canConfirm)
|
||||
const publishedAgain = await request('POST', publishPath, publishBody)
|
||||
check('余位发布重放不重复推进版本', publishedAgain?.replayed === true && JSON.stringify(publishedAgain.offer) === JSON.stringify(published.offer))
|
||||
proof.status = 'OPEN'
|
||||
await context.checkpoint()
|
||||
const binding = await bind(context, { demand, tripProof, offerNo: proof.offerNo, destinationKey, unitPrice })
|
||||
check('散板原单绑定已确认且指向原车余位', binding?.status === 'CONFIRMED_ORIGINAL_ORDER'
|
||||
&& binding.tripNo === tripProof.tripNo && binding.offerNo === proof.offerNo)
|
||||
proof.status = 'BOUND_ORIGINAL_DEMAND'
|
||||
await context.checkpoint()
|
||||
return binding
|
||||
} catch (error) {
|
||||
proof.status = 'FAILED'; proof.failure = error.message
|
||||
await context.checkpoint()
|
||||
throw error
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user