```
refactor(tenant): 优化系统更新服务和租户数据修复逻辑 - 添加版本文件更新功能,在更新完成后写入新版本号到 VERSION 文件 - 重命名租户数据修复方法,从 consolidate_private_tenants 改为 fix_orphan_tenant_data - 重构租户数据修复逻辑,支持多租户合并和孤儿数据修复两种场景 - 优化数据库操作,使用更精确的查询条件修复孤儿租户数据 - 改进迁移过程的日志输出和状态记录机制 ```
这个提交包含在:
父节点
e3e16352d5
当前提交
898597d6b6
@ -137,6 +137,21 @@ public class SystemUpdateService {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 更新完成后写入新版本号到 VERSION 文件。 */
|
||||||
|
private void bumpVersionFile(Consumer<String> 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<String> emit) {
|
public void runUpdate(Consumer<String> emit) {
|
||||||
String composeFile = deployRoot + "/docker-compose.yml";
|
String composeFile = deployRoot + "/docker-compose.yml";
|
||||||
@ -154,6 +169,8 @@ public class SystemUpdateService {
|
|||||||
exec(emit, "docker", "compose", "-f", composeFile, "pull", "--quiet", "tenant-service");
|
exec(emit, "docker", "compose", "-f", composeFile, "pull", "--quiet", "tenant-service");
|
||||||
emit.accept(">>> 镜像拉取完成");
|
emit.accept(">>> 镜像拉取完成");
|
||||||
|
|
||||||
|
bumpVersionFile(emit);
|
||||||
|
|
||||||
restartAndSelfUpdate(emit, composeFile);
|
restartAndSelfUpdate(emit, composeFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +430,7 @@ public class SystemUpdateService {
|
|||||||
|
|
||||||
migrate_v20260101_drop_device_id_unique_index(emit);
|
migrate_v20260101_drop_device_id_unique_index(emit);
|
||||||
migrate_v20260527_push_license_operation_logs(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);
|
// 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 时执行。
|
* 仅在 DEPLOYMENT_MODE=PRIVATE 时执行。
|
||||||
*/
|
*/
|
||||||
private void migrate_v20260527_consolidate_private_tenants(Consumer<String> emit) {
|
private void migrate_v20260527_fix_orphan_tenant_data(Consumer<String> emit) {
|
||||||
if (!deployProps.isPrivate()) {
|
if (!deployProps.isPrivate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String id = "v20260527_consolidate_private_tenants";
|
final String id = "v20260527_fix_orphan_tenant_data";
|
||||||
if (migrationApplied(id)) {
|
if (migrationApplied(id)) {
|
||||||
emit.accept(" [已应用] " + id);
|
emit.accept(" [已应用] " + id);
|
||||||
return;
|
return;
|
||||||
@ -541,63 +560,61 @@ public class SystemUpdateService {
|
|||||||
keepId = rs.getString("id");
|
keepId = rs.getString("id");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 统计需要迁移的租户数
|
boolean changed = false;
|
||||||
int totalTenants;
|
|
||||||
try (PreparedStatement ps = conn.prepareStatement("SELECT COUNT(*) FROM t_tenant");
|
// 2. 多租户合并:删除多余租户
|
||||||
|
List<String> extraTenants = new java.util.ArrayList<>();
|
||||||
|
try (PreparedStatement ps = conn.prepareStatement("SELECT id FROM t_tenant WHERE id != ?");
|
||||||
ResultSet rs = ps.executeQuery()) {
|
ResultSet rs = ps.executeQuery()) {
|
||||||
rs.next();
|
ps.setString(1, keepId);
|
||||||
totalTenants = rs.getInt(1);
|
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) {
|
// 3. 修复孤儿数据:tenant_id 不在 t_tenant 中的记录
|
||||||
emit.accept(" [跳过] " + id + ": 仅 " + totalTenants + " 个租户,无需合并");
|
|
||||||
recordMigration(id, "私有化合并多租户(仅" + totalTenants + "个)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit.accept(" [合并] 保留最早租户 " + keepId + ",共 " + totalTenants + " 个租户");
|
|
||||||
|
|
||||||
// 3. 替换所有表中的 tenant_id
|
|
||||||
String[][] tables = {
|
String[][] tables = {
|
||||||
{"t_app", "tenant_id"},
|
{"t_app", "tenant_id"},
|
||||||
{"t_operation_log", "tenant_id"},
|
{"t_operation_log", "tenant_id"},
|
||||||
{"t_migrate_key", "tenant_id"},
|
{"t_migrate_key", "tenant_id"},
|
||||||
};
|
};
|
||||||
int totalUpdated = 0;
|
|
||||||
for (String[] tbl : tables) {
|
for (String[] tbl : tables) {
|
||||||
try (PreparedStatement ps = conn.prepareStatement(
|
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(1, keepId);
|
||||||
ps.setString(2, keepId);
|
|
||||||
int rows = ps.executeUpdate();
|
int rows = ps.executeUpdate();
|
||||||
if (rows > 0) {
|
if (rows > 0) {
|
||||||
emit.accept(" " + tbl[0] + ": 更新 " + rows + " 行");
|
emit.accept(" " + tbl[0] + ": 修复 " + rows + " 条孤儿记录");
|
||||||
totalUpdated += rows;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 子账号的 parent_id 指向保留的租户
|
if (changed) {
|
||||||
try (PreparedStatement ps = conn.prepareStatement(
|
emit.accept(" [已迁移] " + id + ": 修复完成,保留租户 " + keepId);
|
||||||
"UPDATE t_tenant SET parent_id = ? WHERE parent_id IS NOT NULL AND parent_id != ?")) {
|
recordMigration(id, "私有化修复租户数据,保留 " + keepId);
|
||||||
ps.setString(1, keepId);
|
} else {
|
||||||
ps.setString(2, keepId);
|
emit.accept(" [跳过] " + id + ": 数据正常,无需修复");
|
||||||
int rows = ps.executeUpdate();
|
recordMigration(id, "私有化修复租户数据(无需修复)");
|
||||||
if (rows > 0) {
|
|
||||||
emit.accept(" t_tenant.parent_id: 更新 " + rows + " 个子账号");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
} catch (Exception e) {
|
||||||
emit.accept(" [错误] " + id + ": " + e.getMessage());
|
emit.accept(" [错误] " + id + ": " + e.getMessage());
|
||||||
log.error("migration {} failed", id, e);
|
log.error("migration {} failed", id, e);
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户