From 1a092bc9d71de07c3bf3a2bafafb348b7e509274 Mon Sep 17 00:00:00 2001
From: ouyangxq <775153912@qq.com>
Date: Wed, 9 Sep 2026 13:51:01 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
deploy/sql/头程数据库统一增量-MySQL8.sql | 7 +++++--
firstleg/pom.xml | 1 +
.../firstleg/client/WarehouseSupplierClient.java | 6 ++++++
.../db/phase3-terminal-drayage-schema.sql | 3 +--
.../mapper/TerminalDrayageSqlContractTest.java | 14 ++++++++++++++
.../controller/InternalSupplierController.java | 12 ++++++++++++
6 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql
index 07b97973b..47161a1ca 100644
--- a/deploy/sql/头程数据库统一增量-MySQL8.sql
+++ b/deploy/sql/头程数据库统一增量-MySQL8.sql
@@ -18131,8 +18131,7 @@ CREATE TABLE IF NOT EXISTS fl_terminal_drayage (
KEY idx_terminal_drayage_warning(actual_empty_return_at,estimated_empty_return_at),
CONSTRAINT fk_terminal_drayage_container FOREIGN KEY(container_booking_id) REFERENCES fl_container_booking(id),
CONSTRAINT chk_terminal_drayage_times CHECK((actual_pickup_at IS NULL OR pickup_appointment_at IS NOT NULL)
- AND (actual_empty_return_at IS NULL OR actual_pickup_at IS NOT NULL)
- AND (actual_empty_return_at IS NULL OR actual_empty_return_at >= actual_pickup_at))
+ AND (actual_empty_return_at IS NULL OR actual_pickup_at IS NULL OR actual_empty_return_at >= actual_pickup_at))
) ENGINE=InnoDB COMMENT='目的港货柜级码头拖车与还空事实';
CREATE TABLE IF NOT EXISTS fl_terminal_drayage_history (
id BIGINT UNSIGNED NOT NULL COMMENT '历史ID', drayage_id BIGINT UNSIGNED NOT NULL COMMENT '码头拖车聚合ID',
@@ -18158,6 +18157,10 @@ CREATE TABLE IF NOT EXISTS fl_terminal_drayage_operation (
-- MySQL DDL 会隐式提交;如果前次执行在末段校验失败,重跑时需把已创建表的列注释收敛到当前基线。
ALTER TABLE fl_terminal_drayage
+ DROP CHECK chk_terminal_drayage_times,
+ ADD CONSTRAINT chk_terminal_drayage_times CHECK(
+ (actual_pickup_at IS NULL OR pickup_appointment_at IS NOT NULL) AND
+ (actual_empty_return_at IS NULL OR actual_pickup_at IS NULL OR actual_empty_return_at >= actual_pickup_at)),
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '目的港码头拖车聚合ID',
MODIFY COLUMN container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '唯一归属物理货柜ID',
MODIFY COLUMN fleet_supplier_id BIGINT UNSIGNED NULL COMMENT 'Warehouse ACTIVE/TRUCKING供应商ID快照',
diff --git a/firstleg/pom.xml b/firstleg/pom.xml
index 1a35216ef..98dc43916 100644
--- a/firstleg/pom.xml
+++ b/firstleg/pom.xml
@@ -5,6 +5,7 @@
com.baiyue.firstlegbaiyue-firstleg-platform1.0.0-SNAPSHOT../pom.xml
firstleg-service
+ org.springframework.bootspring-boot-starter-mail
com.baiyue.sharedbaiyue-common-core${project.version}
com.baiyue.sharedcommon-event-contract${project.version}
com.baiyue.sharedbaiyue-common-audit${project.version}
diff --git a/firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseSupplierClient.java b/firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseSupplierClient.java
index 7559f7e14..a0874578a 100644
--- a/firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseSupplierClient.java
+++ b/firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseSupplierClient.java
@@ -24,4 +24,10 @@ public interface WarehouseSupplierClient {
record Snapshot(long supplierId, String name, String category, String status,
List carrierCodes) {}
+
+ @GetMapping("/{supplierId}/trucking-contact")
+ ApiResponse truckingContact(@RequestHeader("X-Internal-Service-Token") String token,
+ @PathVariable long supplierId, @RequestParam boolean requireActive);
+
+ record TruckingContact(long supplierId, String name, String email) {}
}
diff --git a/firstleg/src/main/resources/db/phase3-terminal-drayage-schema.sql b/firstleg/src/main/resources/db/phase3-terminal-drayage-schema.sql
index f68365ccd..4b779b7fa 100644
--- a/firstleg/src/main/resources/db/phase3-terminal-drayage-schema.sql
+++ b/firstleg/src/main/resources/db/phase3-terminal-drayage-schema.sql
@@ -23,8 +23,7 @@ CREATE TABLE IF NOT EXISTS fl_terminal_drayage (
CONSTRAINT fk_terminal_drayage_container FOREIGN KEY(container_booking_id) REFERENCES fl_container_booking(id),
CONSTRAINT chk_terminal_drayage_times CHECK(
(actual_pickup_at IS NULL OR pickup_appointment_at IS NOT NULL) AND
- (actual_empty_return_at IS NULL OR actual_pickup_at IS NOT NULL) AND
- (actual_empty_return_at IS NULL OR actual_empty_return_at >= actual_pickup_at))
+ (actual_empty_return_at IS NULL OR actual_pickup_at IS NULL OR actual_empty_return_at >= actual_pickup_at))
) ENGINE=InnoDB COMMENT='目的港货柜级码头拖车与还空事实';
CREATE TABLE IF NOT EXISTS fl_terminal_drayage_history (
diff --git a/firstleg/src/test/java/com/baiyue/firstleg/terminaldrayage/mapper/TerminalDrayageSqlContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/terminaldrayage/mapper/TerminalDrayageSqlContractTest.java
index fd6635a2b..cac6e7aaf 100644
--- a/firstleg/src/test/java/com/baiyue/firstleg/terminaldrayage/mapper/TerminalDrayageSqlContractTest.java
+++ b/firstleg/src/test/java/com/baiyue/firstleg/terminaldrayage/mapper/TerminalDrayageSqlContractTest.java
@@ -8,6 +8,20 @@ import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class TerminalDrayageSqlContractTest {
+ @Test
+ void bothSqlEntrypointsAllowReturnWithoutPickupAndRetainKnownTimeOrdering() throws Exception {
+ for (Path path : java.util.List.of(
+ Path.of("src/main/resources/db/phase3-terminal-drayage-schema.sql"),
+ Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"))) {
+ String sql = Files.readString(path);
+ assertThat(sql).contains("actual_empty_return_at IS NULL OR actual_pickup_at IS NULL OR actual_empty_return_at >= actual_pickup_at")
+ .doesNotContain("actual_empty_return_at IS NULL OR actual_pickup_at IS NOT NULL");
+ }
+ String release = Files.readString(Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"));
+ assertThat(release).contains("DROP CHECK chk_terminal_drayage_times",
+ "ADD CONSTRAINT chk_terminal_drayage_times CHECK(");
+ }
+
@Test
void schemaKeepsOneAggregatePerContainerAndImmutableAudit() throws Exception {
String sql = Files.readString(Path.of("src/main/resources/db/phase3-terminal-drayage-schema.sql"));
diff --git a/warehouse/src/main/java/com/baiyue/warehouse/controller/InternalSupplierController.java b/warehouse/src/main/java/com/baiyue/warehouse/controller/InternalSupplierController.java
index be193cc9c..78cfb51e8 100644
--- a/warehouse/src/main/java/com/baiyue/warehouse/controller/InternalSupplierController.java
+++ b/warehouse/src/main/java/com/baiyue/warehouse/controller/InternalSupplierController.java
@@ -37,5 +37,17 @@ public class InternalSupplierController {
return ApiResponse.success(service.internalBookingCandidates(carrierCode));
}
+ public record TruckingContact(long supplierId, String name, String email) {}
+
+ @GetMapping("/{supplierId}/trucking-contact")
+ public ApiResponse truckingContact(
+ @RequestHeader(value="X-Internal-Service-Token", required=false) String token,
+ @PathVariable long supplierId, @RequestParam(defaultValue="true") boolean requireActive) {
+ requireToken(token);
+ service.internalSnapshot(supplierId, requireActive, "TRUCKING");
+ var supplier = service.detail(supplierId);
+ return ApiResponse.success(new TruckingContact(supplierId, supplier.name(), supplier.email()));
+ }
+
private void requireToken(String token) { if (token == null || !MessageDigest.isEqual(expected, token.getBytes(StandardCharsets.UTF_8))) throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效"); }
}