From 898597d6b6bf8d7c43ed61f93709ed2755bb305c Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Wed, 27 May 2026 19:14:45 +0800 Subject: [PATCH] =?UTF-8?q?```=20refactor(tenant):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=9B=B4=E6=96=B0=E6=9C=8D=E5=8A=A1=E5=92=8C?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E6=95=B0=E6=8D=AE=E4=BF=AE=E5=A4=8D=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加版本文件更新功能,在更新完成后写入新版本号到 VERSION 文件 - 重命名租户数据修复方法,从 consolidate_private_tenants 改为 fix_orphan_tenant_data - 重构租户数据修复逻辑,支持多租户合并和孤儿数据修复两种场景 - 优化数据库操作,使用更精确的查询条件修复孤儿租户数据 - 改进迁移过程的日志输出和状态记录机制 ``` --- .../tenant/service/SystemUpdateService.java | 103 ++++++++++-------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java b/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java index 38e373d..c145f3d 100644 --- a/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java +++ b/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java @@ -137,6 +137,21 @@ public class SystemUpdateService { return "unknown"; } + /** 更新完成后写入新版本号到 VERSION 文件。 */ + private void bumpVersionFile(Consumer emit) { + String oldVersion = readCurrentVersion(); + String newVersion = java.time.LocalDate.now().toString(); // yyyy-MM-dd + try { + Path versionFile = Paths.get(deployRoot, "VERSION"); + Files.createDirectories(versionFile.getParent()); + Files.writeString(versionFile, newVersion, + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + emit.accept(">>> 版本号: " + oldVersion + " → " + newVersion); + } catch (IOException e) { + emit.accept(" [警告] 写入 VERSION 文件失败: " + e.getMessage()); + } + } + /** 拉取最新镜像并重建所有容器。 */ public void runUpdate(Consumer emit) { String composeFile = deployRoot + "/docker-compose.yml"; @@ -154,6 +169,8 @@ public class SystemUpdateService { exec(emit, "docker", "compose", "-f", composeFile, "pull", "--quiet", "tenant-service"); emit.accept(">>> 镜像拉取完成"); + bumpVersionFile(emit); + restartAndSelfUpdate(emit, composeFile); } @@ -413,7 +430,7 @@ public class SystemUpdateService { migrate_v20260101_drop_device_id_unique_index(emit); migrate_v20260527_push_license_operation_logs(emit); - migrate_v20260527_consolidate_private_tenants(emit); + migrate_v20260527_fix_orphan_tenant_data(emit); // 新版本迁移在此追加,例如: // migrate_v20260601_add_app_extra_column(emit); @@ -515,14 +532,16 @@ public class SystemUpdateService { } /** - * 私有化部署下合并多租户:保留最早的租户,将其余租户的 tenant_id 全部替换,然后删除多余租户。 + * 私有化部署下修复租户数据: + * 1. 多个租户 → 保留最早的,删除其余 + * 2. 孤儿数据(tenant_id 不存在于 t_tenant)→ 指向唯一租户 * 仅在 DEPLOYMENT_MODE=PRIVATE 时执行。 */ - private void migrate_v20260527_consolidate_private_tenants(Consumer emit) { + private void migrate_v20260527_fix_orphan_tenant_data(Consumer emit) { if (!deployProps.isPrivate()) { return; } - final String id = "v20260527_consolidate_private_tenants"; + final String id = "v20260527_fix_orphan_tenant_data"; if (migrationApplied(id)) { emit.accept(" [已应用] " + id); return; @@ -541,63 +560,61 @@ public class SystemUpdateService { keepId = rs.getString("id"); } - // 2. 统计需要迁移的租户数 - int totalTenants; - try (PreparedStatement ps = conn.prepareStatement("SELECT COUNT(*) FROM t_tenant"); + boolean changed = false; + + // 2. 多租户合并:删除多余租户 + List extraTenants = new java.util.ArrayList<>(); + try (PreparedStatement ps = conn.prepareStatement("SELECT id FROM t_tenant WHERE id != ?"); ResultSet rs = ps.executeQuery()) { - rs.next(); - totalTenants = rs.getInt(1); + ps.setString(1, keepId); + while (rs.next()) { + extraTenants.add(rs.getString("id")); + } + } + if (!extraTenants.isEmpty()) { + emit.accept(" [合并] 保留最早租户 " + keepId + ",删除 " + extraTenants.size() + " 个多余租户"); + // 子账号 parent_id 指向保留的租户 + try (PreparedStatement ps = conn.prepareStatement( + "UPDATE t_tenant SET parent_id = ? WHERE parent_id IS NOT NULL AND parent_id != ?")) { + ps.setString(1, keepId); + ps.setString(2, keepId); + ps.executeUpdate(); + } + // 删除多余租户 + try (PreparedStatement ps = conn.prepareStatement("DELETE FROM t_tenant WHERE id != ?")) { + ps.setString(1, keepId); + int deleted = ps.executeUpdate(); + emit.accept(" 删除多余租户: " + deleted + " 个"); + } + changed = true; } - if (totalTenants <= 1) { - emit.accept(" [跳过] " + id + ": 仅 " + totalTenants + " 个租户,无需合并"); - recordMigration(id, "私有化合并多租户(仅" + totalTenants + "个)"); - return; - } - - emit.accept(" [合并] 保留最早租户 " + keepId + ",共 " + totalTenants + " 个租户"); - - // 3. 替换所有表中的 tenant_id + // 3. 修复孤儿数据:tenant_id 不在 t_tenant 中的记录 String[][] tables = { {"t_app", "tenant_id"}, {"t_operation_log", "tenant_id"}, {"t_migrate_key", "tenant_id"}, }; - int totalUpdated = 0; for (String[] tbl : tables) { try (PreparedStatement ps = conn.prepareStatement( - "UPDATE " + tbl[0] + " SET " + tbl[1] + " = ? WHERE " + tbl[1] + " != ?")) { + "UPDATE " + tbl[0] + " SET " + tbl[1] + " = ?" + + " WHERE " + tbl[1] + " NOT IN (SELECT id FROM t_tenant)")) { ps.setString(1, keepId); - ps.setString(2, keepId); int rows = ps.executeUpdate(); if (rows > 0) { - emit.accept(" " + tbl[0] + ": 更新 " + rows + " 行"); - totalUpdated += rows; + emit.accept(" " + tbl[0] + ": 修复 " + rows + " 条孤儿记录"); + changed = true; } } } - // 4. 子账号的 parent_id 指向保留的租户 - try (PreparedStatement ps = conn.prepareStatement( - "UPDATE t_tenant SET parent_id = ? WHERE parent_id IS NOT NULL AND parent_id != ?")) { - ps.setString(1, keepId); - ps.setString(2, keepId); - int rows = ps.executeUpdate(); - if (rows > 0) { - emit.accept(" t_tenant.parent_id: 更新 " + rows + " 个子账号"); - } + if (changed) { + emit.accept(" [已迁移] " + id + ": 修复完成,保留租户 " + keepId); + recordMigration(id, "私有化修复租户数据,保留 " + keepId); + } else { + emit.accept(" [跳过] " + id + ": 数据正常,无需修复"); + recordMigration(id, "私有化修复租户数据(无需修复)"); } - - // 5. 删除多余租户 - try (PreparedStatement ps = conn.prepareStatement( - "DELETE FROM t_tenant WHERE id != ?")) { - ps.setString(1, keepId); - int deleted = ps.executeUpdate(); - emit.accept(" 删除多余租户: " + deleted + " 个"); - } - - emit.accept(" [已迁移] " + id + ": 合并完成,保留 " + keepId); - recordMigration(id, "私有化合并多租户,保留最早租户 " + keepId); } catch (Exception e) { emit.accept(" [错误] " + id + ": " + e.getMessage()); log.error("migration {} failed", id, e);