Merge remote-tracking branch 'websoft/dev'
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/LoadingManageServiceImpl.java
This commit is contained in:
@@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS `blade_loading_manage` (
|
||||
`trailer_vehicle_no` varchar(100) DEFAULT NULL COMMENT '挂车车牌号',
|
||||
`driver_name` varchar(100) DEFAULT NULL COMMENT '司机',
|
||||
`driver_phone` varchar(50) DEFAULT NULL COMMENT '联系电话',
|
||||
`driver_id` bigint(20) DEFAULT NULL COMMENT '司机ID',
|
||||
`escort_name` varchar(100) DEFAULT NULL COMMENT '押运人',
|
||||
`escort_phone` varchar(50) DEFAULT NULL COMMENT '押运人手机号',
|
||||
`carrier_type` varchar(50) DEFAULT NULL COMMENT '承运类型',
|
||||
@@ -50,7 +51,8 @@ CREATE TABLE IF NOT EXISTS `blade_loading_manage` (
|
||||
UNIQUE KEY `uk_loading_manage_no` (`loading_no`) USING BTREE,
|
||||
KEY `idx_loading_manage_carrier_contract_id` (`carrier_contract_id`) USING BTREE,
|
||||
KEY `idx_loading_manage_dept` (`dept_id`) USING BTREE,
|
||||
KEY `idx_loading_manage_create_time` (`create_time`) USING BTREE
|
||||
KEY `idx_loading_manage_create_time` (`create_time`) USING BTREE,
|
||||
KEY `idx_loading_manage_tenant_driver_id` (`tenant_id`, `driver_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配载管理';
|
||||
|
||||
INSERT IGNORE INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `component`, `remark`, `is_deleted`) VALUES
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
-- ============================================================
|
||||
-- blade_loading_manage_driver_id_20261028.sql
|
||||
-- ============================================================
|
||||
-- 运单/配载单写入司机档案 ID,把「归属」与「手机号快照」解耦:
|
||||
-- blade_loading_manage.driver_id 配载单当前司机的司机档案 ID(blade_transport_driver.id)
|
||||
-- blade_waybill.driver_id 已有列(本脚本只补索引),派单/配载/导入时写入
|
||||
-- 归属判据:driver_id > 0 才算有主;0、-1 与 NULL 一律等同「无主」(承运商司机、未登录过小程序的司机)。
|
||||
-- 历史数据不回填:手机号二次放号后按号反查会张冠李戴,旧单统一按无主处理,等下一次派单/配载再写入。
|
||||
-- 本脚本只做结构升级,不改任何业务数据;以下结构变更可重复执行。
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 0. 存档
|
||||
-- ------------------------------------------------------------
|
||||
-- 本脚本无数据订正,无需备份表。
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 1. 结构变更
|
||||
-- ------------------------------------------------------------
|
||||
-- ① blade_loading_manage.driver_id
|
||||
SET @ddl_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_loading_manage` ADD COLUMN `driver_id` bigint(20) DEFAULT NULL COMMENT ''司机ID'' AFTER `driver_phone`',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'blade_loading_manage'
|
||||
AND COLUMN_NAME = 'driver_id'
|
||||
);
|
||||
PREPARE ddl_stmt FROM @ddl_sql;
|
||||
EXECUTE ddl_stmt;
|
||||
DEALLOCATE PREPARE ddl_stmt;
|
||||
|
||||
-- ② blade_loading_manage 按租户 + 司机归属检索(司机端/归属巡检)
|
||||
SET @ddl_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_loading_manage` ADD KEY `idx_loading_manage_tenant_driver_id` (`tenant_id`, `driver_id`) USING BTREE',
|
||||
'SELECT 1')
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'blade_loading_manage'
|
||||
AND INDEX_NAME = 'idx_loading_manage_tenant_driver_id'
|
||||
);
|
||||
PREPARE ddl_stmt FROM @ddl_sql;
|
||||
EXECUTE ddl_stmt;
|
||||
DEALLOCATE PREPARE ddl_stmt;
|
||||
|
||||
-- ③ blade_waybill 按租户 + 司机归属检索(列已存在,只补索引)
|
||||
SET @ddl_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_waybill` ADD KEY `idx_waybill_tenant_driver_id` (`tenant_id`, `driver_id`) USING BTREE',
|
||||
'SELECT 1')
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'blade_waybill'
|
||||
AND INDEX_NAME = 'idx_waybill_tenant_driver_id'
|
||||
);
|
||||
PREPARE ddl_stmt FROM @ddl_sql;
|
||||
EXECUTE ddl_stmt;
|
||||
DEALLOCATE PREPARE ddl_stmt;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 2. 校验
|
||||
-- ------------------------------------------------------------
|
||||
-- 列存在(期望 1 行)
|
||||
-- SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
-- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'blade_loading_manage' AND COLUMN_NAME = 'driver_id';
|
||||
-- 索引存在(期望 2 行)
|
||||
-- SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME FROM information_schema.STATISTICS
|
||||
-- WHERE TABLE_SCHEMA = DATABASE()
|
||||
-- AND INDEX_NAME IN ('idx_loading_manage_tenant_driver_id', 'idx_waybill_tenant_driver_id')
|
||||
-- ORDER BY TABLE_NAME, SEQ_IN_INDEX;
|
||||
-- 归属分布(历史单 driver_id 应为空)
|
||||
-- SELECT COUNT(*) AS total, SUM(driver_id IS NOT NULL AND driver_id > 0) AS owned FROM blade_waybill;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 3. 回退
|
||||
-- ------------------------------------------------------------
|
||||
-- ALTER TABLE `blade_waybill` DROP INDEX `idx_waybill_tenant_driver_id`;
|
||||
-- ALTER TABLE `blade_loading_manage` DROP INDEX `idx_loading_manage_tenant_driver_id`;
|
||||
-- ALTER TABLE `blade_loading_manage` DROP COLUMN `driver_id`;
|
||||
@@ -0,0 +1,239 @@
|
||||
-- ============================================================
|
||||
-- blade_loading_manage_driver_id_backfill_20261028.sql
|
||||
-- ============================================================
|
||||
-- 存量配载单补司机档案 ID:把「归属」从手机号快照切到 blade_transport_driver.id
|
||||
-- 写业务数据。第 0 节建存档表,第 1 节分批回填,第 2 节校验(只读,执行后必看),第 3 节回退。可重复执行。
|
||||
--
|
||||
-- 为什么要订正:
|
||||
-- 票 01 之后配载单的归属键是 driver_id(blade_transport_driver.id),driver_phone 只是配载时的号码快照。
|
||||
-- 存量配载单全部只有号码、没有 driver_id(票 01 才加的列),司机端按档案 ID 查配载单会一张都查不到;
|
||||
-- 按号码反查又会踩二次放号与改号,所以才需要先按「唯一命中」把能确定的补齐。
|
||||
-- 本脚本只处理「号码在本租户内唯一命中一条未删除档案」的配载单,其余留在原样、进清单等人工处置。
|
||||
--
|
||||
-- 判定与边界(严格按票面口径,不扩大):
|
||||
-- ① 只动 driver_id IS NULL 的行。driver_id = -1 是「无主」哨兵,正数表示已有归属,两者一行都不动;
|
||||
-- 第 1 节的 WHERE 里另加了一道显式排除((driver_id = -1) IS NOT TRUE),
|
||||
-- 防止后人把条件放宽成 driver_id <= 0 时把哨兵一起覆盖。
|
||||
-- ② 唯一命中:同租户内、is_deleted = 0、mobile 与 driver_phone 相等的档案恰好 1 条。
|
||||
-- 命中 0 条 → 进「无法匹配」清单;命中 ≥2 条 → 进「冲突」清单。两类都跳过,不猜。
|
||||
-- 逻辑删除的档案不参与命中:档案删了以后重跑脚本,不能把这些单挂到废弃档案上。
|
||||
-- ③ 手机号为空、空白、含 * 的跳过(* 是脱敏占位,不是真号码)。
|
||||
-- ④ driver_phone、update_time 与其它任何列都不改,只写 driver_id。
|
||||
-- ⑤ 逻辑删除的配载单同样回填:票面口径只有 driver_id IS NULL,不额外收窄范围;
|
||||
-- 这些单不会回到列表,回填对它们无副作用。
|
||||
-- ⑥ 跨表比较显式 COLLATE:blade_loading_manage.driver_phone / tenant_id 是 utf8mb4_0900_ai_ci,
|
||||
-- blade_transport_driver.mobile / tenant_id 与存档表都是 utf8mb4_general_ci;
|
||||
-- 不显式 COLLATE 会抛 ERROR 1267 Illegal mix of collations,只有真跑库才会暴露。
|
||||
-- 第 2.4 节拿存档表比 driver_phone 同样要带 COLLATE utf8mb4_0900_ai_ci。
|
||||
-- ⑦ 取数与写入全在 SQL 内完成,不经 JSON / 客户端中转:19 位雪花 ID 过 JSON 会丢精度。
|
||||
-- ⑧ 分批执行(默认每批 500 行),每批独立提交,中断后重跑从剩余 NULL 行继续。
|
||||
--
|
||||
-- 范围说明:与运单脚本同一口径,只做「手机号 → 档案唯一命中」这一条规则;其余留给人工处置。
|
||||
-- 幂等:命中映射先落存档表;重复执行时映射新增 0 行、回填更新 0 行,结果不变(第 2.2 节校验)。
|
||||
-- 执行方式(脚本里有 DELIMITER 与存储过程,按分号切句的 GUI 客户端会切错):
|
||||
-- mysql -h127.0.0.1 -uroot -p transport < blade_loading_manage_driver_id_backfill_20261028.sql
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 0. 存档(必须在订正之前执行;重复执行不会覆盖已有存档)
|
||||
-- ------------------------------------------------------------
|
||||
-- 存档表同时是第 1 节的「命中映射」:一行 = 一个待回填的配载单 + 它唯一命中的档案 ID。
|
||||
-- 回填前先把映射定下来:回填只按映射写,跑之前就能看出「打算填谁」,回退也按它还原。
|
||||
CREATE TABLE IF NOT EXISTS blade_loading_manage_driver_id_bak_20261028 (
|
||||
loading_id bigint NOT NULL COMMENT '配载单ID',
|
||||
driver_id_before bigint DEFAULT NULL COMMENT '回填前的司机ID(本脚本只处理 NULL)',
|
||||
driver_id_after bigint DEFAULT NULL COMMENT '回填写入的司机档案ID(blade_transport_driver.id)',
|
||||
driver_phone varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '回填前的手机号快照,用来证明没有被改写',
|
||||
update_time_before datetime DEFAULT NULL COMMENT '回填前的更新时间,用来证明没有被改写',
|
||||
PRIMARY KEY (loading_id),
|
||||
KEY idx_driver_id_after (driver_id_after)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='20261028 配载单司机档案ID回填存档';
|
||||
|
||||
-- 命中映射:只收「号码唯一命中一条未删除档案」的 NULL 行。
|
||||
-- INSERT IGNORE + WHERE driver_id IS NULL:第二次执行不会重复建映射,已经填过的行不会再进映射。
|
||||
INSERT IGNORE INTO blade_loading_manage_driver_id_bak_20261028 (loading_id, driver_id_before, driver_id_after, driver_phone, update_time_before)
|
||||
SELECT matched.biz_id, NULL, matched.driver_id, matched.driver_phone, matched.update_time
|
||||
FROM (
|
||||
SELECT w.id AS biz_id,
|
||||
MIN(d.id) AS driver_id,
|
||||
w.driver_phone AS driver_phone,
|
||||
w.update_time AS update_time
|
||||
FROM blade_loading_manage w
|
||||
JOIN blade_transport_driver d
|
||||
ON d.tenant_id COLLATE utf8mb4_0900_ai_ci = w.tenant_id
|
||||
AND d.mobile COLLATE utf8mb4_0900_ai_ci = w.driver_phone
|
||||
AND d.is_deleted = 0
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND w.driver_phone IS NOT NULL
|
||||
AND TRIM(w.driver_phone) <> ''
|
||||
AND w.driver_phone NOT LIKE '%*%'
|
||||
GROUP BY w.id, w.driver_phone, w.update_time
|
||||
HAVING COUNT(*) = 1
|
||||
) matched;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 1. 订正(分批回填,默认每批 500 行)
|
||||
-- ------------------------------------------------------------
|
||||
-- 循环退出条件是「这一批没有更新任何行」,所以重复执行时第一批就是 0 行,脚本空转结束。
|
||||
-- 每批单独提交(不开长事务):大表上不会长时间持锁,中断后重跑从剩余 NULL 行继续。
|
||||
DELIMITER $$
|
||||
DROP PROCEDURE IF EXISTS backfill_blade_loading_manage_driver_id_20261028$$
|
||||
CREATE PROCEDURE backfill_blade_loading_manage_driver_id_20261028(IN p_batch_size INT)
|
||||
BEGIN
|
||||
DECLARE v_batch_size INT DEFAULT 500;
|
||||
DECLARE v_affected INT DEFAULT 0;
|
||||
|
||||
IF p_batch_size IS NOT NULL AND p_batch_size > 0 THEN
|
||||
SET v_batch_size = p_batch_size;
|
||||
END IF;
|
||||
|
||||
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_loading_manage_driver_id_20261028 (
|
||||
loading_id bigint NOT NULL,
|
||||
PRIMARY KEY (loading_id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
REPEAT
|
||||
-- 取批:只取还空着的行,填过的行不再入批,所以每一批都在推进,不会原地打转
|
||||
DELETE FROM tmp_loading_manage_driver_id_20261028;
|
||||
|
||||
INSERT INTO tmp_loading_manage_driver_id_20261028 (loading_id)
|
||||
SELECT b.loading_id
|
||||
FROM blade_loading_manage_driver_id_bak_20261028 b
|
||||
JOIN blade_loading_manage w ON w.id = b.loading_id
|
||||
WHERE w.driver_id IS NULL
|
||||
ORDER BY b.loading_id
|
||||
LIMIT v_batch_size;
|
||||
|
||||
UPDATE blade_loading_manage w
|
||||
JOIN blade_loading_manage_driver_id_bak_20261028 b ON b.loading_id = w.id
|
||||
JOIN tmp_loading_manage_driver_id_20261028 t ON t.loading_id = w.id
|
||||
SET w.driver_id = b.driver_id_after
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND b.driver_id_after IS NOT NULL;
|
||||
|
||||
SET v_affected = ROW_COUNT();
|
||||
UNTIL v_affected = 0 END REPEAT;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_loading_manage_driver_id_20261028;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL backfill_blade_loading_manage_driver_id_20261028(500);
|
||||
DROP PROCEDURE IF EXISTS backfill_blade_loading_manage_driver_id_20261028;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 2. 校验(执行后必看;本节只有 SELECT,不动数据)
|
||||
-- ------------------------------------------------------------
|
||||
-- 2.1 规模:存档多少行、还剩多少 NULL 行、正数归属多少行(跑前跑后各查一次)
|
||||
-- remaining_null_rows 只会减少「本次命中」的那部分;差额应当等于第 2.7 节的命中行数。
|
||||
SELECT COUNT(*) AS archived_rows FROM blade_loading_manage_driver_id_bak_20261028;
|
||||
SELECT COUNT(*) AS remaining_null_rows FROM blade_loading_manage WHERE driver_id IS NULL;
|
||||
SELECT SUM(driver_id > 0) AS owned_rows,
|
||||
SUM(driver_id = -1) AS sentinel_rows,
|
||||
SUM(driver_id IS NULL) AS null_rows
|
||||
FROM blade_loading_manage;
|
||||
|
||||
-- 2.2 哨兵不变:跑前跑后各执行一次,四列必须完全相同(-1 一行都不能动)
|
||||
SELECT COUNT(*) AS sentinel_rows,
|
||||
SUM(id) AS sentinel_id_sum,
|
||||
MIN(id) AS min_id,
|
||||
MAX(id) AS max_id
|
||||
FROM blade_loading_manage
|
||||
WHERE driver_id = -1;
|
||||
|
||||
-- 2.3 写入正确:本次映射的行,driver_id 必须等于映射值(期望 0 行)
|
||||
SELECT COUNT(*) AS wrong_driver_id
|
||||
FROM blade_loading_manage_driver_id_bak_20261028 b
|
||||
JOIN blade_loading_manage w ON w.id = b.loading_id
|
||||
WHERE NOT (w.driver_id <=> b.driver_id_after);
|
||||
|
||||
-- 2.4 快照没有被改写:本次映射的行,driver_phone 与 update_time 必须与存档逐行一致(期望 0 行)
|
||||
SELECT COUNT(*) AS snapshot_rewritten
|
||||
FROM blade_loading_manage_driver_id_bak_20261028 b
|
||||
JOIN blade_loading_manage w ON w.id = b.loading_id
|
||||
WHERE NOT (w.driver_phone <=> b.driver_phone COLLATE utf8mb4_0900_ai_ci)
|
||||
OR NOT (w.update_time <=> b.update_time_before);
|
||||
|
||||
-- 2.5 冲突清单:同号多档案,这些行被跳过(期望 0 行;有输出就是「为什么没填」的答案)
|
||||
SELECT w.driver_phone AS conflicting_phone,
|
||||
COUNT(DISTINCT w.id) AS pending_rows,
|
||||
COUNT(DISTINCT d.id) AS driver_profiles,
|
||||
GROUP_CONCAT(DISTINCT d.id ORDER BY d.id) AS driver_profile_ids
|
||||
FROM blade_loading_manage w
|
||||
JOIN blade_transport_driver d
|
||||
ON d.tenant_id COLLATE utf8mb4_0900_ai_ci = w.tenant_id
|
||||
AND d.mobile COLLATE utf8mb4_0900_ai_ci = w.driver_phone
|
||||
AND d.is_deleted = 0
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND w.driver_phone IS NOT NULL
|
||||
AND TRIM(w.driver_phone) <> ''
|
||||
AND w.driver_phone NOT LIKE '%*%'
|
||||
GROUP BY w.driver_phone
|
||||
HAVING COUNT(DISTINCT d.id) > 1;
|
||||
|
||||
-- 2.6 无法匹配清单:号码在本租户内命中 0 条档案,这些行按规则跳过(等人工处置,脚本不猜)
|
||||
SELECT w.driver_phone AS unmatched_phone,
|
||||
COUNT(*) AS pending_rows
|
||||
FROM blade_loading_manage w
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND w.driver_phone IS NOT NULL
|
||||
AND TRIM(w.driver_phone) <> ''
|
||||
AND w.driver_phone NOT LIKE '%*%'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM blade_transport_driver d
|
||||
WHERE d.tenant_id COLLATE utf8mb4_0900_ai_ci = w.tenant_id
|
||||
AND d.mobile COLLATE utf8mb4_0900_ai_ci = w.driver_phone
|
||||
AND d.is_deleted = 0)
|
||||
GROUP BY w.driver_phone
|
||||
ORDER BY pending_rows DESC, unmatched_phone;
|
||||
|
||||
-- 2.7 逐司机对照:按手机号可见单量 vs 按档案 ID 可见单量
|
||||
-- by_phone = 号码快照等于该档案号码的单量(旧口径,改号/二次放号会串)
|
||||
-- by_driver_id = 档案 ID 等于该档案的单量(新口径)
|
||||
-- phone_only_null = 号码对上、但归属还空着的单(本次应被回填的那部分)
|
||||
-- phone_only_sentinel = 号码对上、但归属被标成 -1 无主的单(回填不碰,需要人工确认)
|
||||
-- phone_only_other_owner = 号码对上、却已归属另一条档案的单(改号/换司机后号码被复用)
|
||||
-- id_only_phone_differs = 已归属该档案、但号码快照不是该档案当前号码的单(改号后的历史快照)
|
||||
SELECT d.id AS driver_id,
|
||||
d.driver_name,
|
||||
d.mobile,
|
||||
(SELECT COUNT(*) FROM blade_loading_manage w WHERE w.driver_id = d.id) AS by_driver_id,
|
||||
(SELECT COUNT(*) FROM blade_loading_manage w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile) AS by_phone,
|
||||
(SELECT COUNT(*) FROM blade_loading_manage w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile
|
||||
AND w.driver_id IS NULL) AS phone_only_null,
|
||||
(SELECT COUNT(*) FROM blade_loading_manage w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile
|
||||
AND w.driver_id = -1) AS phone_only_sentinel,
|
||||
(SELECT COUNT(*) FROM blade_loading_manage w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile
|
||||
AND w.driver_id > 0
|
||||
AND w.driver_id <> d.id) AS phone_only_other_owner,
|
||||
(SELECT COUNT(*) FROM blade_loading_manage w
|
||||
WHERE w.driver_id = d.id
|
||||
AND (w.driver_phone IS NULL
|
||||
OR NOT (w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile))) AS id_only_phone_differs
|
||||
FROM blade_transport_driver d
|
||||
WHERE d.is_deleted = 0
|
||||
ORDER BY by_phone DESC, by_driver_id DESC, driver_id;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 3. 回退(仅在需要撤销本次回填时手工执行)
|
||||
-- ------------------------------------------------------------
|
||||
-- 按存档表把本次写入的行还原成 NULL。只还原「当前值仍等于本次写入值」的行:
|
||||
-- 回填之后又被人改派过的单不会被冲掉。
|
||||
-- UPDATE blade_loading_manage w
|
||||
-- JOIN blade_loading_manage_driver_id_bak_20261028 b ON b.loading_id = w.id
|
||||
-- SET w.driver_id = b.driver_id_before
|
||||
-- WHERE w.driver_id <=> b.driver_id_after;
|
||||
-- 回退只碰 driver_id,不碰 driver_phone / update_time。备份表确认不再需要时再删:
|
||||
-- DROP TABLE blade_loading_manage_driver_id_bak_20261028;
|
||||
@@ -300,7 +300,8 @@ CREATE TABLE `blade_waybill` (
|
||||
KEY `idx_waybill_dept` (`dept_id`) USING BTREE,
|
||||
KEY `idx_waybill_plan_id` (`plan_id`) USING BTREE,
|
||||
KEY `idx_waybill_carrier_contract_id` (`carrier_contract_id`) USING BTREE,
|
||||
KEY `idx_waybill_create_time` (`create_time`) USING BTREE
|
||||
KEY `idx_waybill_create_time` (`create_time`) USING BTREE,
|
||||
KEY `idx_waybill_tenant_driver_id` (`tenant_id`,`driver_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运单管理';
|
||||
|
||||
-- ----------------------------
|
||||
@@ -330,6 +331,7 @@ CREATE TABLE `blade_loading_manage` (
|
||||
`trailer_vehicle_no` varchar(100) DEFAULT NULL COMMENT '挂车车牌号',
|
||||
`driver_name` varchar(100) DEFAULT NULL COMMENT '司机',
|
||||
`driver_phone` varchar(50) DEFAULT NULL COMMENT '联系电话',
|
||||
`driver_id` bigint(20) DEFAULT NULL COMMENT '司机ID',
|
||||
`escort_name` varchar(100) DEFAULT NULL COMMENT '押运人',
|
||||
`escort_phone` varchar(50) DEFAULT NULL COMMENT '押运人手机号',
|
||||
`carrier_type` varchar(50) DEFAULT NULL COMMENT '承运类型',
|
||||
@@ -359,7 +361,8 @@ CREATE TABLE `blade_loading_manage` (
|
||||
UNIQUE KEY `uk_loading_manage_no` (`loading_no`) USING BTREE,
|
||||
KEY `idx_loading_manage_carrier_contract_id` (`carrier_contract_id`) USING BTREE,
|
||||
KEY `idx_loading_manage_dept` (`dept_id`) USING BTREE,
|
||||
KEY `idx_loading_manage_create_time` (`create_time`) USING BTREE
|
||||
KEY `idx_loading_manage_create_time` (`create_time`) USING BTREE,
|
||||
KEY `idx_loading_manage_tenant_driver_id` (`tenant_id`,`driver_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配载管理';
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
@@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS `blade_transport_cert_record` (
|
||||
`biz_key` varchar(100) DEFAULT NULL COMMENT '展示键:司机姓名/车牌号',
|
||||
`status` int(11) NOT NULL COMMENT '审核状态:0审核中 1已通过 2已驳回 3已作废',
|
||||
`snapshot_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '该次提交的全量认证内容JSON,一经创建不可修改',
|
||||
`audited_snapshot_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '审核通过时确定的修订版内容;NULL=未修订',
|
||||
`reject_reason` varchar(200) DEFAULT NULL COMMENT '驳回原因,仅状态为已驳回时有值',
|
||||
`submit_time` datetime DEFAULT NULL COMMENT '提交时间',
|
||||
`submit_user` bigint(20) DEFAULT NULL COMMENT '提交人',
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
-- ----------------------------
|
||||
-- 认证记录:新增修订版快照列 audited_snapshot_json(2026-10-28)
|
||||
-- ----------------------------
|
||||
-- 背景:审核员可以在审核页面对提交内容做修订(全字段可改,身份字段除外)。
|
||||
-- 修订结果必须留痕:snapshot_json 是「司机当时提交了什么」,一经创建逐字节不可改;
|
||||
-- 修订版另存一列,「审核通过时到底认定了什么内容」才查得清。
|
||||
--
|
||||
-- 语义:
|
||||
-- NULL —— 未修订,生效内容即 snapshot_json;
|
||||
-- 非 NULL —— 审核通过时认定的修订版全量 JSON,生效内容以它为准。
|
||||
-- 只在「通过」时同事务写入;驳回不写(驳回不产生生效内容,留修订版只会让人误读)。
|
||||
-- 修订人/修订时间复用 audit_user / audit_time,不新增列。
|
||||
--
|
||||
-- 只做结构变更,不回填数据:存量记录一律保持 NULL(历史审核确实没有修订动作)。
|
||||
--
|
||||
-- 不加索引:本列只在审核详情与生效内容读取时按主键取用,从不作为查询条件;
|
||||
-- longtext 建索引会超出索引键长度并拖慢写入。
|
||||
--
|
||||
-- 幂等:先查 information_schema 判列,列已存在(或类型/可空性已符合)即跳过;
|
||||
-- 只用 PREPARE 动态语句、不用存储过程,因此不需要 DELIMITER——按分号逐句切分的
|
||||
-- 客户端(含数据库驱动)也能直接执行。重复执行零变更。
|
||||
|
||||
SET @cert_record_schema = DATABASE();
|
||||
|
||||
-- ----------------------------
|
||||
-- 0. 表存在性守卫
|
||||
-- ----------------------------
|
||||
-- 表不存在时故意执行一条只读查询,让客户端报 1146「Table ... doesn't exist」;
|
||||
-- 否则下面因查不到列而静默跳过,脚本会假绿。
|
||||
SET @cert_record_guard_sql = (
|
||||
SELECT IF(COUNT(*) > 0,
|
||||
'SELECT 1',
|
||||
'SELECT 1 FROM `blade_transport_cert_record` LIMIT 0')
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = @cert_record_schema
|
||||
AND table_name = 'blade_transport_cert_record'
|
||||
);
|
||||
PREPARE cert_record_guard_stmt FROM @cert_record_guard_sql;
|
||||
EXECUTE cert_record_guard_stmt;
|
||||
DEALLOCATE PREPARE cert_record_guard_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 1. 加列 audited_snapshot_json
|
||||
-- ----------------------------
|
||||
-- 位置紧接 snapshot_json:两列是同一份内容的前后两态,放一起便于对照。
|
||||
-- 条件分三种:列不存在则 ADD;列存在但类型不是 longtext 或可空性不是 YES 则 MODIFY
|
||||
-- (防止历史上的手工变更把列建成 varchar 而截断 JSON);其余情况跳过。
|
||||
SET @cert_record_audited_snapshot_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_transport_cert_record` ADD COLUMN `audited_snapshot_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''审核通过时确定的修订版内容;NULL=未修订'' AFTER `snapshot_json`',
|
||||
IF(SUM(data_type <> 'longtext' OR is_nullable <> 'YES') > 0,
|
||||
'ALTER TABLE `blade_transport_cert_record` MODIFY COLUMN `audited_snapshot_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''审核通过时确定的修订版内容;NULL=未修订''',
|
||||
'SELECT 1'))
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @cert_record_schema
|
||||
AND table_name = 'blade_transport_cert_record'
|
||||
AND column_name = 'audited_snapshot_json'
|
||||
);
|
||||
PREPARE cert_record_audited_snapshot_stmt FROM @cert_record_audited_snapshot_sql;
|
||||
EXECUTE cert_record_audited_snapshot_stmt;
|
||||
DEALLOCATE PREPARE cert_record_audited_snapshot_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 2. 回滚
|
||||
-- ----------------------------
|
||||
-- 只新增了一列、未进任何索引,回滚即删该列;
|
||||
-- snapshot_json 的 updateStrategy=NEVER 与生成列 pending_biz_id、唯一键 uk_cert_record_pending 均不受影响:
|
||||
-- ALTER TABLE `blade_transport_cert_record` DROP COLUMN `audited_snapshot_json`;
|
||||
@@ -7,6 +7,7 @@ CREATE TABLE `blade_vehicle_maintenance_record` (
|
||||
`tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',
|
||||
`vehicle_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '车船类型',
|
||||
`vehicle_no` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '车牌号/船号',
|
||||
`exception_id` bigint NULL DEFAULT NULL COMMENT '关联异常处置ID(小程序录入时写入)',
|
||||
`maintainer` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '维修人',
|
||||
`maintenance_time` datetime NOT NULL COMMENT '维修时间',
|
||||
`location` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '维修位置',
|
||||
@@ -29,5 +30,6 @@ CREATE TABLE `blade_vehicle_maintenance_record` (
|
||||
`is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_vehicle_maintenance_record_no`(`vehicle_no`) USING BTREE,
|
||||
INDEX `idx_vehicle_maintenance_record_time`(`maintenance_time`) USING BTREE
|
||||
INDEX `idx_vehicle_maintenance_record_time`(`maintenance_time`) USING BTREE,
|
||||
UNIQUE INDEX `uk_vehicle_maintenance_record_exception`(`exception_id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '车辆维修记录';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
-- 维修记录关联异常处置
|
||||
-- 日期:2026-10-29
|
||||
-- 小程序调度端在「车辆故障维修」类异常下录入维修记录,记录通过 exception_id 关联该条异常。
|
||||
-- 一条异常至多一条维修记录,故建唯一索引;唯一索引允许多个 NULL,PC 手工录入/导入的记录不受影响。
|
||||
|
||||
DELIMITER $$
|
||||
|
||||
DROP PROCEDURE IF EXISTS `upgrade_blade_vehicle_maintenance_record_exception_20261029`$$
|
||||
CREATE PROCEDURE `upgrade_blade_vehicle_maintenance_record_exception_20261029`()
|
||||
BEGIN
|
||||
DECLARE db_name varchar(128);
|
||||
SET db_name = DATABASE();
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = db_name
|
||||
AND table_name = 'blade_vehicle_maintenance_record'
|
||||
AND column_name = 'exception_id') THEN
|
||||
ALTER TABLE `blade_vehicle_maintenance_record`
|
||||
ADD COLUMN `exception_id` bigint NULL DEFAULT NULL COMMENT '关联异常处置ID(小程序录入时写入)'
|
||||
AFTER `vehicle_no`;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = db_name
|
||||
AND table_name = 'blade_vehicle_maintenance_record'
|
||||
AND index_name = 'uk_vehicle_maintenance_record_exception') THEN
|
||||
ALTER TABLE `blade_vehicle_maintenance_record`
|
||||
ADD UNIQUE INDEX `uk_vehicle_maintenance_record_exception`(`exception_id`) USING BTREE;
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
CALL `upgrade_blade_vehicle_maintenance_record_exception_20261029`()$$
|
||||
DROP PROCEDURE `upgrade_blade_vehicle_maintenance_record_exception_20261029`$$
|
||||
|
||||
DELIMITER ;
|
||||
@@ -0,0 +1,242 @@
|
||||
-- ============================================================
|
||||
-- blade_waybill_driver_id_backfill_20261028.sql
|
||||
-- ============================================================
|
||||
-- 存量运单补司机档案 ID:把「归属」从手机号快照切到 blade_transport_driver.id
|
||||
-- 写业务数据。第 0 节建存档表,第 1 节分批回填,第 2 节校验(只读,执行后必看),第 3 节回退。可重复执行。
|
||||
--
|
||||
-- 为什么要订正:
|
||||
-- 票 01 之后运单的归属键是 driver_id(blade_transport_driver.id),driver_phone 只是派单时的号码快照。
|
||||
-- 存量运单大多只有号码、没有 driver_id,司机端按档案 ID 查单会整批漏掉它们;
|
||||
-- 按号码反查又会踩二次放号与改号(本机库实测:黄宇富档案已改号 15577122721,
|
||||
-- 名下 92 张单仍写着旧号 17587421208,其中 20 张 driver_id 为空、5 张能从接单司机推出来,
|
||||
-- 剩下 15 张号码已经对不上任何档案——本脚本不猜,它们会出现在第 2.6 节清单里)。
|
||||
-- 本脚本只处理「号码在本租户内唯一命中一条未删除档案」的运单,其余留在原样、进清单等人工处置。
|
||||
--
|
||||
-- 判定与边界(严格按票面口径,不扩大):
|
||||
-- ① 只动 driver_id IS NULL 的行。driver_id = -1 是「无主」哨兵,正数表示已有归属,两者一行都不动;
|
||||
-- 第 1 节的 WHERE 里另加了一道显式排除((driver_id = -1) IS NOT TRUE),
|
||||
-- 防止后人把条件放宽成 driver_id <= 0 时把哨兵一起覆盖。
|
||||
-- ② 唯一命中:同租户内、is_deleted = 0、mobile 与 driver_phone 相等的档案恰好 1 条。
|
||||
-- 命中 0 条 → 进「无法匹配」清单;命中 ≥2 条 → 进「冲突」清单。两类都跳过,不猜。
|
||||
-- 逻辑删除的档案不参与命中:档案删了以后重跑脚本,不能把这些单挂到废弃档案上。
|
||||
-- ③ 手机号为空、空白、含 * 的跳过(* 是脱敏占位,不是真号码)。
|
||||
-- ④ driver_phone、update_time 与其它任何列都不改,只写 driver_id。
|
||||
-- ⑤ 逻辑删除的运单同样回填:票面口径只有 driver_id IS NULL,不额外收窄范围;
|
||||
-- 这些单不会回到列表,回填对它们无副作用。
|
||||
-- ⑥ 跨表比较显式 COLLATE:blade_waybill.driver_phone / tenant_id 是 utf8mb4_0900_ai_ci,
|
||||
-- blade_transport_driver.mobile / tenant_id 与存档表都是 utf8mb4_general_ci;
|
||||
-- 不显式 COLLATE 会抛 ERROR 1267 Illegal mix of collations,只有真跑库才会暴露。
|
||||
-- 第 2.4 节拿存档表比 driver_phone 同样要带 COLLATE utf8mb4_0900_ai_ci。
|
||||
-- ⑦ 取数与写入全在 SQL 内完成,不经 JSON / 客户端中转:19 位雪花 ID 过 JSON 会丢精度。
|
||||
-- ⑧ 分批执行(默认每批 500 行),每批独立提交,中断后重跑从剩余 NULL 行继续。
|
||||
--
|
||||
-- 范围说明:本脚本只做「手机号 → 档案唯一命中」这一条规则。
|
||||
-- 不引入 driver_accept_driver_id 推断,也不按姓名/历史号码猜归属:这些属于人工处置,在票面范围外。
|
||||
-- 幂等:命中映射先落存档表;重复执行时映射新增 0 行、回填更新 0 行,结果不变(第 2.2 节校验)。
|
||||
-- 执行方式(脚本里有 DELIMITER 与存储过程,按分号切句的 GUI 客户端会切错):
|
||||
-- mysql -h127.0.0.1 -uroot -p transport < blade_waybill_driver_id_backfill_20261028.sql
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 0. 存档(必须在订正之前执行;重复执行不会覆盖已有存档)
|
||||
-- ------------------------------------------------------------
|
||||
-- 存档表同时是第 1 节的「命中映射」:一行 = 一个待回填的运单 + 它唯一命中的档案 ID。
|
||||
-- 回填前先把映射定下来:回填只按映射写,跑之前就能看出「打算填谁」,回退也按它还原。
|
||||
CREATE TABLE IF NOT EXISTS blade_waybill_driver_id_bak_20261028 (
|
||||
waybill_id bigint NOT NULL COMMENT '运单ID',
|
||||
driver_id_before bigint DEFAULT NULL COMMENT '回填前的司机ID(本脚本只处理 NULL)',
|
||||
driver_id_after bigint DEFAULT NULL COMMENT '回填写入的司机档案ID(blade_transport_driver.id)',
|
||||
driver_phone varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '回填前的手机号快照,用来证明没有被改写',
|
||||
update_time_before datetime DEFAULT NULL COMMENT '回填前的更新时间,用来证明没有被改写',
|
||||
PRIMARY KEY (waybill_id),
|
||||
KEY idx_driver_id_after (driver_id_after)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='20261028 运单司机档案ID回填存档';
|
||||
|
||||
-- 命中映射:只收「号码唯一命中一条未删除档案」的 NULL 行。
|
||||
-- INSERT IGNORE + WHERE driver_id IS NULL:第二次执行不会重复建映射,已经填过的行不会再进映射。
|
||||
INSERT IGNORE INTO blade_waybill_driver_id_bak_20261028 (waybill_id, driver_id_before, driver_id_after, driver_phone, update_time_before)
|
||||
SELECT matched.biz_id, NULL, matched.driver_id, matched.driver_phone, matched.update_time
|
||||
FROM (
|
||||
SELECT w.id AS biz_id,
|
||||
MIN(d.id) AS driver_id,
|
||||
w.driver_phone AS driver_phone,
|
||||
w.update_time AS update_time
|
||||
FROM blade_waybill w
|
||||
JOIN blade_transport_driver d
|
||||
ON d.tenant_id COLLATE utf8mb4_0900_ai_ci = w.tenant_id
|
||||
AND d.mobile COLLATE utf8mb4_0900_ai_ci = w.driver_phone
|
||||
AND d.is_deleted = 0
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND w.driver_phone IS NOT NULL
|
||||
AND TRIM(w.driver_phone) <> ''
|
||||
AND w.driver_phone NOT LIKE '%*%'
|
||||
GROUP BY w.id, w.driver_phone, w.update_time
|
||||
HAVING COUNT(*) = 1
|
||||
) matched;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 1. 订正(分批回填,默认每批 500 行)
|
||||
-- ------------------------------------------------------------
|
||||
-- 循环退出条件是「这一批没有更新任何行」,所以重复执行时第一批就是 0 行,脚本空转结束。
|
||||
-- 每批单独提交(不开长事务):大表上不会长时间持锁,中断后重跑从剩余 NULL 行继续。
|
||||
DELIMITER $$
|
||||
DROP PROCEDURE IF EXISTS backfill_blade_waybill_driver_id_20261028$$
|
||||
CREATE PROCEDURE backfill_blade_waybill_driver_id_20261028(IN p_batch_size INT)
|
||||
BEGIN
|
||||
DECLARE v_batch_size INT DEFAULT 500;
|
||||
DECLARE v_affected INT DEFAULT 0;
|
||||
|
||||
IF p_batch_size IS NOT NULL AND p_batch_size > 0 THEN
|
||||
SET v_batch_size = p_batch_size;
|
||||
END IF;
|
||||
|
||||
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_waybill_driver_id_20261028 (
|
||||
waybill_id bigint NOT NULL,
|
||||
PRIMARY KEY (waybill_id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
REPEAT
|
||||
-- 取批:只取还空着的行,填过的行不再入批,所以每一批都在推进,不会原地打转
|
||||
DELETE FROM tmp_waybill_driver_id_20261028;
|
||||
|
||||
INSERT INTO tmp_waybill_driver_id_20261028 (waybill_id)
|
||||
SELECT b.waybill_id
|
||||
FROM blade_waybill_driver_id_bak_20261028 b
|
||||
JOIN blade_waybill w ON w.id = b.waybill_id
|
||||
WHERE w.driver_id IS NULL
|
||||
ORDER BY b.waybill_id
|
||||
LIMIT v_batch_size;
|
||||
|
||||
UPDATE blade_waybill w
|
||||
JOIN blade_waybill_driver_id_bak_20261028 b ON b.waybill_id = w.id
|
||||
JOIN tmp_waybill_driver_id_20261028 t ON t.waybill_id = w.id
|
||||
SET w.driver_id = b.driver_id_after
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND b.driver_id_after IS NOT NULL;
|
||||
|
||||
SET v_affected = ROW_COUNT();
|
||||
UNTIL v_affected = 0 END REPEAT;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_waybill_driver_id_20261028;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL backfill_blade_waybill_driver_id_20261028(500);
|
||||
DROP PROCEDURE IF EXISTS backfill_blade_waybill_driver_id_20261028;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 2. 校验(执行后必看;本节只有 SELECT,不动数据)
|
||||
-- ------------------------------------------------------------
|
||||
-- 2.1 规模:存档多少行、还剩多少 NULL 行、正数归属多少行(跑前跑后各查一次)
|
||||
-- remaining_null_rows 只会减少「本次命中」的那部分;差额应当等于第 2.7 节的命中行数。
|
||||
SELECT COUNT(*) AS archived_rows FROM blade_waybill_driver_id_bak_20261028;
|
||||
SELECT COUNT(*) AS remaining_null_rows FROM blade_waybill WHERE driver_id IS NULL;
|
||||
SELECT SUM(driver_id > 0) AS owned_rows,
|
||||
SUM(driver_id = -1) AS sentinel_rows,
|
||||
SUM(driver_id IS NULL) AS null_rows
|
||||
FROM blade_waybill;
|
||||
|
||||
-- 2.2 哨兵不变:跑前跑后各执行一次,四列必须完全相同(-1 一行都不能动)
|
||||
SELECT COUNT(*) AS sentinel_rows,
|
||||
SUM(id) AS sentinel_id_sum,
|
||||
MIN(id) AS min_id,
|
||||
MAX(id) AS max_id
|
||||
FROM blade_waybill
|
||||
WHERE driver_id = -1;
|
||||
|
||||
-- 2.3 写入正确:本次映射的行,driver_id 必须等于映射值(期望 0 行)
|
||||
SELECT COUNT(*) AS wrong_driver_id
|
||||
FROM blade_waybill_driver_id_bak_20261028 b
|
||||
JOIN blade_waybill w ON w.id = b.waybill_id
|
||||
WHERE NOT (w.driver_id <=> b.driver_id_after);
|
||||
|
||||
-- 2.4 快照没有被改写:本次映射的行,driver_phone 与 update_time 必须与存档逐行一致(期望 0 行)
|
||||
SELECT COUNT(*) AS snapshot_rewritten
|
||||
FROM blade_waybill_driver_id_bak_20261028 b
|
||||
JOIN blade_waybill w ON w.id = b.waybill_id
|
||||
WHERE NOT (w.driver_phone <=> b.driver_phone COLLATE utf8mb4_0900_ai_ci)
|
||||
OR NOT (w.update_time <=> b.update_time_before);
|
||||
|
||||
-- 2.5 冲突清单:同号多档案,这些行被跳过(期望 0 行;有输出就是「为什么没填」的答案)
|
||||
SELECT w.driver_phone AS conflicting_phone,
|
||||
COUNT(DISTINCT w.id) AS pending_rows,
|
||||
COUNT(DISTINCT d.id) AS driver_profiles,
|
||||
GROUP_CONCAT(DISTINCT d.id ORDER BY d.id) AS driver_profile_ids
|
||||
FROM blade_waybill w
|
||||
JOIN blade_transport_driver d
|
||||
ON d.tenant_id COLLATE utf8mb4_0900_ai_ci = w.tenant_id
|
||||
AND d.mobile COLLATE utf8mb4_0900_ai_ci = w.driver_phone
|
||||
AND d.is_deleted = 0
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND w.driver_phone IS NOT NULL
|
||||
AND TRIM(w.driver_phone) <> ''
|
||||
AND w.driver_phone NOT LIKE '%*%'
|
||||
GROUP BY w.driver_phone
|
||||
HAVING COUNT(DISTINCT d.id) > 1;
|
||||
|
||||
-- 2.6 无法匹配清单:号码在本租户内命中 0 条档案,这些行按规则跳过(等人工处置,脚本不猜)
|
||||
SELECT w.driver_phone AS unmatched_phone,
|
||||
COUNT(*) AS pending_rows
|
||||
FROM blade_waybill w
|
||||
WHERE w.driver_id IS NULL
|
||||
AND (w.driver_id = -1) IS NOT TRUE
|
||||
AND w.driver_phone IS NOT NULL
|
||||
AND TRIM(w.driver_phone) <> ''
|
||||
AND w.driver_phone NOT LIKE '%*%'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM blade_transport_driver d
|
||||
WHERE d.tenant_id COLLATE utf8mb4_0900_ai_ci = w.tenant_id
|
||||
AND d.mobile COLLATE utf8mb4_0900_ai_ci = w.driver_phone
|
||||
AND d.is_deleted = 0)
|
||||
GROUP BY w.driver_phone
|
||||
ORDER BY pending_rows DESC, unmatched_phone;
|
||||
|
||||
-- 2.7 逐司机对照:按手机号可见单量 vs 按档案 ID 可见单量
|
||||
-- by_phone = 号码快照等于该档案号码的单量(旧口径,改号/二次放号会串)
|
||||
-- by_driver_id = 档案 ID 等于该档案的单量(新口径)
|
||||
-- phone_only_null = 号码对上、但归属还空着的单(本次应被回填的那部分)
|
||||
-- phone_only_sentinel = 号码对上、但归属被标成 -1 无主的单(回填不碰,需要人工确认)
|
||||
-- phone_only_other_owner = 号码对上、却已归属另一条档案的单(改号/换司机后号码被复用)
|
||||
-- id_only_phone_differs = 已归属该档案、但号码快照不是该档案当前号码的单(改号后的历史快照)
|
||||
SELECT d.id AS driver_id,
|
||||
d.driver_name,
|
||||
d.mobile,
|
||||
(SELECT COUNT(*) FROM blade_waybill w WHERE w.driver_id = d.id) AS by_driver_id,
|
||||
(SELECT COUNT(*) FROM blade_waybill w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile) AS by_phone,
|
||||
(SELECT COUNT(*) FROM blade_waybill w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile
|
||||
AND w.driver_id IS NULL) AS phone_only_null,
|
||||
(SELECT COUNT(*) FROM blade_waybill w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile
|
||||
AND w.driver_id = -1) AS phone_only_sentinel,
|
||||
(SELECT COUNT(*) FROM blade_waybill w
|
||||
WHERE w.tenant_id COLLATE utf8mb4_0900_ai_ci = d.tenant_id
|
||||
AND w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile
|
||||
AND w.driver_id > 0
|
||||
AND w.driver_id <> d.id) AS phone_only_other_owner,
|
||||
(SELECT COUNT(*) FROM blade_waybill w
|
||||
WHERE w.driver_id = d.id
|
||||
AND (w.driver_phone IS NULL
|
||||
OR NOT (w.driver_phone COLLATE utf8mb4_0900_ai_ci = d.mobile))) AS id_only_phone_differs
|
||||
FROM blade_transport_driver d
|
||||
WHERE d.is_deleted = 0
|
||||
ORDER BY by_phone DESC, by_driver_id DESC, driver_id;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 3. 回退(仅在需要撤销本次回填时手工执行)
|
||||
-- ------------------------------------------------------------
|
||||
-- 按存档表把本次写入的行还原成 NULL。只还原「当前值仍等于本次写入值」的行:
|
||||
-- 回填之后又被人改派过的单不会被冲掉。
|
||||
-- UPDATE blade_waybill w
|
||||
-- JOIN blade_waybill_driver_id_bak_20261028 b ON b.waybill_id = w.id
|
||||
-- SET w.driver_id = b.driver_id_before
|
||||
-- WHERE w.driver_id <=> b.driver_id_after;
|
||||
-- 回退只碰 driver_id,不碰 driver_phone / update_time。备份表确认不再需要时再删:
|
||||
-- DROP TABLE blade_waybill_driver_id_bak_20261028;
|
||||
@@ -5620,6 +5620,7 @@ CREATE TABLE `blade_vehicle_maintenance_record` (
|
||||
`tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID',
|
||||
`vehicle_type` varchar(20) NOT NULL COMMENT '车船类型',
|
||||
`vehicle_no` varchar(30) NOT NULL COMMENT '车牌号/船号',
|
||||
`exception_id` bigint(20) DEFAULT NULL COMMENT '关联异常处置ID(小程序录入时写入)',
|
||||
`maintainer` varchar(20) DEFAULT NULL COMMENT '维修人',
|
||||
`maintenance_time` datetime NOT NULL COMMENT '维修时间',
|
||||
`location` varchar(100) DEFAULT NULL COMMENT '维修位置',
|
||||
@@ -5642,7 +5643,8 @@ CREATE TABLE `blade_vehicle_maintenance_record` (
|
||||
`is_deleted` int(11) DEFAULT '0' COMMENT '是否已删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_vehicle_maintenance_record_no` (`vehicle_no`) USING BTREE,
|
||||
KEY `idx_vehicle_maintenance_record_time` (`maintenance_time`) USING BTREE
|
||||
KEY `idx_vehicle_maintenance_record_time` (`maintenance_time`) USING BTREE,
|
||||
UNIQUE KEY `uk_vehicle_maintenance_record_exception` (`exception_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆维修记录';
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
Reference in New Issue
Block a user