Files
baiyue-firstleg/lastleg-service/AGENTS.md
T
2026-09-15 11:54:14 +08:00

35 KiB
Raw Blame History

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 向国内头程回传货柜报空事实。
  • 不查询头程数据库,不调用头程接口补齐建单字段;事件快照缺失时应拒绝消费并保留可诊断错误。

快速索引

退货预报、签收、库存、合箱与退货出库:

  • 客户预报 returnreceipt/controller/MerchantReturnOrderController.java → returnreceipt/service/ReturnReceiptService.java,路径 /api/merchant/v1/last-leg/return-orders;客户范围取认证身份,预报按 SKU 保存期望数量。状态 PENDING_RECEIPT → PARTIALLY_RECEIVED → COMPLETED 表示签收进度,不等于换标或出库完成。
  • PDA 后端签收 returnreceipt/controller/PdaReturnReceiptController.java,前缀 /api/pda/v1/last-leg/return-receipts,依次 start/sku-candidates/confirm;按冻结仓和退货单校验实收,不允许未预报 SKU 或超量,幂等重放不得重复入账。确认同时生成签收箱与 SKU 库存;设备页面是否接通另查 PDA 工作台,不能以 Controller 存在代替前端接入。
  • 库存/箱号 returnreceipt/controller/PlatformReturnInventoryController.java、MerchantReturnInventoryController.java → returnreceipt/service/ReturnInventoryService.java,前缀分别为 /api/platform/v1/last-leg/return-inventory、/api/merchant/v1/last-leg/return-inventory;PDA 合箱入口 PdaReturnInventoryController.java 的 /packing/orders、/cartons/merge、/cartons/{cartonMark}/label。同仓同客户选取退货单及可用 SKU,合箱扣减来源箱数量并生成目标箱,空来源箱标为 MERGED,不得跨客户或超出可用量。
  • 退货出库继续进入 delivery/service/DeliveryTransferService.java 的 RETURN_OUTBOUND,按退货库存可用箱唛创建与拣货;不能与 exceptionorder/ 的派送退回恢复库存混用。契约见 src/main/resources/openapi/overseas-business-v1.yaml,表入口 src/main/resources/db/lastleg.sql 的 ll_return_*。
  • 客户箱号管理使用 GET /api/merchant/v1/last-leg/return-inventory/cartons 按实物箱分页,库存箱内 SKU 明细仍使用根查询;两者均按登录客户精确隔离。合箱 sourceBatchNo/sourceReturnNo 保存实际消耗来源的去重业务编号(多个以逗号分隔),不得写状态或包含未取货订单。ReturnCartonBarcode 按箱号生成 Code 128 SVG,打印内容不得使用内部客户/仓库 ID。

人工计划自动生成托盘与资源重试:inboundplan/controller/InboundPlanController.java → inboundplan/service/ContainerInboundPlanConversionCoordinator.java 的 convertManualAndAutoMaterialize;保存单票后重读全柜预览,全员为 PLANNED/RESOURCE_PENDING、预览就绪且存在待绑定资源时自动调用柜级 confirm。一票柜立即触发,多票柜在最后一票齐全后触发;资源异常沿 resourcePendingPlanCount 和原柜级预览/确认入口重试,不新增独立正常人工步骤。回归入口为 src/test/java/com/baiyue/lastleg/inboundplan/service/ContainerInboundPlanConversionCoordinatorTest.java。

柜级预览同时返回 sourcePalletCount/supplementPalletCount/targetPalletCount:头程冻结托盘码原样保留,海外仓最终托数较多时按同柜同 FBA 编码序列补足差额,不因头尾装托密度不同阻断;页面必须分别展示头程已有、海外补充和最终托数,不能把最终总数误称为“本次新增”。

头程来源托盘快照:service/LoadingCompletedConsumer.java、mapper/LastlegSourcePalletMapper.java;来源标签随装柜完成事件落入 ll_order_source_pallet,后续由 inboundplan/service/InboundPlanService.java 与 ContainerInboundPlanConversionCoordinator.java 传递给海外资源批量预留。排查标签跨区丢失时沿此链路核对,不能在尾程页面重新生成头程托盘码。

功能 入口
内部模块化基线与依赖分析 docs/lastleg-internal-modularization-baseline.md、scripts/analyze-lastleg-dependencies.mjs、scripts/analyze-lastleg-dependencies.test.mjs、src/test/resources/architecture/lastleg-slice-dependencies.json;只约束本服务内部包依赖,不改变部署单元或数据库边界
事件接收、平台订单列表/详情/导出、柜级进度、目的仓调整 src/main/java/com/baiyue/lastleg/controller/LastlegOrderController.java;/api/platform/v1/last-leg/orders/container-summaries 按 sourceContainerBookingId 服务端聚合全柜订单、入仓码、有效计划及托盘资源异常并分页返回全部稳定来源柜,已完成计划转换的柜仍保留用于接柜仓查询与修改;单票手工计划保存后,整柜最后一票齐全时自动生成并绑定全柜托盘码,一票柜立即生成,resourcePendingPlanCount 只作为失败重试事实;/{sourceContainerBookingId} 返回柜内全部订单/入仓码/计划状态且不受订单分页截断;plannedArrivalAt 只取装柜完成事件 estimatedWarehouseArrivalAt 冻结到 ll_order.estimated_warehouse_at 的正式事实,按其升序、null 最后、稳定柜 ID 排序;目的仓调整仅接受国内 Warehouse Service 平台公共 ACTIVE + FBA 主档,写入前由 service/FbaWarehouseValidator.java 经内部只读目录校验 ID/编码/名称一致
尾程订单完整计划、暂扣与人工节点 controller/LastlegOrderController.java、service/LastlegOrderCommandService.java、inboundplan/service/InboundPlanService.java、dto/LastlegOrderCommandDtos.java;单票计划写入校验订单版本、客户范围、订单冻结入仓码、稳定接柜仓 ID、当前仓库主档与幂等键;一件代发人工计划托数必须严格等于不可变头程封托托盘数,禁止保存可增减托数但无法绑定真实资源的半成品计划;接柜仓编码/名称以该 ID 当前目录快照为准,不要求订单残留旧展示快照。暂扣/解除写不可变历史并发布 InboundOrderDetainedOutboxService,已拣货、已发运或已有出库单时禁止新增暂扣;暂扣期间不得进入排车或出库。人工节点必须一次提交固定八行完整快照且至少一个时间,首次保存后永久使用 MANUAL 来源,不得回退柜级节点或反向修改头程船节点
客户尾程订单列表与详情 controller/MerchantLastlegOrderController.java;客户范围只取认证身份,稳定类型/状态见 model/LastlegOrderType.java、LastlegOrderStatus.java
装柜完成事件消费 service/LoadingCompletedConsumer.java;当前生产信封为 FIRST_LEG_LOADING_PLAN_COMPLETED V2,消费端冻结 destinationType:FBA 可按箱数且不生成逐箱作业账,OVERSEAS 必须逐箱校验并生成箱级事实;历史缺字段事件仅按明确 FBA 单号或目的仓与接柜仓一致性安全推断,歧义直接拒绝
尾程单号生成 service/LastlegOrderNoGenerator.java
订单查询与更新 service/LastlegOrderService.java
业务 Inbox entity/LastlegEventInbox.java、mapper/LastlegEventInboxMapper.java
尾程订单、明细、状态历史 entity/LastlegOrder.java、LastlegOrderDetail.java、LastlegOrderStatusHistory.java 及对应 Mapper
独立尾程入仓单快照 entity/LastlegInboundOrder.java、mapper/LastlegInboundOrderMapper.java;按客户与来源入仓标识冻结 inboundCode、来源/根订单及目的仓,不以尾程订单行代替入仓单聚合
内部令牌保护 security/InternalServiceTokenGuard.java
IAM 海外安全投影与实时校验 内部入口 security/controller/LastlegSecurityController.java,固定 /internal/lastleg/security/v1/events、/validate 且复用 InternalServiceTokenGuard;投影/校验 security/service/,DTO security/dto/,表固定 ll_security_event_inbox、ll_security_projection;MySQL 投影缺失、落后或不可用一律 fail-closed,Redis 仅为提交后可重建镜像;业务切片不得直接依赖投影内部类,边界由 SecurityProjectionArchitectureTest 与 Node 合同锁定
入仓计划分页、柜级预览、转换与详情 平台 inboundplan/controller/InboundPlanController.java,客户只读 inboundplan/controller/MerchantInboundPlanController.java;共用 inboundplan/dto/InboundPlanRequests.java、InboundPlanListResponse.java、ContainerInboundPlanPreviewResponse.java、inboundplan/mapper/InboundPlanQueryMapper.java、inboundplan/service/InboundPlanService.java;客户入口固定 /api/merchant/v1/last-leg/inbound-plans,范围只取 Token。/container-preview 按全柜返回 FBA/一件代发/私人地址分组、托数、就绪、阻断项及 snapshotVersion/snapshotHash;ContainerInboundPlanConversionCoordinator.java 用唯一柜级 Saga 锁柜重算,先经 WarehouseResourceGateway 批量预留真实托盘,再本地原子生成剩余独立计划并确认资源,本地失败只补偿未确认资源,远端确认失败的重放只重试确认,同柜不同幂等键也不得产生第二批资源
托盘分配算法 inboundplan/service/PalletAllocator.java
FBA 预约创建、详情与编辑 InboundPlanController.java 的 /fba-appointments/**、inboundplan/service/InboundFbaAppointmentService.java、BatchFbaAppointmentRequest.java、UpdateFbaAppointmentRequest.java
PDA 入仓、实收、上架与完成 inboundplan/pda/controller/ReceivingContainerController.java、inboundplan/pda/service/ReceivingContainerService.java、内部编排 InboundPdaWorkflowService.java;公网入口固定为 /api/pda/v1/last-leg/receiving-containers/**,/warehouse-profile 只按 Token 绑定仓解析共享仓库中文名称与编码,旧 planNo/allocationId Controller 已移除。多计划同柜返回计划级 assigned/mergeAllowed/urgent/mergeHint;普通 FBA/OVERSEAS 分别使用 bulk/carton 收货,一件代发只允许 /current/receipts/pallet 按国内冻结来源托盘整托收货。合托只作现场可选提示;全部计划完整绑定真实 pallet/location/lock 前不得查看、准备或签收该柜,首扫只确认已预留绑定,不得重新分配计划资源
PDA 卸柜照片上传、归属校验与预览 ReceivingContainerController.java 的 /current/unload-photos/files、/current/unload-photos/files/{fileId}/download,网关 inboundplan/pda/gateway/UnloadPhotoFileGateway.java、HttpUnloadPhotoFileGateway.java;File 类型固定 OVERSEAS_UNLOAD_PHOTO,下载只允许当前会话已签收的 fileId 并返回 no-store
卸柜照片与海外卸柜完成 Outbox ReceivingContainerController.java、ReceivingContainerService.java、inboundplan/pda/service/InboundUnloadCompletedOutboxService.java、entity/LastlegEventOutbox.java、mapper/LastlegEventOutboxMapper.java;多计划同柜只能在全柜完成后调用 publishContainerCompleted,按来源柜预订 ID 幂等发布一次,完成时刻与拍照时刻分离;事件模型见 ../common-event-contract/AGENTS.md
拆柜打托成本与客户应收分摊 inboundplan/service/DestuffingPalletCostJob.java、DestuffingPalletCostService.java;仅全柜所有 PDA 入仓任务完成后执行,按实物托盘单价汇总并按客户实收 CBM 分摊,尾差落最后一位客户;每份分摊发布 OVERSEAS_CONTAINER_FEE/DESTUFF_PALLET 应收费用来源,不直接改国内钱包
尾程暂扣状态回传 service/InboundOrderDetainedOutboxService.java;暂扣与解除暂扣在订单更新事务内按 sourceInboundId 写入海外到国内 Outbox,国内按来源入仓单绑定更新
派送完成回传 delivery/service/DeliveryCompletedOutboxService.java;签收资料保存并自动完成时在业务事务内按关联 sourceInboundId 分别写 OVERSEAS_DELIVERY_COMPLETED Outbox,同车不同 FBA 派送单互不代完成;不保留人工完成入口
入仓后 SKU 库存余额、库位和流水 inventory/;平台只读查询与非出库库存命令分别从 inventory/query/controller/InventoryQueryController.java、inventory/command/controller/InventoryCommandController.java 进入
海外PDA盘点、同仓整托移库、托盘组拆换绑与换标现场作业 pda/stocktake/、pda/move/、palletops/、relabel/pda/;版本化契约 src/main/resources/openapi/overseas-pda-operations-v1.yaml;全部范围取自PDA Token,排车和出库使用独立 PDA Controller
贴标申请、箱唛/SKU/组合换标与 PDA 完工 relabel/、relabel/pda/;一条 ll_relabel_item 对应一个客户填写的我方箱唛任务,CARTON_LABEL、SKU_LABEL、CARTON_AND_SKU 分别保存箱唛、SKU 或组合需求,SKU 子项 JSON 数量按件;普通尾程入库没有箱唛到实物托盘的精确快照,历史入仓库存仓位只能作为冻结记账锚点和现场大概位置,禁止把该仓位解释为箱唛已绑定具体托盘。库存只按箱任务冻结一次。涉及箱唛换标时申请必须选择 TRUCK/EXPRESS/TRUCK_PICKUP/EXPRESS_PICKUP,两种快递方式可要求打印并上传面单;PDA 扫我方箱唛完成逐箱事实和照片,仓库复核通过进入 CUSTOMER_CONFIRM_PENDING,客户查看照片确认后进入 READY_FOR_OUTBOUND:仅卡车派送为 WAIT_PRE_DISPATCH,快递派送和两种自提直接创建换标出库单。客户确认前不得进入出库
PDA 仓内通用扫码与操作历史 pda/warehouse/controller/PdaWarehouseController.java、pda/warehouse/service/PdaWarehouseService.java;解析库存、库位和托盘码并查询操作历史,固定要求 lastleg:pda:operate 与 ROLE_WAREHOUSE_PICKER,仓库范围只能取自 PDA Token
船节点接收与海外 PDA 可收货门禁 shipping/service/ContainerShippingNodeConsumer.java、shipping/entity/ContainerShippingGate.java、shipping/mapper/ContainerShippingGateMapper.java;按 containerBookingId 柜级接收,信封使用 tenantId=0 且门禁不存客户;只单调接受实际标准节点,PICKUP_READY 及以后才允许对应柜进入 PDA 收货候选
平台排车列表/详情与智能预排 dispatch/controller/DispatchQueryController.java、SmartDispatchController.java、dispatch/mapper/DispatchCandidateMapper.java、dispatch/service/DispatchService.java;草稿柜 entity/DispatchDraftContainer.java、mapper/DispatchDraftContainerMapper.java,托盘锁 DispatchPalletLock.java;入口 /api/platform/v1/last-leg/dispatches,查询、预排与执行边界见“海外仓全流程开发路由”
排车编辑、PDA 出库、派送与交接 dispatch/controller/DispatchController.java、dispatch/pda/controller/DispatchPdaController.java、delivery/controller/DeliveryTransferController.java、MerchantDeliveryController.java、OutboundPdaController.java
仓储资源调用 inboundplan/pda/gateway/WarehouseResourceGateway.java、HttpWarehouseResourceGateway.java
仓储转运、历史订单/入仓绑定、可选换标联动 transfer/controller/WarehouseTransferController.java、transfer/service/WarehouseTransferService.java
一件代发接收、SKU/箱唛库存 内部接收入口 dropship/controller/DropshipOrderController.java 的 /internal/v1/lastleg/dropship/receive,必须经 InternalServiceTokenGuard;版本化契约 src/main/resources/openapi/lastleg-internal-dropship-v1.yaml;处理 dropship/service/DropshipOrderService.java、dropship/entity/、dropship/mapper/
货物废弃、待审锁箱、审批扣减 discard/controller/CargoDiscardController.java、discard/service/CargoDiscardService.java
客户拜访、派送状态与签收附件 customervisit/controller/CustomerVisitController.java、customervisit/service/CustomerVisitService.java、delivery/model/DeliveryStatusPolicy.java;派送单返回服务端 allowedActions/version,状态只允许单调迁移,同状态幂等;人工动作不再提供完成派送,签收资料保存后自动完成当前目的FBA仓派送单
派送三类资料、摘要与历史证据 delivery/controller/DeliveryDocumentController.java、delivery/service/DeliveryDocumentService.java、delivery/gateway/DeliveryDocumentFileGateway.java、src/main/resources/openapi/delivery-documents-v1.yaml;固定装车照片 LOADING_PHOTO、BOL文件(含签名)BOL、签收文件(POD签收/快递签收)RECEIPT,按已保存类别计数 x/3。每类0至20文件,文件精确绑定客户、按目的FBA仓拆分的派送单和类别;全量保存共用派送版本并记录不可变历史,替换删除仅解绑,未保存上传不计数、不授权下载。RECEIPT 成功保存进权威快照时事务内自动完成当前派送单,单纯上传不触发完成
异常订单与派送退回入仓 exceptionorder/controller/ExceptionOrderController.java、exceptionorder/service/ExceptionOrderService.java、exceptionorder/gateway/ExceptionEvidenceFileGateway.java、inventory/command/service/InventoryCommandService.java;证据固定按客户、异常单 ID 和 OVERSEAS_EXCEPTION_EVIDENCE 校验。退回候选按原出库拣货事实冻结,不接受自由 SKU 或数量;异常按 PENDING→PROCESSING→COMPLETED(待处理可取消)、版本与幂等键流转。退回海外仓须把实收按原出库来源恢复到服务端校验的真实托盘/库位并写 RETURN_STOCK_IN 流水,库存恢复、异常完成与派送 RETURNED 在同一事务完成
海外采购、客户下单、采购审核、客户/采购仓候选与操作流水 procurement/controller/、procurement/service/OverseasProcurementService.java;客户入口要求 merchant:cross-border:procurement 且只取 Token 客户范围;OpenAPI src/main/resources/openapi/overseas-business-v1.yaml
海外业务资金指令与结果 wallet/WalletCommandOutboxService.java、WalletCommandCompletedConsumer.java、WalletResultHandler.java;采购和换标通过 WALLET_COMMAND_REQUESTED 请求国内 Finance 冻结、结算或释放,并以 WALLET_COMMAND_COMPLETED 幂等回写业务资金状态;不得跨区同步调用 Finance 钱包或在 Lastleg 自建余额账本
海外业务费用来源 financefee/controller/PlatformOverseasFeeSourceController.java、financefee/service/OverseasFeeSourceService.java、OverseasFeeBusinessGuard.java、OverseasFeeOutboxService.java、OverseasFeeOutboxRelay.java;海外入仓、采购、美国卡车、本土散板和海外仓服务费必须匹配当前客户的真实业务,入仓记录和入仓码按事实选填;审核通过、调整或作废后以版本化完整快照经 Outbox/Event Bridge 进入独立 Finance,OpenAPI src/main/resources/openapi/overseas-finance-fees-v1.yaml
美国/本土卡车订单 trucking/controller/PlatformUsTruckOrderController.java、MerchantUsTruckOrderController.java 及同子域 Service/DTO;客户入口要求 merchant:cross-border:domestic-trucking
本土散板订单 looseboard/controller/PlatformLocalLooseboardController.java、MerchantLocalLooseboardController.java 及同子域 Service/DTO/Entity/Mapper;客户入口要求 merchant:cross-border:pallet-shipping;OpenAPI src/main/resources/openapi/overseas-business-v1.yaml
海外仓报价、月度汇率、车辆主数据 报价 masterdata/quote/、汇率 masterdata/exchange/;车辆入口 masterdata/vehicle/adapter/in/web/PlatformVehicleController.java,应用服务 application/VehicleApplicationService.java,持久化端口与适配器 application/port/out/VehicleRepository.java、adapter/out/persistence/;OpenAPI src/main/resources/openapi/overseas-master-data-v1.yaml,车辆档案不含派送履约历史
跨服务 HTTP 客户端装配 config/LastlegRestClientFactory.java、config/LastlegRestClientProperties.java;目标地址与内部服务令牌只来自配置,不在业务子域手工创建客户端
数据库基线、表索引与中文注释契约 src/main/resources/db/lastleg.sql、src/test/java/com/baiyue/lastleg/schema/SchemaChineseCommentContractTest.java
测试 src/test/java/com/baiyue/lastleg/
真实事件消费验收 src/test/java/com/baiyue/lastleg/service/TaillegRealConsumerAcceptanceTest.java;覆盖真实 V2 建单、独立入仓单快照及下游转运/换标可消费性

海外仓全流程开发路由

  • 海外仓全流程连续覆盖尾程订单、入仓计划/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,未传时跨客户展示供页面分页筛选,创建时必须从所选候选解析为唯一客户和唯一海外仓,禁止一张出库单跨客户或跨仓。
  • 排车转派送允许在 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 独立落一条雪花 ID ll_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_UNLOADED V1 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。

修改与验证

  • 移动包、拆分端口/适配器或引入新的跨子域依赖时,先核对内部模块化基线并运行 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。
  • 事件链路变更至少覆盖重复事件、相同来源入仓单、事务回滚、并发单号、内部令牌和租户越权测试。