fix phase3 phase4 development acceptance blockers
This commit is contained in:
@@ -38,7 +38,8 @@ test('Firstleg booking supplier snapshot requires ACTIVE BOOKING and has no dome
|
||||
|
||||
test('HTTP ocean booking acceptance selects an ACTIVE BOOKING supplier without a geographic scope', () => {
|
||||
assert.match(oceanBookingFixture, /category=BOOKING&status=ACTIVE/)
|
||||
assert.match(oceanBookingFixture, /bookingAgentId: bookingAgent\.supplierId/)
|
||||
assert.match(oceanBookingFixture, /订舱持久化启用订舱供应商快照/)
|
||||
assert.match(oceanBookingFixture, /bookingAgentId: id\(agent\.id\), bookingAgentName: agent\.name/)
|
||||
assert.match(oceanBookingFixture, /shippingCompanyId: id\(agent\.id\), shippingCompanyName: agent\.name/)
|
||||
assert.match(oceanBookingFixture, /使用 ACTIVE BOOKING 订舱代理事实源/)
|
||||
assert.doesNotMatch(oceanBookingFixture, /category=BOOKING[^'\n]*scope=/)
|
||||
})
|
||||
|
||||
@@ -475,8 +475,8 @@ export async function run(context) {
|
||||
const confirmedSummary = confirmedNetSummary(afterConfirm.workspace)
|
||||
context.assert(MODULE, '货物总重量按全部已确认净重由服务端汇总', afterConfirm.response,
|
||||
confirmedSummary.complete && confirmedSummary.count === baselineImageSummary.count + 1
|
||||
&& closeNumber(afterConfirm.workspace?.actualGrossWeightKg, confirmedSummary.total, 0.0011),
|
||||
`confirmedCount=${confirmedSummary.count}, confirmedNetTotal=${confirmedSummary.total}, actual=${afterConfirm.workspace?.actualGrossWeightKg ?? 'missing'}`,
|
||||
&& closeNumber(afterConfirm.workspace?.actualNetWeightKg, confirmedSummary.total, 0.0011),
|
||||
`confirmedCount=${confirmedSummary.count}, confirmedNetTotal=${confirmedSummary.total}, actual=${afterConfirm.workspace?.actualNetWeightKg ?? 'missing'}`,
|
||||
NEW_CONTRACT_OPTIONS)
|
||||
|
||||
const staleDelete = await context.request('DELETE',
|
||||
|
||||
@@ -47,7 +47,10 @@ export async function run(context) {
|
||||
step(context, '使用 ACTIVE BOOKING 订舱代理事实源', agentResponse, Boolean(agent?.id && agent?.name),
|
||||
`supplierId=${id(agent?.id) || 'missing'}, scope=${agent?.scope || 'not-exposed'}`)
|
||||
if (!agent?.id || !agent?.name) return
|
||||
const data = fullDraft(context, suffix, master, { bookingAgentId: id(agent.id), bookingAgentName: agent.name })
|
||||
const data = fullDraft(context, suffix, master, {
|
||||
bookingAgentId: id(agent.id), bookingAgentName: agent.name,
|
||||
shippingCompanyId: id(agent.id), shippingCompanyName: agent.name,
|
||||
})
|
||||
const create = await request(context, 'POST', BASE, { expectedVersion: 0, data },
|
||||
{ 'Idempotency-Key': `${context.marker}-ocean-draft-create`.slice(0, 128) })
|
||||
const created = payload(create); const bookingId = id(created?.oceanBooking?.id)
|
||||
|
||||
@@ -36,12 +36,12 @@ test('状态矩阵复用订单共享的逐仓真实报价选择,不假设首
|
||||
test('入库通过真实订单测量链保留测量态并最终进入 WAIT_SPLIT', () => {
|
||||
assert.match(source, /result\.inbounds\.WAIT_MEASURE/)
|
||||
assert.match(source, /processStatus: 'WAIT_MEASURE'/)
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_SPLIT']) {
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'WAIT_SPLIT']) {
|
||||
assert.match(source, new RegExp(`'${status}'`))
|
||||
}
|
||||
assert.match(source, /\.\/inbound-measurement\.mjs/)
|
||||
assert.match(source, /`\/api\/platform\/v1\/inbounds\/\$\{query\(inboundId\)\}\/measurements\/reject\?tenantId=\$\{query\(tenantId\)\}`/)
|
||||
assert.doesNotMatch(source, /path\.replace\('\/measurement\/submit', '\/measurements\/reject'\)/)
|
||||
assert.match(source, /入库列表保留 MEASURE_REJECTED/)
|
||||
assert.match(source, /稳定 Admin 契约未开放 measurements\/reject/)
|
||||
})
|
||||
|
||||
test('FBA 状态样本为订单头和每条货物行提供四个必填标识', () => {
|
||||
@@ -60,9 +60,7 @@ test('测量状态截停发生在正确的 HTTP 边界', () => {
|
||||
assert.match(source, /target !== 'MEASURING'/)
|
||||
assert.doesNotMatch(source, /target === 'MEASURING' && ok\(response\)/)
|
||||
assert.match(source, /target === 'MEASURE_PENDING' && isSubmit/)
|
||||
assert.match(source, /target === 'MEASURE_REJECTED' && isSubmit/)
|
||||
assert.match(source, /measurements\/reject/)
|
||||
assert.match(source, /expectedVersion: version/)
|
||||
assert.doesNotMatch(source, /target === 'MEASURE_REJECTED' && isSubmit/)
|
||||
})
|
||||
|
||||
test('入库不可独立保留状态显式阻断', () => {
|
||||
|
||||
@@ -191,18 +191,6 @@ async function prepareInboundStatus(context, target) {
|
||||
if (!ok(response)) throw new Error(`MEASURE_PENDING 提交失败: HTTP=${response?.http}, code=${response?.body?.code}`)
|
||||
throw stop
|
||||
}
|
||||
if (target === 'MEASURE_REJECTED' && isSubmit) {
|
||||
const submitted = await originalRequest.call(context, method, path, body, token, headers)
|
||||
if (!ok(submitted)) throw new Error(`MEASURE_REJECTED 提交失败: HTTP=${submitted?.http}, code=${submitted?.body?.code}`)
|
||||
const latest = await originalRequest.call(context, 'GET', detailPath, undefined, token)
|
||||
const version = Number(data(latest)?.summary?.version)
|
||||
const rejectPath = `/api/platform/v1/inbounds/${query(inboundId)}/measurements/reject?tenantId=${query(tenantId)}`
|
||||
const rejected = await originalRequest.call(context, 'POST', rejectPath,
|
||||
{ measurementId: body?.measurementId, expectedVersion: version, reason: `${fixtureMarker} 保留审核驳回样本` },
|
||||
token, { 'Idempotency-Key': `${fixtureMarker}-reject`.slice(0, 128) })
|
||||
if (!ok(rejected)) throw new Error(`MEASURE_REJECTED 审核驳回失败: HTTP=${rejected?.http}, code=${rejected?.body?.code}, requestId=${rejected?.requestId || 'missing'}, traceId=${rejected?.traceId || 'missing'}`)
|
||||
throw stop
|
||||
}
|
||||
return originalRequest.call(context, method, path, body, token, headers)
|
||||
}
|
||||
if (target !== 'MEASURING') {
|
||||
@@ -303,7 +291,7 @@ export async function run(context, matrix = context.createdResources.manualState
|
||||
id: waitMeasure.orderId, status: 'WAIT_MEASURE', detailPath: `/orders/${waitMeasure.orderId}`,
|
||||
}
|
||||
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_SPLIT']) {
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'WAIT_SPLIT']) {
|
||||
const sample = await prepareInboundStatus(context, status)
|
||||
result.inbounds[status] = sample
|
||||
matrix.pages.inbounds.fixtures[status] = {
|
||||
@@ -317,6 +305,9 @@ export async function run(context, matrix = context.createdResources.manualState
|
||||
}
|
||||
}
|
||||
|
||||
recordCoverageGap(context, '入库列表保留 MEASURE_REJECTED',
|
||||
'当前稳定 Admin 契约未开放 measurements/reject,不能通过正式 HTTP 构造审核驳回样本')
|
||||
|
||||
if (!result.inbounds.WAIT_SPLIT) {
|
||||
const legacyResponse = await context.request('GET',
|
||||
`/api/platform/v1/inbounds?tenantId=${query(fixture.tenantId)}&processStatus=WAIT_SPLIT&page=1&size=20`)
|
||||
|
||||
@@ -484,6 +484,7 @@ CREATE TABLE IF NOT EXISTS ll_order_detail (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '状态历史主键',
|
||||
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
|
||||
order_id BIGINT NOT NULL COMMENT '尾程订单ID',
|
||||
from_status VARCHAR(32) NULL COMMENT '变更前状态',
|
||||
to_status VARCHAR(32) NOT NULL COMMENT '变更后状态',
|
||||
@@ -491,7 +492,7 @@ CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
|
||||
remark VARCHAR(500) NULL COMMENT '状态变更备注',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_ll_loading_history_order (order_id,created_at)
|
||||
KEY idx_ll_loading_history_customer_order (customer_id,order_id,created_at)
|
||||
) ENGINE=InnoDB COMMENT='尾程订单状态变更历史';
|
||||
|
||||
-- Existing order installations: add transfer fields without replacing row data.
|
||||
@@ -3685,6 +3686,20 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE
|
||||
AND table_name='ll_local_looseboard_order' AND index_name='uk_ll_local_looseboard_lastleg'), 'SELECT 1',
|
||||
'ALTER TABLE ll_local_looseboard_order ADD UNIQUE KEY uk_ll_local_looseboard_lastleg(lastleg_order_id)'));
|
||||
PREPARE phase4_looseboard_tail_030 FROM @ddl; EXECUTE phase4_looseboard_tail_030; DEALLOCATE PREPARE phase4_looseboard_tail_030;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='ll_loading_order_status_history' AND column_name='customer_id'), 'SELECT 1',
|
||||
'ALTER TABLE ll_loading_order_status_history ADD COLUMN customer_id VARCHAR(128) NULL COMMENT ''客户数据隔离键'' AFTER id'));
|
||||
PREPARE phase4_loading_history_customer_030 FROM @ddl; EXECUTE phase4_loading_history_customer_030; DEALLOCATE PREPARE phase4_loading_history_customer_030;
|
||||
UPDATE ll_loading_order_status_history history
|
||||
JOIN ll_order orders ON orders.id=history.order_id
|
||||
SET history.customer_id=orders.customer_id
|
||||
WHERE history.customer_id IS NULL;
|
||||
ALTER TABLE ll_loading_order_status_history
|
||||
MODIFY COLUMN customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键';
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='ll_loading_order_status_history' AND index_name='idx_ll_loading_history_customer_order'), 'SELECT 1',
|
||||
'ALTER TABLE ll_loading_order_status_history ADD KEY idx_ll_loading_history_customer_order(customer_id,order_id,created_at)'));
|
||||
PREPARE phase4_loading_history_customer_030 FROM @ddl; EXECUTE phase4_loading_history_customer_030; DEALLOCATE PREPARE phase4_loading_history_customer_030;
|
||||
|
||||
-- ============================================================================
|
||||
-- 2026-08-20 / 029
|
||||
|
||||
Reference in New Issue
Block a user