# 阶段四:海运、目的港与尾程入仓计划 > 对应全景流程:F24~F27。 > 阶段起点:柜次已完成送港/进港或甩柜处理,并产生承运船舶实际 `VESSEL_DEPARTED`/`DEPARTED_ORIGIN_PORT` 节点;真实 `DEPARTED_WAREHOUSE` 和 `PORT_RECEIVED` 属于阶段三送港线事实。 > 阶段终点:跨区尾程订单完整生成,整柜每票入仓计划及托盘/库位资源已生成或有明确资源失败待办,订单进入 `INBOUND_PLANNED`。 > 本阶段同时维护船舶/货柜轨迹和尾程业务准备,但两类状态不得互相冒充。 ## 1. 阶段目标和边界 本阶段完成四条相互关联但独立的链路: 1. 从承运船舶实际开航开始查询并保存十个标准船节点; 2. 管理目的港清关、海外码头车队、提柜、预计到仓和还空; 3. 将国内最终实装快照可靠投递到海外区域并创建尾程订单; 4. 按整柜维护每票计划并转换为可执行的海外入仓计划和资源。 阶段四不执行海外仓实收、库存入账、库内服务、排车或出库。`PICKUP_READY` 只表示货柜已清关可提,并开放海外 PDA 待收柜候选;它不表示已经到仓或已经产生库存。 ## 2. 角色、终端和系统边界 | 角色/系统 | 终端 | 主要职责 | |---|---|---| | 船期操作员 | Admin | 查看查询任务、标准节点、异常与查询历史,必要时人工补录 | | 目的港操作员 | Admin | 维护清关/DO/PU、选择海外车队、发提柜邮件、确认提柜和还柜 | | 海外仓计划员 | Admin | 维护整柜预计到仓、每票托数与作业属性、执行柜级转换 | | 企业成员 | Client | 查看本企业尾程订单和允许展示的船期/计划进度 | | Firstleg | 后端 | 拥有查询任务、标准节点、目的港码头拖车和装柜完成 Outbox | | Integration Service | 后端 | 调用船司/轨迹供应商,解析并返回标准化结果,不保存业务主数据 | | Event Bridge | 跨区通道 | 版本化、认证地投递业务事件 | | Lastleg | 海外后端 | Inbox 幂等消费、尾程订单、计划、在途库存和海外业务状态 | | Warehouse | 国内主数据 | 海外共享仓、FBA 目的仓和供应商主档权威 | | Warehouse Overseas | 海外资源 | 海外库位、实物托盘和资源锁 | 所有浏览器海外接口经 `gateway-overseas/`。国内 Auth 仍是账号和 Token 签发方;跨区业务写入通过内部身份、Inbox/Outbox 和事件契约完成,不允许浏览器伪造跨区事件。 ## 3. 阶段前置检查 - 柜次存在真实集装箱号、订舱、提单号、送港/进港事实和实际开船节点; - 装柜完成事件包含最终实装订单、入仓单、目的类型、箱数及必要托盘/SKU/箱唛快照; - 国内五类装柜证据和锁码文本已经冻结; - 接柜海外仓是 Warehouse 中 `ACTIVE + OVERSEAS + RECEIVING` 的平台共享物理仓; - FBA 目的仓是 Warehouse 有效公共主档,不能用接柜仓或国内仓代替; - 船司/轨迹查询标识足以唯一指向目标订舱和柜次; - 同柜订单的预计到仓事实必须能够收敛为一个柜级日期。 ## 4. F24 海运十节点 ### 4.1 查询任务创建 承运船舶实际开航事务幂等创建 `CarrierScheduleQuery`。任务必须绑定真实订舱、柜次以及可用的船司、船名航次、提单号或柜号,不创建脱离业务单据的孤立轮询任务。送港、进港、甩柜和客户报关确认在阶段三分别记录,不能用预计 ETD 提前创建“海运中”展示。 标准节点保留国内送港事实,但海运查询的起点是实际开船: `DEPARTED_WAREHOUSE`、`AT_ORIGIN_PORT` 和 `LOADED_ON_VESSEL` 可以作为送港/装船历史补录;客户的“海运中”节点和定时船节点查询以真实 `DEPARTED_ORIGIN_PORT`/`VESSEL_DEPARTED` 为起点。 ```text 10 DEPARTED_WAREHOUSE 已离开国内仓 20 AT_ORIGIN_PORT 已到起运港 30 LOADED_ON_VESSEL 已装船 40 DEPARTED_ORIGIN_PORT 已离开起运港 50 IN_TRANSIT 海运途中 60 ARRIVED_DESTINATION_PORT 已到目的港 70 DISCHARGED_FROM_VESSEL 已卸船 80 PICKUP_READY 已清关、可提柜 90 PICKED_UP 已提柜 100 EMPTY_RETURNED 已还空柜 ``` ### 4.2 自动查询操作与频率 入口:Admin → 船期/货柜详情 → 船节点查询。 平台查看:查询任务状态、当前最新实际节点、预计节点、最近成功时间、下次允许时间、供应商、失败原因、异常事实和每次尝试历史。 正常查询槽位按北京时间每天 `09:00`、`12:30`、`16:00`。调度器每分钟只扫描已到期任务,不代表每分钟调用船司。 - 成功、无变化、需人工或永久失败:进入下一固定槽位; - 技术性可重试失败:当前槽内按 15/30 分钟退避,含首次最多三次; - 重试不得跨越下一正常槽位; - 服务恢复后错过的槽位直接顺延,不集中补打供应商; - 实际 `EMPTY_RETURNED` 后任务停止。 ### 4.3 节点时间和推进规则 | 时间 | 含义 | 推进最新实际节点 | |---|---|---| | `estimatedAt` | 预计发生时间 | 否 | | `actualAt` | 已确认实际时间 | 是 | | `observedAt` | 本次观测到事实的时间 | 否 | | `createdAt` | 平台写入时间 | 否 | 只有带 `actualAt` 的标准节点可以推进。节点按序号单调前进;迟到的较早节点可作为历史保存,但不能回退当前节点。供应商缺少中间节点时保持缺失,不能自动补齐。 同一供应商事件按稳定 `providerEventId` 幂等;无原生 ID 时用稳定字段组合生成哈希。每条事实保留原始节点编码、来源、地点、观测时间和原始响应哈希,但业务表不保存含密钥的完整网页。 ### 4.4 异常事实 以下异常与正常节点并行保存: - `CUSTOMS_HOLD`:海关扣留; - `TERMINAL_HOLD`:码头/堆场扣留; - `DOCUMENT_HOLD`:单证问题; - `UNKNOWN_HOLD`:其他无法分类扣留。 异常不能覆盖最近正常节点。解除时新增解除事实,不删除原异常。 ### 4.5 人工补录 供应商缺节点、网页需要人工验证或业务已有可信凭证时,有权限人员可在查询详情补录: 1. 选择标准节点; 2. 填写实际时间,预计事实不能冒充实际时间; 3. 填写地点、来源说明和必要凭证; 4. 核对节点不会回退当前实际进度; 5. 携带版本和幂等键提交; 6. 查看历史中来源标识为人工。 人工补录 `PICKUP_READY` 会通过跨区事实开放海外 PDA 柜号候选;人工补录 `EMPTY_RETURNED` 会停止任务,因此必须使用真实还柜事实。 ## 5. F25 目的港清关、车队、到仓和还空 ### 5.1 清关和文件 入口:Admin → 清关提柜。 1. 按柜号、提单号、目的港和节点筛选; 2. 核对目的港清关资料、DO 和 PU 当前版本; 3. 上传或替换合法文件引用; 4. 将线下清关结果反映为可信业务事实; 5. 清关完成、码头明确可提后,确保实际 `PICKUP_READY` 已进入标准节点链。 清关资料状态和标准船节点有关联但不互相覆盖。只有明确可提事实才能推进 `PICKUP_READY`。 ### 5.2 选择海外码头车队 1. 选择 Warehouse 中 `ACTIVE/TRUCKING/OVERSEAS` 供应商; 2. 服务端读取供应商名称和邮箱快照,不能用自由文本绕过主档; 3. 首次选择车队只保存选择,不自动发信; 4. 操作员确认后发送提柜请求邮件; 5. 邮件进入顺序发送队列,`SENT` 只表示 SMTP 接受,不代表车队已读或已执行; 6. 失败邮件保留原因,人工确认后重试。 更换车队时,事务内同时冻结旧车队取消邮件和新车队提柜邮件,并按顺序投递。同一车队重复选择不重复发信。 ### 5.3 实际提柜 车队从码头取得重箱后,目的港操作员确认实际提柜时间。服务端推进实际 `PICKED_UP`,但不创建海外库存、不替代 PDA 到仓收货。 ### 5.4 预计到仓 清关提柜列表中的“预计到仓”由平台维护: 1. 输入整柜预计抵达接柜仓的日期/时间; 2. 按 `sourceContainerBookingId` 锁定整柜; 3. 原子同步柜内全部尾程订单的 `estimated_warehouse_at`; 4. 记录版本、操作人和幂等历史; 5. 任一入仓计划已生成后锁定,不再允许修改。 预计到仓不能使用订单创建时间、预计到港时间或当前时间替代。同柜订单缺失或日期不一致时,阶段四的计划转换必须阻断。 ### 5.5 实际到仓与还空 实际到仓不由 Admin 手填,也不取预约提柜时间;只取海外 PDA 首次开始收货会话的 `receiving_at`。因此本阶段只能展示“待实际到仓”,真实时间在阶段五产生。 海外 PDA 完成卸柜后,跨区 `OVERSEAS_CONTAINER_UNLOADED` 事件回到 Firstleg。预计报空/还空截止按卸柜完成时间加 72 小时计算并产生预警。 真实还柜可由供应商节点或有权限人员确认: - 时间不得早于卸柜完成时间; - 若已有实际提柜时间,也不得早于提柜; - 无需为了还柜补造车队、预约或提柜事实; - 首次命令可以版本 0 创建记录,车队字段允许为空; - 成功后推进实际 `EMPTY_RETURNED`、解除预警并停止查询任务。 目的港码头拖车、国内订舱级拖车和海外配送排车是三张不同业务单,不得复用状态或费用。 ## 6. F26 跨区尾程建单 ### 6.1 国内生产事件 Firstleg 在装柜完成后按最终实装生成 `FIRST_LEG_LOADING_PLAN_COMPLETED` V3: - 信封包含事件 ID、版本、来源区域、聚合版本和 Payload 哈希; - Payload 包含来源柜次/计划/真实柜号和每票最终实装; - FBA 可以按实装箱数冻结,不伪造逐箱作业账; - `OVERSEAS` 必须逐箱冻结箱码事实; - 一件代发必须携带一个或多个不可变来源托盘,以及每托 SKU、箱数、数量和箱唛; - 物理柜五类国内装柜照片引用一并冻结。 Outbox 可重复投递,不能因网络失败直接丢事件或在事务外临时拼装 Payload。 ### 6.2 Event Bridge 投递 Event Bridge 校验允许的事件类型、版本、来源/目标区域和内部服务身份后投递 Lastleg。IAM 安全事件使用独立入口,不能交给装柜完成消费者。 ### 6.3 Lastleg Inbox 消费 Lastleg 消费步骤: 1. 常量时间校验内部令牌; 2. 校验事件 ID、版本、来源区域、客户和必要字段; 3. 校验 Payload 哈希; 4. 以事件 ID、哈希和来源入仓单唯一约束防重; 5. 在一个事务内处理整柜全部订单; 6. 任一票失败时整柜回滚,不留下半套订单; 7. 重放相同事件返回既有结果。 ### 6.4 尾程订单和在途库存 成功消费后: - 同一实际集装箱共享业务尾程单号; - 每个 `sourceInboundId` 独立生成一条 `ll_order`; - 保存来源订单、入仓码、客户、接柜仓、目的仓和货物冻结快照; - 主状态为 `PENDING_ASSIGNMENT`; - 生成 `FIRST_LEG` 在途库存; - 计划生成前以真实来源入仓码作为库存展示快照; - 事件只建订单,不自动生成入仓计划。 一件代发额外生成来源托盘、SKU、箱唛档案和在途库存事实;海外实物托盘资源仍由 Warehouse Overseas 分配,不覆盖国内来源托盘快照。 ## 7. F27 尾程计划维护和整柜转换 ### 7.1 柜级工作台 入口:Admin → 海外仓 → 尾程订单 → 柜级工作台。 列表使用 `/container-summaries` 按稳定 `sourceContainerBookingId` 服务端聚合,不允许用当前订单分页在浏览器拼柜。列表至少展示: - 柜号、来源柜次和预计到仓; - 订单数、已建计划数、待建计划数; - `allPlansReady`; - `resourcePendingPlanCount`; - 接柜仓、目的仓分组和异常摘要。 已完成转换的柜仍保留供查询和允许范围内维护。详情接口返回柜内全部订单和计划,不受列表分页截断。 ### 7.2 单票计划维护 对柜内每票填写: - 计划托数; - 托盘类型; - 是否已分配; - 是否允许合托; - 是否加急; - 现场备注。 入仓码、实际接柜仓和预计到仓只读。保存时校验订单版本、客户范围、Warehouse 当前仓库主档和幂等键。 “可合托”只给阶段五现场提供选择,不是实收或完成门禁:合托时扫描同目的仓已使用托盘,独立收货扫描新托盘。 ### 7.3 普通货与一件代发托盘规则 普通散货: - 保留头程已经冻结并跨区传入的托盘码; - 海外计划托数不得少于已有托盘码数量; - 海外装托密度不同导致托数增加时,在同柜、同 FBA 仓连续补码; - 预览分别展示头程已有数、海外补充数和最终托数。 一件代发: - 必须逐托沿用国内封托的 `DP` 托盘码和每托箱数; - 不允许重算托数、重分配箱子或改写 SKU/箱唛; - 无法绑定真实资源时禁止保存半成品计划。 ### 7.4 自动触发与柜级预览 单票柜保存完整计划后立即尝试生成资源;多票柜在最后一票计划齐全时自动触发整柜资源生成。 转换前读取服务端预览,核对: - FBA、一件代发、私人地址等分组; - 每票来源托数、补充托数和最终托数; - 柜内计划是否全部就绪; - 接柜仓、预计到仓是否一致; - 阻断项; - `snapshotVersion` 和 `snapshotHash`。 `pendingPlanCount` 表示尚未建立完整计划;`resourcePendingPlanCount` 只表示自动物化资源失败,需要按原柜级入口重试,不能把它当作正常人工步骤。 ### 7.5 确认转换 系统执行柜级 Saga: 1. 以 `sourceContainerBookingId` 锁柜; 2. 用版本/哈希重新计算预览; 3. 向 Warehouse Overseas 批量预留真实托盘、库位和资源锁; 4. 为柜内每票独立生成入仓计划; 5. 本地事务确认计划并绑定资源; 6. 尾程订单进入 `INBOUND_PLANNED`; 7. `FIRST_LEG` 在途库存展示编号从来源入仓码收口为各自 `planNo`。 整批转换原子且幂等:相同命令重放不生成第二批计划或资源;本地失败只补偿尚未确认的远端资源;远端确认失败的重放只重试确认。 ## 8. 状态、编号与数据归属 | 维度 | 关键状态/编号 | 权威系统 | |---|---|---| | 船节点任务 | ACTIVE/人工/失败/`STOPPED` | Firstleg | | 最新实际节点 | 十节点之一 | Firstleg | | 目的港码头拖车 | 车队、提柜、卸柜、还柜 | Firstleg | | 尾程主单 | `PENDING_ASSIGNMENT → INBOUND_PLANNED` | Lastleg | | 入仓计划 | `planNo`、计划属性、资源绑定 | Lastleg | | 在途库存 | `FIRST_LEG` | Lastleg | | 海外实物资源 | 托盘、库位、锁 | Warehouse Overseas | | 仓库/目的仓/供应商 | 业务主档 | Warehouse | `containerNo` 是真实集装箱号;`sourceContainerBookingId` 是跨区稳定柜聚合键;`inboundCode` 永远保存真实来源入仓码;`planNo` 只在尾程入仓计划生成后产生。任何页面不得以内建数据库 ID 回退展示业务编号。 ## 9. 异常与恢复 | 场景 | 系统行为 | 操作处理 | |---|---|---| | 船司无变化 | 保存查询尝试,进入下一槽位 | 无需伪造节点 | | 船司技术失败 | 15/30 分钟受控重试 | 查看失败原因,必要时人工 | | 只有预计到港 | 保存预计事实,不推进实际节点 | 等待真实节点 | | 较早节点迟到 | 保存历史,不回退当前节点 | 无需修改最新进度 | | 清关/单证扣留 | 单独保存异常 | 处理后新增解除事实 | | 车队邮箱无效 | 阻断发信/换队 | 修正 Warehouse 供应商资料 | | 事件重复投递 | 返回既有消费结果 | 不人工删 Inbox | | 事件哈希不同 | 冲突失败 | 核对生产端版本和 Payload | | 柜内任一票事件失败 | 整柜消费回滚 | 修复数据后重放原事件 | | 预计到仓缺失/不一致 | 阻断计划生成 | 在计划前按柜统一维护 | | 计划版本过期 | 转换冲突 | 刷新预览,重新确认 | | 资源预留失败 | `resourcePendingPlanCount` 暴露 | 用原柜级入口定向重试 | | 重试结果不确定 | 查询计划和资源事实 | 复用原幂等键,不另建资源 | ## 10. 阶段完成判定 现有测试环境综合证据 `baiyue-firstleg/reports/admin-full-postdeploy-66c2651b.json` 中,`phase4-carrier-nodes`、`phase4-overseas-pda`、`phase4-overseas-pda-operations`、`phase4-overseas-inbound-operations`、`phase4-overseas-dispatch`、`phase4-tail-order-controls` 和 `phase5-overseas-fulfillment` 均为通过;每个模块的失败数为 0。F23.3 的最新船期客户端投影另见 `baiyue-firstleg/reports/transport-projection-after-schedule-update-20260927.json`。 - [ ] 国内离仓产生的船节点任务绑定真实订舱和柜次 - [ ] 节点预计/实际/观测时间没有混用 - [ ] 异常事实与正常节点分开保存 - [ ] 实际 `PICKUP_READY` 已到达或已有明确待处理状态 - [ ] 装柜完成 V3 事件已被 Lastleg 幂等消费 - [ ] 柜内每个来源入仓单只生成一张尾程订单 - [ ] 尾程订单冻结客户、入仓码、接柜仓、目的仓和最终实装事实 - [ ] `FIRST_LEG` 在途库存存在且归属正确 - [ ] 整柜预计到仓完整且一致 - [ ] 每票入仓计划属性完整 - [ ] 柜级转换后每票独立 `planNo` 存在 - [ ] 托盘、库位和资源锁已绑定,或存在可定位的资源失败待办 - [ ] 尾程订单状态为 `INBOUND_PLANNED` - [ ] 重放事件/转换不会重复建单、建计划或占资源 ## 11. 易用性与数据流评审问题 1. 船节点、目的港清关和尾程计划是否需要整合为一条柜级时间线,同时保留三个权威状态? 2. 预计节点与实际节点的视觉差异是否足够明显? 3. 船司失败、需人工、扣留异常分别由谁负责,是否需要责任人和 SLA? 4. 发车队邮件后,页面是否明确区分“已入队、SMTP 已接受、车队已执行”? 5. 预计到仓锁定前是否应显示会影响哪些订单和计划? 6. 跨区事件失败是否需要业务友好的“缺哪一票/哪一字段”诊断,而不是只显示事件错误? 7. 柜级工作台的 `pendingPlanCount` 与 `resourcePendingPlanCount` 是否容易混淆? 8. 普通散货的“头程已有/海外补充/最终托数”是否需要图形化对比? 9. 一件代发不可重排托盘的约束是否在编辑前就锁定控件? 10. 转换前预览是否需要展示资源占用和预计库位,减少确认后人工调整? 11. `PICKUP_READY` 开放 PDA 候选后,若预计到仓和资源计划尚未完成,现场应看到什么阻断提示? 12. 还柜预警当前同时存在“两天展示”和“72 小时业务规则”的历史描述,正式页面应统一采用服务端 72 小时口径。 ## 12. 阶段五交接包 交给阶段五“海外接柜、实收、库存和库内服务”的输入包括: - 来源柜次、真实柜号、订舱和最新实际船节点; - `PICKUP_READY` 及以后产生的 PDA 候选门禁; - 实际接柜海外仓和柜级预计到仓; - 柜内全部尾程订单、来源入仓码、目的类型和冻结货物; - 每票独立入仓计划及 `assigned/mergeAllowed/urgent/mergeHint`; - 已预留并确认的托盘、库位和资源锁; - 普通货头程托盘码与海外补码; - 一件代发不可变 `DP` 托盘、箱唛和 SKU; - 国内装柜五类照片引用及跨区事件审计历史。 ## 13. 事实来源 - `docs/海外仓业务/README.md` - `docs/海外仓业务/船节点标准化契约.md` - `docs/海外仓业务/虫虫出海全流程人工验收手册.md` - `docs/跨区域认证与服务通信契约.md` - `baiyue-firstleg/firstleg/AGENTS.md` - `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/portschedule/` - `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/terminaldrayage/` - `baiyue-firstleg/common-event-contract/AGENTS.md` - `baiyue-firstleg/lastleg-service/AGENTS.md` - `baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/service/LoadingCompletedConsumer.java` - `baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/` - `apps/admin-web/src/modules/port-schedules/` - `apps/admin-web/src/modules/overseas-warehouse/`