修复尾程货主快照与短单号合同

This commit is contained in:
2026-09-01 11:06:26 +08:00
parent 4991536065
commit de0eddeee8
7 changed files with 97 additions and 47 deletions
@@ -39,9 +39,3 @@ export function validEventReplay(before, after) {
&& Number(before?.detailCount) === Number(after?.detailCount)
&& Number(before?.historyCount) === Number(after?.historyCount)
}
export function adminPreservesOrderNumber(apiValue, displayedValue, copiedValue) {
return TAILLEG_ORDER_NUMBER.pattern.test(String(apiValue ?? ''))
&& displayedValue === apiValue
&& copiedValue === apiValue
}
@@ -3,7 +3,6 @@ import { readFileSync } from 'node:fs'
import test from 'node:test'
import {
adminPreservesOrderNumber,
IMMUTABLE_TAILLEG_EVENT_FIXTURE,
TAILLEG_ORDER_NUMBER,
validContainerOrderGroup,
@@ -13,18 +12,14 @@ import {
const read = (relativeUrl) => readFileSync(new URL(relativeUrl, import.meta.url), 'utf8')
const eventV2 = read('../../common-event-contract/src/main/java/com/baiyue/common/event/FirstLegLoadingPlanCompletedV2.java')
const producer = read('../../firstleg/src/main/java/com/baiyue/firstleg/service/LoadingCompletedOutboxService.java')
const producerRows = read('../../firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingCompletedEventMapper.java')
const eventV2Tests = read('../../common-event-contract/src/test/java/com/baiyue/common/event/FirstLegLoadingPlanCompletedV2Test.java')
const consumer = read('../../lastleg-service/src/main/java/com/baiyue/lastleg/service/LoadingCompletedConsumer.java')
const orderEntity = read('../../lastleg-service/src/main/java/com/baiyue/lastleg/entity/LastlegOrder.java')
const orderSummary = read('../../lastleg-service/src/main/java/com/baiyue/lastleg/dto/LastlegOrderSummary.java')
const orderService = read('../../lastleg-service/src/main/java/com/baiyue/lastleg/service/LastlegOrderService.java')
const generator = read('../../lastleg-service/src/main/java/com/baiyue/lastleg/service/LastlegOrderNoGenerator.java')
const generatorTests = read('../../lastleg-service/src/test/java/com/baiyue/lastleg/service/LastlegOrderNoGeneratorTest.java')
const consumerTests = read('../../lastleg-service/src/test/java/com/baiyue/lastleg/service/LoadingCompletedConsumerTest.java')
const adminApi = read('../../../apps/admin-web/src/modules/overseas-warehouse/api/lastMileOrders.ts')
const adminPresentation = read('../../../apps/admin-web/src/modules/overseas-warehouse/lastMilePresentation.ts')
const adminList = read('../../../apps/admin-web/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue')
const adminDetail = read('../../../apps/admin-web/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue')
test('new 15-order event requires one non-empty trusted owner snapshot', () => {
const orders = Array.from({ length: 15 }, (_, index) => ({
@@ -44,26 +39,27 @@ test('the processed historical event is immutable and cannot be reused for accep
})
})
test('V2 producer freezes customerId and customerName and rejects a blank name', () => {
test('V2 event contract freezes customerId and customerName and rejects a blank or mixed owner', () => {
assert.match(eventV2, /List<Item> items,\s*String customerId,\s*String customerName,\s*String orderType/)
assert.match(producerRows, /b\.customer_name/)
assert.match(producer, /first\.getCustomerName\(\)/)
assert.match(producer, /(?:customerName|getCustomerName)[\s\S]{0,300}(?:isBlank|hasText)/)
assert.match(eventV2, /customerId\s*=\s*customerId\.trim\(\)/)
assert.match(eventV2, /customerName\s*=\s*customerName\.trim\(\)/)
assert.match(eventV2, /orders must share one immutable customerId\/customerName snapshot/)
assert.match(eventV2Tests, /rejectsMissingImmutableCustomerName/)
assert.match(eventV2Tests, /freezesTrimmedOwnerSnapshotAndRejectsMixedOwners/)
})
test('Lastleg rejects a missing owner snapshot, persists it, and never calls IAM to repair it', () => {
assert.match(consumer, /source\.customerName\(\)/)
assert.match(consumer, /customerName[\s\S]{0,240}(?:isBlank|hasText)/)
assert.match(consumer, /OwnerSnapshot owner\s*=\s*requireOwnerSnapshot\(payload\.orders\(\),\s*envelope\.tenantId\(\)\)/)
assert.match(consumer, /requiredOwnerValue\(orders\.get\(0\)\.customerName\(\),\s*"customerName"\)/)
assert.match(consumer, /value\s*==\s*null\s*\|\|\s*value\.isBlank\(\)/)
assert.match(consumer, /order\.setCustomerName\(customerName\)/)
assert.match(orderEntity, /private String customerName;/)
assert.doesNotMatch(consumer, /(?:Iam|IAM|\/iam|CustomerClient|customer.*client)/)
})
test('Lastleg query DTO and Admin list/detail use the frozen owner name', () => {
test('Lastleg query DTO uses the persisted frozen owner name', () => {
assert.match(orderSummary, /String customerName/)
assert.match(orderService, /o\.getCustomerName\(\)/)
assert.match(adminApi, /customerName\?: string \| null/)
assert.match(adminPresentation, /owner:\s*text\(dto\.customerName\)/)
assert.match(adminDetail, /label:\s*'货主',\s*value:\s*text\(order\?\.customerName\)/)
})
test('new tailleg number is Shanghai daily LMO-yyMMdd-#### with concurrency and cross-day tests', () => {
@@ -76,7 +72,8 @@ test('new tailleg number is Shanghai daily LMO-yyMMdd-#### with concurrency and
assert.match(generator, /%04d|padStart\(4|String\.format/)
assert.match(generatorTests, /(?:concurrent|并发)/i)
assert.match(generatorTests, /(?:cross.?day|next.?day|跨天)/i)
assert.match(generatorTests, /\^LMO-\\d\{6\}-\\d\{4\}\$/)
assert.match(generatorTests, /LMO-260827-0001/)
assert.match(generatorTests, /LMO-260828-0001/)
})
test('one container with 15 inbound rows shares one new LMO number', () => {
@@ -86,8 +83,9 @@ test('one container with 15 inbound rows shares one new LMO number', () => {
assert.equal(validContainerOrderGroup(rows), true)
assert.equal(validContainerOrderGroup(rows.map((row, index) => index === 14
? { ...row, orderNo: 'LMO-260827-0043' } : row)), false)
assert.match(consumer, /String orderNo\s*=\s*orderNoGenerator\.[^(]+\(payload\.containerNo\(\)\);[\s\S]*for \(int index/)
assert.match(consumer, /createOrder\(envelope, payload, source, orderNo, rawSource\)/)
assert.match(consumer, /String orderNo\s*=\s*orderNoGenerator\.next\(\);[\s\S]*for \(int index/)
assert.match(consumer, /createOrder\(envelope, payload, source, orderNo, rawSource, containerNo/)
assert.match(consumerTests, /oneContainerWith15InboundRowsSharesOneNewLmoNumber/)
})
test('same event replay adds nothing and preserves the allocated number', () => {
@@ -95,16 +93,6 @@ test('same event replay adds nothing and preserves the allocated number', () =>
assert.equal(validEventReplay(before, { ...before }), true)
assert.equal(validEventReplay(before, { ...before, orderCount: 30 }), false)
assert.match(consumer, /if\s*\(existing\s*!=\s*null\)[\s\S]{0,400}return countOrders/)
assert.match(consumerTests, /(?:replay|duplicate|重复)[\s\S]{0,900}(?:orderNo|OrderNo)/i)
})
test('Admin displays and copies the exact queried number without reformatting', () => {
const value = 'LMO-260827-0042'
assert.equal(adminPreservesOrderNumber(value, value, value), true)
assert.equal(adminPreservesOrderNumber(value, value.replaceAll('-', ''), value), false)
assert.match(adminPresentation, /orderNo:\s*text\(dto\.orderNo\)/)
assert.match(adminList, /prop:\s*'orderNo'/)
assert.match(adminDetail, /reference-value="referenceValue"/)
const adminOrderSources = `${adminList}\n${adminDetail}`
assert.match(adminOrderSources, /(?:clipboard\.writeText|copyText|copyToClipboard)[\s\S]{0,240}(?:orderNo|referenceValue)|(?:orderNo|referenceValue)[\s\S]{0,240}(?:clipboard\.writeText|copyText|copyToClipboard)/)
assert.match(consumerTests, /replayPreservesExistingLmoNumberAndAddsNothing/)
assert.match(consumerTests, /verify\(sequences,never\(\)\)\.nextValue/)
})