672 lines
64 KiB
Markdown
672 lines
64 KiB
Markdown
# 船期日历功能规划
|
||
|
||
> 文档状态:首版开发规划;2026-09-27 复核补充了数据闭环规则。本文是开发依据,不是代码完成或上线验收证明。
|
||
> 规划对象:Admin「国内头程 → 船期管理」独立船期日历。
|
||
> 目标:支持人工维护渠道和船期规律,按周或单次生成具体船期,并按渠道/船期查看收货体积。
|
||
|
||
> **当前展示版范围已重新冻结:只做船期维护和只读展示,不做自动归班、不消费预报、不改写收货审核、不自动改绑,也不改变现有智能排柜流程。** 首版只需要让管理人员看到每个航次的已收/预计货量、可用容量、按柜型初算的建议柜数、已订柜和待补柜,并据此人工判断是否合并到其他船。后续若要增加货物归班,再单独立项评审。第 15.8 节覆盖本文此前关于自动归班和异常改配的旧口径。
|
||
|
||
> **展示版统计口径**:每个船期按“收货仓 + 渠道 + 起运港 + 目的港”展示;已收使用现有收货/测量结果,预计量使用现有入仓和智能排柜可读数据;不创建或变更船期归班事实。收货截止时间只用于标记“截止前参考量/截止后风险”,不触发自动分配。订柜数只做只读初算,最终以现有智能排柜和人工确认结果为准。
|
||
|
||
## 1. 输入来源与现状边界
|
||
|
||
本规划以当前需求描述为业务输入,以用户提供的截图作为页面布局参考。截图中的红框、日期、船名、航次、数量和示例数据均不构成额外指令,也不代表需要沿用的真实业务数据。
|
||
|
||
仓库当前已有 `apps/admin-web/src/modules/port-schedules/` 和 Firstleg 的 `portschedule` 服务。现有“港务船期”记录绑定客户、订舱或柜次,主要保存船公司/码头/API 的船期观测、过期时间和海运节点查询结果;对应表为 `fl_port_schedule`。它服务于已存在的订舱和货柜履约链路,不直接承载本规划的排班模板、周循环生成或收货量统计。现有 `port-schedules/routes.ts` 也不是本规划新页面的正式入口;新日历应作为独立 Admin 页面、路由和权限接入,不能把现有隐藏/嵌入式船期组件直接改成排班管理页。
|
||
|
||
本功能建议新增“船期计划”域,与现有港务船期并列:
|
||
|
||
- 船期计划是运营人员提前安排“哪个渠道哪天开哪班船”,可以没有订舱和真实柜次;价格簿中的参考船期、最晚进仓和参考时效也不能直接当作该计划的实际开船事实。
|
||
- 港务船期是某个订舱/柜次的外部船期事实或人工观测,继续按现有接口和状态机运行。
|
||
- V1 先建立“计划实例 ↔ RELEASED 订舱”的最小关联用于订柜统计;真实柜次、港务船期和海运节点的完整关联后置,不能用计划数据覆盖已产生的运输事实。
|
||
|
||
本规划默认这是平台内部运营日历:渠道主数据平台级维护,模板和实例至少按收货仓隔离,实收汇总按“收货仓 + 渠道 + 收货日期”聚合并对平台运营人员可见。V1 不按客户拆分船期计划;如果实际业务存在客户专属船期或价格承诺,必须在开发前把客户作为模板、实例、查询和权限的正式维度,不能在上线后再用名称筛选补救。
|
||
|
||
## 2. 要解决的问题
|
||
|
||
运营人员需要在一个页面完成以下工作:
|
||
|
||
1. 维护当前可售或可安排的运输渠道,例如“MATSON / 美森”。
|
||
2. 配置固定每周哪几天有船,也可以只添加一班临时船期。
|
||
3. 根据开船时间和渠道的提前收货天数,自动算出收货截止时间。
|
||
4. 在月历上看到每天有几班船,以及每个渠道已收多少立方米、预计有多少货、容量还剩多少和建议订几个柜。
|
||
5. 对已生成的具体船期进行编辑、取消、复制和查看关联货量,并保留变更历史。
|
||
|
||
## 3. 建议的 V1 范围
|
||
|
||
### 3.1 本轮纳入
|
||
|
||
- 船期渠道的新增、编辑、启用/停用。
|
||
- 一次性船期的手工添加。
|
||
- 每周模板的配置,按指定周几生成未来具体船期。
|
||
- 每条模板/实例必填收货仓;首版不使用“全部仓”这种模糊范围。
|
||
- 开船时间、预计到港时间、收货截止时间和容量的维护与计算。
|
||
- 月历视图、月份切换、按收货仓/起运港/渠道/船名航次筛选。
|
||
- 选中日期的船期详情和渠道体积汇总。
|
||
- 船期实例的人工编辑、取消、复制下一班和变更历史。
|
||
- 货量按收货仓与渠道只读展示已收实测体积、预计体积、截止前参考量、截止后风险量、已订柜和待补柜;不产生归班事实、不提供货量改绑。
|
||
- 按现有柜型目录计算只读的“建议订柜数”,不直接创建正式订舱或货柜。
|
||
|
||
### 3.2 暂不纳入
|
||
|
||
- 船公司官网或第三方 API 自动抓取船期。
|
||
- 自动订舱、舱位分配、放舱、拖车、报关和实际出仓状态推进。
|
||
- 自动归班、预报消费、实收批次绑定、取消后自动改配和异常重新安排。
|
||
- 在收货审核、测量审核或智能排柜流程中增加回调、写入或阻断。
|
||
- 复杂的价格计算、客户报价和财务结算。
|
||
- 以计划日历替代现有订舱/柜次船期事实。
|
||
- 在没有业务确认前自动修改历史入仓数据或批量回溯全部历史货量。
|
||
- 复杂节假日规则、跨时区排班和通知中心;这些作为后续扩展点保留。
|
||
- 容量预占、超容强制处理、正常货量的批量改绑和跨客户拆分;异常货的最小重新安排按第 6.10 节纳入 V1。
|
||
|
||
## 4. 核心概念与生命周期
|
||
|
||
| 对象 | 作用 | 关键生命周期 |
|
||
| --- | --- | --- |
|
||
| 渠道 Channel | 表示一类运输服务,如 MATSON / 美森 | `ACTIVE`、`INACTIVE` |
|
||
| 船期模板 Schedule Template | 描述每周固定规律;固定添加一班直接创建实例,不再套一层单次模板 | `DRAFT`、`ACTIVE`、`INACTIVE` |
|
||
| 船期实例 Sailing | 某一具体日期的某一班船,是日历和货量统计的主体 | `DRAFT`、`OPEN`、`CLOSED`、`DEPARTED`、`CANCELLED`、`COMPLETED` |
|
||
| 归班事实 Sailing Assignment | 保存预计量/收货批次与船期的自动归班结果及历史 | `FORECAST_MATCHED`、`RECEIVED`、`LATE`、`UNMATCHED`、`SUPERSEDED` |
|
||
| 订舱关联 Sailing Booking Link | 将已放舱订舱的柜型需求关联到计划船期,用于订柜建议 | `ACTIVE`、`REVOKED` |
|
||
|
||
模板生成实例后,实例保存自己的船名、航次、港口、截仓时间和容量快照。以后修改模板不回写已经生成、已编辑或已关联货量的实例;需要调整时由运营人员编辑实例或重新生成未落地的未来实例。
|
||
|
||
## 5. 页面信息架构
|
||
|
||
### 5.1 顶部筛选和操作区
|
||
|
||
- 月份:默认当前月,提供上月、下月和“今天”。
|
||
- 收货仓:按仓库筛选;船期计划至少要绑定一个收货仓,不能只靠渠道和起运港推断。
|
||
- 出发港:按港口代码和名称筛选。
|
||
- 船期渠道:按启用渠道筛选,停用渠道仍可用于查询历史。
|
||
- 船名/航次:模糊搜索。
|
||
- 状态:全部、可收货、已截止、已取消、已开船、已完成。
|
||
- 归班状态:全部、预计可赶船、已收、晚到、未安排;用于把“漏排或赶不上”的货量变成运营待办。
|
||
- 操作:`新增船期`、`渠道与模板管理`。
|
||
|
||
另设“待安排/异常货量”入口,集中展示当前权限和仓库/渠道范围内仍待处理的货物,显示实际收货日或预计到仓日、等待天数及异常原因。未安排记录没有开船日期,不强行放进某个开船月,也不混入月历主合计;切换月份不使这些待办消失。
|
||
|
||
筛选条件应由服务端参与查询,不能先拉取整月全部记录后在浏览器本地拼装统计。
|
||
|
||
### 5.2 月历区
|
||
|
||
- 周一至周日为列,显示跨月日期但以浅色区分。
|
||
- 每个日期格显示当天船期数量和航线摘要。
|
||
- 摘要优先显示渠道名和收货体积,例如“美森 120 m³ / 200 m³”;渠道较多时显示前几项并提供“更多”。
|
||
- 当前日期、选中日期、已截止日期、容量超限日期分别使用清晰但不依赖颜色的标签/图标表达。
|
||
- 点击日期后,右侧详情区同步刷新;点击船期卡片可直接进入实例详情。
|
||
|
||
### 5.3 选中日期详情区
|
||
|
||
每个船期卡片至少展示:
|
||
|
||
- 渠道、船名、航次和船期业务编号。
|
||
- 起运港 → 目的港。
|
||
- 预计开船、预计到港、最终收货截止时间。
|
||
- 已收总量、本船可赶实收、预计可赶、晚到量、已订柜数、建议订柜数和待补柜数;各指标按第 6.6 节计算,已收总量不能直接当作可赶量。班次容量上限和剩余体积仅在已配置班次容量时展示,V1 不因容量不足自动改绑或强制拦截。
|
||
- 当前状态和风险提示:已过截止、接近满舱、超过容量、已取消。
|
||
- `编辑`、`复制为下一班`、`取消船期`、`查看货量明细`。
|
||
|
||
删除仅适用于未开船、未关联任何货量的草稿/实例;有货量或已产生履约事实的实例只能取消,不能物理删除。
|
||
|
||
### 5.4 新增/编辑弹窗
|
||
|
||
弹窗提供两个明确入口:
|
||
|
||
**按周添加**
|
||
|
||
- 选择渠道、起运港、目的港。
|
||
- 选择收货仓;同一渠道可按不同收货仓配置不同截止规则。
|
||
- 选择一个或多个周几。
|
||
- 选择开船时间、航程天数或预计到港时间。
|
||
- 设置提前收货天数、有效起止日期和默认容量。
|
||
- 设置船名固定值或船名规则;设置航次生成规则。
|
||
- 选择未来生成范围(建议 4 / 8 / 12 周)。
|
||
- 先预览将生成的实例,再确认批量生成。
|
||
|
||
**固定添加一个**
|
||
|
||
- 直接填写开船日期时间、船名、航次、起运港、目的港。
|
||
- 填写或确认收货截止时间、预计到港时间、容量。
|
||
- 保存为一条独立实例,可选关联一个模板作为来源。
|
||
|
||
周模板中的船名是生成实例的默认值;船司确认后可逐班编辑船名、航次和截止时间。当前展示版编辑实例时保留已保存的绝对截止时间,切换到“按开船前 N 天”后才重新计算;规则快照和覆盖原因待后端契约补齐后再做完整留痕。
|
||
|
||
### 5.5 区间汇总
|
||
|
||
月历上方建议提供当前筛选范围的渠道汇总卡:已收实测 CBM、预计可赶 CBM、风险/未安排 CBM、已订柜数、建议订柜数、待补柜数、晚到批次数和船期数量。V1 卡片固定标明“按开船月”,收货月仅在详情和后续报表中使用;多班同渠道同日时仍按船期实例分开,不能只显示一个渠道总数。
|
||
|
||
## 6. 业务规则
|
||
|
||
### 6.1 收货截止时间
|
||
|
||
首版把“收货截止时间”正式命名为**最晚到仓时间 `warehouseCutoffAt`**,表示货物最晚什么时候到达我司收货仓,和船公司的截单、截关、截港时间分开保存。默认公式:
|
||
|
||
```text
|
||
最晚到仓日期 = 预计开船本地日期 - 提前收货天数
|
||
最晚到仓时间 = 收货仓截收时刻
|
||
```
|
||
|
||
- 计算使用仓库业务时区,V1 默认 `Asia/Shanghai`;接口统一传 ISO 8601 时间。
|
||
- 计划截止计算只基于实例保存的计划 `etdAt`;后续实际开船时间属于订舱/船节点事实,不反向改写已经产生的收货归班。
|
||
- “提前 N 天”首版固定按自然日计算;工作日和节假日规则放到后续工作日历阶段。
|
||
- 若收货仓暂未配置截收时刻,首版按 18:00 默认并记录规则来源,不能由前端使用浏览器本地时刻隐式补齐。
|
||
- 首版默认使用 `3` 个自然日和 `18:00`;这个默认值不是从截图推断的,而是初始运营参数,后续按试运行数据校准。
|
||
- 单班页面允许选择提前 2/3/4 个自然日或自定义本班截止时间;当前展示版保存计算后的最终 `warehouseCutoffAt`,规则快照与覆盖原因审计字段待后端契约补齐后再启用,页面不宣称已完成服务端留痕。
|
||
- 最终截止时间必须严格早于开船时间;开船时间、截止时间和容量的校验由服务端最终执行。
|
||
- 时间边界统一到毫秒:实际/预计到仓时间等于截止时间视为准时;当前时间大于截止时间才停止新归班,不能由不同页面采用不同的 `<`、`<=` 判断。建议值由服务端计算并返回,前端只展示。
|
||
- 是否跳过周末/节假日、节假日向前顺延几天,需要在产品确认后接入工作日历,不能由前端自行猜测。
|
||
|
||
### 6.2 每周模板生成
|
||
|
||
- 按模板有效期和周几生成具体日期,跨月日期仍属于实例,查询某月时按开船时间落入月份返回。
|
||
- V1 采用“预览后手工确认生成”,不做后台滚动生成;页面默认未来 8 周,并提供未来 4/8/12 周快捷范围,生成范围由操作人确认后提交。
|
||
- 生成请求必须幂等;同一模板、日期、航次规则只能生成一条有效实例。
|
||
- 已编辑、已关联货量、已关闭、已开船或已完成的实例不被重新生成覆盖。
|
||
- 发现相同日期/航次冲突时返回冲突清单,让用户选择跳过或进入实例处理,不能静默覆盖。
|
||
- 模板停用只阻止未来生成,不删除历史实例。
|
||
- V1 如遇某周停航或临时改船,允许在生成预览中跳过日期或生成后取消实例;节假日批量例外规则放到后续阶段。
|
||
- 去重和冲突检查必须跨模板执行,至少按“收货仓 + 渠道代码 + ETD 本地日期时间 + 航次”判断,不能只用 `template_id + date` 作为唯一键。
|
||
- 同一收货仓、渠道、起运港和目的港下,若多班船的 ETD 相同,V1 按不可变的 `sailing_no` 升序确定唯一归班;复杂的运营优先级后置。
|
||
|
||
### 6.3 航次号和船名
|
||
|
||
V1 支持固定值和“前缀 + ETD 本地日期”两种航次生成规则;每个生成实例允许人工覆盖航次。顺序号和按船名递增规则后置,避免首版因船公司编号规则不同而误生成正式航次。
|
||
|
||
业务唯一性首版约束“收货仓 + 渠道代码 + 起运港 + 目的港 + ETD 本地日期时间 + 航次”;同一航次跨不同起运港可以存在,但不能在同一组合内重复。`sailing_no` 在实例创建时生成后保持不变,用于同一 ETD 的确定性排序;展示给用户的业务编号应为船期编号、船名和航次,不能回退显示数据库 ID。
|
||
|
||
### 6.4 可赶船判断和人工改绑
|
||
|
||
默认推荐某一入仓记录时,只允许候选实例同时满足:
|
||
|
||
- 渠道、起运港和目的港匹配。
|
||
- 状态为 `OPEN`,且当前时间未超过最终收货截止时间。
|
||
- 后续启用容量控制时再校验未超过容量;V1 只展示已配置容量和超量提示,不因容量字段缺失阻断归班。无上限实例可明确标记为“不限舱位”。
|
||
- 按“最早可开船”或产品确认的优先规则排序。
|
||
|
||
超过截止时间时不能自动分配到该船期;容量不足在 V1 只显示明确预警,不阻断自动归班,也不能把预警状态伪装成已锁定舱位。任何已关闭、已取消或已开船的船期都不能作为新的自动归班候选。人工改绑必须记录操作者、原船期、新船期、时间和原因。
|
||
|
||
建议把“预计归班”和“实际收货统计”分成两条事实:
|
||
|
||
- 预计归班可使用入仓单的 `expectedArrivalAt`,只有预计到仓时间早于或等于最终截止时间,才标记为“预计可赶船”。没有预计到仓时间的记录进入“待确认”,不能凭渠道名称或创建时间猜测。
|
||
- 实际统计使用已确认的实际收货时间和审核后的实测体积。已有计划归班的货实际到仓晚于截止时,保留原计划并标记“晚到风险”,不能自动塞到下一班。首次形成实收事实且从未归班的货,若仓库、正式渠道和航线明确,可首次自动选择仍开放且未过截止的最早匹配船期,同时校验实际到仓时间不晚于该船截止;无候选或资料不全则写入“未安排”。因取消、晚到而进入未安排的货不属于“从未归班”,须走第 6.10 节。
|
||
- 同一收货批次只能有一个有效计划归班。自动匹配以收货仓、渠道、港口和最早可赶上的有效实例为条件;同一时间存在多班时,必须有固定排序规则。
|
||
- 归班在入仓计划审核、预计到仓时间/渠道/仓库变化或实际收货确认时重新计算;船期取消时只新增一条 `UNMATCHED` 变更记录并标记待人工处理,不自动塞入下一班。船期仍为 `OPEN` 且尚未达到最终收货截止时间、没有锁班/订舱事实时允许动态重算。船期达到截止时间后冻结当前归班,即使实例仍显示为 `OPEN` 也不再自动换到下一班;截止前尚未匹配的对象才继续寻找后续可赶船。运营确认订舱、船期进入 `CLOSED` 或船期取消后的改配必须产生新的人工或明确改配记录。
|
||
|
||
建议的状态落库规则是:预计匹配但尚未收货为 `FORECAST_MATCHED`;实际收货时间不晚于截止且有审核实测体积为 `RECEIVED`;实际收货晚于截止仍沿用原计划为 `LATE`;没有可解释的计划归班为 `UNMATCHED`;重新计算后被新结果替代的旧记录为 `SUPERSEDED`。这些状态首版必须持久化,详情中追溯匹配规则、时间、来源和被替代关系。
|
||
|
||
首版自动归班采用“写入事实 + 版本化重算”:
|
||
|
||
- 预计匹配、实际收货、晚到和未安排都写入归班事实,不能只在查询时临时计算。
|
||
- 船期仍为 `OPEN` 且未锁班时,重新计算不覆盖旧记录,而是将旧记录标记 `SUPERSEDED` 并写入新版本。
|
||
- 船期进入 `CLOSED` 或已确认订舱后,原归班结果保留;取消、甩柜或改配只新增变更记录,不静默改写历史。
|
||
- V1 仅开放第 6.10 节的异常重新安排,来源记录为 `MANUAL`;正常货量的批量改绑、拆分分配和强制赶船后置。
|
||
- 冻结的是船期归属,不是收货事实的数值。冻结后最终审核、复测、冲正仍更新事实版本及其统计值;原归属保留,不因数值变化自动换船。
|
||
|
||
### 6.5 货量统计
|
||
|
||
V1 建议至少将“已收体积”和“预计体积”分开;“预占体积”只有在确认需要锁定容量后再启用:
|
||
|
||
- **已收体积**:入仓事实已达到产品确认的可计入状态,并有有效体积。
|
||
- **预计体积**:按预计归班的未收货量单独展示,不直接计入“已收体积”,也不自动扣减容量,除非产品确认要按预计量锁舱。
|
||
- **预占体积**:已明确锁定该船期但尚未形成收货事实的货量;V1 若不做预占则固定为 0。
|
||
- **剩余体积**:配置容量上限时按 `容量上限 - 已收体积 - 预占体积` 计算;V1 未启用预占时预占值为 0,预计体积不扣减剩余容量;结果不得小于 0,无上限显示“—”。
|
||
|
||
体积字段必须统一单位为 m³,并保存统计快照时间。V1 只统计明确达到“已收货”状态、且有正数 `actualVolumeCbm` 的收货批次;实测体积是后续作业的权威输入,申报体积仅作对比,计费体积不能冒充“已收体积”。当前 `inboundedAt` 会在多个入库流程节点写入,不能单独视为最终收货事实;V1 必须新增或正式暴露稳定的收货批次事实,不能把 `inboundedAt` 作为替代。取消、草稿、作废或不符合统计口径的记录不计入已收体积。拆批、复测和冲正均按收货批次版本守恒。
|
||
|
||
### 6.5.1 V1 数据来源契约(必须一并实现)
|
||
|
||
当前入仓模型已经有 `warehouseId`、`channelCode`、`expectedArrivalAt`、`actualVolumeCbm` 和测量审核状态,但没有稳定的收货批次 ID、实际收货时间和可靠的未收预计 CBM;现有 `originalVolumeCbm` 是客户申报值,不能作为预计可赶体积。首版必须补齐下面两类事实,不能在页面层临时拼接:
|
||
|
||
| 事实 | V1 必须提供的字段 | 统计和归班规则 |
|
||
| --- | --- | --- |
|
||
| 收货批次事实 `ReceiptBatchFact` | `receiptBatchId`、`inboundId`、`receivingWarehouseId`、`channelCode`、起运港/目的港快照、`receivedAt`、`actualVolumeCbm`、最终测量批次 ID、审核状态、冲正/作废状态 | 仅 `RECEIVED/APPROVED` 且实测 CBM 大于 0 的批次计入已收;复测替换同一批次最新有效值,冲正/作废不重复计入 |
|
||
| 未收货量预报 `InboundVolumeForecast` | `forecastId`、`inboundId` 或可收货批次键、收货仓/渠道/起运港/目的港、`expectedArrivalAt`、`forecastVolumeCbm`、来源、有效状态、版本和更新时间 | 只有有效状态、预计体积大于 0 且有 ETA 的预报可计入预计可赶量;不得使用 `originalVolumeCbm`、申报箱数或计费体积代替 |
|
||
|
||
补充规则:
|
||
|
||
- 当前系统没有上述稳定事实的部分,V1 必须由 Firstleg 提供正式的收货批次和货量预报服务事实;页面不直接读取入仓单分页拼装。没有可靠预报时允许只显示已收和风险,但应显示“未提供有效预报”及缺资料票数,不能把未知量写成“预计无货”。数据生产入口按第 6.5.2 节落地。
|
||
- 预报和实收按明确的预报批次关联,不以同一 `inboundId` 就认定整票预报已收完。实收只替换其明确对应的预计批次;未到货部分保留独立有效预报,禁止用实测 CBM 直接减整票预计 CBM。具体拆批与复测规则见第 6.5.2 节。
|
||
- 航线匹配优先使用入仓/预报事实中的起运港和目的港;事实缺失时,仅当启用渠道存在唯一的默认起运港/目的港映射,才使用该映射并保存 `route_source=CHANNEL_DEFAULT`。渠道没有唯一默认航线时进入 `UNMATCHED`/风险,不按目的仓名称或自由文本猜测。
|
||
- 一票分批到仓时按批次统计;复测、冲正和作废必须以版本或状态替换旧值,并保留审计历史。
|
||
- 收货仓、渠道或航线缺失时,批次/预报进入 `UNMATCHED` 或风险清单,不得按渠道名称、订单创建时间或目的地名称猜测。
|
||
|
||
日历汇总应与右侧详情使用同一服务端统计口径,避免页面格子和详情卡片出现不同数字。建议由服务端按收货仓、渠道、船期、收货日聚合后一次返回;前端不得跨分页拉取入仓单再自行求和。超过容量时返回明确的 `overCapacity=true`,前端只负责展示。月历按开船日期展示班次,详情中应能展开实际收货日期和未安排/晚到记录,避免把“收货发生日”和“船期开船日”混为一个日期。
|
||
|
||
日期维度固定为两种:月历落格使用 `etdAt` 的本地日期;已收体积明细保留收货批次的 `receivedAt` 日期。V1 月历默认按“开船月”汇总,并将归属该船期的已收实测体积展示在对应开船日;收货月只用于详情展开和后续报表,不参与首版月历主合计。没有稳定 `receivedAt` 的批次只能进入风险/未安排,不能回退使用 `inboundedAt`。月历主合计排除已取消实例;取消实例仍可查历史快照,当前货量进入独立异常待办,重新安排后只计入目标实例,不能在两班重复相加。
|
||
|
||
### 6.5.2 事实产生、分批收货与预报替换
|
||
|
||
- **实际收货来源**:仓库完成一批到货确认时生成稳定收货批次业务键,记录真实 `receivedAt`、仓库和对应箱/件范围;最终测量审核通过后,该批次的实测体积才能计入已收。到货确认和最终审核是两个独立条件,审核操作时间不能冒充实际到仓时间。到货待审核单列票数;体积未知显示“待测量”,不补成 0 m³。
|
||
- **未收预报来源**:V1 推荐由运营在入仓详情的“货量预报”区域维护预计批次、预计到仓时间、预计 CBM 和航线,Firstleg 保存来源、录入人及版本;更新/作废预报沿用入仓写权限,服务端核验仓库范围。日历消费这些正式事实。客户申报值可作录入参考,但不得未经运营确认直接成为有效预报。
|
||
- **分批规则**:一条有效预报表示一批可对应收货的货物;部分到仓前或确认到仓时,将其拆成“本次到货预报部分”和“剩余未到货部分”,子批次保留父预报键、各自预计 CBM 和预计到仓时间。拆分必须显式确认且预计量合计等于拆分前预计量;修订预计总量则另存一个有原因的新版本。
|
||
- **替换规则**:本次到货批次一经确认,就停止把该部分计为未收预报;尚未测量审核时归入“已到待审核”,审核通过后用该批的实际体积替换。剩余批次继续按自己的预计体积统计。复测只更新对应实收批次,不能改变其他未到货批次的预计量。
|
||
- **无法对应**:实收与预报无法确认对应关系时,相关预计量暂列“待核对”,不与可能重复的实收量一起计入可赶总量;展示缺口原因,运营补齐关系后再恢复。不得凭实测体积相近自动抵消。
|
||
- **首版批次关联入口**:审核事件若能携带 `SAILING_FORECAST:<forecastKey>`,服务端只消费该预报批次;多条有效预报且事件没有明确批次键时,实收保持 `UNMATCHED`/待核对,不自动按相近体积抵销。运营可通过预报维护接口显式绑定实收批次、修订预报或作废预报,所有动作均追加版本并保留原记录。
|
||
- **事件与版本**:到货、审核、预报修订/拆分、冲正和作废均带稳定业务键及版本;同一事件重复到达不重复记量。来源版本更新与归班更新必须具备重试及对账机制,返回事实版本、归班版本和计算时间,不能只声明建了四张表就认为数据链路完成。
|
||
|
||
示例:整票预计 100 m³,先拆成本批预计 40、后批预计 60。本批实际到货审核后为 45,则“已收 45 + 未收预计 60 = 可赶 105”;复测为 43 后变成 103。不能用 `100 - 45` 把后批预计改成 55。
|
||
|
||
### 6.6 订柜数量建议
|
||
|
||
订柜数量是只读的运营建议,不是正式订舱或货柜创建。首版按以下口径展示:
|
||
|
||
```text
|
||
本船可赶实收 CBM = 当前有效归班为 RECEIVED 的审核实测 CBM
|
||
可赶总 CBM = 本船可赶实收 CBM + 有效未收预计可赶 CBM
|
||
已收口径建议柜数 = 向上取整(本船可赶实收 CBM / 规划装载容量)
|
||
建议订柜数 = 向上取整(可赶总 CBM / 规划装载容量)
|
||
已订规划容量 = 求和(每个有效订舱需求的柜数 × 该柜型规划装载容量)
|
||
待补 CBM = max(0, 可赶总 CBM - 已订规划容量)
|
||
待补柜数(某柜型方案) = 向上取整(待补 CBM / 该方案柜型规划装载容量)
|
||
```
|
||
|
||
- “有效预计 CBM”只取有明确预计到仓时间、渠道/仓库/航线匹配且不晚于最晚到仓时间的有效入仓计划;没有 ETA 的货列为风险,不直接导致多订柜。
|
||
- `LATE` 仍保留在该船历史和已收总量中,但不计入本船可赶实收或订柜建议。已收总量与可赶量是不同指标,不要求相等。
|
||
- 超过截止但尚无到货确认的原预报,保留原归属并标为“到仓待核实”,退出有效预计可赶量;实际到货事实到达后依据真实到仓时间判为准时或晚到。不得仅凭“当前时间已过截止”把所有未审核批次判成晚到。
|
||
- `规划装载容量`、柜型、重量和装载约束复用现有统一柜型目录和智能排柜规则,不在船期日历中另写一套容量或利用率。未选择柜型时,按 40GP/40HQ/45HQ 分别展示方案,不能给出无条件的单一柜数。
|
||
- 已订柜型混合时,展示各柜型数量及合计柜数,先按各自规划容量折算已订容量再计算待补量;不能拿“已订 1 个小柜”直接抵掉“建议 1 个大柜”。柜型容量缺失时待补数显示“待核实”,不默认为 0。
|
||
- 班次容量上限只触发超量预警,不用它截断可赶总量。例如可赶 150 m³、班次上限 100 m³,建议仍按 150 m³ 计算,并标明超出 50 m³;不能少显示所需柜数。
|
||
- 各柜型方案表示“如果待补量全部采用该柜型”的体积初算,未执行重量、混装及实际摆放优化;页面不用“必须订柜数”表示已经确认的订舱指令。
|
||
- 页面必须标注“按体积初算,最终以智能排柜/人工确认结果为准”;重量、箱数、货物混装限制或数据缺失会使结果进入风险提示。
|
||
- 已订柜数只读取 V1 计划船期-订舱关联下的有效 `RELEASED` 订舱需求;日历只保存建议计算的输入、规则版本、计算时间和结果快照,不直接生成正式柜次。
|
||
|
||
### 6.7 容量提示
|
||
|
||
- 0%~80%:正常。
|
||
- 80%~100%:接近满舱提示。
|
||
- 大于 100%:超容量提示;V1 不自动阻断只读汇总,也不提供绕过容量的强制关联。异常重新安排仍须满足目标船期状态、截止时间和履约锁定校验。
|
||
- 无容量上限:展示“—”或“不限”,不参与百分比计算。
|
||
|
||
### 6.8 船期实例状态转换
|
||
|
||
建议只允许以下状态转换:
|
||
|
||
```text
|
||
DRAFT → OPEN → CLOSED → DEPARTED → COMPLETED
|
||
└──────────────→ CANCELLED
|
||
OPEN → CANCELLED
|
||
CLOSED → CANCELLED(尚未开船;必须确认影响)
|
||
```
|
||
|
||
`CLOSED` 表示停止收货,`DEPARTED`/`COMPLETED` 表示计划记录的运营状态,不得代替现有订舱、装柜或实际船节点事实。实例一旦有有效收货或预占记录,不能回到 `DRAFT`,取消后仍保留货量关联和审计历史。
|
||
|
||
建议 `OPEN → CLOSED` 由服务端根据最终截止时间自动生效,页面只展示结果;`CLOSED` 不因刷新或修改筛选重新打开。`DEPARTED`、`COMPLETED` 在 V1 仅允许明确的运营动作或后续已确认事实推进,不能由“预计开船时间已到”直接推断。
|
||
|
||
### 6.9 停用、取消和甩柜
|
||
|
||
- 渠道停用只阻止新模板和新实例生成;已经生成且仍为 `OPEN` 的实例默认继续有效,是否批量关闭必须由运营人员明确操作。
|
||
- 取消实例前必须展示影响范围:预计归班量、已收量、预占量和已关联订舱/柜次。已有已收量的实例只能取消或进入改配流程,不能删除。
|
||
- 取消后,将原有效归班标记 `SUPERSEDED`,新增 `UNMATCHED` 版本并保存原船期及 `reason=CANCELLED`,展示为“未安排/待人工处理”;不自动把货量塞进下一班。新版本保留事实引用,原版本可追溯,不原地覆盖历史。
|
||
- 同一取消操作将有效订舱关联改为 `REVOKED`,保留历史快照;不自动作废、重开或修改正式订舱。关联解除后当前订柜统计重新计算,正式订舱是否改船由已有订舱流程处理。
|
||
- 甩柜/改配时保留原实例、原归班和实际收货事实,新增目标实例关联;不能回写或覆盖现有港务船期、订舱资料或报关事实。
|
||
|
||
### 6.10 V1 受限的异常重新安排
|
||
|
||
这是本轮推荐的范围补充,解决“异常已展示,但首版没有办法处理”的缺口。只允许处理未安排、晚到或因船期取消而待处理的货,正常批次批量改绑仍后置。
|
||
|
||
1. 运营在异常待办选择货物,查看原船期、当前事实版本、原因及匹配候选;选择目标并填写原因后确认,保存前先显示两班的货量变化。
|
||
2. 目标须仓库、正式渠道、港口相符,状态 `OPEN` 且当前未过截止;预报另核验预计到仓时间,实收另核验真实到仓时间。V1 不提供绕过截止、强行修改实收时间或跨仓挪量的入口。
|
||
3. 服务端核验独立异常安排权限、当前归班版本及目标船期版本;原有效版本转 `SUPERSEDED`,新版本写 `MANUAL`,保留原/新船期、操作者、时间和原因。并发重复确认只能成功一次。
|
||
4. 货物如已绑定真实柜次、预装或装柜履约,先阻止在日历直接改配,并指向既有履约流程;不得通过改日历归班绕过实物锁定。无符合条件的目标时继续待安排,可先新增临时船期。
|
||
5. 归班重新安排不自动转移订舱关联;目标班的已订柜仍只来自重新校验后建立的有效订舱关联。
|
||
|
||
## 7. 建议数据模型
|
||
|
||
以下为规划字段,具体命名应在接口和 SQL 评审时统一。
|
||
|
||
### 7.1 `fl_sailing_channel`
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `id` | 渠道业务主键 |
|
||
| `channel_code` | 稳定渠道代码;固定渠道可映射现有渠道目录,自定义渠道也必须先建立稳定代码。现有装柜的九类渠道分组只作为兼容映射,不能把枚举写死为日历渠道全集 |
|
||
| `channel_name` | 渠道名称,如 MATSON / 美森 |
|
||
| `source_type` | `CATALOG`(复用现有物流渠道)或 `SCHEDULE_ONLY`(仅用于排班,尚未映射为可订舱渠道) |
|
||
| `mapped_channel_code` | 自定义排班渠道对应的正式物流渠道代码,可为空;为空时不能被订舱/价格接口当作正式渠道 |
|
||
| `default_departure_port_code` | 默认起运港,可被模板覆盖 |
|
||
| `default_destination_port_code` | 默认目的港,可被模板覆盖 |
|
||
| `default_cutoff_days` | 默认提前收货天数 |
|
||
| `status` | `ACTIVE` / `INACTIVE` |
|
||
| `remark` | 备注 |
|
||
| `created_at` / `updated_at` | 审计时间 |
|
||
|
||
标准渠道优先复用现有仓库范围内的 ACTIVE 物流渠道和稳定 `channelCode`,不能只保存一个自由文本名称。自定义排班渠道必须通过 `SCHEDULE_ONLY` 明确标记,后续若要用于订舱或报价,先完成正式渠道映射。渠道停用不影响历史实例和历史统计;停用后不能新建模板或自动生成未来实例。
|
||
|
||
### 7.2 `fl_sailing_template`
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `template_no` | 模板业务编号 |
|
||
| `channel_id` | 所属渠道 |
|
||
| `receiving_warehouse_id` | 收货仓;截止规则和可赶船判断的范围键 |
|
||
| `template_type` | V1 固定为 `WEEKLY` |
|
||
| `weekdays` | 周一至周日的选中集合 |
|
||
| `effective_from` / `effective_to` | 规则有效期 |
|
||
| `departure_time` | 开船本地时间 |
|
||
| `voyage_rule` | 船名/航次生成规则快照 |
|
||
| `transit_days` 或 `eta_time` | 预计到港计算依据 |
|
||
| `cutoff_days` | 模板级提前收货天数 |
|
||
| `cutoff_local_time` | 收货仓默认截收时刻,可被实例覆盖 |
|
||
| `default_capacity_cbm` | 可选的班次计划容量;不替代统一柜型目录的柜容 |
|
||
| `status` | `DRAFT` / `ACTIVE` / `INACTIVE` |
|
||
|
||
### 7.3 `fl_sailing`
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `sailing_no` | 船期实例业务编号 |
|
||
| `template_id` | 来源模板,可为空(独立单次船期) |
|
||
| `channel_id` | 渠道快照引用 |
|
||
| `channel_code_snapshot` / `channel_name_snapshot` | 生成时的渠道编码和名称快照 |
|
||
| `receiving_warehouse_id` | 收货仓 |
|
||
| `departure_port_code` / `destination_port_code` | 港口快照 |
|
||
| `vessel_name` / `voyage_no` | 船名和航次 |
|
||
| `etd_at` / `eta_at` | 预计开船、预计到港 |
|
||
| `suggested_cutoff_at` | 计算出的建议截止时间 |
|
||
| `cutoff_at` | 最终收货截止时间 |
|
||
| `cutoff_override` / `cutoff_override_reason` | 是否人工覆盖及原因 |
|
||
| `cutoff_rule_version` | 计算时使用的时区、自然日/工作日和截收时刻规则版本 |
|
||
| `capacity_cbm` | 可选的班次计划容量上限;空值表示不做班次容量提示,不替代统一柜型目录的柜容 |
|
||
| `container_type_code` | 默认柜型;为空时按可用柜型分别给出建议 |
|
||
| `status` | 实例生命周期状态 |
|
||
| `generated_key` | 模板生成幂等键 |
|
||
| `version` | 乐观锁版本 |
|
||
| `created_at` / `updated_at` | 审计时间 |
|
||
|
||
不建议把已收体积、剩余体积作为可直接修改的主表字段;它们应由关联事实聚合,必要时做带统计时间的服务端快照。
|
||
|
||
### 7.4 `fl_sailing_assignment`(V1 自动归班;V1 受限异常重新安排)
|
||
|
||
V1 必须由服务端持久化自动归班结果;常规人工改绑、拆分分配和预占仍放到 Phase 2,第 6.10 节的异常重新安排除外。归班对象以收货批次或货量预报为最小粒度,不以整张订单或整张入仓单代替。`sailing_id` 可以为空,表示已记录但尚未找到可解释班次的 `UNMATCHED` 结果。
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `id` | 归班事实 ID |
|
||
| `sailing_id` | 船期实例;`UNMATCHED` 时为空 |
|
||
| `inbound_id` / `receipt_batch_id` / `forecast_id` | 归班对象;至少有一个稳定业务键 |
|
||
| `assignment_source` | V1 为 `AUTO`;预留 `MANUAL`、`ROLLOVER` |
|
||
| `assignment_version` | 同一归班对象的版本号,重算递增 |
|
||
| `match_status` | `FORECAST_MATCHED` / `RECEIVED` / `LATE` / `UNMATCHED` / `SUPERSEDED` |
|
||
| `volume_cbm` | 本次归属的已收实测体积;V1 固定来自审核后的 `actualVolumeCbm` |
|
||
| `forecast_volume_cbm` | 本次归属的未收有效预计体积,与已收体积分开 |
|
||
| `forecast_source` | 预计体积来源;没有可靠预计体积时为空,不得用申报值冒充已收体积 |
|
||
| `route_source` | 航线来自入仓/预报事实或 `CHANNEL_DEFAULT`,用于追溯匹配依据 |
|
||
| `volume_source` | 已收状态固定为 `MEASURED_ACTUAL`;仅预计状态为空,计费/申报仅作对比或后续扩展 |
|
||
| `rule_version` | 自动归班规则版本 |
|
||
| `matched_at` / `superseded_at` | 归班和被替代时间 |
|
||
| `locked_at` / `lock_reason` | 达到船期截止、确认订舱或船期关闭后冻结归班的时间和原因 |
|
||
| `operator_id` / `reason` | 自动任务/后续人工操作审计 |
|
||
|
||
同一批次或预报在同一时刻只能有一个 `AUTO` 有效归班版本;重新计算采用旧版本 `SUPERSEDED` + 新建版本,不能静默覆盖旧历史。
|
||
|
||
“同一业务对象”在这里指收货批次,不是整票订单或整张入仓单;一票分批到仓、复测或冲正时,必须按批次守恒,不能用单据级唯一键把后续批次锁死。
|
||
|
||
### 7.5 `fl_sailing_booking_advice_snapshot`
|
||
|
||
这是订柜建议的可追溯快照,不是正式订舱记录。每次刷新或人工确认建议时保存:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `sailing_id` / `container_type_code` | 船期和柜型 |
|
||
| `received_cbm` / `forecast_catchable_cbm` / `risk_cbm` | 已收、可赶预计和风险体积 |
|
||
| `planning_capacity_cbm` | 从统一柜型目录读取的规划容量快照 |
|
||
| `recommended_container_count` | 按体积初算的建议柜数 |
|
||
| `booked_container_count` / `shortage_count` | 已订柜数和待补柜数 |
|
||
| `rule_version` / `calculated_at` | 计算规则版本和计算时间 |
|
||
| `risk_codes` | ETA 缺失、重量缺失、箱数缺失、混装限制等风险 |
|
||
|
||
### 7.6 `fl_sailing_booking_link`(V1 最小订舱关联)
|
||
|
||
首版要显示“已订柜数”和“待补柜数”,因此必须在 V1 建立计划船期与订舱的最小关系;复杂的实际柜次、甩柜和海运节点关联仍放到后续阶段。
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `sailing_id` | 计划船期实例 |
|
||
| `ocean_booking_id` | 海运订舱 |
|
||
| `link_status` | `ACTIVE` / `REVOKED`;解除关系保留历史 |
|
||
| `booking_status_snapshot` | 关联时的订舱状态快照;当前只有订舱状态为 `RELEASED` 才允许计数 |
|
||
| `warehouse_id` / `channel_code` / 起运港 / 目的港 | 关联时的业务匹配快照 |
|
||
| `linked_at` / `operator_id` / `reason` | 关联审计 |
|
||
|
||
V1 采用以下计数契约:
|
||
|
||
- 只有当前订舱状态为 `RELEASED` 且关联记录为 `ACTIVE` 的订舱才计入已订柜;`DRAFT`、已作废或已解除关联的不计入。`RELEASED` 是订舱状态,不是关联记录状态。
|
||
- 已订柜数按该订舱的柜型需求 `quantity` 汇总,不按已生成真实柜次数量、不按装柜完成数量替代。
|
||
- 一个 `RELEASED` 订舱首版只能关联一个计划船期;如果未来需要一个订舱拆到多班,再升级到按柜型需求行分摊的关系。
|
||
- 关联时必须校验收货仓、渠道代码、起运港、目的港和计划船期一致;不一致进入冲突,不允许仅凭船名/航次文字关联。
|
||
- 订舱解除、作废或计划取消后,已订柜数应在服务端重新计算,并保留原关联历史;不得把已订柜数当作人工可编辑字段。
|
||
|
||
## 8. 接口草案
|
||
|
||
接口路径和 DTO 以最终服务端归属为准,以下只作为拆分和评审入口:
|
||
|
||
| 能力 | 建议接口 | 说明 |
|
||
| --- | --- | --- |
|
||
| 月历查询 | `GET /platform/v1/sailings/calendar` | 按月份、收货仓、港口、渠道、关键词、状态返回实例和服务端汇总;同时返回未安排/晚到摘要 |
|
||
| 实例详情 | `GET /platform/v1/sailings/{sailingId}` | 返回实例、统计、关联货量和操作权限 |
|
||
| 新增单次实例 | `POST /platform/v1/sailings` | 幂等创建,支持无模板来源 |
|
||
| 编辑/取消实例 | `PUT/PATCH /platform/v1/sailings/{sailingId}` | 乐观锁;取消保留历史 |
|
||
| 渠道管理 | `GET/POST/PUT /platform/v1/sailing-channels` | 启用、停用和主数据维护 |
|
||
| 模板管理 | `GET/POST/PUT /platform/v1/sailing-templates` | 周模板和启停;固定单次船期直接走实例接口 |
|
||
| 生成预览 | `POST /platform/v1/sailing-templates/{id}/generation-preview` | Phase 1 必须支持;返回日期、航次、冲突和截止时间预览 |
|
||
| 批量生成 | `POST /platform/v1/sailing-templates/{id}/generate` | 使用幂等键,返回成功、跳过和冲突清单 |
|
||
| 货量明细 | `GET /platform/v1/sailings/{id}/volume-summary` | V1 返回自动归班结果、服务端汇总和收货批次明细,不要求已经存在人工改绑 |
|
||
| 收货批次事实 | Firstleg 服务正式事实查询(路径以 Controller 归属评审为准) | 提供 `receiptBatchId`、`receivedAt`、最终实测 CBM、审核/冲正状态及仓库/渠道/航线快照;船期页面不得从入仓单分页自行拼装 |
|
||
| 货量预报事实 | Firstleg 服务正式事实查询(路径以 Controller 归属评审为准) | 提供 `forecastId`、`expectedArrivalAt`、`forecastVolumeCbm`、有效状态、版本和来源;没有可靠预报时只返回风险/未安排 |
|
||
| 订柜建议 | `GET /platform/v1/sailings/{id}/booking-advice` | 按柜型返回已收、可赶预计、风险、已订柜、建议柜数和待补柜数;只读 |
|
||
| 订舱关联 | `POST /platform/v1/sailings/{id}/booking-links` | V1 关联一个 `RELEASED` 订舱并校验仓库、渠道、港口和船期;只建立最小关系,不创建柜次 |
|
||
| 手工关联/改绑 | `POST /platform/v1/sailings/{id}/volume-allocations` | Phase 2+;校验截止、容量、收货仓归属和幂等键 |
|
||
| 变更历史 | `GET /platform/v1/sailings/{id}/history` | 实例、模板和手工改绑审计 |
|
||
|
||
所有写接口建议使用 `Idempotency-Key`、`expectedVersion` 和服务端权限校验;错误响应应能区分重复生成、容量不足、已截止、状态冲突和归属不一致。
|
||
|
||
## 9. 权限建议
|
||
|
||
权限按“看”和“改”拆分,至少包括:
|
||
|
||
- `platform:firstleg:sailing-calendar:list`:查看月历、详情、货量汇总和只读历史。
|
||
- `platform:firstleg:sailing-calendar:create`:新增单次船期。
|
||
- `platform:firstleg:sailing-calendar:edit`:编辑、复制和取消。
|
||
- `platform:firstleg:sailing-volume:exception-reassign`:V1 受限异常重新安排;只能处理取消、晚到和未安排货量,不能绕过截止、容量或真实履约锁定。
|
||
- `platform:firstleg:sailing-volume:bind`:常规人工改绑、拆分和预占,Phase 2+ 才启用。
|
||
- `platform:firstleg:sailing-booking:link`:建立或解除计划船期与订舱的最小关联。
|
||
- `platform:firstleg:sailing-template:manage`:渠道、模板、批量生成和停用。
|
||
- `platform:firstleg:sailing-volume:bind`(Phase 2+):关联/改绑货量。
|
||
- 审计导出或跨业务检索如确有需要,再单独增加权限;普通详情历史不另造重复的只读权限。
|
||
|
||
权限只由服务端决定,前端按钮隐藏不能替代后端校验。
|
||
|
||
## 10. 分期建议
|
||
|
||
### Phase 1:可用的日历排班
|
||
|
||
- 渠道主数据、单次船期、周模板。
|
||
- 截止时间计算和实例生成。
|
||
- 生成预览、冲突清单和确认后批量生成。
|
||
- 月历、日期详情、渠道维度的预计/已收体积汇总。
|
||
- 服务端自动归班的只读结果:预计可赶船、已收、晚到风险、未安排。
|
||
- 按现有柜型目录计算只读订柜建议:可赶总 CBM、建议柜数、已订柜数、待补柜数和风险提示。
|
||
- 保存自动归班事实和版本历史;建立计划船期与 `RELEASED` 订舱的最小关联,用于计算已订柜数;为取消、晚到和未安排货提供受限异常重新安排。
|
||
- 实例编辑、取消和基础历史。
|
||
|
||
Phase 1 不提供正常货量的批量改绑、强制赶截止和容量预占;只提供第 6.10 节的受限异常重新安排。若没有明确归班事实,只展示“未安排”,不能把按渠道汇总的体积伪装成某一班船已收。
|
||
|
||
### Phase 2:排班辅助和容量控制
|
||
|
||
- 模板复制和批量调整。
|
||
- 在 V1 已实现的收货仓、渠道、港口、截止时间和确定性排序基础上,Phase 2 再增加复杂优先级、容量约束和多班冲突处理。
|
||
- 容量阈值、超容拦截和有权限的强制处理。
|
||
- 手工关联、改绑、预占和货量明细。
|
||
|
||
### Phase 3:运营增强
|
||
|
||
- 工作日/节假日日历。
|
||
- 月度/渠道汇总导出和报表。
|
||
- 截止提醒、异常通知和更完整的审计检索。
|
||
- 与真实柜次、港务船期、甩柜/改配和实际海运节点的完整可追溯关联,但不覆盖已有运输事实;V1 的最小订舱关联已经前置。
|
||
|
||
## 11. 验收标准
|
||
|
||
1. 新建“美森每周周四”模板,预览并生成指定月份的所有周四实例;再次生成不产生重复实例。
|
||
2. 新增一条固定船期,能够编辑开船时间、船名、航次、截止时间和容量;已有归班或已收货事实后只能取消,不能物理删除。
|
||
3. 日历格和日期详情显示同一套渠道统计口径;至少能区分预计体积、已收体积、未安排/晚到和剩余容量。
|
||
4. 根据收货仓、起运港、渠道、船名/航次和月份筛选后,月历数量、日期详情和统计合计一致;跨月日期不会重复或漏显示。
|
||
5. 截止时间按服务端规则计算,人工覆盖有明确标识;超过截止或已取消的船期不能被自动推荐。
|
||
6. 接近容量上限和超容量有明确状态;V1 只读提示,不能因为体积初算直接生成正式柜次。
|
||
7. 停用渠道的历史实例可查询,停用后不能创建新的模板实例;渠道改名不改变历史实例的名称快照。
|
||
8. 写接口具备幂等和乐观锁;重复提交、并发编辑、重复生成均返回可理解的冲突信息。
|
||
9. 列表/详情只读、实例编辑/取消、模板管理、货量改绑分别受权限控制;操作历史可追溯到操作者和原因。
|
||
10. 页面展示业务编号、船名、航次、港口和渠道,不以内部数据库 ID 作为用户可见回退。
|
||
11. 对一班固定船和一班临时船,系统能分别展示已收 CBM、截止前预计 CBM、风险 CBM、已订柜数、建议订柜数和待补柜数;建议结果标注计算时间、柜型和规则版本,并明确最终以智能排柜/人工确认结果为准。
|
||
12. 同一收货批次先产生预计归班,之后实际收货并复测;系统保留旧归班版本、只计最新有效实测体积,且不把预计体积与实测体积重复相加。
|
||
13. 同一时刻建立两班船,自动归班结果稳定选择 `sailing_no` 较小者;船期截止后刷新页面,原归班仍显示为 `LATE` 或 `RECEIVED`,不会变成无历史的未安排。
|
||
14. 一个 `RELEASED` 订舱关联一条计划船期,柜型需求数量计入已订柜;`DRAFT` 订舱、未关联订舱和真实柜次数量不会被错误计入。
|
||
15. 自动归班在截止前可以重算;达到截止后原归班被冻结,后续实际收货标记为 `LATE` 时仍保留原船期。没有稳定收货批次或预报事实时只显示风险/未安排,不得用 `inboundedAt`、`originalVolumeCbm` 或入仓单分页合计补出数字。
|
||
|
||
## 12. 首版已冻结与后续校准
|
||
|
||
本轮已先采用以下口径,开发按此实现:
|
||
|
||
1. 船期模板/实例必须绑定收货仓、渠道、起运港和目的港;不使用“全部仓”作为模糊范围。
|
||
2. 已收只统计 V1 收货批次事实中明确完成收货、最终测量审核通过且 `actualVolumeCbm > 0` 的批次;申报体积和计费体积不计入已收。
|
||
3. V1 新增或正式暴露稳定的 `receiptBatchId + receivedAt` 收货批次事实;不能用现有会被多个流程节点更新的 `inboundedAt` 单独代替。
|
||
4. V1 新增或正式暴露 `forecastVolumeCbm + expectedArrivalAt` 货量预报事实;`originalVolumeCbm`、申报箱数和计费体积不得作为预计可赶量。
|
||
5. 预计可赶量只纳入有明确 ETA、预计体积、渠道/仓库/航线匹配且不晚于 `warehouseCutoffAt` 的有效预报;没有 ETA 或航线的货列为风险/未安排。
|
||
6. 自动归班选择最早可赶的 `OPEN` 船期,并在 V1 持久化 `FORECAST_MATCHED`、`RECEIVED`、`LATE`、`UNMATCHED` 和 `SUPERSEDED` 历史;截止前且未锁班时可以版本化重算,达到截止、`CLOSED` 或已确认订舱后冻结当前归班;船期取消只转为风险/待人工处理,不自动换班;晚到不自动改班。
|
||
7. 同一收货仓、渠道、起运港和目的港下,ETD 相同的多班船按 `sailing_no` 升序确定唯一归班;同一归班对象同一时刻只能有一个有效版本。
|
||
8. `warehouseCutoffAt` 暂定为开船本地日期前 3 个自然日、收货仓 18:00,时区为 `Asia/Shanghai`;它与官方截单/截关/截港分开。
|
||
9. V1 建立计划船期与 `RELEASED` 订舱的最小关联;已订柜数按关联订舱的柜型需求 `quantity` 统计,`DRAFT` 不计入,不按真实柜次数量替代。
|
||
10. 订柜建议复用现有柜型目录、容量和已订柜事实,只读展示建议柜数和待补柜数,不直接创建正式柜次;结果标注“按体积初算,最终以智能排柜/人工确认结果为准”。
|
||
11. V1 航次规则只支持固定值或“前缀 + ETD 本地日期”,生成实例允许人工覆盖;不做顺序号自动递增。
|
||
12. V1 采用人工预览后生成固定周模板实例;停航或临时变更先手工跳过/取消,暂不做节假日自动日历。
|
||
|
||
以下事项不阻断首版,但需要在试运行或进入后续阶段前校准:
|
||
|
||
1. 多个收货仓是否能共同供货给同一班船;如果可以,建立明确的收货仓组,再做线路级合计,不能直接跨仓混算。
|
||
2. 提前天数是否从自然日升级为工作日,周末/节假日是否接入工作日历。
|
||
3. 渠道是否需要客户专属范围;若需要,客户必须进入模板、实例、查询和权限维度。
|
||
4. 同一渠道同一天多班船的复杂运营优先级、航次生成规则和模板覆盖策略。
|
||
5. 订柜建议是否支持多柜型混合、重量约束和装载率优化;首版按体积初算,最终以智能排柜/人工确认结果为准。
|
||
6. 计划实例与真实柜次、港务船期的完整关联时机,以及甩柜、改配后的运输事实展示。
|
||
|
||
## 13. 工程接入门禁
|
||
|
||
进入实现阶段后,页面与接口需要同步维护以下入口:
|
||
|
||
- Admin 页面放在 `apps/admin-web/src/modules/` 的独立业务模块内,包含 `pages/`、`routes.ts`、`api/` 和 `types.ts`;不能把模板和实例逻辑塞入现有 `CarrierScheduleQueryPanel.vue`。
|
||
- 正式接入时同步 `src/app/router/index.ts`、`src/core/navigation/adminNavigation.ts` 和平台 IAM 权限初始化;菜单归属应在“国内仓”与“货柜管理”中选定后固定。
|
||
- Firstleg 若承载服务端能力,需同步 Controller、OpenAPI、数据库基线、Gateway 路由和权限契约;月历统计由服务端返回,前端不以 `InboundOrderSummary` 分页结果自行拼装。
|
||
- 需要优先复用现有仓库范围内的正式 `channelCode`/渠道目录,再为确有需要的排班专用渠道建立明确映射;同时校验入库实测字段、订舱/货柜船期和价格簿的引用边界。新计划不能回写港务船期观测或订舱快照。
|
||
- V1 开发前必须先落地 `ReceiptBatchFact`、`InboundVolumeForecast`、`fl_sailing_assignment` 和 `fl_sailing_booking_link` 的服务端事实与索引;Firstleg 的当前基线对应 `phase4-sailing-facts-schema.sql`、`sailingcalendar/fact/entity` 和 `SailingFactsRepository`,部署统一增量也必须包含这四张表。如果这些事实没有来源,接口只能返回风险/未安排,不能伪造已收或已订柜数字。
|
||
- 订柜建议必须读取统一柜型目录和 `RELEASED` 订舱需求;不得另建 40GP/40HQ/45HQ 容量常量,也不得用真实柜次数量替代订舱需求数量。
|
||
- 页面验收覆盖正常、空数据、加载、失败、无权限、并发冲突、已截止、超容量和已取消状态,并按 Admin 当前 1920×1080、100% 缩放基线执行。
|
||
|
||
## 14. 建议实施顺序
|
||
|
||
1. 按第 12 节首版冻结口径建立统一的术语、体积、归班、截止时间和订柜建议规则。
|
||
2. 画出模板、实例、货量关联和现有港务船期之间的关系,并评审权限与审计边界。
|
||
3. 先落地服务端查询模型和月历返回结构,再实现 Admin 月历页面,避免前端自行拼装统计。
|
||
4. 用“美森每周周四 + 一条临时船期 + 一批预计到仓数据 + 一批已收实测数据 + 一批晚到数据”做端到端造数和验收。
|
||
5. 完成 Phase 1 后再决定手工改绑、容量预占、工作日历、导出和外部船期接入,避免首期把订舱和船公司接口一起带入。
|
||
|
||
## 15. 本轮复核结论与验收算例
|
||
|
||
本轮复核后的结论是:**基础排班可以进入开发;完整的收货统计、订柜建议和异常处理必须按本节算例验收后,才能认为首版可用。** 下面的数字是验收用例,不是初始化数据,也不是对截图示例的默认值。
|
||
|
||
### 15.1 分批收货不能用整票相减
|
||
|
||
- 一票有效预报拆为 A 批 40 m³、B 批 60 m³。
|
||
- A 批实际到货并审核为 45 m³时,结果为“已收 45 m³ + 未收预计 60 m³”;不能把整票 100 m³ 直接减去 45 m³后把 B 批改成 55 m³。
|
||
- A 批复测为 43 m³时,只更新 A 批实收版本;B 批仍为 60 m³。任一版本重复消费都不得重复计量。
|
||
- 如果实收不能明确对应 A 或 B,相关预报暂列“待核对”,不把实收和预报同时计入可赶总量。
|
||
|
||
### 15.2 无预报直接收货与晚到的区别
|
||
|
||
- 没有预报但首次形成 20 m³审核实收,且仓库、渠道、航线明确、实际到仓时间不晚于某个 `OPEN` 船期截止:自动归入该船,状态为 `RECEIVED`。
|
||
- 已经归属于周四船的批次,实际到仓晚于周四截止:保留周四历史,状态为 `LATE`;已收总量增加,但周四的“本船可赶实收”和订柜建议不增加。
|
||
- 没有归班、资料缺失或已过截止才首次到仓的批次进入异常待办,不能仅因体积或渠道相似自动塞进下一班。
|
||
|
||
### 15.3 订柜建议要排除晚到并抵扣真实柜型容量
|
||
|
||
- 40HQ 规划容量按统一柜型目录读取,不能在日历模块复制常量。
|
||
- 一班船有本船可赶实收 40 m³、有效未收预报 28 m³、晚到 35 m³;建议计算量为 68 m³,晚到 35 m³只在已收总量/风险中展示。
|
||
- 若已有一个 40GP 和一个 40HQ 的 `RELEASED` 订舱关联,已订规划容量按两个柜型各自容量折算;不能只按“已订 2 柜”抵扣,也不能把 `DRAFT` 订舱计入。
|
||
- 班次规划容量小于可赶总量时只显示超容量和超出体积,不能静默截断可赶量或降低建议柜数。
|
||
|
||
### 15.4 取消后的异常重新安排
|
||
|
||
- `CLOSED` 且尚未开船的船期可以取消;取消会保留原归班和订舱关联历史,将当前货量转为异常待办,不自动改到下一班。
|
||
- 运营有异常重新安排权限时,选择目标 `OPEN` 船期、填写原因,系统同时校验仓库、渠道、港口、截止时间、目标版本和履约锁定状态。
|
||
- 成功后旧归班为 `SUPERSEDED`,新归班为 `MANUAL`,目标班只计一次;目标班订柜关联必须另行建立或确认,不随货量改配自动转移。
|
||
- 已有真实柜次、预装或装柜履约锁定的货不能从日历直接改配,必须回到对应履约流程处理。
|
||
|
||
### 15.5 开发完成判定
|
||
|
||
满足以下条件才通过首版业务验收:事实来源可追溯、预报和实收不重复、晚到不增加原班可赶量、取消后有异常处理入口、订柜按柜型需求折算且仅统计有效 `RELEASED` 关联、月历与详情使用同一服务端快照。仅完成页面、接口返回空数组或通过类型/编译检查,均不能作为通过依据。
|
||
|
||
### 15.6 成熟货代系统的排船期做法
|
||
|
||
成熟系统把“排船期”视为一条可执行的运输计划,而不是日历上的一个标记,通常分为四层:
|
||
|
||
1. **服务/周班模板**:维护渠道、收货仓、起运港、目的港、固定星期、开船时间、时区、默认航程和默认截止规则。固定周班按滚动窗口生成未来 8~12 周的具体航次;模板停用只影响未来生成,不回写历史实例。Oracle OTM 也支持按固定星期时间或相对开船时间的偏移配置航次截点:[Oracle Cutoff Recovery Time](https://docs.oracle.com/en/cloud/saas/transportation/26c/otmol/planning/power_data/rates_and_codes/cutoff_recovery_time_details.htm)。
|
||
2. **具体航次实例**:每班保存自己的船名、航次、计划 ETD/ETA、内部收货截止、船司确认的各类截止、状态和容量。模板只提供默认值;船司确认或运营人工覆盖后,只更新实例并保留变更历史。
|
||
3. **货物归班事实**:预计到仓、审核后的实收批次、体积版本和当前归属独立保存,能追溯自动归班、晚到、取消和人工改配的前后航次。
|
||
4. **订舱执行**:预报量、已收量、已订柜量、待补柜量分开统计。只有已确认/已放舱的订舱关联计入已订柜,草稿和取消订舱不计入。
|
||
|
||
一个航次不应只有一个“截止时间”。至少区分:我司仓库最晚收货时间、订舱接受截止、截单/SI、截关/CY、VGM,以及危险品/冷柜等特殊货类截止。DCSA 将这些作为不同的业务节点,并指出其会随船期、码头和货类变化:[DCSA Cut-off Times](https://dcsa.org/newsroom/cut-off-times-in-shipping)。VGM 还必须在装船前提前提交,才能满足装载条件:[IMO VGM](https://www.imo.org/en/ourwork/safety/pages/verification-of-the-gross-mass.aspx)。因此,本系统的“开船前 3 个自然日、收货仓 18:00”只能作为我司内部 `warehouseCutoffAt` 默认值,不能当作船司截关时间。
|
||
|
||
自动归班采用以下顺序:先按收货仓、渠道、起运港、目的港筛选 `OPEN` 且尚未超过 `warehouseCutoffAt` 的候选,再按 ETD 最早排序。实际到仓时间等于截止时间仍算准时;超过截止时间后,未锁班、未订舱的货物自动寻找下一班,下一班不存在时进入 `UNMATCHED/待人工`。已经锁班或已确认订舱的货物即使晚到,也保留原 `sailingId`,标记 `LATE/MISSED_CUTOFF`,由运营确认是否转下一班,不能静默改写原归属。
|
||
|
||
例如美森固定周四 10:00 开船,义乌仓内部收货截止为周二 18:00:周二 17:59 和 18:00 到仓的货物可赶本周四;周二 18:01 首次到仓的货物只能进入下一周四;已经确认归属本周四的晚到货物则保留本周四并进入晚到异常。
|
||
|
||
建议状态补充 `NEAR_CUTOFF` 和 `BOOKING_CONFIRMED/LOCKED`。`NEAR_CUTOFF` 只用于预警,仍允许收货;超过内部截止后由服务端禁止新的自动归班。船司延误、甩柜或取消时,保留原实例和归班历史,生成异常待办并要求明确改配,不直接把货物静默塞入下一班。
|
||
|
||
### 15.7 从当前版本到可运营版本的改造顺序
|
||
|
||
结论是保留现有“模板 → 航次实例 → 收货归班 → 货量汇总 → 订柜建议”主模型,不重做日历页面,先补齐以下规则和闭环。
|
||
|
||
**P0:上线前必须完成**
|
||
|
||
1. 服务端统一执行收货截止门禁。候选航次必须同时满足 `status=OPEN` 和 `arrivalAt <= warehouseCutoffAt`;首次晚到不能继续挂当前航次,已存在归班的晚到只能保留原航次并标记 `LATE`。
|
||
2. 建立锁班和字段冻结。已有确认订舱、人工锁班或履约事实后,禁止直接修改收货仓、渠道、港口、ETD、截止时间等关键字段;需要调整时走复制、取消或异常改配。
|
||
3. 完善预报与实收的批次关联。同一收货批次只能消费一个有效预报;多个预报、错误的预报键、预报作废和拆分必须进入待核对,不能同时计入预报量和实收量。
|
||
4. 取消/甩柜只产生异常待办,不自动把货物静默塞进下一班;人工改配必须记录原航次、目标航次、操作者、时间、原因和目标版本。
|
||
5. 收货仓、渠道、起运港和目的港使用主数据选择并由服务端校验,不能长期依赖前端自由填写名称或 ID。
|
||
6. 月历和详情只统计同一服务端口径:已收使用审核实测 CBM,晚到不计入原班可赶量,取消航次不计入主汇总,已订柜只统计有效的 `RELEASED/CONFIRMED` 关联。
|
||
|
||
**P1:首版稳定运行后完成**
|
||
|
||
1. 增加预报维护、异常待办、人工改配、订舱关联/撤销等 Admin 操作入口。
|
||
2. 模板支持完整有效期、停班例外、跨月生成和生成前冲突预览;默认滚动生成未来 8~12 周。
|
||
3. 增加 `NEAR_CUTOFF`、待补柜、超容量、未安排等风险提示,并提供按渠道的 CBM 和柜量分组汇总。
|
||
4. 所有写操作接入统一幂等和版本校验,避免重复点击、重复生成和并发覆盖。
|
||
|
||
**P2:运营增强**
|
||
|
||
再接入船司/码头实际截点、节假日工作日历、容量预占、自动提醒、外部船期同步和更复杂的混合柜型优化。它们不应阻塞首版排船和收货统计上线。
|
||
|
||
### 15.8 当前展示版范围修正(以本节覆盖前文自动归班口径)
|
||
|
||
本轮需求已明确:船期日历先作为**展示和决策辅助页面**,不接管现有收货、预报、智能排柜或订舱流程。前文关于 `Sailing Assignment` 自动写入、自动下一班、晚到改配、取消后异常重排和收货审核回调的内容,全部降为后续规划,不纳入当前开发验收。
|
||
|
||
当前版本允许运营人员维护渠道、周模板和具体船期;货量相关能力全部只读:
|
||
|
||
- **已收 CBM**:读取现有收货/测量流程已经产生的有效实测结果,不在船期页面重新生成收货事实。
|
||
- **预计 CBM**:读取现有入仓预报或智能排柜可读数据;没有可靠来源时显示“暂无数据”,不凭渠道名称或订单分页猜测。
|
||
- **截止前参考量**:按船期的收货仓、渠道、起运港、目的港和 `warehouseCutoffAt` 做查询口径,只用于显示“理论上可供管理人员参考的货量池”,不表示货物已经归入该船。
|
||
- **已明确本船量**:只读取现有系统已经明确关联到该船名/航次或现有订舱/柜次事实的数据;日历不新增关联。
|
||
- **建议订柜数**:按可展示货量和统一柜型容量做初算,显示计算时间、柜型和“最终以智能排柜/人工确认结果为准”;不创建订舱、不锁舱位、不改柜次。
|
||
- **合船判断**:页面只提供“本船未满、建议关注相邻船期”的提示,并展示相邻船的可用容量和货量对比;不自动合并、不自动改绑。
|
||
|
||
展示版的数据实现口径再明确一层:已收实测只统计测量审核为 `APPROVED` 且有 `actual_volume_cbm` 的有效入库单,按上一班截止时间到当前班截止时间的窗口汇总;主单只在未拆分时统计,拆分后的有效余量子单单独统计,避免父子单重复。当前没有可核验的预计体积来源时,预计量按 0 展示并在页面提示“暂无可核验数据”,不得把申报体积、原始体积或分页列表合计冒充预计量。已订柜读取已放舱订舱的柜型需求,优先识别已有船期关联,同时允许用仓库、渠道、港口、船名、航次和开船日期做确定性只读匹配;日历页面不创建关联。建议柜数只按统一柜型容量计算,待补柜只做提示。
|
||
|
||
当前版本明确不做以下写操作:
|
||
|
||
1. 收货审核通过时,不调用船期日历服务,不写入 `fl_sailing_assignment`、预报消费或船期实收事实。
|
||
2. 创建或修订预报不自动生成船期归班;预报维护接口不作为本轮页面入口。
|
||
3. 取消船期不自动把任何货物转到下一班;货物归属仍由原智能排柜和订舱流程维护。
|
||
4. 页面不提供货量异常改配、手工绑定实收、订舱关联/解除等操作;这些操作留在现有业务流程。
|
||
|
||
因此,本轮验收重点从“自动归班正确”改为:船期维护正确、展示口径透明、已收/预计/已订柜/建议柜数不重复、取消或截止只影响展示标签、不会改变原收货和智能排柜数据。后续如果需要让管理人员在页面上确认“合并到哪一班”,再单独增加人工确认动作和正式归属模型。
|