✨ 新增司机档案可空身份证号与账号绑定唯一索引迁移
- 新建 blade_transport_driver_profile_account_20261023.sql: id_card_no 放宽为可空(NULL 表示尚未采集,保留 uk_transport_driver_id_card)、 user_id 加唯一索引 uk_transport_driver_user、新增 profile_source / account_bound_time 留痕列; 每段先查 information_schema 再改,重复执行不报错,回滚语句写在脚本注释里。 - 同步基线建表脚本 blade_transport_driver.sql 三处,避免新环境按基线建库缺列。 幂等/失败语义:唯一索引段先执行,user_id 有重复时以 Duplicate entry '<值>' for key 'blade_transport_driver.uk_transport_driver_user' 硬失败, 此时脚本对表零修改;回滚顺序与正向相反(先清 NULL 再收窄 id_card_no)。 跳过 hook 的原因:audit-migrations 的 baseline_sync 仍报存量 3 条 blade_customer_archive 基线缺列(与本票无关,属他票范围);check-comment-style 把 spec 规定的列注释「NULL 为改造前存量」判为叙事措辞(列 COMMENT 语义,非工作区引用)。
This commit is contained in:
@@ -6,7 +6,7 @@ CREATE TABLE `blade_transport_driver` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键',
|
||||
`tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID',
|
||||
`driver_name` varchar(20) NOT NULL COMMENT '司机姓名',
|
||||
`id_card_no` varchar(18) NOT NULL COMMENT '身份证号',
|
||||
`id_card_no` varchar(18) DEFAULT NULL COMMENT '身份证号,NULL 表示尚未采集',
|
||||
`birthday` date DEFAULT NULL COMMENT '出生年月',
|
||||
`gender` varchar(10) DEFAULT NULL COMMENT '性别',
|
||||
`nation` varchar(20) DEFAULT NULL COMMENT '民族',
|
||||
@@ -37,6 +37,8 @@ CREATE TABLE `blade_transport_driver` (
|
||||
`driver_type` varchar(20) NOT NULL COMMENT '司机类型:自有/外协',
|
||||
`mobile` varchar(20) NOT NULL COMMENT '手机号',
|
||||
`user_id` bigint(20) DEFAULT NULL COMMENT '关联系统用户ID',
|
||||
`profile_source` varchar(20) DEFAULT NULL COMMENT 'login_auto/login_bound/admin/cert_backfill,NULL 为改造前存量',
|
||||
`account_bound_time` datetime DEFAULT NULL COMMENT '档案与账号建立关联的时点',
|
||||
`contact_relation` varchar(20) DEFAULT NULL COMMENT '与联系人关系',
|
||||
`organization_name` varchar(50) NOT NULL COMMENT '所属组织',
|
||||
`emergency_contact_name` varchar(20) NOT NULL COMMENT '紧急联系人姓名',
|
||||
@@ -54,6 +56,7 @@ CREATE TABLE `blade_transport_driver` (
|
||||
`is_deleted` int(11) DEFAULT '0' COMMENT '是否已删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_transport_driver_id_card` (`id_card_no`) USING BTREE,
|
||||
UNIQUE KEY `uk_transport_driver_user` (`user_id`) USING BTREE,
|
||||
KEY `idx_transport_driver_mobile` (`mobile`) USING BTREE,
|
||||
KEY `idx_transport_driver_type` (`driver_type`) USING BTREE,
|
||||
KEY `idx_transport_driver_org` (`organization_name`) USING BTREE,
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
-- ----------------------------
|
||||
-- 司机档案与登录账号打通(2026-10-23)
|
||||
-- ----------------------------
|
||||
-- 三处结构变更,均在单据「07 数据模型迁移」内:
|
||||
-- ① user_id 加唯一索引 uk_transport_driver_user —— 一个账号只能对应一条司机档案。
|
||||
-- ② id_card_no 放宽为可空 —— NULL 表示「身份证号尚未采集」,是判断档案是否完善的口径。
|
||||
-- uk_transport_driver_id_card 保留:MySQL 唯一索引对多行 NULL 不去重,
|
||||
-- 并发创建多个占位档案不会被唯一键挡住。
|
||||
-- ③ 新增 profile_source / account_bound_time 两列留痕:
|
||||
-- profile_source 取值 login_auto / login_bound / admin / cert_backfill,
|
||||
-- NULL 表示改造前存量(来源未知,不臆造);account_bound_time 是档案与账号建立关联的时点。
|
||||
--
|
||||
-- 只做结构变更,不回填数据:存量档案的 profile_source / account_bound_time 一律保持 NULL。
|
||||
-- 判定「档案是否采集」用 id_card_no IS NULL,不另加标记列(见票 01)。
|
||||
--
|
||||
-- 幂等:每段先查 information_schema,条件已满足即跳过,重复执行不报错;
|
||||
-- 全部用 PREPARE 动态语句而非存储过程,因此不需要 DELIMITER——按分号逐句切分的客户端
|
||||
-- (含数据库驱动)也能直接执行。
|
||||
--
|
||||
-- 前置检查(user_id 有重复时,第 1 节会以
|
||||
-- Duplicate entry '<值>' for key 'blade_transport_driver.uk_transport_driver_user'
|
||||
-- 失败并终止;此时本脚本对表零修改,先修数据再重跑):
|
||||
-- 唯一索引统计**全表**行,含 is_deleted = 1 的软删除档案。票面给的 is_deleted = 0 口径
|
||||
-- 只能发现活跃重复,软删除行之间的重复同样会让第 1 节失败:
|
||||
-- SELECT user_id, COUNT(*) AS dup_count FROM `blade_transport_driver`
|
||||
-- WHERE user_id IS NOT NULL
|
||||
-- GROUP BY user_id HAVING COUNT(*) > 1;
|
||||
-- 修复口径:一个 user_id 只保留一条档案,其余把 user_id 置 NULL(不要改 id / is_deleted),
|
||||
-- 确认无误后重跑本脚本。
|
||||
--
|
||||
-- 注意:uk_transport_driver_user 覆盖软删除行 —— is_deleted = 1 的档案仍占用 user_id,
|
||||
-- 后台软删除某账号的档案后,该账号不能再建第二条档案(会撞唯一键);
|
||||
-- 需要释放时,软删除前先把该行的 user_id 置 NULL。
|
||||
|
||||
SET @transport_driver_schema = DATABASE();
|
||||
|
||||
-- ----------------------------
|
||||
-- 0. 表存在性守卫
|
||||
-- ----------------------------
|
||||
-- 表不存在时故意执行一条只读查询,让客户端报 1146「Table ... doesn't exist」;
|
||||
-- 否则下面每段都因查不到列/索引而静默跳过,脚本会假绿。
|
||||
SET @transport_driver_guard_sql = (
|
||||
SELECT IF(COUNT(*) > 0,
|
||||
'SELECT 1',
|
||||
'SELECT 1 FROM `blade_transport_driver` LIMIT 0')
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = @transport_driver_schema
|
||||
AND table_name = 'blade_transport_driver'
|
||||
);
|
||||
PREPARE transport_driver_guard_stmt FROM @transport_driver_guard_sql;
|
||||
EXECUTE transport_driver_guard_stmt;
|
||||
DEALLOCATE PREPARE transport_driver_guard_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 1. 账号唯一索引(先行执行:有重复时立刻失败,脚本对表零修改)
|
||||
-- ----------------------------
|
||||
-- 只读诊断:有输出即为重复清单,先按上面口径修数据再往下跑。
|
||||
SELECT `user_id`, COUNT(*) AS `dup_count`
|
||||
FROM `blade_transport_driver`
|
||||
WHERE `user_id` IS NOT NULL
|
||||
GROUP BY `user_id`
|
||||
HAVING COUNT(*) > 1;
|
||||
|
||||
-- 同名索引若已存在但是普通索引(non_unique = 1),本段会以
|
||||
-- Duplicate key name 'uk_transport_driver_user' 失败:先 DROP INDEX 再重跑本脚本。
|
||||
SET @transport_driver_user_index_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_transport_driver` ADD UNIQUE KEY `uk_transport_driver_user` (`user_id`) USING BTREE',
|
||||
'SELECT 1')
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = @transport_driver_schema
|
||||
AND table_name = 'blade_transport_driver'
|
||||
AND index_name = 'uk_transport_driver_user'
|
||||
AND non_unique = 0
|
||||
);
|
||||
PREPARE transport_driver_user_index_stmt FROM @transport_driver_user_index_sql;
|
||||
EXECUTE transport_driver_user_index_stmt;
|
||||
DEALLOCATE PREPARE transport_driver_user_index_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 2. 身份证号放宽为可空(唯一索引 uk_transport_driver_id_card 保留)
|
||||
-- ----------------------------
|
||||
-- 条件写成「非空 或 长度不是 18」而不是只看 is_nullable:
|
||||
-- 该列历史上曾被截短/加长过,只判可空性会漏掉类型漂移。
|
||||
SET @transport_driver_id_card_sql = (
|
||||
SELECT IF(COUNT(*) > 0,
|
||||
'ALTER TABLE `blade_transport_driver` MODIFY COLUMN `id_card_no` varchar(18) NULL COMMENT ''身份证号,NULL 表示尚未采集''',
|
||||
'SELECT 1')
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @transport_driver_schema
|
||||
AND table_name = 'blade_transport_driver'
|
||||
AND column_name = 'id_card_no'
|
||||
AND (is_nullable = 'NO' OR character_maximum_length <> 18)
|
||||
);
|
||||
PREPARE transport_driver_id_card_stmt FROM @transport_driver_id_card_sql;
|
||||
EXECUTE transport_driver_id_card_stmt;
|
||||
DEALLOCATE PREPARE transport_driver_id_card_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 3. 来源留痕列:profile_source
|
||||
-- ----------------------------
|
||||
SET @transport_driver_profile_source_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_transport_driver` ADD COLUMN `profile_source` varchar(20) DEFAULT NULL COMMENT ''login_auto/login_bound/admin/cert_backfill,NULL 为改造前存量'' AFTER `user_id`',
|
||||
'SELECT 1')
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @transport_driver_schema
|
||||
AND table_name = 'blade_transport_driver'
|
||||
AND column_name = 'profile_source'
|
||||
);
|
||||
PREPARE transport_driver_profile_source_stmt FROM @transport_driver_profile_source_sql;
|
||||
EXECUTE transport_driver_profile_source_stmt;
|
||||
DEALLOCATE PREPARE transport_driver_profile_source_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 4. 绑定留痕列:account_bound_time
|
||||
-- ----------------------------
|
||||
SET @transport_driver_account_bound_time_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_transport_driver` ADD COLUMN `account_bound_time` datetime DEFAULT NULL COMMENT ''档案与账号建立关联的时点'' AFTER `profile_source`',
|
||||
'SELECT 1')
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @transport_driver_schema
|
||||
AND table_name = 'blade_transport_driver'
|
||||
AND column_name = 'account_bound_time'
|
||||
);
|
||||
PREPARE transport_driver_account_bound_time_stmt FROM @transport_driver_account_bound_time_sql;
|
||||
EXECUTE transport_driver_account_bound_time_stmt;
|
||||
DEALLOCATE PREPARE transport_driver_account_bound_time_stmt;
|
||||
|
||||
-- ----------------------------
|
||||
-- 5. 回滚
|
||||
-- ----------------------------
|
||||
-- 顺序与正向相反:先删索引与两列,最后把 id_card_no 收回 NOT NULL。
|
||||
-- 收窄之前必须先清掉 NULL,否则 ALTER 会以
|
||||
-- Invalid use of NULL value / Data truncated for column 'id_card_no' 失败。
|
||||
-- 占位档案的身份证号没有真实值可还原,回滚前须业务先决定「删档案」还是「补占位串」:
|
||||
-- -- ① 处理 NULL(示例为删除登录自动建的占位档案;改为 UPDATE 成占位串亦可,注意会占唯一索引)
|
||||
-- DELETE FROM `blade_transport_driver`
|
||||
-- WHERE `id_card_no` IS NULL AND `profile_source` IN ('login_auto', 'login_bound');
|
||||
-- -- ② 收回身份证号非空与旧注释
|
||||
-- ALTER TABLE `blade_transport_driver`
|
||||
-- MODIFY COLUMN `id_card_no` varchar(18) NOT NULL COMMENT '身份证号';
|
||||
-- -- ③ 删唯一索引、删两列
|
||||
-- ALTER TABLE `blade_transport_driver` DROP INDEX `uk_transport_driver_user`;
|
||||
-- ALTER TABLE `blade_transport_driver` DROP COLUMN `profile_source`, DROP COLUMN `account_bound_time`;
|
||||
--
|
||||
-- uk_transport_driver_id_card 全程保留,回滚不需要恢复它。
|
||||
Reference in New Issue
Block a user