From 868557d79db3207c7ad5471a2422806710f19447 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Tue, 20 Oct 2026 21:48:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/lastleg-dispatch-transport-tasks.sql | 108 ++++++++++++++++++ deploy/sql/排车运输接入-数据库变更汇总.md | 21 ++++ .../trip/FleetTransportVehicleGuard.java | 20 ++++ .../fleet/trip/TransportTripChanged.java | 5 + 4 files changed, 154 insertions(+) create mode 100644 deploy/sql/lastleg-dispatch-transport-tasks.sql create mode 100644 deploy/sql/排车运输接入-数据库变更汇总.md create mode 100644 lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/FleetTransportVehicleGuard.java create mode 100644 lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/TransportTripChanged.java diff --git a/deploy/sql/lastleg-dispatch-transport-tasks.sql b/deploy/sql/lastleg-dispatch-transport-tasks.sql new file mode 100644 index 000000000..a36d454bc --- /dev/null +++ b/deploy/sql/lastleg-dispatch-transport-tasks.sql @@ -0,0 +1,108 @@ +-- MySQL 8 可重复执行增量:仓储转运到仓取货与本土卡车接入现有排车。 +-- 目标数据库固定为 baiyue_lastleg;仅新增运输类型、原单关联及请求账本,不回填或删除业务数据。 +-- 前置:已完成 Lastleg 现有整车排车结构升级;如部署为缺少每日流水表的旧库,也创建正式排车流水表。 +USE baiyue_lastleg; + +SET @dispatch_transport_column_ddl = IF( + EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'll_dispatch_order' AND column_name = 'task_type' + ), + 'SELECT 1', + 'ALTER TABLE ll_dispatch_order ADD COLUMN task_type VARCHAR(32) NOT NULL DEFAULT ''DELIVERY'' COMMENT ''任务类型:仓库派送、到仓取货、本土卡车'' AFTER dispatch_no' +); +PREPARE dispatch_transport_column_stmt FROM @dispatch_transport_column_ddl; +EXECUTE dispatch_transport_column_stmt; +DEALLOCATE PREPARE dispatch_transport_column_stmt; + +-- 旧库兼容:整车主单跨客户,原来单客户排车头字段不再要求填写。 +-- 如目标库已删除旧字段则跳过;已有客户值保留,不置空、不修改。 +SET @dispatch_transport_legacy_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_dispatch_order' AND column_name = 'customer_id' AND is_nullable = 'NO'), + 'ALTER TABLE ll_dispatch_order MODIFY COLUMN customer_id VARCHAR(128) NULL COMMENT ''旧客户排车归属,整车主单不使用''', + 'SELECT 1' +); +PREPARE dispatch_transport_legacy_stmt FROM @dispatch_transport_legacy_ddl; +EXECUTE dispatch_transport_legacy_stmt; +DEALLOCATE PREPARE dispatch_transport_legacy_stmt; + +-- 旧本土卡车结构兼容:新单沿用已有的起终点联系人及pickup_at/delivery_at字段。 +-- 旧单字段保留原值,仅放宽空值约束,避免新单被已弃用字段阻断。 +SET @dispatch_transport_legacy_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_us_truck_order' AND column_name = 'contact_name' AND is_nullable = 'NO'), + 'ALTER TABLE ll_us_truck_order MODIFY COLUMN contact_name VARCHAR(128) NULL COMMENT ''旧本土卡车联系人,新单使用起终点联系人''', + 'SELECT 1' +); +PREPARE dispatch_transport_legacy_stmt FROM @dispatch_transport_legacy_ddl; +EXECUTE dispatch_transport_legacy_stmt; +DEALLOCATE PREPARE dispatch_transport_legacy_stmt; + +SET @dispatch_transport_legacy_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_us_truck_order' AND column_name = 'contact_info' AND is_nullable = 'NO'), + 'ALTER TABLE ll_us_truck_order MODIFY COLUMN contact_info VARCHAR(128) NULL COMMENT ''旧本土卡车联系方式,新单使用起终点联系方式''', + 'SELECT 1' +); +PREPARE dispatch_transport_legacy_stmt FROM @dispatch_transport_legacy_ddl; +EXECUTE dispatch_transport_legacy_stmt; +DEALLOCATE PREPARE dispatch_transport_legacy_stmt; + +SET @dispatch_transport_legacy_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_us_truck_order' AND column_name = 'requested_date' AND is_nullable = 'NO'), + 'ALTER TABLE ll_us_truck_order MODIFY COLUMN requested_date DATE NULL COMMENT ''旧本土卡车请求日期,新单使用取货和送货时间''', + 'SELECT 1' +); +PREPARE dispatch_transport_legacy_stmt FROM @dispatch_transport_legacy_ddl; +EXECUTE dispatch_transport_legacy_stmt; +DEALLOCATE PREPARE dispatch_transport_legacy_stmt; + +CREATE TABLE IF NOT EXISTS ll_dispatch_daily_sequence ( + business_date DATE NOT NULL COMMENT '排车编号业务日期(Asia/Shanghai)', + last_sequence INT NOT NULL COMMENT '当日已分配排车流水号,范围0001至9999', + PRIMARY KEY (business_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车单号每日独立原子流水'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_truck_task ( + dispatch_no VARCHAR(64) NOT NULL COMMENT '排车业务号', + trip_no VARCHAR(64) NOT NULL COMMENT '本次运输资源记录号', + customer_id VARCHAR(128) NOT NULL COMMENT '本土卡车原单客户标识', + order_no VARCHAR(64) NOT NULL COMMENT '本土卡车原单业务号', + active_order VARCHAR(193) NULL COMMENT '有效原单唯一键;取消置空,完成保留', + PRIMARY KEY (dispatch_no), + UNIQUE KEY uk_transport_trip (trip_no), + UNIQUE KEY uk_transport_source (active_order), + KEY idx_transport_order (customer_id, order_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车与本土卡车原单关联'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_transport_create ( + trip_no VARCHAR(64) NOT NULL COMMENT '本次运输资源记录号', + request_hash CHAR(64) NOT NULL COMMENT '首次安排请求摘要', + PRIMARY KEY (trip_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土卡车排车创建请求账本'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_transport_command ( + dispatch_no VARCHAR(64) NOT NULL COMMENT '排车业务号', + request_key VARCHAR(128) NOT NULL COMMENT '客户端幂等请求键', + request_hash CHAR(64) NOT NULL COMMENT '执行命令请求摘要', + evidence_json JSON NULL COMMENT '已确认运输完成凭证标识列表', + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '业务命令提交时间', + PRIMARY KEY (dispatch_no, request_key) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车运输执行命令与凭证账本'; + +-- 当前承运占用查询还依赖散板来源标识(与V33一致);仅补结构,不创建散板业务记录。 +SET @dispatch_transport_pool_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_fleet_external_booking' AND column_name = 'source_looseboard_order_id'), + 'SELECT 1', + 'ALTER TABLE ll_fleet_external_booking ADD COLUMN source_looseboard_order_id BIGINT NULL COMMENT ''客户原始散板商业单ID,取消后保留来源''' +); +PREPARE dispatch_transport_pool_stmt FROM @dispatch_transport_pool_ddl; +EXECUTE dispatch_transport_pool_stmt; +DEALLOCATE PREPARE dispatch_transport_pool_stmt; + +SET @dispatch_transport_pool_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'll_fleet_external_booking' AND index_name = 'idx_booking_looseboard_source'), + 'SELECT 1', + 'ALTER TABLE ll_fleet_external_booking ADD KEY idx_booking_looseboard_source (customer_id, source_looseboard_order_id, status)' +); +PREPARE dispatch_transport_pool_stmt FROM @dispatch_transport_pool_ddl; +EXECUTE dispatch_transport_pool_stmt; +DEALLOCATE PREPARE dispatch_transport_pool_stmt; diff --git a/deploy/sql/排车运输接入-数据库变更汇总.md b/deploy/sql/排车运输接入-数据库变更汇总.md new file mode 100644 index 000000000..c02772b3b --- /dev/null +++ b/deploy/sql/排车运输接入-数据库变更汇总.md @@ -0,0 +1,21 @@ +# 排车运输接入数据库变更汇总 + +执行文件:[lastleg-dispatch-transport-tasks.sql](lastleg-dispatch-transport-tasks.sql)。目标为 **MySQL 8 / baiyue_lastleg**,文件内已有 `USE baiyue_lastleg`。可整份重复执行;按 information_schema 判断字段及索引,按 IF NOT EXISTS 创建表。已在本机既有业务数据环境连续执行两次通过;未在远端环境执行。 + +| 对象 | 新增或调整 | 用途 | +|---|---|---| +| ll_dispatch_order.task_type | 新增 VARCHAR(32) NOT NULL DEFAULT DELIVERY | 区分仓库派送、到仓取货、本土卡车;旧单保留派送类型 | +| ll_dispatch_truck_task | 新表;dispatch_no、trip_no、customer_id、order_no、active_order | 真实排车关联原单;运输记录唯一、有效原单唯一;取消释放有效键,完成保留 | +| ll_dispatch_transport_create | 新表;trip_no、request_hash | 原安排请求摘要,避免重复建单及同键更换原单 | +| ll_dispatch_transport_command | 新表;dispatch_no、request_key、request_hash、evidence_json、created_at | 执行命令幂等、已确认完成凭证持久化 | +| ll_dispatch_daily_sequence | 既有正式日流水表,缺失时补建 | 统一 PC 日期与四位流水;不修改历史单号 | +| ll_dispatch_order.customer_id | 仅旧库存在且为 NOT NULL 时改为允许 NULL | 兼容整车跨客户主单;保留原值,不回填或置空 | +| ll_us_truck_order.contact_name / contact_info / requested_date | 仅旧库仍为 NOT NULL 时改为允许 NULL | 兼容现有原单起终点联系人及取送时间模型,保留历史字段和数据 | +| ll_fleet_external_booking.source_looseboard_order_id | 缺失时补 BIGINT NULL | 当前承运占用查询依赖,等同 V33;不创建散板业务 | +| ll_fleet_external_booking.idx_booking_looseboard_source | 缺失时补客户、原散板ID、状态联合索引 | 原单承运占用查询 | + +所有新增表、字段均有中文注释。SQL不创建账号、不改角色权限、不删除表或记录、不改库存及历史单号,也不回填旧任务。 + +前置:目标库已具备当前整车排车、FleetTrip、仓储转运取货及本土卡车业务结构;本文件是本次运输接入增量,不替代这些模块的完整升级。执行完成后配套发布 Lastleg 和 Admin。迁移系统记录为 V32,承运占用依赖结构为 V33;直接交付执行时使用本份汇总 SQL 即可,不再重复手动执行两个版本文件。 + +本机验收中发现的旧字段约束与缺少日流水表已一并纳入。业务数据由正式 Gateway HTTP 创建,结构验证未写业务SQL。 diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/FleetTransportVehicleGuard.java b/lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/FleetTransportVehicleGuard.java new file mode 100644 index 000000000..6f707ec9e --- /dev/null +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/FleetTransportVehicleGuard.java @@ -0,0 +1,20 @@ +package com.baiyue.lastleg.fleet.trip; + +import com.baiyue.lastleg.masterdata.vehicle.application.port.out.VehicleRepository; +import org.springframework.stereotype.Service; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/** 运输执行仍由车队应用边界核对车辆主档,不向排车暴露车辆持久化。 */ +@Service +public class FleetTransportVehicleGuard { + private final VehicleRepository vehicles; + /** 复用车辆应用端口及其行锁。 */ + public FleetTransportVehicleGuard(VehicleRepository vehicles){this.vehicles=vehicles;} + /** 发车核对实时车辆资格,并冻结当次有效容量供业务量复核。 */ + public void validate(FleetTripEntity trip){ + var vehicle=vehicles.lockById(trip.getVehicleId()).orElseThrow(()->new ResponseStatusException(HttpStatus.CONFLICT,"车辆不存在")); + if(!"OVERSEAS".equals(vehicle.region()) || !"IN_SERVICE".equals(vehicle.status()))throw new ResponseStatusException(HttpStatus.CONFLICT,"当前车辆不可出车"); + trip.setMaxPalletSpaces(vehicle.maxPalletSpaces());trip.setMaxLoadKg(vehicle.maxLoadKg());trip.setMaxVolumeCbm(vehicle.maxVolumeCbm()); + } +} diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/TransportTripChanged.java b/lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/TransportTripChanged.java new file mode 100644 index 000000000..fb395ffac --- /dev/null +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/fleet/trip/TransportTripChanged.java @@ -0,0 +1,5 @@ +package com.baiyue.lastleg.fleet.trip; + + +/** 同步事务内的运输事实;任一投影失败时原执行一起回滚。 */ +public record TransportTripChanged(FleetTripEntity trip,String action,String reason,String operator) { }