Files
docs/三期/货柜管理全链验收矩阵-2026-08-13.md
T
2026-08-17 15:41:47 +08:00

196 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 货柜管理全链验收矩阵
> 更新时间:2026-08-15
> 当前任务:`019ff90b-a72d-7000-abdf-e5363357a564`
> 父任务:`019ff600-f9f1-78f1-a357-7c80c13e574e`
> 依赖任务:后端主链 `019ff90b-9b45-7191-b68f-c3b52567e3e4`、动态增量后端 `01a00e91-2124-7960-95d6-e0d0f81959f9`、Admin `019ff90b-a037-79b0-b945-8f88bdbde9c5`
> 边界:只维护根目录脚本、验收文档和测试矩阵;不修改 Admin 或 Java 业务源码,不部署、不触发 Jenkins、不写测试环境数据库。
## 验收口径
- 本矩阵以当前对话、三期产品确认版开发规划和实际冻结契约为准。旧确认单中的待确认项不覆盖后续已冻结结论。
- 已冻结能力按事实来源落具体路径和断言;尚未冻结能力继续只保留能力级占位,不预设 HTTP 方法、路径、DTO 字段或业务错误码。
- 自动化必须通过业务 API 生成数据;人工验收必须能从 Admin/PDA 可见入口点击到对应状态。不得以数据库直写、mock、原型或前端本地假状态代替。
- 每次真实 HTTP 验收记录请求用途、响应状态、业务码、`requestId`、`traceId`、关键资源 ID 和清理/保留策略;敏感凭据不得写入报告。
- 柜态看板不纳入本轮。
## 状态数据目录
| 数据代号 | 必备状态/形态 | 生成前置 | 自动化断言 | 人工可点击入口 | 当前状态 |
|---|---|---|---|---|---|
| CT-D01 | 线下已确认订舱登记 N 个真实柜 | 创建请求必须携带五项运输快照及 `containers[1..20]`;每柜唯一 `containerNo` 和固定规格 | 同一事务直接产生 `CONFIRMED`、N 个 quantity=1 requirement、N 个 `OCEAN_BOOKING/ACTIVE` 操作柜及 N 个 allocation;任一柜失败整单零写入 | 订舱和货柜列表均可逐柜打开 | 已冻结,待环境验收 |
| CT-D02 | 不同渠道各一单 | 渠道主数据可用 | 柜次/清单/任务不得跨渠道混装或串查 | 排柜候选可按渠道辨识 | 待契约 |
| CT-D03 | 内部模拟柜已排货、已 push,但无真实柜号/订舱 | 正式装柜仓库、ACTIVE 物流渠道、箱型和容量可用 | 服务端生成稳定 `containerBookingId/cabinetNo`;同仓同渠道 WAIT_SPLIT 可排;无号 `push-preload` 成功并冻结明细、建立唯一 bridge、使预装 `EDITING` 可见,但 PDA 查询必须为 `A0404` | 货柜列表可新增模拟柜,预装清单显示待关联/待补号 | 新时序已冻结,待环境验收 |
| CT-D04 | 模拟柜合并真实柜、订舱放舱且预装已确认,PDA 可领取 | CT-D03;选择未消费 `OCEAN_BOOKING/ACTIVE` 真实柜合并;订舱进入 `RELEASED`;预装进入 `CONFIRMED` | link 将真实柜号/订舱/规格快照原子复制到原模拟柜,任务四元身份只使用存活的模拟柜 ID;三项前置任缺一项都无 PDA,claim 后才进入 `LOADING` | PDA 任务列表可进入 | 新合并契约已冻结,待环境验收 |
| CT-D05 | 作业中更换真实柜号 | CT-D04,PDA 已开始 | 柜号变化但柜次 ID、任务、清单、证据归属不漂移 | 详情与 PDA 同步显示新柜号 | 待契约 |
| CT-D06 | 预装清单已生成/确认 | CT-D03 | push 后 `EDITING` 清单可补真实柜号;订舱 `RELEASED` 后通过计划级 `/loading-plans/{loadingPlanId}/confirm` 原子确认全部关联预装票据,返回 `confirmationStatus=CONFIRMED`、`readyForPda=true` 和唯一 `pdaTaskId`;版本、明细、数量及确认历史可追溯 | 预装清单列表/详情可点击 | 计划级确认已冻结,待环境验收 |
| CT-D07 | 装柜清单进行中/已完成 | CT-D04 | 装载明细、数量、状态与预装来源一致 | 装柜清单列表/详情可点击 | 待契约 |
| CT-D08 | PDA 领取、拣货、复核、异常摘要 | CT-D04 | overview 分别返回 `taskStatus`、`workflowStatus`、任务 ID、版本、计数、`photoCounts`、`photos`、`downloadPath` 和异常原因;逐照片代理下载成功 | PDA 可完成主流程;Admin 入口待回传 | 服务端已冻结;人工入口待定 |
| CT-D09 | 报关/清关资料及有效回执 | CT-D06 | 目的港清关走货柜 `/clearance`;出口报关走 loading-list `/customs`;分别验证上传、完成和完成后替换锁定 | 报关协同入口待回传 | 服务端已冻结;人工入口待定 |
| CT-D10 | DO/PU 未齐、部分完成、全部完成 | 柜次与运输协同已建立 | `DELIVERY_ORDER`、`PICKUP_ORDER` 可独立查询、上传和鉴权下载,版本与幂等有效 | 柜次详情入口待回传 | 服务端已冻结;人工入口待定 |
| CT-D11 | 正常、异常、取消异常 | 任意有效柜次及显式货柜版本 | 标记/取消使用版本和必填原因;overview 保留 `MARK`/`CANCEL` 全部历史 | 货柜详情入口待回传 | 服务端已冻结;人工入口待定 |
| CT-D12 | 多节点运输轨迹与操作日志 | CT-D10 | overview 时间线含运输更新、异常和 PDA 事件,按发生时间升序 | 详情时间线入口待回传 | 服务端聚合已冻结;人工入口待定 |
| CT-D13 | 满足全部 `LOADED` 硬门禁 | 订舱、拖车、有效放行回执、PDA 拣货、五类证据完成 | PDA `complete` 写前预检 inbound `LOADING`、bridge、订舱 `RELEASED`、同稳定 `containerBookingId` 的拖车 `COMPLETED` 和五类 ACTIVE 证据;缺项 A0409 且 task/plan/inbound 零写入,齐备后同一事务完成三者并推进 `LOADED` | 订单/柜次详情显示完成且编辑锁定 | `LANDED_STATIC`:OpenAPI 与 HTTP 脚本已接线;待部署后真实 HTTP 复核 |
| CT-D14 | PDA 未最终完成前向同一预装计划增加入库单 | 已有稳定 `loadingPlanId/taskId/containerBookingId`,任务尚未最终完成;新增票满足同客户、仓库、渠道、容量与柜型约束 | `EDITING/LOADING` 可增量;`LOADING` 不回退且复用同一 task;plan/task/item 版本递增,旧 inventory token A0409 后必须刷新;全拣后增项将 `AWAITING_REVIEW` 撤回 `IN_PROGRESS`,证据保留;未拣项可删,已拣数量形成 `minPackageCount` | Admin 预装增量入口与 PDA 同步显示新增项 | `LANDED_STATIC`:assign/adjust/delete 与 capability DTO 已冻结并接入脚本;待部署后真实 HTTP 复核 |
## 全链验收用例
| 用例 | 场景 | 服务端/契约断言 | UI/设备断言 | 证据要求 | 状态 |
|---|---|---|---|---|---|
| CT-E01 | 模拟柜原地合并真实订舱柜 | 订舱创建的真实柜先以 `OCEAN_BOOKING/ACTIVE` 出现在货柜列表;link 请求只传 `realContainerBookingId+expectedVersion`,成功后模拟柜 ID/cabinetNo/计划/明细稳定,来源变 `MERGED` 且 allocation 改指模拟柜 | 货柜详情只显示合并后的一个可操作身份 | link 前后柜次、计划、明细、allocation 及 ACTIVE 列表唯一性快照 | 已冻结,待环境验收 |
| CT-E02 | 渠道隔离 | 不同渠道不能进入同一排柜结果;交叉 ID 请求失败 | 候选、清单和详情不串渠道 | 双渠道正向及交叉负向响应 | 待契约 |
| CT-E03 | 无真实柜号排柜与 push | 临时身份可完成排柜;`push-preload` 在无 `containerNo` 时成功,只冻结计划、建立 bridge、开放 `EDITING` 预装,不创建 PDA;exact replay 原样返回且计划/版本/历史/link/task 零漂移 | 页面明确展示待补号,不伪造柜号 | push 原始 path/body/key、重放前后全快照、PDA `A0404` | 新时序已冻结,待环境验收 |
| CT-E04 | 合并及三项前置开放 PDA | 无号 push 后仍无 PDA;未合并真实柜、订舱未 `RELEASED`、预装未 `CONFIRMED` 分别查询 PDA 均为 `A0404`;合并本身带入唯一真实柜号,最后一个前置完成后仅生成一个 task,claim 才推进 `LOADING` | 关联、放舱、确认前后 PDA 入口状态同步 | 三个逐项负向、唯一 task 四元身份、禁止 MERGED 来源 ID、claim 前后状态 | 新合并契约已冻结,待环境验收 |
| CT-E05 | 作业中换号身份不漂移 | 只变更展示柜号;柜次 ID、任务 ID、清单、证据关联不变 | Admin/PDA 刷新后显示新号且进度不丢 | 换号前后关联快照 | 待契约 |
| CT-E06 | 预装/装柜清单 | push 自动生成/关联预装聚合;仅允许按 `loadingPlanId` 原子确认整份计划,禁止旧单票 `/preload/confirming`、`/preload/confirm`;返回唯一 task 后 claim 才进入 `LOADING` | 两类列表和详情均可人工点击 | `PENDING -> EDITING -> CONFIRMED`、计划版本、readiness、taskId、幂等重放与历史 | 计划级确认已冻结,待环境验收 |
| CT-E07 | PDA 主链 | 领取、扫码、批量提交、复核、完成;幂等与版本冲突有效 | 真机/目标设备可完成,失败不假成功 | 请求链、设备记录 | 待契约 |
| CT-E08 | PDA 进度异常摘要 | `GET /api/platform/v1/containers/{id}/overview` 返回 PDA `taskStatus/workflowStatus`、任务身份、版本、计数、照片摘要、文件 ID 与鉴权下载路径;每个路径真实 GET 200 | Admin 入口待回传 | overview 响应、字段完整性及二进制下载证据 | 服务端契约已冻结 |
| CT-E09 | 清关/报关资料闭环 | 目的港清关:货柜 `/clearance` 查询/上传/完成;出口报关:loading-list `/customs` 查询/上传/完成;两者完成后均禁止替换 | Admin 入口待回传 | 两条专用链路的版本、幂等、完成和锁定负向响应 | 服务端契约已冻结 |
| CT-E10 | DO/PU | 通用查询、multipart 上传、鉴权下载;上传要求 `Idempotency-Key`、真实文件、`expectedVersion`,重放不得重复上传 | Admin 入口待回传 | 两类型响应、文件哈希/ID、二进制下载、幂等重放 | 服务端契约已冻结 |
| CT-E11 | 货柜异常及取消历史 | `POST/DELETE /api/platform/v1/containers/{id}/exception` 要求 `expectedVersion` 和必填原因;冲突失败;overview 历史只追加不覆盖 | Admin 入口待回传 | 标记/取消响应、版本冲突、overview 历史 | 服务端契约已冻结;版本来源阻断 |
| CT-E12 | 运输轨迹/日志 | overview `timeline` 聚合 `TRANSPORT_UPDATE`、`MARK/CANCEL`、`PDA_*`,时间升序且含详情、操作者和发生时间 | Admin 入口待回传 | overview 时间线响应 | 服务端契约已冻结 |
| CT-E13 | `LOADED` 正向与逐项负向 | 五项硬门禁分别缺失时 fail-closed;拖车按稳定 `containerBookingId` 归属,历史 `loadingPlanId` 为空不再构成错误门禁;正式拖车完成后才允许 PDA `complete` 迁移一次 | PDA 完成页、入库单和订单详情不得出现无解释的完成/`LOADING` 分裂 | 逐项负向 + 1 个正向 + 幂等重放;记录拖车 ID、柜次 ID、计划 ID、状态、业务码、`requestId/traceId` | `LANDED_STATIC`:失败零写入和最终同事务响应已锁定;待部署后真实 HTTP 复核 |
| CT-E14 | 跨客户与跨仓库隔离 | 交叉资源 ID 不可读写下载,不泄露资源存在性 | 页面无越权入口 | 双身份交叉请求证据 | 待契约 |
| CT-E15 | push 幂等、冲突与事务回滚 | 首次成功请求冻结 path/body/expectedVersion/key;同 key 原样重放只读且返回首次结果;同 key 异参、新 key 重复均冲突;各负向后计划、版本、历史、bridge、PDA 全快照零漂移;首次 push 在 bridge 中途失败时整事务零残留且不得触发 PDA | 冲突或失败后页面刷新仍只显示首次成功事实 | 原始请求、三次回查全快照、失败 `requestId/traceId`;中途失败由后端事务合同覆盖 | 新时序合同已冻结,待环境验收 |
| CT-E16 | 默认 Admin HTTP 同 marker 完整链 | 默认入口按 `phase3-container-loading -> phase3-loading -> overview/PDA/evidence -> customs -> loading-finalize` 串行;下游只消费 `phase3Loading` 发布的 `taskId/inboundId/loadingPlanId/containerBookingId`,最终同一订单详情和列表均为 `LOADED` | 同一批次可从订单、柜次、预装、PDA、报关入口逐级回查 | 全新 marker、关键资源 ID、五类二进制证据、各请求 `requestId/traceId`、最终统一订单状态 | 静态合同已接入默认入口,待环境 HTTP |
| CT-E17 | 订舱代理不区分国内/国外 | 供应商必须为正式 `supplierId` 对应的 `ACTIVE + BOOKING` 记录,名称使用服务端快照;查询、创建和内部快照校验不得携带或要求 `DOMESTIC/INTERNATIONAL scope`,但类别或启用状态不符仍 fail-closed | 订舱代理候选不显示国内/国外范围门禁 | fixture 请求、Warehouse 内部快照、Firstleg 类别/启用校验及 Admin 候选静态合同;部署后补真实 HTTP | Warehouse/Firstleg 共享改动已可见;Admin 与环境 HTTP 待收口 |
| CT-E18 | PDA 进行中增量预装与最终完成竞态 | 领取后、最终完成前新增入库单必须复用同一 task;新项出现后撤回最终完成门禁,旧 token 返回 A0409“库存已变化,请刷新后重试”且零写入;未拣项可删除,已拣项删除或减量拒绝;Admin 增量与 PDA final complete 同锁 task 行,只允许一个事务成功;`COMPLETED` 后全部变更拒绝 | Admin 增量结果与 PDA 队列实时一致,失败提示不伪造完成 | 增删前后计划/task/items/evidence 投影、版本和操作历史;并发双方响应及最终唯一事实 | `LANDED_STATIC`:OpenAPI、HTTP 双请求 winner/loser 脚本及离线合同已接线;待部署后定向复核 |
## PDA 完成后未进入 LOADED 的已登记回归
- 真实测试样本:订单 `NYEXX2608170001`;PDA task、review、装柜计划与五类证据均已完成,但 inbound 仍为 `LOADING`。
- 门禁只读证据:`ocean_ready=1`、`truck_ready=0`;同柜拖车预约 `TA2089239590749704194` 的 `loading_plan_id=NULL`、状态为 `LOADING`。
- 当前冻结事实:`phase3-loading` 只完成 claim/batch/证据与动态增量,任务保持 `LOADING/AWAITING_REVIEW`;下游门禁齐备后,`phase3-loading-finalize` 调用 `POST /pda-loading/tasks/{taskId}/complete`。该调用在同一 Firstleg 事务完成 task/plan/projection/inbound 并推进 `LOADED`,不再调用旧 `/inbounds/{id}/loading/complete`。
- 拖车归属统一使用稳定 `containerBookingId`,不要求历史 `loadingPlanId` 非空;新建/编辑由服务端解析并冻结计划。缺拖车、未完成或归属不匹配均 A0409,且 task/plan/inbound 零写入。
- 部署后回归包:① PDA 完成响应与 task/inbound/order 回查一致;② 无拖车、拖车未完成、拖车归属不匹配分别返回明确业务证据并保持 `LOADING`;③ 正式关联拖车完成后 PDA `complete` 推进 `LOADED`;④ 历史空 `loadingPlanId` 按 `containerBookingId` 正式口径覆盖正反例;⑤ 失败分支保留 path、HTTP、业务码、message、`requestId/traceId`,且不得继续执行任何仅限 `LOADED` 的突变请求。
## PDA 进行中增量预装冻结回归
- 冻结来源仅跟踪动态增量后端任务 `01a00e91-2124-7960-95d6-e0d0f81959f9` 落盘的 SmartLoading assign/adjust/remove、capability DTO/OpenAPI 和版本并发契约;不再向后端主链任务重复询问动态字段。
- 用户确认:预装清单在 PDA 尚未最终确认装柜完成前允许继续添加入库单,并由原有同一 PDA task 继续拣货;不得通过新建第二个 task 或替换稳定四元身份实现。
- 必测时序:`EDITING` 添加;task 已领取且仍未完成时添加;新增项进入同一 `taskId + inboundId + loadingPlanId + containerBookingId` 工作集;计划/task/item 版本单调递增;旧 inventory token 返回正式版本冲突并零写入;刷新后可拣新增项并再次满足完成条件。
- 门禁撤回:新增未拣项后,原先的 ready、完成度及五类证据完成投影不得继续允许 final complete;新增项全部拣完并重新满足正式证据/下游条件后才恢复。
- 删除与减量:已拣项删除或减量必须拒绝且全链零漂移;未拣项删除成功后同步 task、计划、库存快照与 readiness。正式写端点为 plan assignments、item PATCH、item DELETE,分别使用 planVersion/itemVersion 与 operationId。
- 并发与终态:Admin 增量与 PDA final complete 并发只允许一个事务成功,失败方必须取得明确业务冲突证据;task/计划进入 `COMPLETED` 后,添加、删除、减量全部拒绝。
- 隔离基线不变:容量/限重、客户、仓库、物流渠道、柜型/规格和 ACTIVE 柜次归属继续 fail-closed;任何增量都不得借历史入库单、跨 marker 或创建第二个任务绕过。
- 默认全量接入:`phase3-loading` 已在首次 batch 与 final complete 之间接入本轮 marker 的独立入库单、旧 token 负向、未拣删除、重新加入、调整、刷新补拣和已拣删除/减量零写入;`phase3-pda-convergence` 回查稳定四元和唯一任务,最后由 `phase3-loading-finalize` 并发提交同数量调整与 final complete,断言恰好一方成功、刷新胜方版本后唯一收敛 `LOADED`,并验证 `COMPLETED` 后全部写拒绝。真实 HTTP 待部署后定向执行。
## P3-Q14 文档同步待总控统一
- 当前冻结验收时序以用户最新确认的“两类柜并存”为准:订舱逐柜登记会生成可直接排货的 `OCEAN_BOOKING/ACTIVE` 真实柜;未订舱时也可先建 `SIMULATION/ACTIVE` 模拟柜并排货、无号 push,之后按 `realContainerBookingId` 原地合并一个未消费真实柜。
- `docs/三期/三期需求确认表.md` 的 P3-Q14 尚未完整描述 `SIMULATION/ACTIVE -> 合并 -> OCEAN_BOOKING/MERGED` 的来源/状态模型。本任务不擅改需求确认表,标记为 `WAIT_CONTROLLER_DOC_SYNC`,由总控统一文档;本矩阵及自动化以当前冻结 DTO/OpenAPI 为准。
- P3-Q14 关于“五项运输快照在订舱创建时冻结、`RELEASED` 不覆盖首次快照”的部分无冲突,继续作为验收基线。
## 契约冻结清单
### 既有冻结基线
以下能力在本轮新增 overview 契约之前已经由三期产品确认版、既有 OpenAPI/Controller 和专项测试冻结。本轮不重定义其路径或状态机。
| 能力标识 | 冻结边界 | 事实来源 | 状态 |
|---|---|---|---|
| `booking-real-containers-and-simulation-merge` | 订舱按 `containers[]` 原子生成真实柜;模拟柜是独立预排货能力;link 消费一个真实柜并保留模拟柜稳定身份,正常列表只返回 ACTIVE | `PlatformContainerController`、`ContainerService`、`OceanBookingService`、`firstleg-v1.yaml`、`ocean-bookings-v1.yaml` | 本轮目标冻结,待环境验收 |
| `channel-isolation` | 渠道隔离与禁止跨渠道混装 | 三期产品确认版及既有排柜契约 | 既有冻结,待环境验收 |
| `container-number-lifecycle` | 模拟柜无真实柜号允许排柜及 push;push 本身不开放 PDA;link 从未消费真实柜带入唯一 `containerNo`,内部柜、计划、bridge 身份稳定;作业中换号身份稳定 | `ContainerService`、`SmartLoadingService`、`SplitPreloadService`、`LoadingListService`、相关 OpenAPI 与合同测试 | 新时序已冻结,待环境验收 |
| `preload-and-loading-list` | `PENDING -> EDITING -> CONFIRMED`;计划级确认原子确认全部关联票据并唯一建 task;存活 ACTIVE 柜已合并 `RELEASED` 订舱、真实柜号和 readiness 全部满足后 `readyForPda=true`,claim 才推进 `LOADING` | `LoadingListService.confirmPreload`、`LoadingPickService`、`POST /loading-plans/{planId}/confirm` 及 Loading List OpenAPI/DTO | 计划级确认已冻结,待环境验收 |
| `customs-documents-lock` | 清关/报关专用流程、完成态禁止替换 | 既有 `LoadingListService`、专用 Controller 与专项测试 | 既有冻结,待环境验收 |
### 本轮新增冻结契约
事实来源:`baiyue-firstleg/firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml`、`PlatformContainerController`、`ContainerOverviewDtos`、`ContainerOverviewService`、`LoadingListService`。
| 能力标识 | 冻结路径与字段 | 权限/并发 | 状态 |
|---|---|---|---|
| `container-overview` | `GET /api/platform/v1/containers/{id}/overview`;聚合 container、loadingPlanId、PDA 摘要、documents、exception/history、timeline | `platform:firstleg:container:list`;旧 `container:view` 已移除 | 已冻结 |
| `pda-progress-exception-summary` | PDA `taskStatus/workflowStatus`、任务 ID/版本、明细计数、`photoCounts`、`photos`(含 fileId/status/downloadPath)及异常原因;照片下载为 `GET /api/platform/v1/containers/{id}/pda/photos/{fileId}/download` | 随 overview 查看权限;下载复核柜次、计划、入库单与文件归属 | 已冻结 |
| `container-abnormal-history` | `POST/DELETE /api/platform/v1/containers/{id}/exception`;请求使用 overview 返回的 container.version、必填原因和幂等键 | `platform:firstleg:container:edit`;版本冲突 fail-closed | 已冻结 |
| `container-documents` | 通用查询、multipart 上传和受控文件下载仅支持 DO/PU;下载透传已校验 Content-Type,非法类型回退 `application/octet-stream`,并返回 UTF-8 文件名、`no-store` 和内容长度 | 查询/下载用 `platform:firstleg:container:list`,上传用 `platform:firstleg:container:edit`;旧 `container:view` 已移除;下载校验柜次、类型、文件 ID 及文件服务 tenant/business 归属 | 已冻结 |
| `do-pu` | `DELIVERY_ORDER`、`PICKUP_ORDER` 通用查询/上传/下载 | 同 `container-documents` | 已冻结 |
| `transport-trajectory-log` | overview timeline 的事件类型、详情、操作者、发生时间;服务端升序 | 随 overview 查看权限 | 已冻结 |
### 最终安全分流
1. overview 与通用 documents 只覆盖 `DELIVERY_ORDER`、`PICKUP_ORDER`,不得向通用路径传清关或报关类型。
2. 目的港清关使用 `/api/platform/v1/containers/{id}/clearance` 及其专用上传、完成动作,权限为 `platform:firstleg:customs:manage`。
3. 出口报关使用 `/api/platform/v1/first-leg/loading-lists/{planId}/customs` 及其专用上传、完成动作;查询使用 `platform:firstleg:loading-list:list`,旧 `loading-list:view` 已移除,写入使用 `platform:firstleg:customs:manage`。
4. 清关和出口报关分别验证完成态禁止替换,不能用 DO/PU 通用上传替代。
5. 下载允许透传经校验的 Content-Type,非法值回退二进制类型;同时必须保留 UTF-8 文件名、`no-store`、内容长度及资源归属复核。
## 自动化落地门禁
真实环境脚本只有在对应能力“已冻结=是”后才能加入请求步骤,并必须满足:
1. 环境地址、账号和令牌只从环境变量读取。
2. 默认不执行;必须通过显式运行参数开启真实 HTTP。
3. 只通过业务 API 造数,不连接数据库,不调用 Jenkins。
4. 支持按本次运行标识检索资源,避免误用历史数据;清理动作仅在契约明确且安全时启用。
5. 输出脱敏 JSON 报告,逐请求记录 HTTP 状态、业务码、`requestId`、`traceId` 和资源 ID。
6. 对未冻结能力或冻结来源不一致输出 `BLOCKED_CONTRACT`,不得以跳过后仍报告全链通过。
## 当前阻断
| 阻断 | 影响 | 解除条件 |
|---|---|---|
| 测试环境未获得部署授权且本任务禁止部署 | 无法验证环境是否已包含依赖实现 | 人工部署明确提交后,再执行获授权的验收轮次 |
| 未提供测试环境凭据和两组隔离身份 | 无法执行跨客户/跨仓库/下载鉴权负向用例 | 由总控提供临时验收身份或既有安全注入方式 |
## Marker 回归增量(ADMIN-API-20260814032734876)
| 回归项 | 当前冻结事实与自动化断言 | 当前结论 |
|---|---|---|
| Release 正式 DTO | `POST /api/platform/v1/ocean-bookings/{id}/release` 已冻结;`ReleaseRequest` 必填提单号、船名、航次、ETD、ETA,并保留 `expectedVersion`。HTTP fixture 必须携带五字段;Admin 未填齐不得调用 release | `LANDED`,不是路径阻断 |
| PDA overview 状态语义 | PDA 任务的 `taskStatus=LOADING` 与业务流转 `workflowStatus=AWAITING_REVIEW` 必须分字段返回;进度 100%、未解决数为 0 且任务仍为 `LOADING` 时必须明确为 `AWAITING_REVIEW` | 后端 DTO/OpenAPI/聚合已回传;`LANDED`,待环境 HTTP 验收 |
| PDA overview 照片下载 | 对 `photos[]` 每个服务端 `downloadPath` 执行真实鉴权 GET;仅接受站内 `/api/` 路径、HTTP 200、非空二进制和非 JSON/HTML Content-Type | HTTP 回归已准备;待获授权环境验收 |
| 客户公开名称一致性 | `customerId` 筛选后的订舱、拖车和预装列表/详情必须显示 IAM `tenantName`,不得优先 `legalName`;列表、详情及关联名称一致 | 列表筛选与拖车预约新增表单已统一 `tenantName`;`LANDED`,待人工筛选回归 |
| 已有 `containerNo` 动作语义 | 已有真实柜号时补号动作不可伪装成首次补号;换号前后柜次 ID、PDA taskId、loadingPlanId 和证据归属必须稳定 | Admin 已按柜号是否存在显示“录入集装箱号/修改集装箱号”,后端 HTTP fixture 已锁定身份稳定;`LANDED` |
| 订舱/拖车详情关联名称 | 详情中的客户、柜次、订舱及关联资源名称必须来自正式详情响应,并与同 ID 列表显示一致 | 后端详情已返回稳定名称字段,Admin 使用详情快照与受控候选回显;`LANDED` |
| 拖车附件闭环 | `POST /truck-appointments/{id}/attachments` 引用、`POST /truck-appointments/{id}/attachments/upload` 上传关联与 `DELETE /truck-appointments/{id}/attachments/{attachmentId}` 已冻结 | Admin 已提供选择文件、上传并关联、列表及删除入口,HTTP 删除后回查已覆盖;`LANDED` |
| 中文状态、弹框与 390px 布局 | 列表/详情状态必须中文;release、附件、柜号动作使用正式弹框;390px 下字段、按钮、弹框不横向溢出或遮挡 | `AWAITING_REVIEW`、照片类型、确认按钮已中文化,放舱/改船/附件弹框已落地;桌面与 390px 浏览器无横向溢出;`LANDED` |
说明:`BLOCKED_CONTRACT` 仅标记明确缺失的 DTO 字段或 Admin 承载,不把已存在的 release、拖车附件 API 路径降级为未冻结能力。
## 订舱物流渠道与柜次隔离增量
| 验收项 | 自动化断言 | 当前结论 |
|---|---|---|
| Admin 渠道选择 | 订舱新增/编辑不允许自由输入 `channelCode/channelName`;选择起运仓后使用渠道下拉,查询必须携带 `originWarehouseId` | `LANDED`:Admin 使用受控渠道下拉并调用正式平台渠道接口;Admin 与根静态合同均锁定渠道选择正向及自由输入负向 |
| 平台渠道目录 | `GET /api/platform/v1/ocean-bookings/channels?originWarehouseId=...` 只返回当前 ACTIVE 价格簿渠道;HTTP fixture 记录本轮 marker、`originWarehouseId` 与服务端路径,不借 Merchant 身份或历史资源 | OpenAPI 已冻结;真实查询生产者已发布 `createdResources.platformLogisticsChannels` |
| 双有效渠道 fixture | 跨渠道负向必须使用同一起运仓下两个真实有效渠道;只有一个有效渠道时输出 `BLOCKED_FIXTURE`,禁止派生 `-OTHER` 伪渠道 | 脚本门禁与正式渠道生产者已落地;待环境数据验证是否存在双渠道 |
| 船公司与物流渠道独立 | `shippingCompanyId/shippingCompanyName` 与渠道选择分别建模,不用船公司字段代替渠道字段 | `LANDED`:Admin 与写请求模型分别承载船公司和物流渠道,静态合同已转绿 |
| `CONFIRMED` 柜次继承渠道 | 订舱确认生成柜次后,货柜详情 `channelCode` 必须等于所选有效渠道;异渠道入库单不能分配到该柜次 | 既有 HTTP 断言保留;异渠道负向已移除伪渠道来源;写 DTO 不再发送服务端拥有的 `channelName` |
## 订舱资料字段与可选客户增量
事实来源:原型 `prototype-src/modules/06-containers.html`、P3-Q14、`OceanBookingDtos.DraftData`、`ocean-bookings-v1.yaml`、Admin `OceanBookingDetailView.vue` 与 `OceanBookingDraftData`。
| 字段/链路 | 原型与阶段时序 | 自动化断言 | 当前结论 |
|---|---|---|---|
| `serviceTerm` | 原型第 516 行以 `*` 标记主要运输条款必填;P3-Q14 未删除该业务快照 | Admin 必填自由文本;Java `@NotBlank`;OpenAPI `required` 且非 nullable;模块基线和统一 SQL 均为 `NOT NULL`;不保留无来源的 `CY-DOOR/DOOR-CY/DOOR-DOOR` 自造字典;验收 fixture 使用原型展示值 `CY-CY` | `LANDED` |
| `freightTerm` | 原型第 518 行以 `*` 标记运费条款必填;示例值为 `NINGBO`,不构成枚举事实来源 | Admin 必填自由文本;Java `@NotBlank`;OpenAPI `required` 且非 nullable;模块基线和统一 SQL 均为 `NOT NULL`;不保留无来源的 `PREPAID/COLLECT` 自造字典;验收 fixture 使用原型展示值 `NINGBO` | `LANDED` |
| 可选客户 | 原型第 527 行“客户/公司名称”无必填标记;当前产品确认允许平台先建订舱后关联客户 | Admin 可清空选择;前端类型、Java DTO 与 OpenAPI 均允许 `customerId/customerName` 同时为空,单边填写必须拒绝 | `LANDED` |
| 无客户 HTTP fixture | 不以历史客户或占位 ID/名称替代可空契约 | `ocean-bookings` 使用本轮 marker、正式渠道和仓库创建无客户订舱,详情回读真实 `bookingNo` 后按订舱号查询列表,断言空客户快照、`CONFIRMED` 及稳定柜次;发布 `createdResources.oceanBookingUnassignedCustomer` | 已准备,待部署后 HTTP 验收 |
| 指定客户隔离链 | 保留来自本轮入库单的正式客户范围 | `phase3-container-loading` 与既有订舱主链继续断言客户、仓库、渠道一致和跨渠道拒绝,不借历史资源 | 已准备,待部署后 HTTP 验收 |
| 提单号、船名、航次、ETD、ETA | 原型存在相关展示/录入字段;最新 P3-Q14 确认线下已订妥资料在创建订舱时完整登记,创建直接进入 `CONFIRMED`;`RELEASED` 仅作为后续放舱/PDA 门禁,不覆盖首次快照 | 创建 payload、持久化详情和列表必须完整携带五字段并返回 `CONFIRMED`;正常路径不得再调用订舱 `/submit`、`/confirm`;后续 `ReleaseRequest` 仍须携带五字段且不得改变首次快照 | `LANDED` |
| 单证号 | 原型第 529 行存在“单证号”,但当前阶段 DTO/OpenAPI 未冻结对应写字段 | 不猜字段名或复用提单号;本轮不扩展写契约 | 非本轮能力,不标记接口阻断 |
离线合同:`ocean-booking-prototype-contract.test.mjs` 锁定原型证据、跨端可选客户、条款字段、创建即确认和放舱门禁时序;`ocean-booking-fixture-contract.test.mjs` 与 `full-regression-entry-contract.test.mjs` 锁定无客户/指定客户双链和默认编排。DRAFT/SUBMITTED/FAILED 不再作为订舱人工矩阵的可稳定生成状态,仅保留正式列表负向覆盖。
## 内部模拟柜关联真实订舱增量
事实来源:`PlatformContainerController`、`ContainerDtos`、`ContainerService`、`OceanBookingService`、`OceanBookingMapper`、`firstleg-v1.yaml`、`ocean-bookings-v1.yaml`。
| 验收项 | 自动化断言 | 当前结论 |
|---|---|---|
| 正式订舱列表 | 仅允许 `CONFIRMED/CHANGE_PENDING/RELEASED/CANCELLED`;`DRAFT/SUBMITTED` 查询失败且默认列表不返回未确认记录;内部模拟柜不进入订舱列表 | 契约与 HTTP fixture 已落地,待部署后验证 |
| 新增内部模拟柜 | `POST /api/platform/v1/containers` 只接收正式装柜仓库、当前 ACTIVE 物流渠道、箱型、容量和限重;不接收真实柜号或订舱;服务端生成稳定 `containerBookingId/cabinetNo` | 契约与 HTTP fixture 已落地,待部署后验证 |
| 订舱逐柜建柜 | `POST /ocean-bookings` 必填 `containers[1..20]`;一单至少两柜 fixture 逐项断言唯一柜号、固定规格、独立 requirement/allocation、`OCEAN_BOOKING/ACTIVE` 身份及订舱/货柜列表一致;请求内重复号、全局重复号、行数或规格错误均整单零写入 | DTO/OpenAPI/静态合同及 HTTP fixture 已落地,待部署后验证 |
| 真实柜直接排货 | 未经过模拟柜时,订舱生成的 `OCEAN_BOOKING/ACTIVE` 真实柜本身可创建计划并接收同仓同渠道货物 | 静态契约已冻结;部署后补定向 HTTP 正向 |
| 排货后首次合并 | 同仓、同渠道、箱型/容量/限重及指定客户范围一致时,已有预排货的 `SIMULATION/ACTIVE` 模拟柜可用 `{realContainerBookingId,expectedVersion}` 合并;模拟柜 `containerBookingId/cabinetNo/loadingPlan/items` 不变,真实柜号与运输快照补入 | 契约与 HTTP fixture 已落地,待部署后验证 |
| ACTIVE 唯一操作身份 | 合并后来源真实柜置 `MERGED`、清空 `containerNo`、`mergedIntoContainerBookingId` 指向模拟柜,allocation 改指模拟柜;正常列表/详情只返回一条存活 ACTIVE 操作柜,PDA 禁止使用 MERGED 来源 ID | 静态合同与 HTTP 列表/详情断言已落地,待部署后验证 |
| 隔离与唯一占用 | 跨仓、跨渠道、箱型/容量/限重/客户范围不匹配拒绝;同一 `realContainerBookingId` 在 MERGED 时不可再次关联;所有失败必须保持柜、计划、明细、allocation 和列表零漂移 | 静态合同已锁定;双渠道和主要 HTTP 负向待部署后验证 |
| 首次关联不可逆门禁 | 存在 PDA、有效装柜证据、业务单证或已进入 `LOADING/COMPLETED` 时首次 link 拒绝 | 服务端静态门禁已锁定;需部署后以独立 marker fixture 验证 |
| 解绑与换绑 | 服务端仍仅允许完全空的 `WAIT_LOADING` 合并柜执行原子 unlink,并恢复来源为 ACTIVE;一旦已有排货、PDA、证据或单证事实,unlink/rebind 均拒绝 | 静态门禁已锁定;排货后负向 HTTP fixture 已落地,待部署后验证 |
| 全量编排 | 默认入口先运行 `phase3-container-loading` 完成“模拟柜新增 -> 同仓同渠道排货 -> 首次 link -> PDA”,再由证据、finalize、出仓模块沿本轮 marker 的稳定 ID 继续;禁止历史数据和 mock 回退 | Node 入口合同已锁定,待默认全量 HTTP 验证 |
离线合同:`container-simulation-association-contract.test.mjs` 锁定跨层契约及全量顺序;`phase3-producer-chain-contract.test.mjs`、`phase3-order-splitting-contract.test.mjs`、`ocean-booking-channel-contract.test.mjs` 锁定现有生产者不再依赖 `CONFIRMED` 自动建柜。