优化
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ node scripts/create-pending-review-orders.mjs
|
||||
| Admin/Client 本地测试与类型检查 | `scripts/frontend-full-regression.ps1` |
|
||||
| PDA 本地页面和状态测试 | `../apps/pda-uniapp/tests/*.test.mjs` |
|
||||
| 本地数据库 SQL 静态/受控验收 | `scripts/local-db-full-acceptance.ps1` |
|
||||
| AGENTS 快速索引一致性 | `scripts/agents-index-contract.ps1`;只读核对根快速表唯一性、部署单元、业务模块、Client active 路由、四期范围漂移、订舱候选与柜量语义、海外 Gateway 覆盖、PDA 页面/运行配置/能力开关、Maven 模块、Lastleg 海外安全投影、同步 Trace、关键入口与排除目录 |
|
||||
| AGENTS 快速索引一致性 | `scripts/agents-index-contract.ps1`;只读核对根快速表唯一性、部署单元、业务模块、Client active 路由、四期范围漂移、仓库主档唯一权威、订舱候选与柜量语义、整柜出仓与船节点单调性、海外 Gateway 覆盖、PDA 页面/运行配置/能力开关、Maven 模块、Lastleg 海外安全投影、同步 Trace、关键入口与排除目录 |
|
||||
| Java 质量聚合摘要 | `node --test scripts/java-quality-summary.test.mjs`、`node scripts/java-quality-summary.mjs --output target/java-quality-summary.json`;聚合 Maven/服务拓扑、OpenAPI 覆盖、JaCoCo 和 SpotBugs 报告,默认只报告存量缺口,CI 用显式 `--require-jacoco --require-spotbugs` 检查报告完整性且当前允许失败 |
|
||||
| 单服务部署失败回滚合同 | `scripts/deploy-built-service-rollback.test.mjs`;模拟成功、健康失败和启动失败,确认仅恢复同服务先前健康镜像且部署任务仍失败 |
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ test('outbound-created query supports automated or manual carrier then reaches P
|
||||
assert.match(nodes, /'DISCHARGED_FROM_VESSEL', 'PICKUP_READY'/)
|
||||
assert.match(nodes, /const pickupReadyIndex = STANDARD_NODES\.indexOf\('PICKUP_READY'\)/)
|
||||
assert.match(nodes, /createdResources\.phase4CarrierPickupReady/)
|
||||
assert.match(nodes, /重复当前实际节点精确返回A0409/)
|
||||
assert.match(nodes, /更早实际节点精确返回A0409/)
|
||||
assert.match(nodes, /operation claim零残留/)
|
||||
assert.match(nodes, /task、node、history零漂移/)
|
||||
assert.match(nodes, /actualProgressSnapshot/)
|
||||
assert.match(pda, /targetContainerNo[\s\S]*phase3Loading\?\.containerNo/)
|
||||
assert.match(pda, /id\(carrierQuery\?\.trackingNo\) === targetContainerNo/)
|
||||
assert.match(pda, /seen\.has\(targetContainerNo\)/)
|
||||
|
||||
@@ -20,8 +20,8 @@ const MARKER = 'T-PHASE4'
|
||||
function ok(data, requestId = 'req-ok') {
|
||||
return { http: 200, body: { code: '200', data, requestId }, requestId, traceId: 'trace-ok' }
|
||||
}
|
||||
function conflict(requestId) {
|
||||
return { http: 200, body: { code: 'A0409', message: 'conflict', requestId }, requestId, traceId: 'trace-conflict' }
|
||||
function conflict(requestId, message = 'conflict') {
|
||||
return { http: 200, body: { code: 'A0409', message, requestId }, requestId, traceId: 'trace-conflict' }
|
||||
}
|
||||
function clone(value) { return structuredClone(value) }
|
||||
|
||||
@@ -121,7 +121,7 @@ test('missing phase3 outbound query is an explicit deployment blocker and perfor
|
||||
|
||||
function markerOwnedContext() {
|
||||
let nextId = 9007199254740994000n
|
||||
const calls = []; const steps = []; const idempotency = new Map()
|
||||
const calls = []; const steps = []; const assertions = []; const idempotency = new Map()
|
||||
const task = {
|
||||
id: QUERY_ID, queryNo: `CSQ${QUERY_ID}`, customerId: CUSTOMER_ID, oceanBookingId: OCEAN_ID,
|
||||
containerBookingId: CONTAINER_ID, carrierCode: 'ZIM', trackingType: 'CONTAINER_NO',
|
||||
@@ -179,6 +179,11 @@ function markerOwnedContext() {
|
||||
: conflict(`req-hash-${key}`)
|
||||
}
|
||||
if (Number(body.expectedVersion) !== Number(task.version)) return conflict(`req-version-${key}`)
|
||||
if (body.actualAt != null
|
||||
&& NODE_SEQUENCES[body.standardNode] <= NODE_SEQUENCES[task.latestActualNode]) {
|
||||
return conflict(`req-monotonic-${key}`,
|
||||
`人工实际节点必须严格晚于当前最新实际节点:${task.latestActualNode}`)
|
||||
}
|
||||
const providerEventId = `manual-${String(nextId)}`
|
||||
nodes.push({
|
||||
id: String(nextId++), providerCode: 'MANUAL', providerEventId, providerNodeCode: body.standardNode,
|
||||
@@ -203,13 +208,16 @@ function markerOwnedContext() {
|
||||
apiSuccess: (_module, _name, response) => {
|
||||
assert.equal(response.http, 200); assert.equal(response.body.code, '200'); return response.body.data
|
||||
},
|
||||
assert: (_module, name, _response, condition, evidence) => assert.ok(condition, `${name}: ${evidence}`),
|
||||
assert: (_module, name, response, condition, evidence, options) => {
|
||||
assertions.push({ name, passed: Boolean(condition), evidence, options, requestId: response?.requestId || null })
|
||||
assert.ok(condition, `${name}: ${evidence}`)
|
||||
},
|
||||
addStep: (_module, name, response, passed, evidence, options) => {
|
||||
steps.push({ name, passed, evidence, options, requestId: response?.requestId || null })
|
||||
assert.ok(passed, `${name}: ${evidence}`)
|
||||
},
|
||||
}
|
||||
return { context, task, nodes, exceptions, histories, steps }
|
||||
return { context, task, nodes, exceptions, histories, steps, assertions }
|
||||
}
|
||||
|
||||
test('marker-owned chain covers idempotency conflicts, all actual nodes, exception DTO and empty-return stop', async () => {
|
||||
@@ -218,11 +226,15 @@ test('marker-owned chain covers idempotency conflicts, all actual nodes, excepti
|
||||
await run(fixture.context)
|
||||
|
||||
const posts = fixture.context.calls.filter((item) => item.method === 'POST')
|
||||
assert.equal(posts.length, 14)
|
||||
assert.equal(posts.length, 17)
|
||||
assert.ok(posts.every((item) => item.path === `${BASE}/${QUERY_ID}/manual-nodes`))
|
||||
assert.ok(posts.every((item) => item.headers['Idempotency-Key']))
|
||||
assert.ok(posts.every((item) => Object.keys(item.body).length === 8))
|
||||
assert.equal(fixture.steps.filter((item) => /A0409/.test(item.name)).length, 2)
|
||||
assert.equal(fixture.steps.filter((item) => /A0409/.test(item.name)).length, 6)
|
||||
assert.ok(fixture.assertions.some((item) => /重复当前实际节点A0409后task、node、history零漂移/.test(item.name)))
|
||||
assert.ok(fixture.assertions.some((item) => /更早实际节点A0409后task、node、history零漂移/.test(item.name)))
|
||||
assert.ok(fixture.steps.some((item) => /同级actual失败请求同键重试.*operation claim零残留/.test(item.name)))
|
||||
assert.ok(fixture.steps.some((item) => /更早actual失败请求同键重试.*operation claim零残留/.test(item.name)))
|
||||
assert.equal(fixture.task.status, 'STOPPED')
|
||||
assert.equal(fixture.task.latestActualNode, 'EMPTY_RETURNED')
|
||||
assert.equal(fixture.task.stopReason, '已还柜')
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -82,6 +82,27 @@ test('PDA module covers the container-number receiving contract and excludes out
|
||||
assert.doesNotMatch(source, /awaitProducerCandidates/)
|
||||
})
|
||||
|
||||
test('PDA receiving lifecycle is ordered from candidate claim through photos, mode-specific receipts, pallet and complete', () => {
|
||||
const orderedTokens = [
|
||||
"'查询当前客户待收柜号'", "'选择柜号并建立WAIT_SIGN会话'",
|
||||
'uploadPhotos(context, token)', "'绑定车牌封签开柜三角色照片并签收'",
|
||||
"`解析${inbound.destinationType}实物托盘`", 'receiveFba(context, token',
|
||||
'receiveOverseas(context, token', "'回查柜内逐入仓码实收进度'",
|
||||
"'完成当前柜并写入库存与卸柜事件'", "'完成后current返回404正常空态'",
|
||||
]
|
||||
let cursor = -1
|
||||
for (const token of orderedTokens) {
|
||||
const next = source.indexOf(token, cursor + 1)
|
||||
assert.ok(next > cursor, `${orderedTokens[Math.max(0, orderedTokens.indexOf(token) - 1)]} -> ${token}`)
|
||||
cursor = next
|
||||
}
|
||||
for (const token of [
|
||||
'/current/receipts/bulk', '/current/receipts/carton',
|
||||
"'FBA模式硬拒绝逐箱收货'", "'OVERSEAS模式硬拒绝批量收货'",
|
||||
'PHOTO_ROLES.every', "finalStatus === 'COMPLETED'", 'completedContainers.push',
|
||||
]) assert.ok(source.includes(token), token)
|
||||
})
|
||||
|
||||
test('missing WAREHOUSE_PICKER overseas authority is a deployment blocker', async () => {
|
||||
const calls = []; const gaps = []; const steps = []
|
||||
const context = {
|
||||
|
||||
@@ -50,6 +50,14 @@ function Assert-Path([string]$relativePath) {
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-PathMissing([string]$relativePath) {
|
||||
$script:checked++
|
||||
$absolutePath = Join-Path $workspaceRoot $relativePath
|
||||
if (Test-Path -LiteralPath $absolutePath) {
|
||||
Add-Failure "已移除入口仍然存在: $relativePath"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-MarkdownTableUnique([string]$path, [string]$heading, [int]$keyColumn, [string]$scope) {
|
||||
$script:checked++
|
||||
$lines = Get-Content -LiteralPath $path
|
||||
@@ -91,6 +99,7 @@ Assert-Contains $rootAgents '源码结构、接口与 DTO、字段、状态机
|
||||
Assert-Contains $rootAgents '数据库记录、上传文件、缓存、会话、消息、搜索索引和其他运行状态均视为可丢弃' '根开发运行状态范围'
|
||||
Assert-Contains $engineeringConstraints '禁止为旧代码、旧调用或开发环境存量保留旧接口/旧字段别名' '工程无兼容执行规则'
|
||||
Assert-Contains $engineeringConstraints '不授权自动重置数据库、删除数据/文件、执行 SQL、部署或修改环境' '无兼容安全边界'
|
||||
Assert-Contains $clientAgents '不得继续扩展 `LEGACY_MANUAL`、旧系统入仓码、C02 不可用回退旧 API' 'Client 头程无兼容边界'
|
||||
|
||||
# 部署单元必须能从根索引进入。
|
||||
Get-ChildItem -LiteralPath (Join-Path $workspaceRoot 'apps') -Directory |
|
||||
@@ -221,6 +230,40 @@ Assert-NotContains $domesticPdaSession 'apiBase: session.apiBase' '国内 PDA
|
||||
$commonWebAutoConfigurations = Read-RequiredText (Join-Path $backendRoot 'common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports')
|
||||
Assert-Contains $commonWebAutoConfigurations 'com.baiyue.common.web.RestClientTracePropagationAutoConfiguration' 'RestClient Trace 自动配置注册'
|
||||
|
||||
# 全部仓库主档统一归国内 Warehouse;海外服务只保存 warehouseId 引用和物理资源。
|
||||
$warehouseAgents = Read-RequiredText (Join-Path $backendRoot 'warehouse/AGENTS.md')
|
||||
$overseasWarehouseAgents = Read-RequiredText (Join-Path $backendRoot 'warehouse-overseas-service/AGENTS.md')
|
||||
$domesticWarehouseService = Read-RequiredText (Join-Path $backendRoot 'warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseApplicationService.java')
|
||||
$domesticWarehouseController = Read-RequiredText (Join-Path $backendRoot 'warehouse/src/main/java/com/baiyue/warehouse/controller/InternalWarehouseController.java')
|
||||
$overseasWarehouseSql = Read-RequiredText (Join-Path $backendRoot 'warehouse-overseas-service/src/main/resources/db/warehouse.sql')
|
||||
$adminIamApi = Read-RequiredText (Join-Path $workspaceRoot 'apps/admin-web/src/modules/iam/api/platformIam.ts')
|
||||
Assert-Contains $backendAgents '全部仓库 ID、类型、状态、地址和能力主档归国内 `warehouse`' '后端仓库主档权威'
|
||||
Assert-Contains $warehouseAgents '/internal/warehouses/shared-overseas' '国内 Warehouse 共享海外目录'
|
||||
Assert-Contains $overseasWarehouseAgents '只保存国内仓库 ID 引用' '海外 Warehouse 引用边界'
|
||||
Assert-Contains $domesticWarehouseController '@GetMapping("/shared-overseas")' '共享海外仓列表入口'
|
||||
Assert-Contains $domesticWarehouseService 'hasReceivingCapability' '共享海外收货能力筛选'
|
||||
Assert-Contains $adminIamApi "capabilityCode: 'RECEIVING'" 'Admin 海外收货仓候选'
|
||||
Assert-NotContains $overseasWarehouseSql 'CREATE TABLE IF NOT EXISTS wh_warehouse' '海外仓库主档表'
|
||||
Assert-NotContains $overseasWarehouseSql 'CREATE TABLE IF NOT EXISTS wh_warehouse_address' '海外仓库地址表'
|
||||
|
||||
# 国内出仓按真实货柜原子确认,船节点实际进度只允许严格向后推进。
|
||||
$outboundDispatchOpenApi = Read-RequiredText (Join-Path $backendRoot 'firstleg/src/main/resources/openapi/phase3-outbound-dispatch-v1.yaml')
|
||||
$carrierScheduleOpenApi = Read-RequiredText (Join-Path $backendRoot 'firstleg/src/main/resources/openapi/carrier-schedule-query-v1.yaml')
|
||||
$adminContainerDetail = Read-RequiredText (Join-Path $workspaceRoot 'apps/admin-web/src/modules/container-loading/pages/ContainerDetailView.vue')
|
||||
Assert-Contains $outboundDispatchOpenApi 'No partial success is possible.' '整柜出仓原子性'
|
||||
Assert-Contains $outboundDispatchOpenApi 'truckAppointmentStatus: {type: string, nullable: true, description: Reference-only status' '拖车状态非门禁语义'
|
||||
Assert-Contains $carrierScheduleOpenApi 'strictly greater standard-node sequence than latestActualNode' '手工实际节点单调推进'
|
||||
Assert-Contains $carrierScheduleOpenApi 'estimated facts never advance actual progress' '预计节点不推进实际进度'
|
||||
Assert-Contains $adminContainerDetail 'confirmDeparture' 'Admin 货柜详情确认离仓'
|
||||
|
||||
# 平台商品图片和海外 PDA 签收照片都必须走鉴权下载,不暴露内部存储路径。
|
||||
$productImageController = Read-RequiredText (Join-Path $backendRoot 'file/src/main/java/com/baiyue/file/controller/PlatformProductImageController.java')
|
||||
$receivingController = Read-RequiredText (Join-Path $backendRoot 'lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/ReceivingContainerController.java')
|
||||
Assert-Contains $productImageController '/api/platform/v1/files/product-images' '平台商品图片入口'
|
||||
Assert-Contains $productImageController "hasAuthority('platform:order:list')" '平台商品图片权限'
|
||||
Assert-Contains $receivingController '/current/unload-photos/files/{fileId}/download' '海外 PDA 签收照片预览'
|
||||
Assert-Contains $receivingController 'CacheControl.noStore()' '海外 PDA 签收照片缓存边界'
|
||||
|
||||
# Maven 聚合声明的全部历史/当前模块必须在后端根指引中可定位。
|
||||
[xml]$pom = Get-Content -LiteralPath (Join-Path $backendRoot 'pom.xml') -Raw
|
||||
foreach ($module in $pom.project.modules.module) {
|
||||
@@ -249,6 +292,7 @@ $criticalPaths = @(
|
||||
'apps/admin-web/src/modules/overseas-warehouse/pages/OutboundsView.vue',
|
||||
'apps/admin-web/src/modules/iam/pages/InternalUsersView.vue',
|
||||
'apps/admin-web/src/modules/iam/api/platformIam.ts',
|
||||
'apps/admin-web/src/modules/unified-orders/api/unifiedOrders.ts',
|
||||
'apps/client-web/src/api/overseasHttp.ts',
|
||||
'apps/client-web/src/modules/overseas-warehouse/routes.ts',
|
||||
'apps/client-web/src/modules/overseas-warehouse/api/warehouseServices.ts',
|
||||
@@ -264,6 +308,8 @@ $criticalPaths = @(
|
||||
'apps/pda-overseas-uniapp/src/pages.json',
|
||||
'apps/pda-overseas-uniapp/src/data/overseas-workbench.js',
|
||||
'apps/pda-overseas-uniapp/src/pages/operation/operation.vue',
|
||||
'apps/pda-overseas-uniapp/src/stores/receiving.js',
|
||||
'apps/pda-overseas-uniapp/tests/receiving-photo-ui-contract.test.mjs',
|
||||
'apps/pda-overseas-uniapp/src/config/runtime.js',
|
||||
'apps/pda-overseas-uniapp/src/api/auth.js',
|
||||
'apps/pda-overseas-uniapp/src/api/inbound.js',
|
||||
@@ -283,6 +329,12 @@ $criticalPaths = @(
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/mapper/DispatchDraftContainerMapper.java',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformSmartLoadingController.java',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerOverviewMapper.java',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/outbound/dispatch/controller/PlatformOutboundDispatchController.java',
|
||||
'baiyue-firstleg/firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml',
|
||||
'baiyue-firstleg/firstleg/src/main/resources/openapi/phase3-outbound-dispatch-v1.yaml',
|
||||
'baiyue-firstleg/firstleg/src/main/resources/openapi/carrier-schedule-query-v1.yaml',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingController.java',
|
||||
'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/mapper/OceanBookingMapper.java',
|
||||
'baiyue-firstleg/firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml',
|
||||
@@ -318,6 +370,7 @@ $criticalPaths = @(
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/security/service/LastlegSecurityValidationService.java',
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/ReceivingContainerController.java',
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java',
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/gateway/UnloadPhotoFileGateway.java',
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/pda/stocktake/controller/PdaStocktakeController.java',
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/pda/stocktake/service/PdaStocktakeService.java',
|
||||
'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/pda/move/controller/PdaInventoryMoveController.java',
|
||||
@@ -332,6 +385,11 @@ $criticalPaths = @(
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/InternalPdaPalletTransitionController.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/service/WarehousePalletTransitionService.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/entity/WarehousePalletOperationEntity.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/client/DomesticWarehouseDirectoryClient.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/service/DomesticWarehouseDirectory.java',
|
||||
'baiyue-firstleg/warehouse/src/main/java/com/baiyue/warehouse/controller/InternalWarehouseController.java',
|
||||
'baiyue-firstleg/warehouse/src/main/java/com/baiyue/warehouse/dto/SharedOverseasWarehouseResponse.java',
|
||||
'baiyue-firstleg/file/src/main/java/com/baiyue/file/controller/PlatformProductImageController.java',
|
||||
'baiyue-firstleg/lastleg-service/docs/lastleg-internal-modularization-baseline.md',
|
||||
'baiyue-firstleg/lastleg-service/scripts/analyze-lastleg-dependencies.mjs',
|
||||
'baiyue-firstleg/lastleg-service/scripts/analyze-lastleg-dependencies.test.mjs',
|
||||
@@ -367,6 +425,16 @@ $criticalPaths = @(
|
||||
)
|
||||
$criticalPaths | ForEach-Object { Assert-Path $_ }
|
||||
|
||||
foreach ($removedOverseasWarehouseMasterPath in @(
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/PlatformWarehouseController.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/MerchantWarehouseController.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/entity/WarehouseEntity.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/entity/WarehouseAddressEntity.java',
|
||||
'baiyue-firstleg/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/entity/ServiceCapabilityEntity.java'
|
||||
)) {
|
||||
Assert-PathMissing $removedOverseasWarehouseMasterPath
|
||||
}
|
||||
|
||||
# 只检查明确维护的快速索引表,避免把排除规则正文中的目录名误判为业务入口。
|
||||
$indexedAgentFiles = @(
|
||||
$rootAgentsPath,
|
||||
@@ -383,6 +451,7 @@ $indexedAgentFiles = @(
|
||||
(Join-Path $backendRoot 'common-web/AGENTS.md'),
|
||||
(Join-Path $backendRoot 'common-openapi/AGENTS.md'),
|
||||
(Join-Path $backendRoot 'warehouse-overseas-service/AGENTS.md'),
|
||||
(Join-Path $backendRoot 'warehouse/AGENTS.md'),
|
||||
(Join-Path $backendRoot 'event-bridge-service/AGENTS.md'),
|
||||
(Join-Path $backendRoot 'file/AGENTS.md'),
|
||||
(Join-Path $backendRoot 'common-event-contract/AGENTS.md')
|
||||
@@ -422,5 +491,5 @@ if ($failures.Count -gt 0) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "AGENTS 索引合同通过:$checked 项检查,覆盖快速表唯一性、部署单元、前端模块、Client active 路由、四期范围漂移、订舱候选与柜量语义、Gateway 覆盖、PDA 页面/运行配置/能力开关、Maven 模块、Lastleg 海外安全投影、同步 Trace、关键入口和排除目录。" -ForegroundColor Green
|
||||
Write-Host "AGENTS 索引合同通过:$checked 项检查,覆盖快速表唯一性、部署单元、前端模块、Client active 路由、四期范围漂移、仓库主档唯一权威、订舱候选与柜量语义、整柜出仓与船节点单调性、Gateway 覆盖、PDA 页面/运行配置/能力开关、Maven 模块、Lastleg 海外安全投影、同步 Trace、关键入口和排除目录。" -ForegroundColor Green
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user