19 KiB
19 KiB
Firstleg Service 模块指引
职责与边界
- 负责履约任务/分配、入库单、测量/OCR、审核后初始计价、FBA 计费、客户在途柜量统计、国内出仓履约链路,以及四期国内出仓后的船节点查询任务和节点事实。
- 订单主数据归
order/,托盘/库位归warehouse/,文件对象归file/,OCR 供应商归integration-service/;通过客户端 DTO 和内部令牌协作。 - 入库、分柜、装柜、PDA 和出仓能力均在本模块按对应服务端契约协作;
WAIT_SPLIT + APPROVED + CONFIRMED是入库链路状态节点,不是全量验收的范围终点。财务状态仍归finance,本模块只生产不可变费用来源和柜次成本快照。
模块入口
| 目标 | 入口 |
|---|---|
| 内部模块化基线与依赖分析 | docs/firstleg-internal-modularization-baseline.md、scripts/analyze-firstleg-dependencies.mjs、scripts/analyze-firstleg-dependencies.test.mjs、src/test/resources/architecture/firstleg-slice-dependencies.json;只约束本服务内部包依赖,不改变部署单元或数据库边界 |
| 履约任务/分配 | controller/PlatformFulfillmentAssignmentController.java、service/FulfillmentTaskService.java |
| 入库单查询/聚合 | controller/PlatformInboundOrderController.java、service/InboundOrderQueryService.java |
| 平台测量/OCR | controller/PlatformInboundMeasurementController.java、service/InboundMeasurementService.java、service/CartonMeasurementService.java;手工单箱测量也从该 Controller 与 CartonMeasurementService 进入 |
| 箱唛箱码生成、打印与测量绑定 | 审核建单生成钩子 controller/InternalInboundOrderController.java;生成/解析 service/InboundCartonLabelService.java、repository/InboundCartonLabelRepository.java;平台与客户打印 controller/PlatformInboundOrderController.java、MerchantInboundCartonLabelController.java、service/InboundCartonLabelPrintService.java;基线/升级 SQL 为 src/main/resources/db/phase2-inbound-carton-label.sql、phase2-carton-label-versioning-migration.sql |
| 墨家设备测量上传 | controller/OpenApiInboundMeasurementController.java、dto/MojiaMeasurementDtos.java、service/MojiaMeasurementOpenApiService.java、security/MeasurementRequestAuthenticator.java;接口契约见 src/main/resources/openapi/firstleg-v1.yaml 与 ../../docs/墨家设备测量上传接口说明-V3.md |
| 审核与初始计价 | controller/PlatformInboundMeasurementReviewController.java、service/MeasurementReviewService.java、service/InitialInboundPricingService.java、dto/MeasurementApprovalResponse.java |
| FBA 主数据/计费 | controller/PlatformFbaMasterDataController.java、PlatformFbaPricingController.java、service/FbaPricingService.java |
| 入库费用明细 | controller/PlatformInboundFeeDetailController.java、service/InboundFeeDetailService.java、dto/InboundFeeDetailDtos.java;Admin 消费端见 ../../apps/admin-web/src/modules/unified-orders/api/inboundFeeDetails.ts |
| 入库/柜次直接已付快照与凭证校验 | service/FeePaymentSnapshotValidator.java、client/FeePaymentVoucherFileClient.java;写入入口分别为 InboundFeeDetailService.java、ContainerFeeDetailService.java,File 业务类型固定 FINANCE_VOUCHER |
| 客户费用查询 | controller/MerchantInboundBillingController.java、service/MerchantInboundBillingService.java |
| 客户运营统计/在途柜量 | customerstatistics/controller/PlatformCustomerTransitStatisticsController.java、customerstatistics/service/CustomerTransitStatisticsService.java;契约见 src/main/resources/openapi/customer-statistics-v1.yaml |
| 费用项目与价格簿 | controller/PlatformFeeItemController.java、PlatformPriceBookController.java、MerchantPriceBookController.java |
| 价格簿查询内部切片 | pricebookquery/application/port/in/PriceBookQueryUseCase.java、application/PriceBookQueryService.java、application/port/out/PriceBookQueryReader.java、adapter/out/persistence/PriceBookQueryMapper.java;查询行模型位于 application/model/PriceBookQueryRows.java |
| 财务费用来源快照 | controller/InternalFinanceFeeSourceController.java;入库费用与柜次费用分别按来源 ID、版本向 Finance 提供不可变快照 |
| 柜次费用明细 | controller/PlatformContainerFeeDetailController.java、service/ContainerFeeDetailService.java、dto/ContainerFeeDetailDtos.java;Admin 消费端见 ../../apps/admin-web/src/modules/loading-lists/api/containerFees.ts |
| 财务整柜成本分摊快照 | loading/controller/InternalFinanceCostSnapshotController.java、loading/service/FinanceCostSnapshotService.java |
| 分柜与预装 | split/controller/PlatformSplitPreloadController.java、controller/PlatformLoadingListController.java |
| 货柜与智能排柜 | controller/PlatformContainerController.java、PlatformSmartLoadingController.java、service/SmartLoadingService.java、repository/SmartLoadingRepository.java、loading/;按入仓码候选与箱数分配分别从 /inbound-candidates、/plans/{planId}/inbound-assignments 进入,明细规格拆分留在服务端 |
| 货柜资料、PDA 证据与业务总览 | controller/PlatformContainerController.java 的 /{id}/overview、/{id}/documents/{documentType},聚合 mapper/ContainerOverviewMapper.java;契约 src/main/resources/openapi/phase3-container-overview-v1.yaml,四类资料固定为目的港清关、国内出口报关、DO、PU 的当前唯一版本 |
| PDA 拣货与装柜 | controller/PlatformLoadingPdaController.java、loading/controller/PlatformLoadingDeliveryController.java |
| 装柜完成跨区事件 | service/LoadingCompletedOutboxService.java、LoadingCompletedOutboxRelay.java、repository/LoadingCompletedOutboxRepository.java、mapper/LoadingCartonManifestMapper.java、client/EventBridgeClient.java;从 fl_inbound_order.destination_type 冻结 V2 `orders[].destinationType=FBA |
| 海外卸柜完成事件回写 | 内部入口 controller/InternalOverseasContainerEventController.java、消费者 service/OverseasContainerEventConsumer.java、Inbox entity/ContainerEventInboxEntity.java 与 mapper/ContainerEventInboxMapper.java;事件模型见 ../common-event-contract/AGENTS.md |
| 三期海运订舱、关联候选与出仓前船期资料 | oceanbooking/controller/PlatformOceanBookingController.java、oceanbooking/service/OceanBookingService.java、oceanbooking/mapper/OceanBookingMapper.java、portschedule/controller/PlatformPortScheduleController.java;关联候选入口 /api/platform/v1/ocean-bookings/link-candidates,订舱契约见 src/main/resources/openapi/ocean-bookings-v1.yaml,表结构见 src/main/resources/db/phase3-ocean-booking-schema.sql |
| 四期国内出仓后船节点定时查询 | outbound/dispatch/service/OutboundDispatchService.java 在确认出仓事务内幂等创建现有 CarrierScheduleQuery;编排与租约 portschedule/service/CarrierScheduleQueryScheduler.java,任务/十节点事实/异常事实/不可变 attempt CarrierScheduleQueryService.java,平台查询与人工补录入口 PlatformCarrierScheduleQueryController.java 的 /{queryId}/manual-nodes;停止只由实际 EMPTY_RETURNED 节点触发,不提供无节点停止入口;外部船司/轨迹调用经 integration-service/;标准见 ../../docs/四期/船节点标准化契约.md |
| 船节点跨区交接事件 | portschedule/service/CarrierScheduleNodeOutboxService.java;实际节点落库后发布 FIRST_LEG_CONTAINER_SHIPPING_NODE_RECORDED V1,复用 LoadingCompletedOutboxRepository/Relay 投 Event Bridge;海外消费见 ../lastleg-service/src/main/java/com/baiyue/lastleg/shipping/service/ContainerShippingNodeConsumer.java |
| Firstleg 业务单据编号 | service/FirstlegDocumentNumberGenerator.java;当前支持 OB、TA、CB、CAB、LP 前缀,按北京时间每日独立编号;具体业务归属以各调用服务为准 |
| 拖车与整柜出仓调度 | controller/PlatformTruckAppointmentController.java、outbound/dispatch/controller/PlatformOutboundDispatchController.java;按真实 containerBookingId 查询聚合和 gate、确认离仓,契约 src/main/resources/openapi/phase3-outbound-dispatch-v1.yaml |
| 整柜出口报关 | customs/container/controller/PlatformContainerCustomsController.java、customs/container/controller/MerchantContainerCustomsController.java、customs/container/service/ContainerCustomsService.java、customs/container/mapper/ContainerCustomsMapper.java;装柜完成后按柜收集退税资料,多对多覆盖入仓码,开始报关冻结,完成文件逐资料一对一回传,全买单柜由平台确认并通过导出中心取明细;契约 src/main/resources/openapi/customs-v1.yaml,基线 src/main/resources/db/phase3-container-customs-schema.sql;消费者为 Admin ../../apps/admin-web/src/modules/container-loading/components/ContainerCustomsPanel.vue 和 Client ../../apps/client-web/src/modules/first-leg/components/CustomsDocumentsDialog.vue |
| 买单订单明细导出 Provider | customs/container/controller/LoadingListBuyDeclarationExportController.java、service/LoadingListBuyDeclarationExportService.java;只为 File 导出中心提供 LOADING_LIST_BUY_DECLARATION_ITEMS 的已完成装柜实际买单行,不在 Firstleg 生成文件 |
| 跨模型桥接 | loading/bridge/controller/PlatformLoadingModelBridgeController.java |
| 跨服务客户端 | client/OrderCargoClient.java、WarehousePalletLocationClient.java、MeasurementImageClient.java、LoadingEvidenceFileClient.java、EventBridgeClient.java |
| 仓库公共供应商快照 | client/WarehouseSupplierClient.java、service/WarehouseSupplierSnapshotService.java;报关、订舱和拖车分别消费 customs-v1.yaml、ocean-bookings-v1.yaml、truck-appointments-v1.yaml 中的供应商 ID,并由 Warehouse 返回服务端名称快照 |
| SQL/OpenAPI | src/main/resources/db/phase2-*.sql、src/main/resources/db/phase3-*.sql、src/main/resources/openapi/;部署统一 ../deploy/sql/头程数据库统一增量-MySQL8.sql |
| 测试/验证 | src/test/java/com/baiyue/firstleg/;mvn -pl firstleg -am test |
修改约束
- 移动包、拆分端口/适配器或引入新的跨切片依赖时,先核对内部模块化基线并运行
node --test scripts/analyze-firstleg-dependencies.test.mjs;确需更新依赖基线时必须说明稳定边界变化,不能把临时循环依赖登记为长期豁免。 - 平台接口必须校验目标客户、权限、乐观锁、幂等键、历史和审计;商户查询只能使用登录态客户。
- 价格簿查询消费者依赖
PriceBookQueryUseCase或稳定查询端口,不得反向依赖持久化 Mapper;移动旧查询类时同步更新生产调用、测试、Mock 与 import,避免同一职责在根repository/mapper和pricebookquery/双写。 - 第三方回传必须签名、时间窗、防重放、请求哈希,并绑定订单货物和 File 证据;不得信任外部客户字段。
- 稳定箱码映射以入库快照和 Order 货物明细为事实;订单审核创建入库单时必须幂等生成,打印接口只复用或补齐同一映射,用户是否下载箱唛不得决定箱码有效性。手工测量、墨家设备和 PDA 扫码统一通过
InboundCartonLabelService校验客户、入库单、货物明细、总箱数/下标和有效批次,不得在测量时临时伪造箱码或接受跨客户、跨入库箱码。 - 测量审核通过后直接进入
WAIT_SPLIT,费用仅作为独立初算和人工维护结果,不得阻塞或回退订单主流程;计费量、单价和附加费调整必须版本化并由服务端重算金额。状态或 DTO 变更时逐个检查 Order/Warehouse/File/Integration 消费者。 - 报关、订舱和拖车引用的供应商必须通过
WarehouseSupplierSnapshotService按 ID 向 Warehouse 校验:报关使用CUSTOMS,订舱使用BOOKING(不区分国内/国外 scope),拖车使用TRUCKING;供应商必须为ACTIVE。请求中的名称只能作为一致性提示,持久化名称必须来自 Warehouse 快照,禁止用自由文本绕过 ID、类别或启用状态校验。修改任一供应商引用 DTO、OpenAPI 或内部客户端时,必须同步检查三条业务链、Warehousesupplier-v1.yaml、数据库快照字段和对应契约测试。 - 订舱状态固定为
DRAFT → RELEASED:草稿允许资料不完整并可替换或删除,放舱时校验完整持久化聚合,放舱后永久只读。创建草稿和放舱都只维护订舱聚合、三类参与方及柜型需求,不创建真实containerNo、货柜行或分配关系;真实货柜只在后续提柜关联时占用已放舱柜型需求。 - 订舱柜型需求至少一项、最多 20 项,数量必须为正整数,容量和限重必须匹配平台柜型规格。起运仓必须是启用的国内中转仓;物流渠道必须来自该仓当前 ACTIVE 价格版本,服务端保存渠道名称快照,渠道失效时失败关闭。
- 订舱号统一使用
OB-yyMMdd-####,按北京时间创建日每日独立递增且单日最多 9999;不得以实体 ID 拼接生成。放舱不要求真实柜号,也不得为满足放舱校验伪造柜号。 - 订舱列表
containerCount固定为该订舱全部柜型需求quantity之和,与真实柜关联/解除无关;关联候选只返回RELEASED、剩余数量大于 0 且warehouseId + channelCode + containerType精确匹配的需求,由服务端集合查询 ACTIVE 分配量。前端不得扇出详情计算或用已关联柜数覆盖该字段,最终关联命令仍须重验目标货柜可修改性。 - 四期船节点任务从确认柜子离开国内仓库后启用,必须绑定真实订舱和柜次;十个标准节点、预计/实际时间、异常事实、供应商缺失节点和还柜停止规则统一以
../../docs/四期/船节点标准化契约.md为准。CarrierScheduleQuery的正常业务查询槽位固定为Asia/Shanghai每日 09:00、12:30、16:00;每分钟任务只扫描已到期next_allowed_at,不是每分钟请求船司。成功、无变化、需人工或永久失败进入下一固定槽位;仅技术性RETRYABLE_FAILURE在当前槽内按15/30分钟退避,含首次最多3次且不得跨下一槽,实际EMPTY_RETURNED后停止。错过的正常槽位直接顺延,不在服务恢复时补跑;单轮按受控批次持续领取并受最大任务数保护。租约、调度尝试和内部停止入口不得暴露为平台页面 API,外部失败不得推进业务节点,不得在三期另建查询任务。 - 国内出口报关在装柜完成后启用,不再阻塞
LOADED。整柜fl_customs_container.status=COMPLETED才通过离仓报关门禁;完成后以系统身份、确定性幂等键立即尝试确认国内离仓,其他门禁未齐时由受控定时补偿重试,禁止部分入仓成功或重复创建离仓/船司查询事实。 - 国内出仓确认以真实
containerBookingId聚合全部入仓绑定并锁定校验;装柜证据按物理货柜只计算一次,拖车状态和独立船期只作参考。全部门禁在同一事务通过后推进所有技术行、记录一条可见历史并幂等创建CarrierScheduleQuery/DEPARTED_WAREHOUSE,不允许单入仓局部成功。 - 手工船节点的预计时间只追加事实,不推进
latestStandardNode;携带actualAt时标准节点序号必须严格大于最新实际节点,重复、相同或回退节点返回冲突。已审核实际时间修正只能更新同一事实的时间/证据,不得制造第二条同节点实际事实或绕过单调推进。 - 海外卸柜完成事件只接受
OVERSEAS -> CN的OVERSEAS_CONTAINER_UNLOADEDV1,并通过内部服务令牌进入。消费者必须校验信封、Payload 哈希、来源货柜和时间顺序,以 event ID + Payload 哈希幂等,使用事件聚合版本阻止乱序覆盖;预计报空时间固定按卸柜照片上传时间加两天计算,实际报空时间可为空。 - Firstleg 只拥有业务费用及履约事实;Finance 拥有费用入池、账期、账单、收款、成本分摊结果和财务状态。修改费用来源版本、发布条件、柜次归属或体积快照时同步检查
../finance/src/main/java/com/baiyue/finance/client/FirstlegFeeSourceClient.java、同目录FirstlegCostSnapshotClient.java及双方契约测试,不在 Firstleg 复制财务状态机。 DIRECT_PAID仅适用于PAYABLE,必须同时保存paidAt、合法paymentMethod,并至少提供paymentReferenceNo或一个当前客户范围内的FINANCE_VOUCHER;RECEIVABLE与ON_ACCOUNT不得残留付款字段。创建、调整和审核前均通过FeePaymentSnapshotValidator失败关闭复核。
三期装柜与 PDA 资源结算
- 智能装柜候选只能按同一国内中转仓合柜;推荐顺序按入库时间先进先出。装柜计划状态为
EDITING -> LOADING -> COMPLETED,PDA 全部明细处理完成后自动完成。 - PDA 拣货以
fl_inbound_carton_label.box_code对应的稳定箱唛箱码为扫描身份;审核建单、重复打印、手工测量和墨家测量复用同一映射,版本字段只用于并发控制,不进入扫码文本。一个有效且未重复的箱码扫描表示拣出一箱,不得以 SKU、商品件数或页面展示字段替代箱码。任务下发与提交契约见src/main/resources/openapi/phase3-pda-convergence-v1.yaml。 - PDA 部分拣货生成二级子入库单时,数量必须守恒:
已拣数量 + 子单数量 = 拣货前主单可用数量。子单只允许一层,不能继续拆分。 releaseMode=AUTO时,释放资源数为ceil(实际拣货箱数 × 原资源数 / 原可用箱数);MANUAL按locationIds选择,空数组表示明确释放 0 个。当前契约中未传模式且locationIds=null时按 AUTO 处理。- 头程快照必须保存仓库
reservation_id。A0 是共享临时库位,多个托盘可具有相同location_id,跨服务结算、转移、释放必须使用reservationId,不得把locationId当作唯一资源标识。 - 部分拣货资源结算必须调用 Warehouse 的
/internal/warehouse/v1/pallet-locations/settlements原子接口;禁止拆成独立释放和转移调用。全部释放但子单仍有数量时,由仓库服务重新选择同仓普通库位,不足才落 A0。 - 数据库字段变更直接维护当前模块基线与
../deploy/sql/头程数据库统一增量-MySQL8.sql;已有phase3-pda-release-allocation-migration.sql只作为当前结构来源,不为历史开发数据再新增兼容迁移。
全量验收导航
本模块承载一期履约、二期入库测量计费、三期订单编排/柜次/排柜/订舱/拖车/PDA/LOADED 门禁等全量链路;按本次验收范围读取对应阶段文档、Controller、DTO、OpenAPI、当前 SQL 定义和调用方,不以历史阶段终点阻断联调。PDA 按 uni-app 在线更新安排开发与验收,不另设设备型号门禁。