Files
docs/海外仓业务/排车列表接口与数据设计.md
T
2026-10-20 10:35:59 +08:00

119 KiB
Raw Blame History

排车列表接口与数据设计

2026-10-19 已按正式契约第八至十节进入首批实现,替代仅准备门禁。四项业务选择已确认;公共Java契约骨架见第0节。运行端点、持久化主流程和集成尚未完成,骨架不表示已部署。仅在独占范围实现及离线验证,不提交、部署、执行环境SQL或真实HTTP;共同SQL/OpenAPI/权限由总控集成。其余尚未实现的设计文字不能覆盖正式契约。

唯一写者:排车后端 01a1524f-4f11-70c2-8a46-8e06e73a2d86。总控 01a15230-a9d6-7503-a8a9-d70691df49e5;TEST 项目、host、cwd 按协作登记 2026-10-19 节。派送设计由 01a1524f-56a9-7320-a5fd-8ff76fa4356b 独占另一设计文件,双方不并写。本文创建前该文件及本文均不存在,未发现本文其他写者;源代码工作区已有其他未提交改动,未覆盖。

0 公共Java契约骨架交接

以下文件已经实际新增,均位于 baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/port/,仅依赖JDK17,无业务Entity或对方实现类。所有下游以实际record组件/端口方法为准,不以旧准备稿伪造同义DTO。Execution与Completion已进入实际实现,草稿由总控分配的唯一写者实现;当前代码/验证交接以10.3为准,公共骨架不表示整链上线。

文件 SHA-256
DispatchTypes.java ecd710d123747fa28f1c39c6fbafcd58f7bbe8656717135b81c80ff0736177d2
DispatchContracts.java 1c96bc5d4e471714a5f5829a5dd264aedd78d08deaa376ad5e8a11d59e2cac5e
DispatchCommands.java f487b7bf7c21aac194b3525aa706da4fa6ab8ba0852de7eed3c68282d5a8497c
DispatchResults.java 2355f33a878c8aba599122b4d94fb97f88db1b27622f541f3082dadc27dc1e94
DispatchViews.java f5f8a273e693496f27075fb8d2e839ee33f6b35317e107048025dbcb96664b85
DispatchDeliveryModels.java a8452477225b71400996992896f0a8a3ed385118cf988d5c4ed7b014c4e85896
DispatchExecutionPort.java 06ab14af3879ba90a7f4e5d6b2c56a8142bbdc3068911c8eaa9336807c8c8457
DispatchPlanningModels.java 33e3512544ec49aa6d9dd332cfc2e271303519058f8aac978125d91fa3c72f8d
DispatchPlanningPort.java 2ecc408b6d5adb31fab23fa677916608e4434013716bade9a88dbd2655f64b88
DispatchDeliveryPort.java 1963618f71d831482a1a8527da68467773721db980c84b5c970832b1c3dafbd6
DispatchCompletionPort.java 4a222f0d3b3a34267f7469aa0242cf62486f70496c15481838ac889b5e64a76d
DispatchSignalPort.java 85b26c63d98f87abbc9ddbbcd5b18961ac32c4bb60e5cb74832997a4ff233c07
DispatchApi.java 584a973627365c4987e4fad05bb26ca8dbea538c779472f0df83b9ee81302c25

实际端口精确签名(类型均为同包对应容器内record):

  • DispatchExecutionPort:Context preview(Actor,String);LoadingPreview loading(Actor,String);Context lockForArrangement(Actor,String);Optional<CommandResult> replay(Context,Actor,CommandIdentity);void requireFreshArrangement(Context,long recordVersion);CommandResult recordArrangement(Context,Actor,CommandIdentity,ArrangementFact,boolean alreadyArranged)。lock方法只取得顺序锁及身份上下文,原命令恢复先于新命令资格;新命令必须调用fresh方法,不能以拿到Context视为已允许。
  • DispatchDeliveryPort(派送实现):ArrangementPreview preview(Context);CommandResult arrange(Actor,String,ArrangeDelivery);DepartureFacts departureFacts(Context,boolean lock);DepartureResult markDeparted(Context,Actor,Instant,String commandKey);NormalDeliveryFacts normalDeliveryFacts(Context)。markDeparted仅改客户单据事实,不执行库存SHIP。
  • DispatchCompletionPort(主流程实现):CompletionResult reconcile(Signal),新事务内主阶段CAS与signal确认同提交。
  • DispatchSignalPort(派送实现):List<Signal> due(Instant,int);void acknowledge(Signal,Instant)(MANDATORY,同汇总事务);void failed(Signal,Instant nextRetryAt,String errorCode)(父事务回滚后REQUIRES_NEW);void enqueue(String dispatchNo,String deliveryNo,long deliveryVersion,Instant occurredAt)(原签收事务内)。due不持信号锁进入父事务;主流程唯一负责调度/退避/重扫。

字段/端点对应:所有端点常量在DispatchApi。平台POST根→DispatchCommands.Create;PUT单号→Update;POST items→AddCargo;remove/cancel→ReasonCommand;submit-review→SubmitReview;dispatch-review/loading-review→ReviewCommand;return-to-review→ReturnToReview;双端loading-complete→LoadingComplete(新增真实measurements和manifestHash,不含补录packages);双端depart→Depart;PDA items/pick→Pick;平台transfer-delivery→DispatchDeliveryModels.ArrangeDelivery。所有命令响应统一DispatchContracts.CommandResult,严格区分replayed与alreadyArranged;arrangement数组在result中,不保留单outboundNo。GET列表/详情分别为DispatchViews.Page/Detail,PDA为PdaTask,组合预览为ArrangementPreview。公开参数均recordVersion,0合法;Actor和CommandIdentity均服务端内部类型,不绑定HTTP请求体。

实际列表统计record字段采用lowerCamelCase:ReviewCounts为notSubmitted/pending/approved/rejected,Stage键仍是五项枚举。实际详情VehicleInfo为vehicle/driver两个显式mode快照,DispatchInfo/Documents/OrderDetails保持四对象;以这些实际字段取代准备稿中的展示性伪代码。数值ID沿项目Long序列化约定输出,内部ID不作业务号回退。

已运行JDK17 javac --release 17 编译上述全部生产骨架及 src/test/java/com/baiyue/lastleg/dispatch/port/DispatchPortCompileContract.java,随后离线合同输出 PASS,不启动Spring/HTTP/数据库。此结果只证明共享类型编译、五阶段/版本域/整车客户边界/完整单据数组及端口方法可静态引用,不证明持久化、鉴权或事务行为。Maven暂未运行,待派送写者稳定后由本任务统一执行。总控已提供路由合同5/5和Lastleg依赖合同既存9通过3失败(pickup→transfer未审阅边)的基线;本任务不修改pickup或豁免,后续分别报告新增结果。

0.1 骨架修订:缺版本、既有预约、实载与草稿

公开已有单/草稿写请求的recordVersion均改为Long并在record紧凑构造器拒绝null/负数,显式0合法;创建主单/草稿没有既有版本。不是仅依赖Controller @Valid或Jackson对primitive的默认0。按最新用户裁定,排车不保留车辆预约字段、类型或关联;Update和DispatchInfo均无该属性,不输出空占位。FBA预约及真实批次继续按原权威核验,日历新增预约不在本批范围。

新增实际种子及端口见 DispatchPlanningModels.java/DispatchPlanningPort.java,源数据生产者已沿原方法核对,前端按下表接入;当前尚无新Controller映射或可调用运行端点,不以端点常量冒充实现。

方法/路径 实际请求/响应类型 权威来源及边界
GET D/candidates CandidateQuery→CandidateList(含Candidate.sourceVersion、selection、真实客户/起运仓/来源/目的地/参考测量/批次) 原DispatchCandidateMapper/ReturnDispatchCandidateMapper及专用换标来源;sourceVersion将由服务端以稳定sourceKey、客户/仓/目的地、当前业务资格、真实来源库存组成版本及箱数、测量依据组成规范化SHA256,选择/转换锁内重算;不存在可靠摘要时不得填0
GET D/drafts;POST D/drafts List;CreateDraft→Draft 当前操作者拥有的持久草稿;服务端生成token,主单和草稿版本分开、均不信任布局赋权
POST D/drafts/containers;POST D/drafts/snapshot CreateContainer/DraftSnapshot→Draft 带draft recordVersion及原请求键,保存类型化containers/sources;不继续使用raw containersJson作另一写协议
POST D/draft-locks/lock、unlock、release DraftSelection或DraftRelease→Draft 选择携sourceVersion,先复验仓、真实来源、实托排他,再保存;不释放其他拥有者占用
DELETE D/drafts/{draftToken}/containers/{containerId} ReasonCommand(含草稿版本及键)→Draft 原容器/拥有者/版本下释放,禁止无版本覆盖新选择
POST D/drafts/pre-arrange ConvertDraft→ConvertedDraft groups每项DraftVehicle(draftContainerId,Create dispatch),每组一个真实整车;全部组一个事务,失败全回滚。vehicles每项为容器ID及完整CommandResult;不按客户拆单、不保留原客户PreArrange DTO
GET D/{dispatchNo}/loading;GET P/{dispatchNo}/loading LoadingPreview 双端分别检查平台list权限或PDA角色/绑定仓;返回当前排车recordVersion、manifestHash、逐实托scheduled/picked、真实实测值/证据、缺项、confirmedLoad及loadingComplete资格。只读、不新增页签
GET详情 Detail.dispatchInfo.appointmentBatches 读取现有AppointmentWorkDispatchPort.references的真实batchKey/batchNo/customer/sources,不新增日历预约权威模型

实载数据必须区分证据:FleetTripExecutionService.loading/confirmLoad已有load_json(manifest匹配时)是CONFIRMED_LOAD;RelabelPalletMeasurementService.snapshots有库存组成stockVersion的真实测量是RELABEL_MEASUREMENT,组成过期必须缺项。普通候选grossWeightKg来自订单/入仓快照按托分摊、volumeCbm来自allocated_volume_cbm,是ORDER_SNAPSHOT_ALLOCATION参考,MeasurementEvidence.actual=false,只显示suggested值,不自动填真实weightKg/volumeCbm;未知托位/卸货段也明确missingFields。LoadingComplete仅接受用户核对的measurements,不接受PDA箱数补录。候选摘要计算及这些真实服务适配尚待主流程实现,不声称GET已可调用。

dispatch/service/DispatchCommandCodec.java已提供按Action封闭解码:Saved/Picked/Loaded/Departed/ArrangementResult具体类型由动作决定,拒绝缺结果字段或错误动作数据;复用正式时间/Long配置,树解析使用BigDecimal保留原精度,避免实测或费用经过double。其SHA256为0e61307658495fb05e6dfed3b7029e89d1f92ab100bae777be8f4f7ef35871f4。DispatchPublicJsonContract已实际通过15类请求missing/null/negative拒绝及显式0、预约伪造字段拒绝、全部Action持久JSON跨Codec/ObjectMapper实例恢复和原键重放;DispatchPublicContractTest供后续Maven统一执行同一组真实合同。此处是跨实例JSON恢复证据,未声称数据库事务/实际进程重启全链验收。

本轮依赖合同重新运行仍为9通过3失败,仅输出总控已登记的3条pickup→transfer.entity及fleet出向变化,无本批新增类型边违规;不扩大到pickup修复或豁免。编译最初受到Windows命令行长度限制,已改用target内javac/java参数文件;随后发现并修复中间JsonNode导致实测BigDecimal精度/scale变化,最终合同通过。测试产物仅在target,不纳入源码。

0.2 严格摘要及命令账本首片实现

独占新增 dispatch/service/DispatchCommandHasher.java、DispatchCommandLedger.java、dispatch/entity/DispatchCommandRecord.java、dispatch/repository/DispatchCommandRepository.java、dispatch/mapper/DispatchCommandMapper.java。摘要绑定actor/action/原版本/排车/规范载荷;业务摘要排除actor/版本/键,无序货物/费用/组排序而路线stops保持顺序。账本全局查询原键后先拒绝异主体,再判断动作/版本/摘要,结果恢复用真实Codec;保存只insert,不更新旧结果,唯一键冲突抛失败让外层回滚。方法MANDATORY,Repository/Mapper分层,不接入旧按customer分片幂等。

总控SQL精确需求:ll_dispatch_command字段为id BIGINT PK、dispatch_id BIGINT、dispatch_no VARCHAR(64)、action VARCHAR(40)、operator_id VARCHAR(128)、idempotency_key VARCHAR(255) NOT NULL UNIQUE、record_version BIGINT NOT NULL(原请求版本)、command_hash CHAR(64)、result_json LONGTEXT、created_at DATETIME(3)(UTC);各成功记录业务字段实际均应NOT NULL,创建前不落半成品行。dispatch_id/dispatch_no在创建成功结果产生后一起落账本。索引建议dispatch_id+created_at辅助审计,不以客户/操作者拆全局唯一键;中文备注由总控与基线/统一SQL一起集成。本任务未写共享SQL,也未执行环境SQL。

新增真实 dispatch/service/DispatchCommandLedgerPersistenceTest.java:H2内存表+实际MyBatis Mapper/Repository保存、提交,关闭Session并换Codec/ObjectMapper实例后读取原结果;异主体/换版本/换单拒绝,以及严格摘要和安排业务摘要区别、无序组与有序路线差异均已独立编译运行PASS。测试仅覆盖此首片持久/业务身份,不表示Spring事务代理、并发主单CAS、所有客户费用回滚或完整HTTP通过;完整Maven仍待另一后端稳定后统一运行。公共13个port文件保持本次哈希,后续主单/审核/装车/发车及Controller集成尚未完成。

1 已核对的现状及必须收敛点

以下 Java 路径相对 baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/。

实际入口 当前事实 本批影响
dispatch/service/DispatchService.java 的 preArrangeContainer/preArrangeDraft/requireSingleCustomerScope 主表 DispatchOrder.customerId 单客户;草稿每组也强制单客户;一组一张客户单 真实改为整车主单和跨客户明细,删除单客户头部推导,不能生成若干客户排车再仅在 UI 合并
fleet/trip/FleetTripPreparationService.java、FleetTripService.bindDispatch 先建客户排车,再建/选共享车次,循环绑定多张排车;调度审核落在绑定记录 新建排车一次完成内部车次关联,审核归整车;不让用户先去车次页
dispatch/dto/DispatchRequests.Stage 装车前对应 PENDING_DEPARTURE;装车中包含 READY_TO_DEPART;装车后实际映射到已发车 替换为五阶段真实状态,不能继续用旧映射表示装车后
FleetTripExecutionService.reviewLoading/confirmLoad 理货通过要求实际装载 hash;实际装载又读取拣货就绪 manifest 理货审核改到装车前,不能保留“先拣货才允许审核”的循环;实载事实核对独立在装车完成门禁
dispatch/pda/service/DispatchPdaWorkflowService.pick 普通仓位依次 allocate/pick/ship;扫码成功是当前库存扣减点;逐入仓归属落在 ll_dispatch_pda_pick_order 保留扣减时点和真实来源,装车完成、实际发车均不得再扣一次库存
同类 pick/completePicking 最后一条扫码自动将 task 设为 COMPLETED、排车设为 READY_TO_DEPART;complete 无版本且仅 PDA 拣货核平事实与用户确认装车完成分开;最后扫码只开放确认资格,后台/PDA调用同一确认服务
delivery/service/DeliveryTransferService.transferResolved 从主单 customer 生成一张客户出库单及按目的地拆分派送单,可在待发车/拣货中提前转派送 仅装车后安排,全车一次命令按客户再按目的地拆分;由派送后端实现
dispatch/fleet/DispatchFleetAdapter.completion 签收或真实退回核平都可满足旧车次结案 保留旧附加结案能力;排车五阶段的“派送完成”单独只认全部正常签收,不能复用“退回也完成”的布尔值
inboundplan/work/AppointmentWorkDispatchBinding/Mapper 分配事实带真实客户;但 bind 内按 dispatch 全量删除重建,部分 SQL 以 d.customer_id Join 不可逐客户循环旧 bind,否则后一个客户覆盖前一个;须一次保存整车完整分配且以明细客户关联

上一批修复保留:取消历史与活动关联分开;已拣货/发车不得取消;普通和退货换标按整申请全部应排托盘;同托多来源按拣货归属核算外接承诺和费用;退货箱不伪造普通库存仓位。工作区 exceptionorder/service/ExceptionOrderDispatchReturnSourceTest.java 有其他未提交修改,本任务只读。

2 推荐模型:排车是真主单,Fleet 保留资源与附加能力

2.1 主单与资源的唯一关系

推荐继续使用 ll_dispatch_order,将其改为平台整车作业主单,删除 customer_id;不填写 0、假客户或所谓共享客户。每张整车主单自动且唯一关联一条 ll_fleet_trip,后者继续承载车辆司机占用、逐段容量、外接预订、成本、交接、资源结束及其历史。两个编号是不同职责的业务编号,排车列表只要求用户使用排车号;不能由前端按客户创建多张子排车后再隐藏。

保留 ll_fleet_trip_dispatch 作为排车与资源记录的一对一关联,删除其中 customer_id/review_json,保留 dispatch_id/dispatch_no/trip_id/trip_no/package_count/bound_by/bound_at。增加 trip_id 唯一约束、原 dispatch_id 主键及 dispatch_no 唯一约束;本批绑定对象只有整车主单。tripPurpose=PICKUP 的接货流程不是本批排车流程,冻结前核实其直接调用,不能因收敛配送车次误改接货能力。

整车新建事务内同时建立上述两条记录及关系;空单允许 Fleet 的未分配资源、未确定卸货点和未知计划时间,不通过虚构站点、计划时间或容量去调用旧 Create DTO。起运仓必须真实存在;空单没有 PDA 拣货事实、实际装载、出库单或派送单。Fleet 的 PLANNED/IN_TRANSIT/COMPLETED 继续只表示资源运输生命周期;排车 stage 是五阶段的唯一事实,排车 status 仅表达 ACTIVE/CANCELLED 生命周期,详见第3节。正常签收汇总不自动执行 Fleet 结案或资源释放。

2.2 主单字段

字段 语义与约束
id/dispatchNo 内部关联键/全局唯一排车业务号;不将 id 当展示编号
overseasWarehouseId/Code/Name 唯一起运仓及目录快照;有货物后禁止换仓;空单换仓如开放需同事务更新 Fleet,不得单改展示
status、stage status仅ACTIVE/CANCELLED;stage仅BEFORE_LOADING/LOADING/AFTER_LOADING/DELIVERING/DELIVERED。取消保留实际停止阶段,不再推进;无第六阶段、无两字段同义状态映射
recordVersion 主流程公共版本,所有前台可写命令必须提交并 CAS 递增;不再让 PDA 用 tripRecordVersion 代替排车版本
cargoRevision/cargoHash 货物集合、真实来源/箱数、目的地与有效批次分配的修订号及稳定摘要;用于审核/装载/派送快照有效性,不与每次扫码计数混用
reviewRound/reviewBasisHash 审核轮次与当轮关键事实摘要;摘要包括 cargo、资源身份/能力、计划/目的地及批次版本;无关备注不改变审核依据
dispatchReview/loadingReview 当前轮状态、决定、原因、操作者、时间、依据摘要;历史追加保存,不能覆盖旧驳回记录
vehicle/driver/carrier 详情读取内部 Fleet 权威分配及临时资源快照;不再允许排车、派送请求分别覆盖出两套司机车牌
plannedDepartureAt/estimatedArrivalAt 可空计划时间,推荐 API 统一 ISO-8601 Instant、数据库 UTC DATETIME(3);不把计划发车保存为实际发车
loadingCompletedAt/loadingCompletionHash/departedAt/deliveryCompletedAt 服务端实际动作时间及装车事实摘要;摘要绑定cargo修订、真实pick/归属集合和实载快照,确认命令不接受客户端伪填实际时间
itemCount/palletCount/packageCount/pickedPackageCount/shippedPackageCount 全车计数;托数按实物托盘去重;逐订单展示不可重复计算同一托盘
remark/createdAt/updatedAt 详情所需备注及时间;updatedAt 当前 DTO 缺失需补齐

departAt 旧混合语义在 API 中直接拆为 plannedDepartureAt/departedAt,同步消费者,不保留双字段别名。路线从起运仓和真实目的地按稳定顺序生成;请求不允许任意改路线字符串。

2.3 货物、客户及来源

ll_dispatch_item 保留真实 customer_id>0,保留计划托盘、实物托盘、退回入仓、换标申请及目的地字段。一次货物选择以来源键表达,客户端不提供权威客户。服务端批量解析选中来源,复验客户、仓、暂扣、可排箱数、实物及有效占用后写入;允许同车不同客户/目的地,不允许跨起运仓。

同托多来源不能用 orderNo/orderRefs/inboundCode 展示快照作为分摊依据。复用 ll_dispatch_pda_pick_order 的逐入仓/退回来源和真实箱数,不另造第二套逐箱库存模型;拣货前的容量/预约/候选读取真实库存组成,拣货后派送和费用读取不可变拣货来源。每条 pick 的客户必须等于对应 item 和真实来源客户。普通、退回入仓、RELABEL、RETURN_RELABEL 四类实际分支均保留;来源阶段不是客户端随意传入的业务状态。

原来源锁 ll_dispatch_pallet_lock 保留真实客户与完整逻辑来源键,引用独立物理占用 ll_dispatch_physical_pallet_lock。活动实托唯一键只属于物理层;逻辑层不保留 active_warehouse_pallet_id 空别名。同一拥有者的多个真实来源可共享一个实托,跨主单、跨草稿容器仍排他。物理拥有者是主单或原草稿容器,不以第一条来源或第一客户代表。释放逻辑来源后,只有本拥有者最后一条活动成员结束才释放物理占用;取消、到期与转换不能触碰后续新拥有者的占用。

2.3.1 同实托多换标来源的共同读取合同

本批支持两张换标单合到同一实托后整托同车。物理托盘、实载重量体积和实际容量只计一次;客户、换标业务号、逻辑箱数、真实扫码与费用归属保留分行。既有整申请全部应排托盘规则保留,因此完整选货集合是“申请包含的全部应排托盘”与“实托包含的全部有效逻辑来源”共同形成的闭包。不得只依据页面当前筛选结果或托盘展示号拼组。

DispatchPlanningModels 的冻结参数顺序如下,无旧签名兼容构造器:

SelectionGroup(String groupKey, String groupVersion,
    List<SelectionGroupSource> sources, List<SelectionGroupPallet> pallets,
    boolean complete, List<String> blockReasons)
SelectionGroupSource(CargoSelection selection, String sourceKey,
    String customerId, String customerName, String sourceBusinessType,
    String sourceBusinessNo, long packages)
SelectionGroupPallet(Long warehousePalletId, String palletNo,
    List<String> sourceKeys, long packageCount)
Candidate(CargoSelection selection, String sourceKey, String sourceVersion,
    String customerId, String customerName, String warehouseId, String warehouseCode,
    String warehouseName, String orderNo, String inboundCode, String planNo,
    String palletNo, String containerNo, Destination destination, long availablePackages,
    BigDecimal suggestedWeightKg, BigDecimal suggestedVolumeCbm, String measurementSource,
    List<BatchReference> appointmentBatches, List<String> blockReasons,
    SelectionGroup selectionGroup)

selectionGroup 由服务端完整来源事实生成;缺失组、缺少组版本或 complete=false 都不构成完整可选证明。组版本绑定全部成员及来源版本、换标完工/确认轮次、实际位置和组成;不得用单条来源版本冒充组版本。页面选择组后,Create/AddCargo.cargo[] 仍显式提交组中全部逐来源选择;服务端在锁内重算闭包并核对精确集合。草稿 DraftSelection.source 请求形状保持,单次加入/移除命令由服务端原子处理完整组并返回全部成员,不由前端循环单来源命令凑组;容器布局与正式转换不得拆开同一完整组。

草稿解除、释放及纯租约动作使用原冻结实托集合先取得共同串行行,再读取逻辑成员及物理拥有者,避免与到期接管的物理→逻辑顺序交叉。来源事实变旧不妨碍合法拥有者释放原占用;这些动作不新增当前来源资格条件。选货、布局和转换仍先锁完整申请/订单,再取得共同实托锁。

DispatchViews 的冻结参数顺序如下:

PdaItem(Long dispatchItemId, String customerId, String customerName, String palletNo,
    Destination destination, long scheduledPackages, long pickedPackages,
    List<Position> positions, List<ReturnCarton> returnCartons,
    String sourceKey, String sourceBusinessType, String sourceBusinessNo)
OrderDetail(String customerId, String customerName, String orderNo, String inboundCode,
    BigDecimal grossWeightKg, Integer palletCount, String destinationFbaWarehouse,
    BigDecimal volumeCbm, String containerNo, String sourceKey,
    String sourceBusinessType, String sourceBusinessNo)

来源业务号直接取真实 sourceBusinessNo,换标来源为对应换标申请号;普通来源无该业务号时保持空,由页面显示“—”。不得解析 sourceKey 或以内部 ID 回退。PDA 保留逐 dispatchItemId 的既有 Pick 形状,不改为整托一次多写;同托多来源必须辨明逻辑来源,所选位置的额度和不可变拣货归属按本来源核对。CargoSelection 形状不变。

LoadingPreview.pallets 按实际 warehousePalletId 一托一行,计划箱数、已拣箱数为该实托各逻辑来源合计。不得把同一整托重量体积复制给每条逻辑 CargoLine;逐来源没有真实测量时保持未知,不按箱数分摊。现有同实托目的地一致、整申请排车、退货箱隔离和重做轮次规则保持,不引入拆托卸货或第五个详情页签。

普通换标贡献须保留逐 RelabelItem 的原货源快照与实际绑定位置。application.lastlegOrderId 可为独立申请生成的换标履约单,不等于所有原货源订单。工作事实先按申请和 item 聚合再关联,通知箱数不冒充成功箱数,成功/异常结果和新旧箱唛保留。无真实 SKU 件数时不能从箱数推算件数。原生退货继续使用真实退货箱及已有转换事实,不能伪造普通库存位置或收货凭据。

新增物理占用与来源贡献表后,scripts/fleet-trip-rollback-test-env.mjs 的全字段数据库基线也是直接消费者,必须纳入新事实后才可继续声称完整回滚;当前已验收脚本版本保持独立冻结,不与来源模型实现同时修改。

PDA 每车一个平台作业 task,删除 task 的 customer 头部;pick 与 pick_order 继续每条真实客户。task 业务号沿正式编号生成器生成,不能继续 DPDA + 内部ID 展示。主流程操作/幂等归平台整车,不借客户表承载。

2.3.2 换标贡献提供者的内部Java合同

提供者入口为 relabel/port/RelabelDispatchSourcePort.java,实现分别位于 relabel/service/RelabelDispatchSourceService.java、relabel/repository/RelabelDispatchSourceRepository.java、relabel/mapper/RelabelDispatchSourceMapper.java,保持 Service → Repository → Mapper。dispatch 只消费公开port,不直读换标持久化实体。方法与嵌套record参数顺序如下:

Snapshot snapshot(String warehouseId, List<Long> warehousePalletIds);
void lockApplications(List<String> relabelNos);
Snapshot lockedSnapshot(String warehouseId, List<Long> warehousePalletIds);

UnitFact(Long id, String oldLabel, String newLabel, String result, Integer quantity)
Application(Long applicationId, String relabelNo, String customerId,
    Long fulfillmentOrderId, String status, String fulfillmentStatus,
    String deliveryMethod, String roundKey,
    LocalDateTime updatedAt, LocalDateTime customerConfirmedAt)
BoundSource(Long applicationId, String relabelNo, Long itemId,
    String customerId, String warehouseId, Long warehousePalletId,
    Long inventoryPositionId, Long inventoryBalanceId,
    Long sourceInventoryBalanceId, Long sourceInboundOrderId,
    String sourceInboundOrderNo, String sourceInboundCode,
    long notifiedPackageCount, long boundPackageCount,
    Long productQuantity, String bindingBasisHash, List<UnitFact> unitFacts)
Position(Long positionId, String customerId, Long inventoryBalanceId,
    Long warehousePalletId, long availablePackages, long holdPackages,
    long allocatedPackages, long pickedPackages, long receivingPackages,
    long recordVersion)
Snapshot(List<Application> applications, List<BoundSource> sources,
    List<Position> positions, String compositionVersion,
    boolean complete, List<String> blockReasons)

snapshot 读取当前完整运输组成,不按页面筛选或排车资格隐藏关系。已作废 CANCELLED 申请保留在原业务历史中,不再扩展为当前运输来源;实际库存位置全部保留,残余HOLD无法由当前贡献解释时仍阻断。其他状态的资格由消费方判断。lockApplications 必须在调用方事务内按换标业务号稳定顺序一次锁齐。lockedSnapshot 重新读取已锁申请组成,不提前取得库存锁。dispatch 先计算闭包,固定申请全集→真实来源订单全集→共同 ll_physical_pallet_guard 实托串行行→组成重读→库存和运输占用顺序;发现新申请、订单或实托不得临时按发现顺序追加锁,整笔冲突重试。仓内组拆托入口使用同一共同串行点。

此port只提供换标及库存事实,不反查dispatch账本;Snapshot.complete 用于零扫码选货阶段。两申请共享position时,贡献总箱数须与该position真实HOLD共同核对,不能只逐item各自判断HOLD足够。实际扫码后仍读取原身份、轮次和逐件事实,但箱数以已冻结 ll_dispatch_item_source 减去不可变 ll_dispatch_pda_pick_order 已消费额度,再核对全实托剩余贡献与真实HOLD;不得将零扫码完整性或最初通知数当作当前剩余额度。

ll_dispatch_item_source.relabel_round_key 保存原提供者的真实作业轮次。原 bindingBasisHash 保留完整选货依据,但含库存数量、版本及申请生命周期,不用于要求正常扫码后摘要保持不变。执行锁内分别比较冻结轮次、申请/明细/客户、实际位置与余额、原货源、通知及商品件数和逐件事实;当前位置剩余HOLD按冻结贡献减已消费额度核对。PDA逐逻辑明细只显示本来源实际位置和剩余额度,同一position不向每个申请重复提供整仓位HOLD。

正常TRUCK客户确认到实际扫码前仍保持HOLD,通用AVAILABLE合拆托不能移动冻结仓位;实际扫码在单一事务连续RELEASE/ALLOCATE/PICK/SHIP,不形成已提交AVAILABLE窗口。转运关联换标走原RESERVED/WAIT_TRANSFER_OUTBOUND分支,退出普通RELABEL候选。公开库存release仍可能使事实改变,因此必须保留锁内聚合及版本校验,不能宣称HOLD全局不可改变。原生退货箱继续由原提供者提供,不被普通位置贡献替代。

2.3.3 派送与费用消费的换标原货源快照

共同字段位于 DispatchContracts,冻结顺序为:

SourceOrigin(Long dispatchItemSourceId, Long relabelApplicationId, Long relabelItemId,
    Long sourceInventoryBalanceId, Long sourceInboundOrderId,
    String sourceInboundOrderNo, String sourceInboundCode, Long productQuantity)

CargoLine 保留原参数顺序,在末尾 List<BatchAllocation> batchAllocations 后追加 SourceOrigin sourceOrigin。SourceOrigin 实际为上述八字段,不另加字段别名或旧签名构造器。普通换标的第四真实拣货来源必须链接冻结贡献,并由此带出原货源;其他分支保留原真实receipt/return凭据,sourceOrigin为空。DepartureFacts等外层形状不变。

CargoLine.orderId 仍是本条真实履约计数归属,不以原货源订单替换或把独立换标申请的履约单伪装成原货源。SourceOrigin.productQuantity 表示本次归属中有真实依据的件数,未知为空;不复制整个贡献或实托的总件数。出库、派送及费用快照保留这份不可变来源,用户界面显示原业务号而不回退内部ID。整托实载重量体积不复制到来源行。

2.4 主档与临时外配

推荐车辆、司机分别使用显式联合类型,支持一个选档案、另一个临时填写:

  • vehicle: { mode: MASTER, vehicleId } 或 { mode: EXTERNAL, plate, carrierName, capacity: { palletSpaces, maxLoadKg, maxVolumeCbm } }。
  • driver: { mode: MASTER, driverId } 或 { mode: EXTERNAL, name, phone }。

MASTER 的状态、区域、能力、证照及占用由服务端主档复验;请求不能覆盖主档快照。EXTERNAL对应ID必须为空,不生成主档、不造资源ID,不进入主档ID占用锁。按已确认规则由建单人补齐本趟真实容量、证照证据及预计任务结束,调度审核核实;空单可先保存、审核前补齐,快照进入审核依据及发车历史。临时车牌与既有主档占用的交集仍须核对正式资源规则,不以mode跳过必要校验。

3 五阶段与审核/动作资格

阶段 当前事实及允许的正常主操作 推进条件
装车前 BEFORE_LOADING 新建/加货/移除未拣货货物、修改资源与计划、提交调度审核、调度决定、理货决定 有货且资源等必需事实齐备才可提交;调度通过后才可理货;理货通过原子进入装车中
装车中 LOADING PDA逐托/来源拣货,后台/PDA确认装车完成 两审核当前轮有效;每条明细 PDA 真实箱数等于应装数量、正数、有完整来源且未超量;确认完成后进入装车后
装车后 AFTER_LOADING 后台安排派送,后台/PDA实际发车 派送/客户出库/费用核对完整且对应当前 cargoHash;资源、审核、批次接收资格再次有效;发车一次性进入派送中
派送中 DELIVERING 派送侧真实执行与正常签收、读取异常提示 所有当前有效派送均正常签收且逐客户逐来源数量覆盖本车实发后,自动汇总派送完成
派送完成 DELIVERED 只读本次作业;附加结案/资源结束保留原权限和入口 不自动释放车辆/司机,不把财务入账或成本结算当成完成条件

审核流程字段为 reviewState=DRAFT|DISPATCH_PENDING|DISPATCH_REJECTED|LOADING_PENDING|LOADING_REJECTED|APPROVED,每张审核记录自己的 status=NOT_SUBMITTED|PENDING|APPROVED|REJECTED。正常为DRAFT→DISPATCH_PENDING→LOADING_PENDING→APPROVED;调度/理货驳回分别进入对应REJECTED状态,两种拒绝不改变五阶段,仍停装车前。驳回原因必填。修改后显式重新提交,从调度开始新轮次,不复用旧理货通过。审核变更不自动补 PDA 或装车事实。

公开主单语义只保留一套:status=ACTIVE|CANCELLED 表示作业单是否有效,stage 表示唯一实际作业阶段,reviewState 表示审核子流程;不能再把Fleet状态、PDA task.status或单张delivery.status写到主单status。取消令status变CANCELLED,stage保留最后真实阶段供历史查询,所有主动作关闭;不生成CANCELLED阶段。主单新建为ACTIVE+BEFORE_LOADING,正常完成为ACTIVE+DELIVERED。数据库和DTO同步替换旧PENDING_DEPARTURE/READY_TO_DEPART/DEPARTED等主单状态,不保留旧字段别名。车辆状态、审核状态等嵌套字段均使用明确对象命名。

审核有效性不以 review.recordVersion == 当前版本 判断:审核命令及扫码本身都会加版本,应以 reviewRound + reviewBasisHash 判断。货物集合/来源数量/目的地、关键资源或资格/能力、关键计划及批次变化使相关审核和装载/派送快照失效。装车前编辑直接回草稿;已进入装车后的变更策略见第 10 节,不以后台编辑偷偷回滚状态或重新扣库存。

最后一次 PDA 扫码可记录 pickingReconciled=true,主单仍为 LOADING;后台/PDA再调用 loading-complete。后台没有数量补录字段,不允许用“全车总数相等”代替逐 item/source 核平。确认装车不要求已生成派送单,避免与“装车后才能安排派送”形成循环。

服务端统一返回 actions,每个动作为 { allowed, blockCode, blockReason },覆盖 create/add/remove/edit/cancel/submit/dispatchReview/loadingReview/pick/loadingComplete/arrangeDelivery/depart。列表批量计算阶段/审核/派送摘要,不逐行查详情;详情返回同一资格函数。资格包括当前用户权限和仓范围,按钮隐藏不替代写入时复验。

4 提议 API 与 DTO(待冻结)

统一 Gateway 前缀仍是 /api;Admin 客户端一般调用 /platform/v1/...,由 http base 拼接 /api;PDA同理。以下列完整公网路径。全部业务拒绝沿统一 ApiResponse 约定,不把代码内 ResponseStatusException 推定为最终 HTTP 响应。

4.1 平台接口

令 D=/api/platform/v1/last-leg/dispatches。

方法与路径 请求/响应提案 调整
GET D、GET D/{dispatchNo} Query及统计见4.5;公开status/stage按第3节唯一定义,ListRow/Detail均带recordVersion 保留路径,替换旧三阶段;平台customerId仅以真实item筛车,不改变主单归属
GET D/candidates 按仓、来源类型、订单/目的仓过滤,返回真实 customer/sourceKey/availablePackages/sourceVersion/blockReasons 保留路径,移除多客户 planIds 的单客户推断;有界批量读取
POST D Create { idempotencyKey, warehouseId, vehicle?, driver?, plannedDepartureAt?, estimatedArrivalAt?, remark?, cargo[], batchAllocations[] } → CommandResult 新入口;cargo为空建空单,非空同事务选货建车;没有customerId/tripNo字段;未取得服务端生成号时通过原POST同键恢复
PUT D/{dispatchNo} Update { recordVersion,idempotencyKey,vehicle?,driver?,plannedDepartureAt?,estimatedArrivalAt?,remark? } 保留路径;关键字段修改触发审核失效;不接收 status 或实际时间
POST D/{dispatchNo}/items AddCargo { recordVersion,idempotencyKey,cargo[],batchAllocations[] } 新入口;可继续在空单加货;批次分配提交最终完整快照,来源锁和保存原子
POST D/{dispatchNo}/items/{itemId}/remove { recordVersion,idempotencyKey,reason? } 替换旧无版本 DELETE,保留删除历史与占用证据;不物理删除已执行事实
POST D/{dispatchNo}/cancel { recordVersion,idempotencyKey,reason } 替换旧无版本 DELETE;取消前真实未拣货、无活动派送/出库;保留原取消历史及资金作废语义
POST D/{dispatchNo}/submit-review { recordVersion,idempotencyKey,remark? } 新入口,建立审核轮次及当前依据
POST D/{dispatchNo}/dispatch-review { recordVersion,idempotencyKey,decision: APPROVED或REJECTED,reason? } 调度审核归整车;不再按客户子单调度
POST D/{dispatchNo}/loading-review 同上 理货审核在拣货前;不要求派送或实际拣货
POST D/{dispatchNo}/loading-complete { recordVersion,idempotencyKey,remark? } → CommandResult 后台/PDA共享服务,后台没有拣货数量字段
POST D/{dispatchNo}/depart 同上 → CommandResult 新增后台入口,与PDA同一版本/命令持久化语义
GET D/{dispatchNo}/prints/bol 全车真实来源、单据与客户分组的PDF 保留路径,SQL去除主单 customer 假设;只读list权限
GET D/{dispatchNo}/external-freight、POST D/{dispatchNo}/transfer-delivery 由派送设计冻结,主流程建议保留已有路径,POST带整车版本/请求键及逐客户目的地费用数据 只在 AFTER_LOADING 开放写;由派送后端独占,本文不定义费用具体字段

CargoSelection 建议为判别联合类型:{ sourceType: INBOUND_PLAN,planPalletId,sourceVersion }、{ sourceType: RETURN_RECEIPT,returnReceiptId,sourceVersion }、{ sourceType: RELABEL,relabelNo,warehousePalletId,sourceVersion };服务端再识别 RELABEL/RETURN_RELABEL,不信任客户传入阶段。sourceVersion 用于识别选择后事实变化;若当前来源没有可靠单行版本,应定义其真实组成摘要,不能常量填0。

batchAllocations 保持 planPalletId/batchKey/customerId/sourceKey/destinationKey/packageCount 当前语义,customerId 是待复验的分配断言,不是写入客户权威。混合客户一次校验完整集合再保存。换标全申请选择门禁保留,不自动开放分批。

4.2 智能草稿与旧车次操作

草稿能力保留:GET /drafts、POST /drafts/snapshot、/drafts/containers、/draft-locks/lock|unlock|release。草稿及每个装车容器不再带单客户归属,锁保留真实客户。每个容器最终生成一张整车主单,跨客户是同一 cargo[],不是先按客户拆子单。正式转换建议由现有 POST D/drafts/pre-arrange 接收每组完整 Create 内容和持久请求键,组装车元数据可空;沿同一 create 服务实现。直接 POST D/pre-arrange 的生产消费者全部改用 POST D 后删除旧入口,不保留双建单协议。

fleet/trips/prepare、/{tripNo}/dispatches/bind|unbind、客户单调度审核旧路径不再用于配送排车主流程,直接调用者同步收敛。旧“多张排车共车”入口必须替换为给同一主单加货;不能把旧操作留为绕过新状态门禁的写入通道。车次容量/外接/成本/交接/资源结束等非该旧协议能力继续保留,进入 DELIVERY 关联主单时遵守共同锁和审核失效规则。PICKUP及独立附加能力的精确接线由总控裁定,不能笼统删整个 Controller。

4.3 PDA 与恢复

令 P=/api/pda/v1/last-leg/dispatches。GET P、GET P/{dispatchNo} 保留,返回本绑定仓整车 task,加入 recordVersion/stage/reviews/actions/pickingReconciled;items 保留真实来源、业务号、应装/已拣箱数。移除要求用户操作旧子单的 tripDispatchCount,内部 tripNo 可在附加追溯中读取,不能作为主动作请求身份。

  • POST P/{dispatchNo}/items/{itemId}/pick:现有 positionId/packages/productQuantity/cartonMark/remark/idempotencyKey 加 recordVersion;客户、起运仓均从服务端明细和Token取得。
  • POST P/{dispatchNo}/loading-complete:与平台同 LoadingComplete DTO;替换旧 /complete 的仅PDA完成协议,两端调用同服务。返回装车后阶段及动作结果。
  • POST P/{dispatchNo}/depart:保留路径,改用整车排车 recordVersion;与平台同 Depart DTO、结果和持久命令。
  • 两端均提供 GET D或P/{dispatchNo}/commands/{idempotencyKey};按当前身份/动作权限/仓绑定及第6.2节授权表读取原结果。未知键返回业务错误 DISPATCH_COMMAND_NOT_FOUND(data为空,非成功结果),不把未找到解释成动作未执行;原POST同键同载荷可安全恢复。数据库提交中途尚不可见也可能暂未找到,客户端继续保留原键及payload,不能立即换键。跨主体键不返回原结果。

统一公开 CommandResult { dispatchNo,action,idempotencyKey,recordVersion,status,stage,occurredAt,effect,counts,result,replayed }:recordVersion是该命令完成时排车版本(0有效),status/stage是该时刻事实;effect为APPLIED或EXISTING;result为动作判别联合类型(CREATE/EDIT返回已保存身份及变化,PICK返回item/source核平事实,LOADING_COMPLETE返回loadingCompletionHash,DEPART返回departedAt/逐客户实发单据,ARRANGE_DELIVERY返回完整outbounds[]/deliveries[]/arrangementKey)。不得只返回布尔成功或单个outboundNo。所有平台/PDA命令及GET命令结果使用同一schema;原结果除replayed标记外不可变,禁止将后来阶段塞进旧动作快照。前端校验同一dispatchNo/action/idempotencyKey后,再GET当前详情恢复UI;旧结果证明原动作,不覆盖新详情。响应不再要求用Fleet IN_TRANSIT加客户子排车数组猜当前排车成功。

4.4 详情字段供四页签使用

ListRow 返回业务号、五阶段、审核和装车/派送摘要、车辆司机、起运仓、计划/实际发车、数量、版本及动作资格;不装载完整订单/附件集合。Detail补充:

  • vehicleInfo { vehicleMode,vehicleId?,plate,driverMode,driverId?,driverName,driverPhone,carrierName,vehicleStatus? };临时车辆没有主档状态时显示“—”,不虚构启用状态。
  • dispatchInfo { route,plannedDepartureAt,departedAt,estimatedArrivalAt,fbaAppointments[],appointmentBatches[] }。多目的仓不能只返回一个 FBA 约号;预约从有效批次读,保留未知与待复核,不用头部可编辑约号覆盖正式预约。
  • documents { dispatchNo,deliveries:[{customerId,deliveryNo,destinationKey,status}],createdAt,updatedAt,remark };完整返回业务单号,列表摘要批量读取。
  • orderDetails[]:真实客户、订单号/入仓码、重量kg、托数、目的FBA仓、体积m³、集装箱号及来源键。按真实订单组成批量组装,不把同托全量重量/体积复制到每个订单;没有可信分摊则对应字段null。containerNo、updatedAt、remark、多派送单当前排车DTO缺失需补齐。

4.5 列表审核分区、分页和统计

完整Query提案:{ page>=1,size=1..200,dispatchNo?,vehiclePlate?,warehouseId?,customerId?,fbaAppointmentNo?,status=ACTIVE|CANCELLED|ALL,stage?,beforeLoadingSection?,reviewStatus? }。status缺省ACTIVE;ALL仅为查询选项,不是保存值/第六阶段。stage严格五项;beforeLoadingSection为VEHICLE_INFO|DISPATCH_REVIEW|LOADING_REVIEW,仅在status=ACTIVE且stage=BEFORE_LOADING时合法,其他组合拒绝参数错误,不能静默忽略。reviewStatus仅在某个审核分区存在时合法。

三个装车前分区保留同一整车集合和不同列投影,不能擅自将“车辆信息”解释成未提交队列并隐藏已审核单。VEHICLE_INFO不接reviewStatus;DISPATCH_REVIEW按本轮dispatchReview.status过滤;LOADING_REVIEW按本轮loadingReview.status过滤,均支持NOT_SUBMITTED/PENDING/APPROVED/REJECTED,未传reviewStatus时不过滤该状态。尚未提交/尚未轮到的审核是NOT_SUBMITTED,不伪造PENDING;旧轮失效通过history/invalidatedReason展示,当前轮重新待提交,不将旧APPROVED算入当前。

返回 DispatchPage { records,total,page,size,pages,statistics,snapshotAt };statistics结构固定为:

statistics {
  matchedTotal, activeTotal, cancelledTotal,
  stageCounts: { BEFORE_LOADING, LOADING, AFTER_LOADING, DELIVERING, DELIVERED },
  beforeLoadingReviewCounts: {
    vehicleInfoTotal,
    dispatchReview: { NOT_SUBMITTED, PENDING, APPROVED, REJECTED },
    loadingReview: { NOT_SUBMITTED, PENDING, APPROVED, REJECTED }
  }
}

total是当前status+stage+审核筛选后的整车总数,与分页records一致;客户筛选通过EXISTS item.customerId选车,每车只计一次。statistics的共同基集只套当前用户范围及dispatchNo/vehiclePlate/warehouse/customer/正式预约号等搜索条件,排除分页、status/stage/分区/reviewStatus;matchedTotal=activeTotal+cancelledTotal,stageCounts仅ACTIVE且五项之和=activeTotal。beforeLoadingReviewCounts只统计ACTIVE+BEFORE_LOADING,vehicleInfoTotal=该阶段数;两审核状态各四项之和同样等于该阶段数,三个分区不是三份互斥货物。零项明确为0,不返回当前页长度冒充总数。

页面、数量及动作投影在同一只读一致快照事务读取,statistics不逐行查详情。stage筛选影响records/total,不影响顶部各阶段计数;审核筛选不影响同基集的审核分布。用户选择客户筛选仍是平台整车查询,行数量为全车数量;若需要仅本客户数量,必须另名customerMatchedPackages,不能把全车总量悄悄改成筛选客户量。本节是后端可实现的查询提案,分区是否默认只看待审属界面默认值,不新增业务阶段。

5 派送后端可读取事实和共同端口提案

端口不共享持久化Entity,不让派送方从 DispatchOrder.customerId 推导归属。建议在本任务 dispatch/port/ 新增只包含 DTO/接口的契约,由总控冻结后移交派送方实现其端口;实现仍遵守各自唯一写权。所有端口同 Lastleg 本地事务,不新建HTTP内部调用。

端口 签名/事实语义 实现者
DispatchExecutionPort.lockForArrangement(actor,dispatchNo) 返回Context:dispatchId/no、tripId/no、warehouse、recordVersion、cargoRevision/hash、stage、审核/装车事实、当前资源及cargo;先锁定和核对身份供命令恢复,首次写入另调requireFreshArrangement 主流程后端;先统一锁,不能派送方先锁delivery再回头锁主单;不得在查原结果前拒绝旧版本
Context.cargo() 返回CargoLine:dispatchItemId、真实customer、来源与pick/attribution、退货/换标/实托、Destination、packages及批次;精确字段见实际Java 主流程提供稳定身份;逐来源查询可由派送方沿现有DispatchCargoSourceQuery/DispatchFeeCargoMapper适配,只保留一份来源权威
DispatchExecutionPort.recordArrangement(context,actor,identity,fact,alreadyArranged) 首次安排CAS递增整车版本并记录公共命令,阶段仍AFTER_LOADING;已存在同业务仅记当前新键EXISTING结果,不重建单据 主流程后端;派送服务全部客户成功后同事务调用,字段/签名以第0节为准
DispatchDeliveryPort.departureFacts(locked,lock) 返回 arrangementRevision/hash、客户出库单和逐客户目的地派送单、覆盖箱数、费用核对有效性及 blockReasons;逐来源精确覆盖,不能只比总数 派送后端
DispatchDeliveryPort.markDeparted(locked,departedAt,commandKey) 所有客户出库/派送推进,返回逐客户实发事实;不扣已扫码库存、不更新排车阶段 派送后端,与主流程发车同事务,任一失败全车回滚
DispatchDeliveryPort.normalDeliveryFacts(context) 批量普通读NormalDeliveryFacts,返回全部本车派送原实发/签收/退回/异常、expectedDeliveryNos、allNormallySigned、factsHash及正常签收最大时间;不加历史来源订单锁 派送后端;空集合不是真,拒签/退回不得正常完成
DeliveryFactsChanged(dispatchNo,deliveryNo,deliveryVersion) 签收事务内记录唯一持久待核对事实,签收提交后消费/重试 派送后端保存事实,主流程在新事务汇总;不在原签收子锁下申请父锁
DispatchCompletionPort.reconcile(signal) 主流程新事务先取得trip/dispatch锁,再普通读全部已提交签收事实,CAS stage DELIVERING→DELIVERED并递增recordVersion,记录唯一汇总依据和完成时间,signal确认同事务 主流程后端;重复提醒无副作用,失败回滚后记录退避;写权按6.5

allNormallySigned 至少要求:当前有效派送单非空、没有缺项/取消/异常,全部有真实正常签收事实且逐客户/目的地/来源覆盖本车实发;分批签收仍未完成。原 FleetCompletionModels 中“正常签收或真实退回可结案”继续只服务附加结案,不能替代此端口。

本稿完成后已只读核对派送稿第6节:采纳其“签收保持本派送→来源订单锁、提交后持久提醒再汇总”的建议,以保留已有跨多趟来源完成证明并避免扩大签收锁范围。正常签收后另发退货不重开本趟完成,完成时间取本趟最后真实签收;不能只因当前派送状态后来出现退货就否认已完成的原运输。汇总不二次发布来源完成事件、回访或费用。提醒待处理记录归派送域,主流程消费/重扫归dispatch域,确切存储和调度入口仍由总控冻结。

发车事务按6.4精确链:trip根锁→原命令恢复或新命令资源锁→dispatch→全部客户outbound/delivery→task/批次→真实来源订单和业务行→单据及订单统计推进→写唯一DEPART历史、stage=DELIVERING及不可变命令结果;status保持ACTIVE。库存扣减已在PDA扫码,不在此事务再ship;资料核验可普通读,不允许适配器在锁齐派送前提前取得来源订单锁。

派送服务安排事务按6.4先取得父级及安排行、outbound/delivery锁(首次为先完成子单插入),再取需要的来源/费用锁和写Outbox,最后recordArrangement及共同命令结果同事务提交;首次命令只允许ACTIVE+AFTER_LOADING。相同目的仓不同客户不合单;逐客户目的地费用具体由派送设计定义,不由本任务猜测。

6 事务、版本、幂等与锁顺序

6.1 共同命令与两种摘要

公开写命令统一 recordVersion(创建无既有单版本除外),不存在公开expectedVersion。新增 ll_dispatch_command { id,dispatch_id,dispatch_no,action,operator_id,idempotency_key,command_hash,result_json,created_at },idempotency_key平台全局唯一,operator作为不可改的所有者字段,不能用(operator,key)分片后让另一个操作者重建同键动作。Admin/PDA共享表、schema及当前身份权限规则;渠道不进入命令身份,同一账号从另一端恢复仍先核验该端权限/绑定仓。

  • commandHash:绑定可信主体operatorId、动作、目标业务号、原recordVersion及规范化完整业务payload(含真实证据版本/hash);排除随机键本身和传输字段。Create无目标号时使用CREATE作用域及完整创建内容,结果绑定服务端生成业务号。对象属性排序、集合按稳定业务键排序、金额精度固定,不能因显示顺序不同误判;集合只有确认为无序时才排序。原键不能改变主体/动作/单号/原版本/任一业务字段。
  • arrangementBusinessHash:仅安排派送有此业务去重摘要,包含dispatch身份、cargoHash、loadingCompletionHash、逐客户目的地完整组、资源快照、费用/报价/覆盖证据及全部业务字段;排除operatorId、recordVersion、idempotencyKey、客户端显示顺序等纯命令身份。另一操作者/另一键也不能创建第二套安排或费用。它不用于吞掉两次数量相同但实际不同的PDA扫码,不代替commandHash。
  • ll_dispatch_delivery_arrangement以dispatchId唯一保存业务摘要、单据集合和原事实状态;派送域独占写,本地command与安排事实、子单、费用及Outbox同事务。首条Arrange命令的不可变原结果保存在共同命令表;不能在delivery再建立一套不同授权的命令键协议。

既有命令查验在版本/阶段门禁之前;当前权限和仓绑定校验始终在结果披露之前。新命令使用CAS及当前recordVersion;版本冲突整体回滚,不将换版本当成同一未决命令重试。原结果恢复不递增版本、不重复扣库存/发布费用。没有持久成功命令的未知结果继续以原payload重试;失败或超时不自动改键。

6.2 授权、恢复与不同请求键判定表

场景 权限和判断顺序 结果/副作用
原键、原主体、原完整payload;当前阶段/版本已推进 先核验当前端身份、原动作权限及PDA绑定仓;再校验commandHash和业务号;先于旧版本拒绝 原不可变CommandResult,replayed=true;不重新执行;再GET当前详情
原键、原主体但改recordVersion/动作/单号/数量/费用等 当前权限后检查严格commandHash DISPATCH_COMMAND_CONFLICT,不覆盖原记录、不返回“按新版本成功”
原键、不同主体,即使有同等权限或同样安排业务 当前身份后比较命令所有者,不披露原payload/结果 DISPATCH_COMMAND_FORBIDDEN;必须用自己的新键走当前事实授权流程;不复用他人命令身份
原主体同键,但权限已撤销、角色不符或PDA已换仓 先检查当前授权范围 拒绝读取/重放;历史成功不授予当前访问权
新键、原主体或另一有权限主体、同一有效安排业务 先核验平台安排写权限和全车范围,再校验新命令recordVersion;锁内比较全车唯一安排的businessHash及有效状态 不新建单据/费用;保存属于新主体/新键的严格命令及EXISTING结果,返回原完整单据集合,replayed=false;同一新键再次请求则replayed=true
新键同安排业务但recordVersion过期 新命令不享受旧命令重放豁免 DISPATCH_VERSION_CONFLICT;GET当前事实后另起明确命令,不静默替换未决payload
新键同车但安排业务不同,或原安排已取消/失效 复验当前唯一安排及businessHash/状态 DISPATCH_ARRANGEMENT_CONFLICT/INACTIVE;返回可公开的阻断原因,不重建、不重复发布;原键仍只能恢复其历史结果
新键重复装车完成/发车 即使载荷看似相同,也无第二次动作;有权限者可读当前事实 DISPATCH_ALREADY_LOADING_COMPLETED/ALREADY_DEPARTED;不插第二执行事实。需恢复成功证明时用原键查询
GET原键不存在/尚未提交可见 当前身份/权限/仓后查询 DISPATCH_COMMAND_NOT_FOUND且无成功data;保留原payload同键POST核对,不把“未找到”等同“可重新生成”
GET他人键/仅有list权限但没有原写动作权限 list仅允许读当前业务事实,不允许冒充原命令所有者 原结果拒绝;另有已定义审计端口时走独立审计权限,不在本次泛化开放

以上错误码是设计提案,由总控统一映射ApiResponse。业务错误沿项目HTTP200+body.code规范;真实认证/基础设施拒绝按现有401/403等规则,不把表内业务冲突强制解释成HTTP409。命令查询与当前详情都不会将内部ID作为业务编号。

6.3 本轮沿实际Java核对的锁点

以下是当前源码事实,不是新设计已证明无死锁;→表示同一事务中先取得/执行,括号“读”不表示行锁。

操作与实际方法 已核对顺序和边界
FleetTripService.assign planned→repository.lockTrip → vehicles.lockById → DriverService.requireDispatchable→drivers.lockById → resource reserve/release DML → DispatchFleetAdapter.lockForAssignment锁dispatch → 锁outbound → 锁task → assign再锁相关deliveries。资源占用FleetResourceMapper.find只是SELECT,不能当成FOR UPDATE;新增/删除占用由唯一键及DML串行化
FleetTripExecutionService.depart trip行锁 → vehicle档案锁 → occupancy普通读 → driver档案锁 → manifests(true)逐dispatch行锁、outbound锁、task锁,manifest末尾DeliveryExecutionGuard.departureBlockReason(...,true)锁deliveries → AppointmentWorkDispatchBinding.requireDeparture批次锁 → 真发车子操作。不可改写成“先来源再派送”
DispatchPdaWorkflowService.depart(MANDATORY,外层已持Fleet父锁) 重入dispatch锁 → binding锁 → outbound锁 → DeliveryExecutionGuard.requireDeparture派送锁 → task锁 → DispatchPickOrderAttributionService.depart按订单数字ID锁来源订单并更新订单计数 → 转运/换标业务更新 → 来源占用释放/DML。此处没有inventory.ship;订单计数移动不等于库存移动
DispatchPdaWorkflowService.pick dispatch行锁 → task行锁 → binding锁 → preparePicking按orderId锁真实来源订单/已有退回收据 → inventory的allocate/pick/ship;每个InventoryCommandService.execute先查/插幂等operation(唯一键DML),再locked取得position行锁→balance行锁。随后写pick/归属;没有outbound/delivery行锁。RETURN_RELABEL先prepareNativeReturn锁来源订单,再ReturnRelabelDispatchStock.pick锁真实退货箱;不伪造position
DeliveryTransferService.platformTransfer/transferResolved dispatch行锁 → 活动outbound锁/原键查询 → binding锁 → bindReceivedPallets更新item(DML锁)→ freight/coverage及费用证据校验 → 插入outbound/子单及费用/来源更新。不是天然trip父锁入口,必须接新上下文端口;安排流程不得照搬旧提前转换资格
DeliveryTransferService.cancelOutbound 非锁读定位 → dispatch锁(有关联时)/独立relabel锁(对应类型)→ outbound锁 → binding锁及未执行资格 → delivery集合锁 → 按业务分支的退货/转运/任务/来源更新;普通dispatch分支后续按订单ID锁tailOrders。不能从outbound锁后才请求trip锁
DispatchService.cancel repository.lockOrder锁dispatch → binding锁且当前requireUnbound → outbound锁 → 来源换标/退货释放及活动锁DML。新一对一主单恒有关联,须改为父级协调取消,不能保留“必须unbound”造成永远不能取消;不等于授权已拣/已装车撤销
DeliveryDocumentService.save及SourceDeliveryCompletionQuery.proveAll 显式READ_COMMITTED,先本delivery行锁;outbound以普通读判断已出库,避免反向outbound锁;随后proveAll按orderId锁相关真实来源订单,普通读历史发运证明,不锁历史delivery/dispatch/exception/inventory。不可持这些子锁再调trip/dispatch汇总

源码证据还表明已有逐来源订单记账方法 LastlegOrderMapper.transferPickedToShipped 是尾程订单picked/shipped统计移动,普通扫码调用的 InventoryCommandService.ship 才是仓位/库存实际扣减。新设计分别命名 finalizeSourceOrderShipmentCounters 与“库存SHIP事实”,不再写含糊的picked→shipped。装车完成、安排、发车只核对并消费已存在pick/attribution/SHIP流水;发车仍要按真实来源更新订单/出库/派送计数,不能因不再ship就漏掉订单统计。

6.4 新实现逐操作锁协议(待冻结,替换相反提案)

共同根为trip行锁,沿现有Fleet顺序固定为 trip→必要vehicle档案→必要driver档案/占用DML→dispatch。不能本任务保留trip→dispatch→资源而另一任务保留资源→dispatch。身份只读解析不加锁,锁trip后重读一对一关系;所有会改主单的原Fleet/后台/PDA/派送入口必须走同一根,旧直锁dispatch入口不得遗留。原键命令可在trip锁后直接授权重放,不能先要求当前司机仍可派车才允许恢复过去结果;新命令才按下列顺序取得资源及子锁并复验。

新操作 根以后实际需要的链 不能做的事
装车前编辑/审核/资源变更 根→资源(涉及才取)→dispatch→binding;涉及批次/来源变更再按batchKey及真实来源ID锁,触及预建出库时先取出库/派送再取来源 不将审核前置查询直接解释为来源已锁;不在库存锁后改批次或派送
PDA扫码 根→必要资源校验→dispatch→binding→全部换标申请→真实来源订单升序→共同实托串行行→组成重读→task/item→退回来源行(对应分支)→position→balance,库存命令和pick归属同事务 本链不请求outbound/delivery行锁,不写整车派送完成;不得按当前item才逐个补取申请和订单
装车完成 根→必要资源→dispatch→binding→全部换标申请→真实来源订单升序→共同实托串行行→库存及冻结贡献复验;读取既有pick/归属与当前装载清单 不先造派送以满足装车门禁;不执行allocate/pick/ship,不再申请delivery锁
安排/发车/关联出库取消 根→资源→dispatch→binding→整车安排行/命令检查→全部客户outbound(客户/ID排序)→全部delivery(客户/ID排序)→task/item(需锁才取)→相关预约批次;涉及实托来源执行/释放时再按全部换标申请→真实来源订单升序→共同实托串行行→库存/运输占用及来源状态取得锁,最后费用来源/Outbox及DML 所有需取得的delivery行必须在第一个来源锁之前取得;不在持来源锁后补锁另一张派送,不再扣库存;取消只适用已有未执行合法分支
签收资料/来源完成证明 本delivery→来源订单升序(保持既有显式READ_COMMITTED链)→业务完成/Outbox/待汇总信号提交 不取得trip/dispatch/outbound行锁;来源证明只读历史证据,不以父锁替代跨趟来源序列化
主流程汇总 新事务trip→dispatch(不请求资源锁)→普通读全部已提交本趟签收/异常事实→CAS主stage→acknowledge本信号→同事务提交 不调用SourceDeliveryCompletionQuery.proveAll、不锁来源/历史派送;资源不因汇总释放;失败阶段与ack同时回滚

关键交集论证:发车和签收都将需要的delivery置于来源订单之前;扫码只持来源/库存而不再申请delivery,持有来源锁的扫码最终可提交,不等待签收持有的delivery;汇总不持子锁申请父锁。仅trip共同根不能解决跨trip共享来源的反向等待,以上限定正为此而设。安排内若费用/报价/来源适配器提前锁来源,必须后移至全部delivery锁已取得(首次新建子单也先完成插入),不把当前旧调用顺序原样当新协议。

多客户统一排序:客户用已验证客户ID的数值顺序(现有API字符串只做传输),内部订单/出库/派送/仓位用Long数值顺序,batchKey按稳定字符串排序。不是把"10"放在"2"前的随意字典序。跨车资源替换应预先按资源类型/数值ID锁定本次全部旧/新主档,再执行占用变化,避免两车交换资源相互等锁;额外主档/交接入口仍需精确写权。

未证实的锁边界保留为实施门禁:预约编辑/取消/替换与来源/费用的外部入口是否会在持batch/source/fee锁后回头申请本车父锁,尚未遍历验证;对这些已列直接消费者须由总控分配后逐一核对,不允许靠一句“顺序统一”宣称全局无死锁。新增测试要覆盖跨车同来源签收/发车、两车资源交换及预约变更竞争;本轮只静态核对,未运行并发验证。

出站费用与业务事件只写本地Outbox并随事务提交,远程成功不等于本地事务原子性;资金/文件/其他服务继续正式端口协作,不跨Schema Join。

6.5 正常签收持久信号的唯一写者

派送任务独占 delivery/ 下拟新增 DeliveryFactsChangedSignal、Repository/Mapper及 DeliveryReconcileSignalPort 实现:签收提交前在同事务插入 (dispatchId,deliveryId,deliveryVersion) 唯一信号,保存PENDING、attempts、nextRetryAt、lastError;相关结构由总控统一SQL集成。纯内存afterCommit可作唤醒加速,不能代替持久信号。

主流程任务独占 dispatch/service/DispatchDeliveryCompletionWorker.java、DispatchCompletionService.java及直接重试/并发测试;使用服务已有调度能力有界扫描due信号,逐车调用新事务reconcile。端口返回信号时不持delivery/source/signal行锁跨入父锁事务;多实例可重复处理,父锁/CAS及唯一汇总依据保证幂等。按正式契约第九节,阶段CAS和本次signal acknowledge在同一汇总事务提交,禁止旧稿“先提交阶段再另事务markProcessed”;任一失败两者均回滚。失败后才独立短事务记attempts/nextRetryAt/错误;未核齐的合法部分签收已完成本次核对,可在同汇总事务确认该信号,后续签收会发新信号。

增加有界修复性重扫只读定位“DELIVERING且存在未处理签收信号/已具备全签候选”的本车,仍调用相同reconcile,不写造签收事实;它是本功能可靠消费重试,不是测试存量回填。信号记录/markProcessed端口由派送写,消费/调度/重试决策/重扫由本主流程写,Maven仍本任务唯一执行。普通最终一致窗口内页面读真实stage=DELIVERING,不在签收POST成功后由前端直接置DELIVERED。

7 权限与隔离提案

平台入口限定 PLATFORM 且 tenantId=0;列表详情及只读候选使用正式list权限,写权限分别检查。真实客户只来自来源,平台头部不是一位客户。客户已有派送/出库读取继续Token客户隔离:按客户明细/单据读投影,不向客户暴露整车客户清单、其他客户费用、附件或完整整车详情。按用户已确认规则,由派送任务移除旧merchantTransfer创建此类派送的写入口,客户继续合法读取本客户单据/资料,不保留客户改整车旁路。

建议具体权限如下,由总控统一维护 OverseasPermissions、IAM/SQL及前端,不由本任务写共享文件:

动作 权限提案
整车列表/详情/BOL 既有 platform:overseas-warehouse:dispatch:list,不因可读详情而可调用写命令
命令结果GET及同键POST恢复 当前原动作写权限+原主体+业务范围/PDA绑定仓,按6.2;list不授予恢复他人命令的权利
新建/选货建单 新增 platform:overseas-warehouse:dispatch:create;旧智能排车权限消费者由总控映射/收敛
修改/加货/移除/提交审核 既有 platform:overseas-warehouse:dispatch:edit;提交是申请,不是审核通过
取消 既有 platform:overseas-warehouse:dispatch:cancel
调度/理货决定 推荐沿用既有 platform:fleet:trip:dispatch-review/loading-review,总控同步排车操作资源,不新增一套同义审核权限
后台装车完成/实际发车 建议新增 platform:overseas-warehouse:dispatch:loading-complete/depart,不借edit或PDA角色授权后台
安排派送 既有 platform:overseas-warehouse:dispatch:transfer-delivery,费用确认权限另按派送设计
PDA列表/扫码/装车完成/发车 lastleg:pda:operate 加海外PDA角色和Token绑定仓;不接收customerId/warehouseId改变范围

已有车辆主档/司机资格/资源占用仍由服务端验证,不能因为排车详情不显示附加能力而省略。主单、task、每条货物、出库派送所属起运仓一致;PDA跨仓读/写和同键恢复都拒绝。后台目前没有PDA的绑定仓事实,不从客户端PDA token或伪造身份调用PDA Controller;后台使用平台权限并复用领域服务。

8 精确实施清单与直接消费者

8.1 本任务后续独占生产触点

路径 计划变更
dispatch/controller/DispatchController.java、DispatchQueryController.java、SmartDispatchController.java 上述平台API、五阶段过滤、整车跨客户建单、删除单客户推断
dispatch/dto/DispatchRequests.java、DispatchResponse.java、DispatchDraftGroupResult.java Create/Add/版本命令、资源联合类型、五阶段、四页签事实、全车结果;禁止别名兼容
dispatch/entity/DispatchOrder.java、DispatchItem.java、DispatchDraft.java、DispatchPalletLock.java 主单/草稿去客户头部、主单版本/审核/实际节点、明细真实客户与实物排他
dispatch/mapper/DispatchOrderMapper.java、DispatchItemMapper.java、DispatchCandidateMapper.java、ReturnDispatchCandidateMapper.java、DispatchPalletLockMapper.java、DispatchDraftMapper.java 全车查锁、按明细客户过滤、来源解析批量化、保留真实客户库存条件和退回来源
dispatch/repository/DispatchMutationRepository.java;拟新增 DispatchRepository.java、DispatchCommandRepository.java 与 dispatch/mapper/DispatchCommandMapper.java 接管触及服务直调Mapper、主单和命令持久化;不把SQL搬进Service
dispatch/service/DispatchService.java;拟新增 DispatchExecutionService.java、DispatchReviewService.java、DispatchCompletionService.java、DispatchDeliveryCompletionWorker.java 创建/加货/审核/五阶段状态机;双端共同装车与发车;主流程唯一消费持久签收信号、重试与重扫,不两端各写一套
dispatch/pda/controller/DispatchPdaController.java、dto/DispatchPdaRequests.java、DispatchPdaResponses.java 整车身份/版本、仓绑定、替换complete协议、恢复结果
dispatch/pda/service/DispatchPdaWorkflowService.java、DispatchPickOrderAttributionService.java,以及 dispatch/service/ReturnRelabelDispatchStock.java item真实客户、全车核平、扣减一次、归属/换标退货事实;不改变已修库存规则
dispatch/pda/entity/DispatchPdaTask.java、DispatchPdaOperation.java 及对应Task/Operation/Pick/PickOrder Mapper task去客户头部、命令集中;pick继续真实客户,查询不能再按单一头部customer过滤全task
拟新增 dispatch/pda/repository/DispatchPdaRepository.java PDA读写经Repository;逐来源账本与库存命令保留其领域入口
dispatch/fleet/DispatchFleetAdapter.java、FleetDispatchPlanningAdapter.java、FleetDispatchPlanningMapper.java 快照不再单客户;manifest分解为拣货核平/实载/派送三个事实,保留sourceCargo/交接/成本直接消费者
dispatch/service/DispatchBolPrintService.java、dispatch/mapper/DispatchBolMapper.java 全车BOL、按明细真实客户取来源;补Repository边界
fleet/trip/FleetTripEntity.java、FleetTripDispatch.java、FleetTripModels.java、FleetExecutionModels.java、FleetTripReview.java 一对一关联、去客户绑定审核、临时资源快照、内部和外部版本区分
fleet/trip/FleetTripService.java、FleetTripExecutionService.java、FleetTripPreparationService.java、FleetDispatchBindingGuard.java、FleetDispatchGateway.java、FleetDispatchWorkflowQuery.java 自动资源记录、统一阶段/审核资格、消除多客户子单绑定流程;保留附加能力及其服务端验证
fleet/trip/FleetTripRepository.java、FleetTripMapper.java、FleetTripDispatchMapper.java、PlatformFleetTripController.java 一对一关系和明确旧接口删除、附加API仍可用;新建资源不伪填旧必填值
fleet/trip/FleetTripCompletionService.java、FleetTripCostService.java、FleetTripCostBasis.java、FleetTripHistory.java、FleetTripHistoryMapper.java 按真实客户货物读取,原结案/成本历史与资源结束保持;不将资源COMPLETED当五阶段完成
fleet/trip/VehicleFulfillmentMapper.java、FleetTripCalendarService.java、fleet/trip/work/AppointmentWorkTripMapper.java 已发现b.customer_id连接需改按派送/货物真实客户;日历保留真实时间和空单缺项,不再假定计划时间全非空

新增文件名为推荐落点,冻结时可按既有层次合并;不能为少建Repository继续跨层SQL。FleetBookingFreight.java 归派送任务,本任务只提出 SourceCargo/绑定关系变化,不修改。

8.2 必须由总控分配或集成的外部触点

精确触点 必须处理的影响/写者
delivery/service/DeliveryTransferService.java、DeliveryFeeCoverageService.java、DispatchCargoSourceQuery.java、delivery/repository/DispatchCargoSourceRepository.java、delivery/mapper/DispatchFeeCargoMapper.java 派送后端;所有 dispatch.getCustomerId 消费改为真实分组,签名不能留旧头部参数
delivery/service/DeliveryDocumentService.java、DeliveryExecutionGuard.java 及客户Controller/DTO、Outbound/Delivery Mapper 派送后端;共同锁在单据锁之前;正常签收汇总端口、跨客户出库覆盖与唯一键
inboundplan/work/AppointmentWorkDispatchBinding.java、AppointmentWorkDispatchMapper.java、appointmentwork/AppointmentWorkDispatchPort.java 超出本任务当前生产写权,总控分配唯一写者;必须一次校验/保存全车各客户分配,移除d.customer_id Join;保留稳定batch/source/customer/目的地和已接收门禁
exceptionorder/service/ExceptionOrderService.java 已发现DispatchOrder直接消费者;改用派送/拣货真实客户归属,保留 RETURN_RELABEL 原箱回溯;总控分配,不能覆盖在途测试修改
fleet/exception/FleetHandoverPdaQuery.java 及直接交接事实调用 客户从派送事实而非整车头部获取;保留未完成派送资源和完成时历史快照,总控分配
customervisit/service/CustomerVisitService.java、delivery/model/DeliveryStatusPolicy.java 已修状态门禁继续有效,签收/状态写入需共同锁入口;总控确认派送后端接管范围
src/main/resources/db/lastleg.sql、deploy/sql/头程数据库统一增量-MySQL8.sql、IAM权限资源 总控唯一集成;按新基线收敛,不迁移/回填测试存量,不执行SQL
OpenAPI overseas-business-v1.yaml、fleet-trips-v1.yaml、fleet-preparation-v1.yaml、appointment-work-v1.yaml、必要新增 dispatch 文档 总控唯一集成;删除旧协议与消费者同批完成;统一Gateway既有last-leg通配应离线验证,不能凭路径推定运行可达
file 附件提供者 共享整车BOL/附件若改变授权归属,先列出正式接口由总控分配;不把客户附件改为平台公共附件,本轮不改file代码

前端直接消费者已定位:Admin overseas-warehouse/api/dispatches.ts、pages/DispatchesView.vue、pages/DispatchDetailView.vue、components/SmartDispatchDialog.vue,以及 overseas-master-data/api/trips.ts、components/TripPreparationDialog.vue、components/TripBookingPanel.vue;PDA src/api/fulfillment.js、src/stores/fulfillment.js、src/pages/dispatch/dispatch.vue。归前端任务。

预约在途消费者只读列明:Admin appointment-calendar/WorkDispatchForm.vue 当前调用 drafts/pre-arrange 后弹车次准备和同仓共车,WorkSharedTripForm.vue要求两张客户排车,WorkExistingDispatchForm.vue仍以旧PENDING_DEPARTURE/workflow判断“继续安排本批车辆”。三者必须接入同一整车创建/加货及actions,保留事项弹窗关闭、原单跳转和已操作批次事实;总控分配接线,不由本任务覆盖。

脚本直接消费者已发现 scripts/admin-test-env-modules/{grouped-dispatch-acceptance,dispatch-repair-acceptance,fleet-cross-customer-acceptance,fleet-cross-customer-fixture,fleet-external-dispatch-acceptance,fleet-fee-coverage-acceptance,fleet-booking-reservation-acceptance,fleet-return-redispatch-acceptance,fleet-handover-acceptance,fleet-trip-cost-acceptance,fleet-trip-fulfillment,phase3-outbound-dispatch,phase4-overseas-dispatch,phase5-overseas-fulfillment}.mjs 等及其合同。这里只登记实际路径命中,不读取历史报告或执行真实HTTP;协议冻结后总控定向分配旧请求/状态更新,不能让旧fixture继续构造客户子排车。

8.3 结构变更给总控的清单

  1. ll_dispatch_order 去customer,新增stage五阶段列、status收敛ACTIVE/CANCELLED、主版本、cargo修订/摘要、当前审核轮/依据、装车/发车/正常派送完成实际时间、备注;索引改仓/status/stage/计划时间及业务号,客户检索走item索引。审核详情可JSON但历史不可覆盖。
  2. ll_fleet_trip_dispatch 去customer/review;一对一唯一约束;ll_fleet_trip新增临时资源快照/模式,允许新建草稿时计划、资源、卸货点未知,检查直接DTO/Mapper均不伪造默认值。
  3. ll_dispatch_item保留customer,新增或明确稳定来源键/摘要,补全详情真实来源字段;索引按dispatch+customer+目的地及来源核对。物理托数与来源明细数分开统计。
  4. ll_dispatch_pda_task去customer且dispatch唯一;pick/pick_order继续真实客户与来源;新增公共command(idempotencyKey全局唯一且operator不可改)及主单历史表,废除重复PDA整车命令记录协议。派送域持久待汇总信号表按6.5由派送任务设计、总控集成。
  5. ll_dispatch_draft去客户头部,锁明细保留客户,补实物唯一活动锁;转换请求原子创建对应整车及Fleet。
  6. ll_dispatch_appointment_allocation保留客户与逐来源箱数,不改成整车客户;全量替换时原子校验全车,必要索引加dispatch+customer+item。
  7. 派送方同步将出库活动唯一键确定为 (dispatch_order_id,customer_id),派送按客户+目的地;取消历史保持可追溯,不能以删除历史满足唯一键。费用和单据结构具体由另一文档给总控。
  8. 全部中文表/字段备注、主外关联和索引同时进模块最新基线及统一结构脚本。无迁移、双写、别名、回填或测试存量修复;本清单不是执行数据库授权。

9 实施后的最小验证包(本轮未执行)

已有测试精确触点(相对 lastleg-service/src/test/java/com/baiyue/lastleg/):

测试 必须覆盖
dispatch/service/DispatchServiceTest.java、DispatchRepairWorkflowTest.java 同仓多客户多目的地一张真主单、空单再加货、来源不可伪造、跨仓拒绝、取消只释放本单、上一批换标全托/退货/合托修复
dispatch/controller/SmartDispatchControllerTest.java、DispatchQueryControllerTest.java、dispatch/DispatchSchemaContractTest.java 新API/五阶段/权限和去客户头部结构;草稿每容器一整车、不同客户同组
dispatch/controller/DispatchQueryControllerTest.java及新增真实Mapper分页/统计用例 status/stage正交,取消保留最后阶段;审核分区+状态SQL过滤、NOT_SUBMITTED不算PENDING、多客户一车只计一次、分页total和全基集统计/零值/一致快照
dispatch/DispatchCandidateMapperContractTest.java、DispatchPalletLockPersistenceContractTest.java、DispatchPhysicalWeightTest.java 实物别名排他、真实来源客户、重量未知不填零或材积重
dispatch/pda/service/DispatchPdaWorkflowServiceTest.java、DispatchPickOrderAttributionServiceTest.java、ReturnedDispatchAllowancePersistenceTest.java 未审核禁扫、后台不能补录、逐项核平、只扣一次库存、最后扫码不自动装车完成、跨客户来源、错仓/超量/同键异内容
fleet/trip/FleetTripPersistenceTest.java、FleetTripAuthorizationTest.java、FleetTripApiContractTest.java、FleetDispatchWorkflowQueryTest.java、FleetTripMatchedStopsTest.java 自动唯一关系、审核重提/关键修改失效、主档占用与资格、保留站点顺序/外接容量、临时ID为空及审核依据
dispatch/service/DispatchAppointmentCancellationPersistenceTest.java、inboundplan/work/AppointmentWorkDispatchBindingTest.java 跨客户batch不互删,旧取消后重排不占用失效批次,改批次版本拒绝,已接收门禁;后者写权需总控分配
fleet/trip/FleetTripCostPersistenceTest.java、FleetTripCostContractTest.java、FleetTripCalendarServiceTest.java、FleetTripCalendarMapperTest.java 附加能力保留;空计划不伪时间;成本实发事实及资源结束仍独立
拟新增 dispatch/service/DispatchExecutionConcurrencyTest.java、DispatchReviewServiceTest.java、DispatchCommandPersistenceTest.java 双端同版本竞争只有一次装车/发车;同键原快照恢复;先派送后发车原子性;任一客户失败全车回滚;审核前后及取消/发车竞争
拟新增 dispatch/service/DispatchDeliveryCompletionWorkerTest.java 信号提交后崩溃恢复、阶段CAS后ack前故障两者均回滚、重复信号/多实例、部分签收、重试/重扫、最后全签只CAS一次;签收持子锁期间不得回调申请父锁

派送方测试由其独占:DeliveryTransferServiceTest、费用覆盖/来源持久化、正常/部分/异常签收和汇总回调。共同集成应证明:未安排不可发车;部分/拒签/退回不自动DELIVERED;全部正常签收只推进一次且不释放资源;多客户同仓费用、来源和客户只读不串。

实现全部集成后,由本任务唯一串行执行后端 Maven mvn -pl lastleg-service -am test-compile 和对应行为测试(最终 mvn -pl lastleg-service -am test);先核对本批测试/基线稳定。总控集成后运行 Gateway 路由合同,新增Mapper运行 mapper-scan,跨包端口变化运行 lastleg-dependencies 检查。输入未变不重复跑。代码检查、真实HTTP、人工截图/PDA真机分别记录;本稿仅做源码只读分析,全部这些验证尚未执行。

10 需要总控冻结或裁定的具体问题

10.1 四项业务选择已确认,不再待问

正式契约第八节用户确认“四项都按建议执行”,替代前两轮选项表。本任务执行以下唯一基线,不再请用户重复决定。

场景 当前确定行为 本任务影响
临时外配取证 建单人保存本趟真实容量/证照资料及预计任务结束,调度核实;空单可先保存,审核前补齐 MASTER/EXTERNAL明确分离,不造主档;证据提供者变更交总控逐文件分配
实际装载核对 确认装车弹框接入既有实载,有依据数据带出、缺项按真实事实确认 LoadingComplete携manifestHash和measurements;必须先核平PDA,不新增第五页签、不代填拣货
零扫码变更 保留原业务号,显式退回装车前,修改后两审重提 ReturnToReview命令及历史;已有任何真实扫码/库存SHIP则不开放本批退装/回库
客户旧创建入口 整车安排仅平台执行,移除旧客户创建此类派送入口,客户保持自己单据读取 派送任务和前端任务按各自写权处理;不实现客户意图第二协议

新增预约明确属于日历,不在本任务设计、矩阵或实现范围;只保留整车改造影响的既有预约读取/关联,提供者未解锁前不修改。

10.2 总控工程冻结清单(无需用户重复裁定技术细节)

  • 本稿已依据共同契约统一公开recordVersion、平台全局唯一请求键绑定主体、严格commandHash与arrangementBusinessHash及6.2授权恢复表;派送稿若尚保留expectedVersion或“同键跨主体也返回原结果”,由总控同步其唯一写者收敛,不让前端拼第三套协议。
  • status/stage按第3节正交唯一语义、审核查询统计按4.5、命令GET及结果按4.3确定一套共同OpenAPI,前端移除旧状态和trip版本协议;这是数据/接口选择,不要求用户再选技术字段名。
  • 锁协议采用6.3源码事实及6.4逐操作新顺序;其中库存position→balance、发车delivery→来源、签收delivery→来源已核对。预约/费用/其他主档入口反向加锁尚未逐一验证,须分配后验,不能标注“已无死锁”。
  • 签收信号记录归派送,主流程worker/消费/重试/重扫及直接测试归本任务(6.5);接口包名选一套消费域端口,禁止双向具体实现依赖或为了编译加循环豁免。
  • AppointmentWorkDispatchBinding/Mapper/Port及WorkSharedTripForm等预约触点、Client、File、脚本和共享SQL/OpenAPI/权限仍需总控精确分配唯一写者;保留预约保存/事项关闭/原单跳转及上一批真实来源修复。
  • 旧多单Fleet prepare/bind/unbind配送协议收敛,PICKUP和外接/容量/成本/交接/资源结束保留原能力;原独立取消“必须unbound”改成父级协调,不新增已扫码撤销路径。本稿已只读比对派送稿,不修改另一文档、不跨对话发消息。

下游按第0节已落地骨架接入,不再以准备稿猜字段;实现范围以正式契约第九节为准,额外文件仍需总控精确分配。公共类型后续变更先更新交接点和哈希,再同步已发现直接消费者。

10.3 真实主单切片与精确集成依赖(2026-10-19)

2026-10-19补充作业公开DTO:新增dispatch/port/DispatchWorkModels.java,SHA256=1e91da656e38aebbc554b4b4275b080d387e6bdb2eefaa14d5ae1ebd5b1aec64。原13文件未改;新文件仅JDK依赖,确切record以实际源码为准:Items(dispatchNo,recordVersion,status,stage,List<CargoItem>),CargoItem含真实dispatchItemId、真实客户、sourceKey/sourceVersion/sourceType、planPalletId/returnReceiptId/relabelNo、warehousePalletId/palletNo、orderNo/inboundCode、destination、scheduledPackages/pickedPackages/remove Gate;不能从orderDetails.sourceKey或warehousePalletId替代删除身份。Route(dispatchNo,recordVersion,List<Stop(index,name,address,destinationKey)>,edit Gate)使用真实Fleet持久顺序,0为起运仓,其余index可直接对应LoadingMeasurement.deliveryIndex;RouteCommand(recordVersion,idempotencyKey,List<String> destinationKeys,reason)只重排完整既有目的地,服务端保留真实名称/地址、起点及外接卸货点,采用既有Action.EDIT严格幂等。

新增已接线平台端点(D=/api/platform/v1/last-leg/dispatches):GET D/{dispatchNo}/items返回Items,GET D/{dispatchNo}/route返回Route,PUT D/{dispatchNo}/route接收RouteCommand返回CommandResult;读权限dispatch:list、改路线dispatch:edit。四详情对象不加第五页签。GET D/{dispatchNo}/loading及POST D/{dispatchNo}/loading-complete使用已冻结LoadingPreview/LoadingComplete/CommandResult;后者权限dispatch:loading-complete,显式确认前保持LOADING。

附件正式入口已接线:POST D/{dispatchNo}/evidence/{kind},kind=VEHICLE|DRIVER,multipart字段file,权限dispatch:edit,返回{fileId,fileName,contentType,sizeBytes};上传不自动替换主单引用,后续使用当前recordVersion的Update保存evidenceFileIds/evidence.fileIds。GET D/{dispatchNo}/evidence/{kind}/{fileId}返回同一元数据,GET .../{fileId}/content返回鉴权代理字节、inline Content-Disposition及no-store,读权限dispatch:list;不能使用匿名URL。内部File按平台0+新类别+真实dispatchId+fileId核验。File类别由总控新分配的派送后端补齐,本任务不修改File源码;本域新入口仍待离线/真实验收分别核对。

主单新增loading_confirmation_json精确字段,已纳入DispatchAggregate和saveState Mapper;内容是已冻结ConfirmedLoad完整快照。DispatchExecutionPort现由DispatchAggregateService实际实现,preview/lock/replay/fresh/recordArrangement使用同一主单、逐项真实扫码和不可变归属;新的DispatchExecutionReadMapper/Repository批量读取,不用计划数量凑核平。装车完成沿原Fleet容量策略并核对外接预订,保存实载但不调用库存SHIP。以上新输入测试正在运行,不用前一轮通过结果替代。

已经实施DispatchAggregate及其Mapper/Repository、DispatchAggregateService/Controller、DispatchReadMapper、DispatchSourceMapper/Repository,并沿允许的Fleet原生应用入口建立真实一对一资源记录。主单没有customer字段;同仓多客户只在真实明细保留归属。空单、选货建单、加货、分页和四对象详情、命令持久恢复已接线。候选返回共同CandidateList,按真实起运仓在SQL取有界多客户集合,来源摘要由当前库存版本、仓、收货组成、目的地及换标轮次生成。两级审核、驳回重提、资源编辑与零真实扫码退回已进入本域实现,新增验证仍进行中。

共享SQL精确输入:主单字段以DispatchAggregate实际属性为准,移除customer_id,trip_id唯一;Fleet计划、资源允许空值,ll_fleet_trip_dispatch移除customer_id/review_json并唯一trip_id;明细新增customer_name/source_key/source_version/source_snapshot_json;来源锁新增nullable且唯一active_warehouse_pallet_id,与customer_id/active_key唯一键共同防别名重复占托;不可变ll_dispatch_history字段id/dispatch_id/record_version/action/operator_id/reason/snapshot_json/occurred_at;命令表见0.2。路线须容纳多目的地真实地址,不能沿用短VARCHAR截断。未写共享DDL、未执行SQL。

本域新增临时资源证据适配dispatch/gateway/DispatchEvidenceGateway及HttpDispatchEvidenceGateway,只读写File现有通用内部业务文件路径。File提供者精确触点是file/src/main/java/com/baiyue/file/service/BusinessFileCategory.java:需要两个平台固定0、businessId为真实dispatchId、PDF/JPG/PNG、10MiB、POSITIVE_LONG类别OVERSEAS_DISPATCH_VEHICLE_EVIDENCE与OVERSEAS_DISPATCH_DRIVER_EVIDENCE,以及直接类别/归属测试和必要OpenAPI。该提供者未分配给本任务,未修改;由总控分配唯一写者。Lastleg按平台0+类别+本车ID+文件ID复验,不复用司机主档证照类别,不为临时输入自动建主档。公开上传/读取入口尚待接线;File类别和真实调用未验收,不把测试夹具的文件提供者模拟视为真实文件验证。

预约精确触点仍是AppointmentWorkDispatchPort.bind/Binding/Mapper:现接口按单客户调用且会删除整车所有分配,查询仍以头客户联接;不能跨客户循环调用或静默丢掉已有批次。本域暂明确拒绝有既有批次的建单/加货和车辆预约编辑;需要总控分配提供者最小适配后解除,不表示业务取消预约能力。候选appointmentBatches尚未填充,后续读取须同一权威提供者接通。

离线证据:首轮生产与全部测试源码编译通过,主单/公共合同/账本/查询及派送交付的19个直接类共144项中143项通过;唯一Merchant OpenAPI残留由总控处理,之后复测推进到第133行旧POST残留。主单3项实际MyBatis/H2事务测试通过,证明空单一车一记录、同仓多客户多目的地一头、过滤不缩减整车、原结果恢复及后项冲突全事务回滚;选货 authority 提供者当时为受控输入,不证明完整来源链。补跑旧回归66项有3项旧夹具缺新依赖,修夹具后Fleet57项全部通过,DispatchRepair还需补真实dispatch_no关联继续验证。Gateway路由合同5/5;Mapper扫描389个/10模块。新增审核和候选改动待下一次串行Maven,不把既往结果覆盖新输入。依赖分析旧fleet/pickup三项基线未改。

2026-10-19后续实现:DispatchAggregateService实际实现Execution端口,DispatchCompletionService实际实现Completion端口;DispatchDeliveryCompletionWorker有界读取到期信号、逐车新事务汇总、失败回滚后独立退避、按真实已签收版本重扫缺失信号。主阶段CAS、唯一完成历史、指定信号ack同事务;合法部分签收只ack本次通知,后续版本独立产生新信号;全部正常签收才DELIVERED,不释放资源、不二次发布来源/费用。

PDA实际Controller已切到共同DTO:GET根与单号返回DispatchViews.PdaTask;列表query使用stage;POST items/{itemId}/pick接收DispatchCommands.Pick;新增GET loading、POST loading-complete、GET commands/{idempotencyKey};POST depart接收DispatchCommands.Depart并返回CommandResult;旧/complete不再注册。身份由DispatchActorFactory.pda→PdaWarehouseIdentity取操作者及唯一绑定仓,请求不接受客户/仓。DispatchPdaQueryService+DispatchWholePdaReadMapper批量装配多客户明细/仓位/原生退货箱/真实归属,列表不逐车查详情。整体taskNo明确以排车业务号识别;底层任务现按dispatch唯一且不含customerId;taskNo直接使用dispatchNo,真实客户继续留在pick/pick_order及库存命令。

新增DispatchPickingService与DispatchDepartureService。扫码沿原InventoryCommandService、DispatchPickOrderAttributionService和原生退货生产者执行;实际来源订单先于库存锁,最后一箱只核平、阶段仍LOADING。版本化发车先根锁,再收齐所有出库/派送锁,再锁全部实际来源订单;调用原来源计数及转仓/换标/FleetPool生产者,不再扣库存;原命令恢复先于阶段资格。旧DELETE移货/取消已退出Controller,使用POST items/{itemId}/remove和POST cancel+ReasonCommand,释放精确原占用,旧键重放不能释放新车占用。实际扫码、发车及原提供者整链正在验证,不能将编译视为这些生产者已证明。

草稿唯一写权已移交原派送后端:Draft/Container实体及Mapper、SmartDispatchController、Draft专用Service/Repository/占用Mapper及直接测试;本任务停止写入。按派送设计第12节已补最小桥接:DispatchService.lockDraftSources(String,List<CargoSelection>): List<WholeSource>,MANDATORY,允许草稿逐托暂存换标但正式create仍要求完整申请;DispatchAggregateService.lockDraftCreationResources(Actor,List<Create>): void,MANDATORY,锁齐全批车辆升序→司机升序→车辆占用升序→司机占用升序,再取全批来源。转换里的新trip是当前事务创建且外部不可见的身份,正式create只重入已持资源身份锁,不在第二组取新资源。已移除旧DispatchService草稿读写/占用/分组转换公开方法及对应旧测试,不恢复Draft.customerId;旧单客户建单/Fleet适配尚待整体退出。

新增离线证据:最新完整Lastleg Maven在固定输入下生产及442个测试源码编译通过,53项中52项通过,包含主单/审核/真实明细与路线/显式实载/失败归属8项SQL测试、Schema中文注释3项;唯一Merchant第133行由原写者修正路径块提取,保留其他业务合法POST,后续新候选待复验。JDT曾把错误字节码写入target/classes,javap确认含Unresolved compilation problems;只对Lastleg target运行一次mvn clean后恢复编译,没有修改转仓/本土卡车源码或停止IDE。

随后在草稿在途输入之外,使用明确19个生产文件的javac独立输出及JUnit 1.10.3实际启动器运行本域23项直接测试全部通过:9项根/版本/审核/路线/实载/移货取消SQL、2项完成CAS与ack共同回滚及重试/部分签收、1项worker退避顺序、2项真实来源订单/库存/实收SQL与锁序、2项持久账本、5项证据归属/模拟HTTP下载MIME长度签名及业务错误、2项公共合同(16类请求缺/null/负/零版本、全部Action重启恢复)。这是固定本域快照,不是新增PDA/草稿完整Maven,也不把受控库存/归属/文件提供者当真实环境证明。后续新增PDA查询与扫码测试正在进行。

共享验证:IAM四类24项全部通过,输入未变不重复;File新证据测试在145/146/171/175行未处理IOException导致测试源码编译失败,已给原写者精确定位,本任务不改File。Gateway路由5/5、Mapper390个/10模块通过;依赖分析仍为原fleet/pickup三项失败,未加豁免,后续新Mapper输入需重新核对。

主要未闭环:既有预约/车辆预约权威写提供者仍按总控保护,禁止循环旧bind或静默丢批次;FleetTripDispatch旧customerId/reviewJson及旧单客户适配/直接消费者尚未完全收敛;草稿新候选及全域测试编译、实际PDA扫码/发车与全部正常签收联合验证尚未完成。所有结果均不表示已部署、真实HTTP、人工页面或PDA真机验收通过。

2026-10-19 16:58本域继续收口:DispatchPdaTask已移除customerId,TaskMapper只按dispatch读取,PickingService按根锁创建唯一任务并以dispatchNo作taskNo。新增双客户实扫用例验证一张任务、两条真实客户扫码、各自库存命令及最后扫码仍LOADING;H2用例有dispatch唯一约束。FleetTripDispatch已移除customerId/reviewJson,DispatchBinding/PreviousDispatch去客户头,新增FleetDispatchGateway.workflows批量从本域真实主单读取两级审核;Fleet详情/历史投影、拣货门禁不再读取绑定级审核。已撤下PlatformFleetTripController旧调度审核、理货审核及独立实载确认写入口;原公开Fleet资源/计划入口对已关联主单拒绝写,必须走共同主单版本与审核。公共原13文件和WorkModels未修改。余下旧手工绑定/prepare、外接来货入车及旧DispatchOrder适配正继续调整,不视为本批完成。

稳定草稿输入上的Maven:1056个生产源码及450个测试源码test-compile通过;31个直接类共178项,176通过,两项草稿失败为SmartDispatchControllerTest:65把非路由方法计入权限遍历,以及DispatchDraftPersistenceTest:151转换重启恢复的抽象Payload反序列化失败;失败报告交原写者,不修改其文件。主单双端发车竞争及源生产者失败全事务回滚2项均通过,明确源发布生产者在该用例受控模拟。File七类串行命令再次停于DispatchEvidenceFileTest:145/146/171/175四个IOException测试编译错误,未执行测试,输入未变不重复。

上述模型收口后PDA Maven34项出现13项非法DispatchPdaTask.class字节码VerifyError(本机JDT写target产生),不能视为业务失败或通过。随后在固定本域显式源码列表的独立javac输出中重编译受影响实体/Mapper/Service,运行JUnit真实启动器36/36通过:根SQL/审核/资源/路线/实载/取消移货9,完成事务/worker3,真实来源SQL2,共同账本2,模拟HTTP证据5,公共JSON/依赖2,PDA真实读1,PDA扫码3,批量资源锁2,PDA路由/DTO6,实际发车事务2。此轮Fleet历史审核已通过真实DispatchFleetAdapter.workflows→AggregateRepository→MyBatis/H2读取,不再模拟绑定级旧审核。仍需稳定后的全域Maven、最新正式SQL合同与附加能力集成验证。

保护范围精确触点:inboundplan/work/FbaAppointmentExecutionAdapter.java:78仍用TerminalEvidence.customerId决定整车归属且状态仍使用旧DEPARTED/PENDING_DEPARTURE/PDA_PENDING;FbaAppointmentTransportAdapterTest.java:48旧DispatchBinding构造器含客户参数,需原提供者在唯一写权下同步。TerminalEvidence本身尚未改,不能设置0、首客户或空值当整车客户完成收敛。实际客户仍必须从逐条TerminalDelivery/扫码/归属取值。

2026-10-19 17:08 File冻结修复验证:DispatchEvidenceFileTest.java当前SHA-256=bba87946457fb1290960218127087552de95b59f443eb6d5dacf354084d04a19,与总控候选匹配。唯一串行命令mvn -pl file -am '-Dtest=DispatchEvidenceFileTest,BusinessFileStorageTest,InternalBusinessFileServiceTest,InternalBusinessFileControllerTest,DriverLicenseFileTest,TripOperationEvidenceFileTest,VehicleCarePhotoTest' '-Dsurefire.failIfNoSpecifiedTests=false' test实际49/49通过,其中证据新类别12/12。输出.logs/dispatch-file-fixed-tests.log,JUnit报告file/target/surefire-reports/,输入未变不重跑。草稿当前稳定摘要b0db1bb5191fa3578dacfa0f07c7c2a6a30346f0198047e34fc1fe3a99b0e554及17项待下一Lastleg编译窗口,不将写好当通过。

36项固定输出证据位于lastleg-service/target/dispatch-owned-compile/model-tests.log(SHA-256 325ccd6c326aee5465639e5b017c46ba5f641b8f9268e81b6b7e9238e3046084)、model-36-evidence.json(当时仍未变化的10份PDA/测试源码指纹及范围说明)、verified-36-classes.jar(SHA-256 43d626ac44389dbbe9ee522a832b980e0a6b85dbedb9b6cb7f2c20d68dcd7ff9)。后续Fleet适配/附加能力接线不由该结果覆盖。

Fleet现继续按真主单收敛:FleetDispatchGateway.Snapshot(dispatchId,dispatchNo,warehouseId,packageCount)与TerminalEvidence(dispatchNo,warehouseId,shippedPackages,deliveries)、FleetTransitEvidence(dispatchNo,warehouseId,shippedPackages,deliveries)均去客户头;逐行TerminalDelivery/Delivery.cargo保留真实客户。SourceCargo结构未变,读取改为sourceCargo(dispatchNo,customerId),客户参数仅由服务端真实货物/预订归属传入;customers(dispatchNo)返回本车真实明细客户。DispatchFleetAdapter不再读旧DispatchOrder或旧客户PDA任务,交接/结束读取所有客户出库及所有派送并逐客户核平,先全部outbound后全部delivery锁。正常全签后退回仍保留原已履约,不从主单客户裁切全车。在途更换只更新对应未终结客户派送。

旧Fleet主流程写协议已撤下:POST fleet/trips、POST /prepare、GET /{tripNo}/dispatch-candidates、POST /{tripNo}/dispatches/bind、POST /{tripNo}/dispatches/unbind、POST /{tripNo}/dispatches/{dispatchNo}/review、POST /{tripNo}/loading-review、POST /{tripNo}/loading。旧PreparationService及旧PDA list/task/pick/complete/depart应用方法退出,PDA SourceWorkflow仅保留真实来源发车计数/状态生产者。外接预订bind/arrange保留,但只核对/加入该Fleet已唯一关联主单,不创建或挂入第二张客户排车;普通取货createPickup保留。PlatformFleetCapacityOfferControllerarrange-dispatch已把旧smart-dispatch改为当前dispatch:create,并继续要求原capacity-offer manage、booking-edit、trip-edit。共同OpenAPI/保护中的预约提供者/外部前端入口由总控协调,当前尚未全域编译/回归完成。

受保护生产直接消费者新增精确交集:FbaAppointmentTransportAdapter.java:95/102调用已退役FleetTripService.unbindDispatch/bindDispatch;FbaAppointmentExecutionAdapter.java:87调用已移除TerminalEvidence.customerId。不得恢复旧头字段、循环旧bind或加成功空实现。旧手工H2与Fleet/预约直接测试必须按新模型继续调整,当前36项不能替代这些回归。外配任务结束现在不对不存在的主档ID释放占用或修改主档状态,仍保存真实结束时间/地点与状态;待直接测试。

2026-10-19 既有预约接线取得用户授权后的公共合同:DispatchContracts.BatchAllocation(planPalletId,batchKey,customerId,sourceKey,destinationKey,packageCount,Long recordVersion)及BatchReference(batchKey,batchNo,customerId,sources,Long recordVersion)末尾增加权威记录版本,compact constructor拒绝缺失/null/负数、0合法,不提供旧签名重载。FBA引用取原预约版本;自动DISPATCH引用取实际对应整车recordVersion,不伪造FBA版本,也不接受客户端以自动引用替代FBA分配。DispatchContracts.java SHA-256=73b5b83fcd0155bf09b18c18b1bd6a5bbb0175221102a13101248f92ca84f641。其余原公开DTO当前未改,原13文件“全部不变”指纹表从本次字段变更起只作历史基线,不再代表当前。

新增JDK内部端口dispatch/port/DispatchAppointmentAmendmentPort.java,SHA-256=45aca62fa8a52f3b97a3e666c87b85d2ae0ca5372ca46e1c29ef76f27887b238。精确方法为void lockExistingAppointmentDispatches(Actor,List<String> dispatchNos)和CommandResult applyExistingAppointmentChange(Actor,String dispatchNo,ReasonCommand identity,List<PhysicalItem> touchedItems,List<BatchAllocation> expectedAllocations,List<BatchAllocation> nextAllocations,boolean removePhysicalItems);PhysicalItem为(Long itemId,String customerId,Long planPalletId,String sourceStage,Integer packages)。两方法MANDATORY内部协作、不注册HTTP;先全范围trip→全部资源→全部dispatch,再全部必要子单→批次→来源→协调guard。ReasonCommand为本车公共版本+原父预约命令键,内部按父键/真实操作者/本车派生稳定子键,共用公共账本并优先恢复原结果。expected/next只接原权威scope,必须逐项核DB真实明细/原分配及完整实物,BEFORE_LOADING/零真实扫码/无下游执行;移客户货物不解绑或取消整车。端口源码先交付,实际实现和主流程接入正继续,不能据接口存在推定已完成。

获得最小写权的ExceptionOrderService.java:548已将任务查找改为findByDispatch(dispatchId),后续本客户pick/item/归属过滤保持;直接ExceptionOrderDispatchReturnSourceTest仅移除两个任务头客户setter并收敛两个mock调用,保留该文件此前未提交业务修改。异常原箱Mapper、客户进度、库存发运证明及派送Mapper中旧头客户SQL仍需总控独占分派,不顺手扩写。

本次公共版本字段变更前的独立1052份源码构建(明确排除原预约ExecutionAdapter/TransportAdapter及当时未获写权的ExceptionOrderService,不是全服务Maven)运行62项,57通过,仅Fleet权限类5项缺新增Gateway测试Bean;补真实方法安全上下文依赖后该5项单独5/5通过。草稿新17项、主单/PDA/发车、SQL实体合同3项及Fleet审核只读1项在57通过之内;输出target/dispatch-owned-compile/fleet-draft-tests.log、fleet-auth-tests.log、输入draft-input-evidence.json(实际13/13及聚合b0db...554匹配)。后续公共预约版本字段、Provider及Fleet在途适配修正未由该结果覆盖,最终Maven仍须串行重跑。

2026-10-19 17:39 IAM权限新输入串行验证:mvn -pl iam -am '-Dtest=Phase5DeliveryPermissionSqlContractTest,Phase5DeliveryPermissionSqlExecutionTest,AuthorizationSnapshotServiceTest,IamOpenApiContractTest,OverseasPhase4PermissionSqlContractTest' '-Dsurefire.failIfNoSpecifiedTests=false' test实际29/29(1+5+7+5+11)通过。源码与target/classes/db/lastleg-permissions.sql SHA-256均为9deef80d2bcf383918b5ad48a63bea893ced2d9a32528e33268c02e73bbd4f67。输出.logs/dispatch-iam-retired-smart-tests.log;报告iam/target/surefire-reports/。File49当前输入未变,本轮未重跑。

预约接线正在实施:已新增DispatchAppointmentAllocations按Provider实际record字段顺序逐字段映射(Provider版本在第3字段,公共DTO在末尾),一次合并当前保留分配+新请求版本预锁,缺失/负数/同批混版本拒绝;create/add与草稿全groups预锁位于来源锁前。仅将已保存DispatchItem的真实客户传给bindItems,touched增量保留他人。源码已增加Fleet全范围父级/全部资源预锁及主单排序锁、内部预约改量端口实现:原主版本/零真实扫码/BEFORE_LOADING、完整实物及原分配集合复验、稳定父键子命令恢复,移出不解绑/取消整车,主单/来源/审核/历史随父事务回滚。新测试与整体验证尚在补齐;候选真实批读仍待Provider实际薄签名后接入,不能将这些源码视为已编译通过或链路完成。旧DispatchService.preArrange及两个私有单客户创建实现已移除,不恢复Provider旧bind/requireReplay;BOL仍待从旧platformDetail读取切到整车投影,旧测试和残留DTO同步继续。

2026-10-19 18:13 当前源码收口:主候选已使用实际Provider candidateBatches有界批读,校验返回覆盖每个真实客户/计划实物;owned draft由外层校验,未将本操作者自己的占用误判为他人占用。BOL已从旧platformDetail改为DispatchBolReadService一致快照,整车SQL逐明细客户取原订单组成,按客户+实际目的地分组;退回实收/换标不套用原完整计划箱数。旧DispatchService单客户页/详情/创建/修改/取消及旧PDA公开扫码流程已移除,来源与原换标占用规则保留,未恢复旧Provider接口。

最近一次实际完整Maven展开1062生产源码及453测试源码:生产编译通过;测试阻断已收敛为移交原派送写者的DispatchRepairWorkflowTest旧调用。原预约两FBA测试由总控修复父命令键;Fleet直接消费者、取货负断言已按现行createPickup收口。新增主单原预约移货/父事务回滚/客户数量原分配校验/真实扫码守卫和BOL两客户/部分退回SQL测试尚待统一运行。旧增量输出曾因IDE更新显示Nothing to compile,不能据此认定完整测试编译或回归通过;最终仍用稳定源码重新构建。主单cancel已改为仅活动非CANCELLED出库阻断,保留历史单据和原版本、阶段、零真实扫码边界,实际回归待运行。

2026-10-19 18:24 本轮实际闭环证据:标准Maven完整test-compile实际重新编译1055个生产源码、454个测试源码并BUILD SUCCESS(此前移除6个本域旧DTO/实体/Mapper及原派送无生产调用Query;当前Mapper扫描为10模块/392 Mapper通过)。记录.logs/dispatch-full-current-test-compile.log SHA256=3852983b13e1182be8d22baeea4adaf774e4ddf57db11fe0354c0a900913d85f;旧客户头类class已核不存在,不用排除消费者的局部编译代替全服务编译。

行为回归先运行71类438项,实际423通过、8断言失败和7错误;随后按新输入只复跑修正项与新增直接消费者,9类69/69、8类90/90、3类16/16均BUILD SUCCESS。按每类最新结果去重共80类501项,0失败/0错误;这是分批组合覆盖,不宣称一次501项全绿Maven。机器可核摘要为.logs/dispatch-final-evidence.json,包含每份日志SHA256和每类最新来源日志。实际覆盖原预约分配/整票移货/父事务回滚、主单PDA双客户真实库存命令、装发/签收消费、来源发布并发、取消重排、归箱与异常来源、候选批读、BOL真实SQL、网关与资源附加能力。测试替身隔离的提供者和手工准备的已发运夹具不等同真实环境整链验收。File49及IAM29输入未变,仍引用先前实际通过结果,未重复计入501。

FleetAppointmentImpactMapper已使用唯一binding+真实item客户EXISTS(SHA256=36ae1dccaff96dcc49a8bc1a43ebaba18950b641440cf6bc5682ffa0e6911381);VehicleFulfillmentMapper也以真实item客户证明替代binding头客户(SHA256=79ec8f7a818c412931e81f7e2b6a5299cb4c8bf38939ca5f645a5a5d43d70761),车辆履约7项与权限/只读合同2项在90项中通过。其夹具为一张整车、两客户、一条车次绑定,并保留伪客户、原发车、换车及交接断言。

依赖门禁本轮先发现新增既有预约→整车公共端口7条精确类型引用未登记,已按获批接线加入reviewedClassDependencies(不加整个slice许可);随后总控接管该基线和反向越界检查,最终Node结果以总控交付后复跑为准。原fleet.pickup的Mapper/Repository/Service到WarehouseTransferOrder三条旧直接实体引用及fleet outgoing限制仍是开工前既存阻断,不在排车作业里放行。未执行部署、SQL、环境写入、真实HTTP/PDA或人工截图验收。

2026-10-19 18:26 总控冻结依赖输入后实际Node复跑13项:10通过、3失败;新增original appointment adapters use only the reviewed dispatch port types合同通过,包括未知消费者及主流程实现类型的反向拒绝。剩余失败仅由上述开工前取货三实体直接依赖及fleet outgoing限制触发,未放宽slice。日志.logs/dispatch-lastleg-dependency-contract.log SHA256=6a767619db627952ce59aaba5d4530413728d18a4c21928b61ca2f05008a6cff;501行为组合覆盖及编译证据摘要已同步依赖结果。最终Mapper合同为10模块/392个Mapper通过,受影响路径git diff --check通过。

2026-10-19 18:27 最终冻结输入的一次联合Maven已实际完成:80类501/501,0失败/0错误/0跳过,BUILD SUCCESS;日志.logs/dispatch-final-joint-regression.log SHA256=b56da123ceb74409530793c3de92467cac686b03d97eb4afc34febc1a4b83238。这次结果独立于前述分批组合,不用组合结果冒充单次通过。完整命令参数及源码/合同快照在.logs/dispatch-final-inputs.json(运行前后1519份文件逐项SHA一致,其中生产Java1055、测试Java454;归一化路径排序聚合3ff451e8b6e6774e53bbc62fba5b2fcd2b7098e129d490434468b3ef91594de9;快照文件SHA256=ae07cb978c639488c8de78b458e9c1995fe6e66bee1fcd4bb8e34a79a644ad12),摘要.logs/dispatch-final-evidence.json已追加finalJointRun,未排除任何测试编译消费者。

Gateway路由合同最终5/5通过(.logs/dispatch-final-gateway-route.log)。AGENTS索引第一次仅“海外PDA指引超过8000字符预算”失败,没有缺失路径;总控已交原前端写者精简该唯一指引,源码及本次501输入不受其文案变化影响,待新指引后单独复验。整体依赖门禁仍为10/13,不标为全通过;现有取货依赖是独立限制。行为已通过输入不再反复测试。

最新用户裁定取消排车车辆预约字段及关联:Create原本无该字段;本次从Update和DispatchInfo彻底删除vehicleAppointment及AppointmentReference/VehicleAppointment类型,Aggregate删除对应拒绝分支和固定null参数,无别名或空占位。车辆预约不再列为待接通依赖。FBA预约及批次保留,日历新增预约不变。此前单次501回归绑定删除字段前的输入;本次签名变更以新的全测试编译及直接消费回归为准,保留旧日志作为历史证据。

2026-10-19 18:38 按“取消车辆预约字段”新输入完成无排除test-compile,BUILD SUCCESS(.logs/dispatch-without-vehicle-appointment-test-compile.log SHA256=50cc293c71a64a8242ca6f5852875542d7a8ff822def7f012fae926dd9103071)。直接受影响5类25/25、0失败/错误/跳过,BUILD SUCCESS;日志.logs/dispatch-without-vehicle-appointment-direct-tests.log SHA256=dbadeab653c67610b8c2b09ac8cd9af8177c0d7decdb0b7ab7bd94f8748475f7,真实Update新增验证计划保存/原键恢复及详情JSON无车辆预约字段,并保留FBA展示。相对旧501快照恰好8份Java/合同/测试输入变化,新源码快照.logs/dispatch-vehicle-appointment-removal-inputs.json,聚合1607ea98c3f9a8fadf66541f95c70f33e4ebae55cb3823b3801d52d5196b0685;完整命令和变更列表见.logs/dispatch-final-evidence.json的新vehicleAppointmentRemovalVerification。不重跑未受影响库存、发车或派送501组合。

公共类型新SHA256:DispatchCommands.java=0553f0af5dbe31bb9d63701872540261398536f1925edfc3d7f309202aeeea56;DispatchViews.java=2c027f2516ee7a1a0a544e52aaa4a8be70b1439a0b94008223335d499ebf2ae9。两被移除嵌套类型的target/classes也已核不存在。新AGENTS长度已合格,但第二次索引仅缺两段固定文字“涉及业务行为变更时必须核对对应细则”“不得与国内 PDA 混用”;继续交原前端指引写者恢复,尚未记索引门禁通过。

Java候选交接边界:总控已核取消车辆预约后无排除test-compile及25项直接回归均BUILD SUCCESS。Java本域停止对未变输入重复检查;Maven写权仅供后续真实Java问题续接。此前501单次回归继续绑定旧字段输入,不冒充新协议501通过。总控继续统一AGENTS索引、前端及现行HTTP验收脚本消费者,未将它们标记为本Java域已完成。总控报告当前OpenAPI按源重生97 schemas、29平台+7PDA路径、649 refs和两SQL静态通过,无车辆预约字段;该重生输出及索引/脚本最终证据由总控持有。依赖门禁仍10/13,旧取货直接实体引用不豁免;无环境发布/真实HTTP/PDA/截图验收。

2026-10-19 C52路线事实修复:FleetTripService.updateDispatchCargo不再仅追加旧路线。起点与本车起运仓一致;按当前真实货物目的地和有效外接预订(CONFIRMED/RECEIVED/BOUND)目的地保留原站点相对顺序,删除没有任何有效支持的旧站,新货目的地追加。有效预订要求的站若原路线已缺失则整笔主单事务失败,不编造站名/地址。原计划容量、实载和理货快照继续随货物修改清空;车次更新增加原版本条件,和明细、来源占用、绑定货量、命令账本同事务回滚。直接提供者仅新增FleetBookingReservations.requiredRouteDestinations并复用到原requireRoute,未扩展其他业务或公共API。

实际检查经scripts/local-check.ps1单槽执行,未提高Maven默认1536MB/2处理器限制:无排除test-compile exit0/BUILD SUCCESS;5类33/33、0失败/错误/跳过/BUILD SUCCESS,其中FleetDispatchRoutePersistenceTest新7项涵盖多站末托移除、有效站原序/新站追加、同目的地多托保留、空车仅起点、三种有效预订保护与取消后去站、缺失必要站冲突、真实绑定写入失败后的主单/路线/占用/货量/账本回滚及同键重放。其余直接回归为DispatchAggregatePersistenceTest11、FleetTripPersistenceTest7、FleetReceiptCapacityTest5、FleetTripMatchedStopsTest3。Fixture复用真实服务/Mapper/H2事务,目录与选货权威仍是受控提供者,预订作为持久化输入;不宣称真实环境HTTP/库存或预订确认整链验收。

C52源码SHA256:FleetTripService.java=28eca09b87337b07a04955039a50c3c55805289955c24db75fe9df6fbf0b3bcc;FleetBookingReservations.java=84906c28ccaf94125a41c03a30c9df392400af74569ed26c2da8abd5c116144c;FleetDispatchRoutePersistenceTest.java=687248640255a40456f4197cab28dd32e8a16f97a833858fc868e171dcf2b62c。3路径聚合9fab733033d4fffaeb716b4cb2720dd72b26c14ece1b97d6bd6d2e30db502aa2(规范化路径Unicode顺序、path+空格+小写SHA、LF连接无尾LF)。命令/资源限制/3份源文件时间与哈希、5份JUnit XML摘要及用例名、Maven日志均在.logs/dispatch-route-evidence.json,SHA256=642624474f2d978247e5d02582dc4f4ced3a89000bf694091de0cb15a5fd892d。Wrapper日志.logs/dispatch-route-test-compile.log/dispatch-route-regression.log,原始子进程输出分别留在dispatch-route-test-compile.maven.log/dispatch-route-regression.maven-tail.log,后者完整保留5类结果及BUILD SUCCESS。33通过后生产和测试输入未再修改;没有重跑501/索引/无变化门禁,没有环境写入或部署。