23 KiB
Lastleg Service 模块指引
职责与边界
- Mapper 统一显式使用
@Mapper,仅由BaiyueLastlegApplication.java对com.baiyue.lastleg根包按annotationClass = Mapper.class扫描;各业务切片不得新增*MapperConfiguration或维护包白名单。静态门禁见根scripts/mapper-scan-contract.mjs,模块内门禁见MapperRegistrationArchitectureTest.java。 - 负责消费头程装柜完成事件,保存业务 Inbox,并按最终实装入仓单建立待分配尾程订单、明细冻结快照和初始状态历史;事件不得自动创建入仓计划,平台人工调用
/api/platform/v1/last-leg/inbound-plans/convert-container后才按柜内每票生成独立计划并推进订单。 - 负责尾程订单列表、详情、导出、完整入库计划、暂扣/解除、人工节点和目的仓调整,以及海外入仓计划、托盘分配与 PDA 入仓作业;PDA 卸柜完成后通过本地 Outbox 向国内头程回传货柜报空事实。
- 不查询头程数据库,不调用头程接口补齐建单字段;事件快照缺失时应拒绝消费并保留可诊断错误。
快速索引
本表只保留定位入口;命中业务后必须用 rg -n -F '关键词' docs/business-index.md 读取对应行/段的详细约束,再沿代码引用追踪;不默认全文加载详细索引。已知目标文件时直接定位,并定向核对该业务约束。
退货预报、签收、库存、合箱与退货出库:returnreceipt/;人工计划自动生成托盘、柜级预览与头程来源托盘快照:inboundplan/。这两类入口的补充段落也在详细索引中,按对应短词查询。
| 功能 | 首个入口 |
|---|---|
| 内部模块化基线与依赖分析 | docs/lastleg-internal-modularization-baseline.md |
| 事件接收、平台订单列表/详情/导出、柜级进度、目的仓调整 | src/main/java/com/baiyue/lastleg/controller/LastlegOrderController.java |
| 尾程订单完整计划、柜级到仓时间、暂扣与人工节点 | controller/LastlegOrderController.java |
| 客户尾程订单列表与详情 | controller/MerchantLastlegOrderController.java |
| 装柜完成事件消费 | service/LoadingCompletedConsumer.java |
| 尾程单号生成 | service/LastlegOrderNoGenerator.java |
| 订单查询与更新 | service/LastlegOrderService.java |
| 业务 Inbox | entity/LastlegEventInbox.java |
| 尾程订单、明细、状态历史 | entity/LastlegOrder.java |
| 独立尾程入仓单快照 | entity/LastlegInboundOrder.java |
| 内部令牌保护 | security/InternalServiceTokenGuard.java |
| IAM 海外安全投影与实时校验 | security/controller/LastlegSecurityController.java |
| 入仓计划分页、柜级预览、转换与详情 | inboundplan/controller/InboundPlanController.java |
| 托盘分配算法 | inboundplan/service/PalletAllocator.java |
| FBA 预约创建、详情与编辑 | InboundPlanController.java |
| PDA 入仓、实收、上架与完成 | inboundplan/pda/controller/ReceivingContainerController.java |
| PDA 卸柜照片上传、归属校验与预览 | ReceivingContainerController.java |
| PDA 国内装柜照片对照 | ReceivingContainerController.java |
| 卸柜照片与海外卸柜完成 Outbox | ReceivingContainerController.java |
| 拆柜打托成本与客户应收分摊 | inboundplan/service/DestuffingPalletCostJob.java |
| 尾程暂扣状态回传 | service/InboundOrderDetainedOutboxService.java |
| 派送完成回传 | delivery/service/DeliveryCompletedOutboxService.java |
| 入仓后 SKU 库存余额、库位和流水 | inventory/ |
| 海外PDA盘点、同仓整托移库、托盘组拆换绑与换标现场作业 | pda/stocktake/ |
| 贴标申请、箱唛/SKU/组合换标与 PDA 完工 | relabel/ |
| PDA 仓内通用扫码与操作历史 | pda/warehouse/controller/PdaWarehouseController.java |
| 船节点接收与海外 PDA 可收货门禁 | shipping/service/ContainerShippingNodeConsumer.java |
| 平台排车列表/详情与智能预排 | dispatch/controller/DispatchQueryController.java |
| 排车编辑、PDA 出库、派送与交接 | dispatch/controller/DispatchController.java |
| 仓储资源调用 | inboundplan/pda/gateway/WarehouseResourceGateway.java |
| 仓储转运、历史订单/入仓绑定、可选换标联动 | transfer/controller/WarehouseTransferController.java |
| 一件代发接收、SKU/箱唛库存 | dropship/controller/DropshipOrderController.java |
| 货物废弃、待审锁箱、审批扣减 | discard/controller/CargoDiscardController.java |
| 客户拜访、派送状态与签收附件 | customervisit/controller/CustomerVisitController.java |
| 派送三类资料、摘要与历史证据 | delivery/controller/DeliveryDocumentController.java |
| 异常订单与派送退回入仓 | exceptionorder/controller/ExceptionOrderController.java |
| 海外采购、客户下单、采购审核、客户/采购仓候选与操作流水 | procurement/controller/ |
| 海外业务资金指令与结果 | wallet/WalletCommandOutboxService.java |
| 海外业务费用来源 | financefee/controller/PlatformOverseasFeeSourceController.java |
| 美国/本土卡车订单 | trucking/controller/PlatformUsTruckOrderController.java |
| 本土散板订单 | looseboard/controller/PlatformLocalLooseboardController.java |
| 海外仓报价、月度汇率、车辆主数据 | masterdata/quote/ |
| 跨服务 HTTP 客户端装配 | config/LastlegRestClientFactory.java |
| 数据库基线、表索引与中文注释契约 | src/main/resources/db/lastleg.sql |
| 测试 | src/test/java/com/baiyue/lastleg/ |
| 真实事件消费验收 | src/test/java/com/baiyue/lastleg/service/TaillegRealConsumerAcceptanceTest.java |
海外仓全流程开发路由
- 海外仓全流程连续覆盖尾程订单、入仓计划/PDA 入仓、换标、产品与箱唛快照、库存、转运、废弃、采购、本土卡车、本土散板、排车查询/智能预排、排车编辑/取消/发车、PDA 拣货与出库、一件代发、转派送、客户派送单、异常退回和客户回访。
- 服务端正式入口包括
dispatch/controller/DispatchController.java、dispatch/pda/controller/DispatchPdaController.java、delivery/controller/DeliveryTransferController.java、delivery/controller/OutboundPdaController.java、delivery/controller/MerchantDeliveryController.java、exceptionorder/与customervisit/;设备页面是否可用以海外 PDA 正式路由和能力开关为准,未由 PDA 承载的操作由 Admin/Client 完成。 - 平台一件代发出库候选由
/outbound-orders/one-click-dropship/candidates统一返回已完成入库且仍有可用箱唛、SKU 汇总和库存仓位绑定的托盘明细;查询不强制先传customerId,未传时跨客户展示供页面分页筛选,创建时必须从所选候选解析为唯一客户和唯一海外仓,禁止一张出库单跨客户或跨仓。创建后出库单按WAIT_PICK → PICKING → PICKED → OUTBOUNDED推进;海外 PDA 扫描托盘码时通过整托命令原子拣取该托盘在当前任务内全部剩余箱件和 SKU,任一库存明细失败须整托回滚,逐箱拣货入口继续保留。 - 排车转派送允许在
PENDING_DEPARTURE、PICKING、READY_TO_DEPART阶段幂等生成一张WAIT_OUTBOUND出库单,并按目的FBA仓生成多张派送单,但必须早于发车;BOL和签收资料均绑定拆分后的单仓派送单,派送费用仍只发布一次。PDA 排车详情由DispatchPdaWorkflowService返回canDepart、departBlockReason,只有车辆信息完整、拣货任务完成且箱数核平、排车为READY_TO_DEPART、关联出库单仍为WAIT_OUTBOUND时才允许发车,PDA 不得自行推导该门禁。 - 排车执行与
delivery/Controller 和其他尾程业务接口一同注册,不受功能开关控制。平台排车列表与详情仍由lastleg.dispatch-query-api.enabled=true独立启用;入库单智能排车候选、草稿锁定/解锁/释放和预排创建仍由lastleg.smart-dispatch-api.enabled=true独立启用。这些排车平台入口均不接收customerId:排车列表跨客户分页,详情按全局排车单号定位后使用记录自身客户范围加载明细;智能排车候选未传计划 ID 时跨客户查询,传入时根据计划 ID 解析范围;托盘移入草稿时立即以操作人和随机草稿标识锁定,移除或关闭时释放,草稿锁超时失效;预排创建根据所选托盘 ID 在服务端解析并校验唯一客户范围,并将当前操作人的草稿锁原子转换为正式排车锁。 - 仓库 ID、类型、状态、地址和能力唯一权威归国内
warehouse-service;海外库位、实物托盘和资源锁归warehouse-overseas-service/且只引用同一国内warehouseId。Lastleg PDA 通过 Warehouse Overseas 内部资源入口校验该国内共享收货仓,不得要求海外数据库重复建仓;浏览器 API 统一经gateway-overseas/。
数据库表索引与注释约束
| 业务域 | 表名 | 中文含义 |
|---|---|---|
| 核心订单与事件 | ll_loading_*、ll_event_*、ll_order*、ll_inbound_order* |
事件收件/发件、尾程订单、明细及状态历史 |
| 入仓计划与作业 | ll_inbound_plan*、ll_inbound_pda*、ll_inbound_pallet_merge、ll_inbound_fba_appointment* |
入仓计划、托盘分配、PDA 作业、合托及 FBA 预约 |
| 库存 | ll_inventory_*、ll_inbound_stock_receipt |
库存余额、按托盘/入仓单拆分的仓位、流水、命令幂等及实收快照;同一托盘可有多条入仓单仓位,汇总数量必须与各明细核平 |
| 派车与出库 | ll_dispatch_*、ll_outbound_*、ll_delivery_order |
派车、PDA 拣货、出库及派送 |
| 转仓与换标 | ll_warehouse_transfer_*、ll_relabel_* |
转仓单、库存转移、目标收货及换标 |
| 一件代发与废弃 | ll_dropship_*、ll_cargo_discard_* |
托盘/箱唛库存、流水和废弃审批 |
| 采购、卡车与本土散板 | ll_overseas_procurement_*、ll_us_truck_*、ll_local_looseboard_* |
海外采购、美国卡车运输及本土散板订单 |
| 通用资料 | ll_overseas_quote*、ll_monthly_exchange_rate*、ll_vehicle |
海外仓报价、月度汇率及车辆档案;不保存车辆派送履约历史 |
| 海外安全投影 | ll_security_event_inbox、ll_security_projection |
IAM 安全事件可靠收件箱与 Lastleg 本地授权投影;只允许内部服务入口访问,不作为 loading、shipping、inbound 等业务模型 |
- 新增或修改表时,表
COMMENT和每个普通字段的COMMENT必须填写准确的中文业务含义,不得只写英文、字段名或类型名。 SchemaChineseCommentContractTest自动扫描全部ll_表,无需维护表名白名单;新增字段必须同步更新基线 SQL、建表增量和存量备注迁移,增量脚本的ADD/MODIFY COLUMN不得丢失中文备注。- 表备注、字段备注和索引变更必须可重复执行;存量库的备注修订通过统一增量脚本落地,不依赖基线建表语句。
事件建单规则
- 只接受冻结契约支持的事件类型和版本;校验事件 ID、租户、区域、Payload 哈希及必要字段。
- 同一实际集装箱
containerNo生成一个共享业务尾程单号;payload.orders每项按sourceInboundId独立落一条雪花 IDll_order,并保存对应明细和初始状态历史。整柜操作不使用cabinetNo。 ONE_CLICK_DROPSHIP每个来源入仓单必须携带一个或多个不可变来源托盘快照;每托按 SKU 携带箱数、数量和箱唛。校验订单汇总一致、托盘号/SKU/箱唛唯一后,按托建立档案及 SKU 库存,并建立箱唛和流水。仓库实物托盘由 Warehouse API 分配,不得覆盖来源托盘快照。- 其他订单类型建立入仓单维度的尾程订单与冻结明细,并保留头程推荐托数;尾程入仓计划至少按推荐托数生成计划托盘,PDA 再把箱级事实绑定到海外仓实物托盘。
- 整个事件消费在一个事务内完成;任意订单失败时不得留下部分订单。
- 相同事件重复投递返回既有结果,不重新生成单号或插入明细。来源入仓单唯一约束是最终防线。
- 装柜完成事件只把尾程订单建立为
PENDING_ASSIGNMENT。FIRST_LEG在途库存使用不可变来源入仓码作为计划生成前的inbound_order_no展示快照;平台人工转换成功后,同一事务将订单推进为INBOUND_PLANNED,并把对应库存余额的该快照收口为新planNo。 - 内部接口先执行常量时间令牌校验;缺失或错误令牌不得进入消费事务。
- IAM 安全事件只能进入
/internal/lastleg/security/v1/events;通用/internal/lastleg/events只白名单接收装柜完成与实际船节点事件,禁止把 IAM 事件交给LoadingCompletedConsumer。 - Lastleg 安全投影只接受
ACTIVE/DISABLED两态安全语义;客户建档和审核的PROFILE_REQUIRED/PENDING/REJECTED/ARCHIVED由 IAM 在发布前统一折叠为DISABLED,Lastleg 不兼容或解释国内工作流状态。 - 开发/测试空库基线只为国内固定
authUserId=1000001、tenantId=0的platform_admin写 USER/AUTHORIZATION version=0 投影;AUTHORIZATION 必须携带当前PLATFORM_ADMIN角色及按 IAM 跨区前缀筛选的完整权限快照,统一 SQL 重放只修正仍处于固定 baseline version=0 的该行,后续 IAM version>=1 事件单调覆盖。不得扩大为其他历史账号运行时补齐或缺失投影默认放行。 - PDA 入仓任务完成前必须先通过
/unload-photos保存非空且不重复的卸柜照片文件 ID;照片上传时间由服务端记录,预计报空时间固定为照片上传时间加两天。完成入仓时,库存入账、计划/任务状态推进与OVERSEAS_CONTAINER_UNLOADEDV1 Outbox 必须处于同一事务;事件绑定来源头程货柜身份,不得从前端请求拼装或回查头程数据库。 - 海外 PDA 柜号签收只允许平台
OVERSEAS_WAREHOUSE_PDA_OPERATOR使用lastleg:pda:operate;国内头程 PDA 角色不得进入。PDA 不接收customerId,候选按来源柜跨客户聚合,同柜允许多个客户;选柜会话、三类照片及整柜完成事实只绑定sourceContainerBookingId + containerNo,文件与柜级跨区事件使用tenantId=0平台技术范围。签收后按每个入仓计划自身客户生成任务,扫码、SKU 实收、托盘和库存始终使用入仓码所属票的真实客户。未完成会话按平台操作账号和柜号分别全局唯一;平台管理员和客户账号不得进入该作业入口。
查询与租户约束
- 平台查询范围由可信身份/平台权限决定;商户查询只能使用认证身份中的客户 ID。
- 尾程平台只读查询接口的
customerId为可选筛选条件:平台身份省略时查询全部客户数据,传入时按指定客户过滤;商户身份忽略客户端传入值并始终按 Token 中的自身客户隔离。创建、编辑、审核等需要明确目标客户的写接口仍必须传入customerId。 - 常规入仓库存余额以客户、海外仓、目的仓和入仓单为唯一业务维度并汇总箱数;SKU 继续保存在箱唛、实收和流水快照中,余额 SKU 仅作单 SKU 展示或以
MIXED标识多 SKU,不参与当前唯一维度。 - 托盘是 PDA 出库的作业单位,但不是库存归属单位:
ll_inventory_position通过余额和托盘保存“托盘包含哪些入仓单、各多少箱”的组成明细。合托后的同一实物托盘允许对应多个入仓单,换标冻结和后续出库核算必须按这些明细分别记账;整托出库可扫描托盘一次完成,部分出库先拆托或按明细核平,不得用 FIFO 猜测来源入仓单。普通尾程入库只确认某入仓单在托盘上的箱数,没有逐箱唛到托盘的快照;贴标下单由客户填写我方箱唛,库存仓位只作为冻结记账锚点和现场大概位置,不能据此声称箱唛位于某个具体托盘。贴标任务以我方箱唛为主键,同箱多 SKU 只存为子明细且不重复冻结。 - 换标箱唛经现场确认后执行
pallet-operations/relabel-disassemble:允许仅移动该换标单对应的hold_packages,不得解冻为普通可用库存;事务内同步更新ll_relabel_item.source_inventory_position_id。若客户确认后已生成换标出库单,还必须在拣货开始前同步拆分或改写ll_outbound_pallet_item的托盘、箱唛、箱数与来源仓位快照,使 PDA 后续按新旧托盘分别扫描且每个指定箱唛只出库一次。 FIRST_LEG在途、计划转换与 PDA 入账统一使用尾程订单冻结的实际接柜海外仓 ID/编码/名称,禁止使用国内来源仓或 FBA 目的仓冒充;接柜仓变更必须在同一事务内修订尚未形成实物仓位的在途余额并写零差额审计流水。余额分页、汇总和 CSV 默认排除所有数量桶均为零的维度,跨仓入账清空的无仓位来源余额必须原子删除。- 列表、详情、导出和更新必须使用同一租户边界,不能仅在列表过滤而让详情或更新越权。
- 目的仓变更必须写状态/操作历史或审计证据,并使用版本控制防止并发覆盖。
- 货物废弃只使用可核对 SKU 和箱唛的一件代发库存。创建申请只能将箱从
AVAILABLE锁定为DISCARD_PENDING,不得扣减 SKU 箱数;实际扣库存节点固定为具备lastleg:cargo-discard:approve权限的仓库/PDA 审核人确认废弃时,并且必须在审核事务内调用专用 discard adjustment。该 adjustment 必须将箱改为DISCARDED、写逐箱业务流水及唯一幂等键,并校验扣减前箱数 - 废弃箱数 = 扣减后箱数且逐箱数等于申请总箱数;任一环节失败须整体回滚。拒绝申请只释放锁箱,不得扣库存。 - 海外采购客户下单固定进入
PENDING_REVIEW;采购编辑可维护入库数量和采购出库明细,出库量为明细数量汇总,审核通过且有入库量后按PENDING_OUTBOUND、PARTIALLY_OUTBOUND、OUTBOUNDED派生展示状态。采购出库只记录采购单据流转,不得写入或冒充available_packages;只有海外仓 PDA 完成实收后,InboundStockReceiptService才能以PDA_STOCK_IN:{taskId}:{allocationId}幂等键调用库存stockIn转入可用库存。禁止通过通用平台接口绕过 PDA 入可用库存。 - 派车状态只允许 PDA 拣货、完成和出库动作推进,普通派车编辑接口不得修改状态。PDA 写操作要求
lastleg:pda:operate;后台库存表单操作要求lastleg:inventory:manage,盘点调整另要求lastleg:inventory:adjust,转仓库存操作要求lastleg:warehouse-transfer:manage。 ll_order.current_status只保存尾程主流程状态;换标、转仓、出库和派送等子单精确状态保存到各自单据并汇总到sub_order_status,不得再用子单状态覆盖主流程状态。PDA 入仓及库存落账完成后,主流程状态固定推进为INBOUND_COMPLETED。
修改与验证
-
RelabelBindingGuard校验已保存组托历史与当前库存托盘一致,箱唛/混合换标正常完工、客户确认与排车提交均复验;纯 SKU 不要求移托,异常手动完工仍须在客户确认前补齐绑定。创建申请按库存余额串行检查箱唛既有申请,拒绝重复申请;历史扫码全局唯一约束尚未改为多轮复用。 -
已确认箱唛改绑复用
PdaPalletOperationService.relabelDisassemble不带actualSource的精确来源分支,按现有绑定移动 HOLD 库存,不重新分配冻结锚点;成功同样记录PALLET_BIND以展示最新目标。requireUnallocatedRelabel在入口与事务锁单后限制排车/出库占用,已创建出库单也不可直接改绑。回归PdaPalletOperationServiceTest。 -
PDA旧SKU单扫:
PdaRelabelService.scanSku和/relabel-tasks/{relabelNo}/skus/scan,按箱itemId+skuIndex核对旧SKU,服务端取新SKU/数量,逐条记录现有SKU_SCAN历史,版本、幂等、认领和仓库校验不变;同箱全部SKU完成再推进SKU阶段,混合单需两阶段才生成一条箱事实。无新增字段;前端按历史当前轮次隐藏已完成子项,重复旧SKU须由操作员选实际箱。 -
数量拆托成功在现有
ll_relabel_pda_history写PALLET_BIND,保存本轮、箱唛和目标托盘;同轮已组托箱从候选排除并在事务内防重复,返工使用新的 reviewedAt 轮次。PDA 通过详情 history 展示组托记录;不需要新增库存表字段。 -
换标按数量拆合托:
palletops/PdaPalletOperationService.relabelSources返回原入仓库存池的托盘组成,不能视为箱唛定位;relabel-disassemble的actualSource携带仓位版本及现场箱数,cartonMarks为本批目标绑定确认。实际托盘与冻结锚点不同时,在同余额/批次内仅重新分配本单冻结桶后合托,库存余额及原入仓单归属不变,保留逐仓位流水、物理资源补偿及幂等。每次一条实际来源仓位,可分批多托;已生成出库单禁止此重分配。认领人处理中即可新增托盘,不需客户确认;此能力不等于已具备真机打印或整单强制组托门禁。 -
换标申请生成的
RELABEL_APPLICATION尾程主单使用WAIT_RELABEL(待换标),不进入普通入库计划池;子状态仍区分处理中/待客户确认。客户确认后统一为WAIT_OUTBOUND(待出库),卡车分支按换标申请的WAIT_PRE_DISPATCH资格参与排车,不重复入库。存量修正脚本../scripts/db/relabel-wait-status-incremental.sql仅调整尚未生成入库单的遗留待分配换标主单。 -
混合换标
CARTON_AND_SKU使用现有 PDA 历史保存MIXED_CARTON/MIXED_SKU分项事实(itemId + 退回轮次);同一项防重复,两项全部完成才写一条箱事实并增加整箱计数。详情返回cartonProcessedPackages/skuProcessedPackages;不得将单项完成当作整箱完成,返工后旧轮次不计入当前分项进度。 -
换标复核闭环:
PdaRelabelService.complete直接提交待客户确认,RelabelApplicationService.confirmCompletion原子完成 PDA 复核;平台RelabelApplicationController的/review-task、/rework按状态权限提供带版本的退回。退回备注必填,上一轮事实及照片引用写入REWORK_ARCHIVE历史,原事实标记REWORK_ARCHIVED并释放扫描唯一键,当前计数归零;客户已确认不可退回。PDA 不再自行批准复核。 -
移动包、拆分端口/适配器或引入新的跨子域依赖时,先核对内部模块化基线并运行
node --test scripts/analyze-lastleg-dependencies.test.mjs;确需更新依赖基线时必须说明稳定边界变化,不能把临时循环依赖登记为长期豁免。 -
安全投影边界变更同时运行
SecurityProjectionArchitectureTest与LastlegSecurityContractTest;security/controller|dto|service只允许依赖本子域和根InternalServiceTokenGuard,其他 Lastleg 业务包不得直接依赖这些投影内部包。 -
表字段、注释和索引同步维护基线 SQL 与统一增量 SQL;生产/存量库只执行增量脚本。
-
事件契约变化同时检查
common-event-contract版本、Event Bridge 消费方和旧 Payload 哈希兼容。 -
定向验证:从后端根执行
mvn -pl lastleg-service -am test。 -
事件链路变更至少覆盖重复事件、相同来源入仓单、事务回滚、并发单号、内部令牌和租户越权测试。