Files
yunzerwebsiteallinone/go/docs/sql/fix_employee_tid_sync.sql
T

224 lines
10 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================================
-- 人员管理(yz_backend_employee) 与 用户管理(yz_system_tenant_user) 老数据对齐
--
-- 背景:员工表早期建的是「账号全局唯一」索引 uk_account(account),多租户后同一账号
-- 在不同企业只能存在一份员工档案;再叠加历史手工录入的人员(如 hero001 这类旧账号),
-- 导致「OA 设置 → 人员管理」与「基本设置 → 用户管理」两边数据对不上。
--
-- 处理内容:
-- 1. 索引调整为 (tid, account) 唯一 ← 必须最先执行,否则第 3 步会撞全局唯一键
-- 2. 员工 tid 为空/0:按唯一账号补 tid
-- 3. 同一企业内「用户缺员工 + 存在同名无账号老员工」:把老员工账号改成用户账号
-- (保留员工 ID,排班 / 日报 / 人事档案的历史引用不受影响)
-- 4. 用户仍然没有员工:按用户资料补建员工记录(与「用户管理 → 同步员工」同规则)
-- 5. 复核查询 + 仍需人工确认的清单
--
-- 反向缺口(「人员管理有、用户管理没有」)不在这里处理:补账号要先在统一认证建身份
-- 并生成 argon2id 密码哈希,SQL 做不到,用 Go 工具:
-- go run scripts/syncemployeeusers/main.go -tid <企业ID> -apply
--
-- 特性:可重复执行(幂等),第 2 次执行不会产生任何变更。
-- 执行前请务必备份:mysqldump -u root -p go-platform > backup_$(date +%F).sql
-- 执行后请重启 backend 服务:services/employee_sync.go 已改为按 (tid, account) 匹配员工。
-- =============================================================================
-- -----------------------------------------------------------------------------
-- 0. 执行前预检(只看不改)
-- -----------------------------------------------------------------------------
SELECT '执行前:用户/员工数量' AS section;
SELECT
(SELECT COUNT(*) FROM yz_system_tenant_user WHERE delete_time IS NULL) AS user_rows,
(SELECT COUNT(*) FROM yz_backend_employee WHERE delete_time IS NULL) AS employee_rows,
(SELECT COUNT(*) FROM yz_backend_employee WHERE delete_time IS NULL AND (tid IS NULL OR tid = 0)) AS employee_tid_empty,
(SELECT COUNT(*) FROM yz_system_tenant_user u
WHERE u.delete_time IS NULL AND u.account IS NOT NULL AND TRIM(u.account) <> ''
AND NOT EXISTS (SELECT 1 FROM yz_backend_employee e
WHERE e.delete_time IS NULL AND e.tid = u.tid AND TRIM(e.account) = TRIM(u.account))
) AS user_without_employee;
-- 若下面这条 > 0,需要先人工处理重复的 (tid, account),否则第 1 步加唯一索引会失败
SELECT '重复的 (tid, account)' AS section;
SELECT tid, TRIM(account) AS account, COUNT(*) AS cnt
FROM yz_backend_employee
WHERE delete_time IS NULL
GROUP BY tid, TRIM(account)
HAVING COUNT(*) > 1;
-- -----------------------------------------------------------------------------
-- 1. 索引:账号全局唯一 → 企业内唯一
-- 关键:不先放开全局唯一,第 3 步把 hero001 改成 hero920103 会因另一企业已占用而失败
-- -----------------------------------------------------------------------------
SET @has_old := (SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'yz_backend_employee'
AND INDEX_NAME = 'uk_account');
SET @sql := IF(@has_old > 0, 'ALTER TABLE yz_backend_employee DROP INDEX uk_account', 'DO 0');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @has_plain := (SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'yz_backend_employee'
AND INDEX_NAME = 'idx_tid_account');
SET @sql := IF(@has_plain > 0, 'ALTER TABLE yz_backend_employee DROP INDEX idx_tid_account', 'DO 0');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @has_uk := (SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'yz_backend_employee'
AND INDEX_NAME = 'uk_tid_account' AND NON_UNIQUE = 0);
SET @sql := IF(@has_uk = 0,
'ALTER TABLE yz_backend_employee ADD UNIQUE KEY uk_tid_account (tid, account)',
'DO 0');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- -----------------------------------------------------------------------------
-- 2. 员工 tid 为空/0:按账号唯一匹配到企业用户后补 tid
-- -----------------------------------------------------------------------------
UPDATE yz_backend_employee e
JOIN (
SELECT TRIM(account) AS account, MIN(tid) AS tid
FROM yz_system_tenant_user
WHERE delete_time IS NULL AND account IS NOT NULL AND TRIM(account) <> ''
GROUP BY TRIM(account)
HAVING COUNT(DISTINCT tid) = 1
) u ON u.account = TRIM(e.account)
SET e.tid = u.tid,
e.update_time = NOW()
WHERE e.delete_time IS NULL
AND (e.tid IS NULL OR e.tid = 0);
-- -----------------------------------------------------------------------------
-- 3. 同企业内同名老员工账号对齐
-- 条件:① 员工账号在本企业没有对应用户(历史手工人员)
-- ② 该用户在本企业还没有员工
-- ③ 按姓名能唯一匹配到一个用户
-- 只改 account,不动 id,历史引用全部保留
-- -----------------------------------------------------------------------------
DROP TEMPORARY TABLE IF EXISTS tmp_emp_align;
CREATE TEMPORARY TABLE tmp_emp_align (
emp_id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
tid BIGINT NOT NULL,
old_account VARCHAR(64) NOT NULL,
new_account VARCHAR(64) NOT NULL
) ENGINE = InnoDB;
INSERT INTO tmp_emp_align (emp_id, tid, old_account, new_account)
SELECT e.id, e.tid, TRIM(e.account), MIN(TRIM(u.account))
FROM yz_backend_employee e
JOIN yz_system_tenant_user u
ON u.tid = e.tid
AND u.delete_time IS NULL
AND u.account IS NOT NULL
AND TRIM(u.account) <> ''
AND TRIM(u.name) = TRIM(e.name)
LEFT JOIN yz_system_tenant_user ux
ON ux.tid = e.tid
AND ux.delete_time IS NULL
AND TRIM(ux.account) = TRIM(e.account)
LEFT JOIN yz_backend_employee ex
ON ex.tid = e.tid
AND ex.delete_time IS NULL
AND TRIM(ex.account) = TRIM(u.account)
WHERE e.delete_time IS NULL
AND e.tid IS NOT NULL
AND e.tid > 0
AND ux.id IS NULL -- 员工账号在本企业没有用户
AND ex.id IS NULL -- 用户在本企业还没有员工
GROUP BY e.id, e.tid, TRIM(e.account)
HAVING COUNT(DISTINCT TRIM(u.account)) = 1; -- 同名用户不唯一时不动,留待人工
UPDATE yz_backend_employee e
JOIN tmp_emp_align t ON t.emp_id = e.id
SET e.account = t.new_account,
e.update_time = NOW();
SELECT '第 3 步:对齐的老员工账号' AS section, emp_id, tid, old_account, new_account
FROM tmp_emp_align;
DROP TEMPORARY TABLE IF EXISTS tmp_emp_align;
-- -----------------------------------------------------------------------------
-- 4. 用户仍然没有员工:按用户资料补建员工记录
-- 与 services/EnsureEmployeeForTenantUser 的规则一致(账号、姓名、性别、生日、
-- 部门、手机、邮箱、启用状态),密码留空(登录走统一认证,不校验员工密码)
-- -----------------------------------------------------------------------------
INSERT INTO yz_backend_employee
(tid, account, password, name, gender, birthday, department, phone, email,
account_status, create_time, update_time)
SELECT
u.tid,
TRIM(u.account),
'',
COALESCE(NULLIF(TRIM(u.name), ''), TRIM(u.account)),
IFNULL(u.sex, 0),
STR_TO_DATE(LEFT(TRIM(IFNULL(u.birth, '')), 10), '%Y-%m-%d'),
CASE WHEN IFNULL(u.org_id, 0) > 0 THEN CAST(u.org_id AS CHAR) ELSE NULL END,
NULLIF(TRIM(IFNULL(u.phone, '')), ''),
NULLIF(TRIM(IFNULL(u.email, '')), ''),
IF(IFNULL(u.status, 1) = 0, 0, 1),
NOW(), NOW()
FROM yz_system_tenant_user u
LEFT JOIN yz_backend_employee e
ON e.delete_time IS NULL
AND e.tid = u.tid
AND TRIM(e.account) = TRIM(u.account)
WHERE u.delete_time IS NULL
AND u.account IS NOT NULL
AND TRIM(u.account) <> ''
AND e.id IS NULL;
-- -----------------------------------------------------------------------------
-- 5. 复核
-- -----------------------------------------------------------------------------
SELECT '执行后:用户/员工数量' AS section;
SELECT
(SELECT COUNT(*) FROM yz_system_tenant_user WHERE delete_time IS NULL) AS user_rows,
(SELECT COUNT(*) FROM yz_backend_employee WHERE delete_time IS NULL) AS employee_rows,
(SELECT COUNT(*) FROM yz_backend_employee WHERE delete_time IS NULL AND (tid IS NULL OR tid = 0)) AS employee_tid_empty,
(SELECT COUNT(*) FROM yz_system_tenant_user u
WHERE u.delete_time IS NULL AND u.account IS NOT NULL AND TRIM(u.account) <> ''
AND NOT EXISTS (SELECT 1 FROM yz_backend_employee e
WHERE e.delete_time IS NULL AND e.tid = u.tid AND TRIM(e.account) = TRIM(u.account))
) AS user_without_employee;
-- 按企业对比:用户数 / 员工数 / 本企业无账号的员工数
SELECT
u.tid,
u.user_cnt,
e.emp_cnt,
e.emp_without_user
FROM (
SELECT tid, COUNT(*) AS user_cnt
FROM yz_system_tenant_user
WHERE delete_time IS NULL
GROUP BY tid
) u
LEFT JOIN (
SELECT x.tid,
COUNT(*) AS emp_cnt,
SUM(CASE WHEN ux.id IS NULL THEN 1 ELSE 0 END) AS emp_without_user
FROM yz_backend_employee x
LEFT JOIN yz_system_tenant_user ux
ON ux.tid = x.tid
AND ux.delete_time IS NULL
AND TRIM(ux.account) = TRIM(x.account)
WHERE x.delete_time IS NULL
GROUP BY x.tid
) e ON e.tid = u.tid
ORDER BY u.tid;
-- -----------------------------------------------------------------------------
-- 6. 仍需人工确认:员工在本企业没有登录账号(老数据,脚本不删除也不改)
-- 处理方式二选一:
-- a) 在「用户管理」用相同账号添加用户 → 会自动关联到已有员工档案,不会重复建档
-- b) 在「人员管理」把这些人员设为离职(account_status = 2)
-- -----------------------------------------------------------------------------
SELECT '仍无登录账号的员工' AS section,
e.tid, e.id, TRIM(e.account) AS account, TRIM(e.name) AS name,
TRIM(IFNULL(e.department, '')) AS department
FROM yz_backend_employee e
LEFT JOIN yz_system_tenant_user u
ON u.tid = e.tid
AND u.delete_time IS NULL
AND TRIM(u.account) = TRIM(e.account)
WHERE e.delete_time IS NULL
AND u.id IS NULL
ORDER BY e.tid, e.id;