```
fix(tenant): 修复私有化部署下的租户注册和引导配置逻辑 - 修改 PrivateDeploymentProperties 中的 tenantRegisterEnabled 方法, 在私有化模式下始终返回 false - 修改 PrivateDeploymentProperties 中的 tenantBootstrapEnabled 方法, 在私有化模式下始终返回 true - 在 SystemUpdateService 中注入 PrivateDeploymentProperties 依赖 - 添加 migrate_v20260527_consolidate_private_tenants 数据库迁移方法 - 实现私有化部署下合并多租户功能,保留最早租户并替换其余租户ID - 迁移涉及 t_app、t_operation_log、t_migrate_key 等表的数据 - 更新子账号的 parent_id 指向保留的租户 - 删除合并后多余的租户记录 ```
这个提交包含在:
父节点
db986808f2
当前提交
e3e16352d5
@ -23,12 +23,12 @@ public class PrivateDeploymentProperties {
|
||||
public String getMode() { return mode; }
|
||||
public void setMode(String mode) { this.mode = mode; }
|
||||
|
||||
public boolean isTenantRegisterEnabled() { return tenantRegisterEnabled; }
|
||||
public boolean isTenantRegisterEnabled() { return isPrivate() ? false : tenantRegisterEnabled; }
|
||||
public void setTenantRegisterEnabled(boolean tenantRegisterEnabled) {
|
||||
this.tenantRegisterEnabled = tenantRegisterEnabled;
|
||||
}
|
||||
|
||||
public boolean isTenantBootstrapEnabled() { return tenantBootstrapEnabled; }
|
||||
public boolean isTenantBootstrapEnabled() { return isPrivate() ? true : tenantBootstrapEnabled; }
|
||||
public void setTenantBootstrapEnabled(boolean tenantBootstrapEnabled) {
|
||||
this.tenantBootstrapEnabled = tenantBootstrapEnabled;
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.xuqm.tenant.config.PrivateDeploymentProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@ -47,9 +49,11 @@ public class SystemUpdateService {
|
||||
private String deployRoot;
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final PrivateDeploymentProperties deployProps;
|
||||
|
||||
public SystemUpdateService(DataSource dataSource) {
|
||||
public SystemUpdateService(DataSource dataSource, PrivateDeploymentProperties deployProps) {
|
||||
this.dataSource = dataSource;
|
||||
this.deployProps = deployProps;
|
||||
}
|
||||
|
||||
// ── 公开接口 ────────────────────────────────────────────────────────────────
|
||||
@ -409,6 +413,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_v20260601_add_app_extra_column(emit);
|
||||
|
||||
@ -509,6 +514,96 @@ public class SystemUpdateService {
|
||||
recordMigration(id, "push-service 和 license-service 新增操作日志表");
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有化部署下合并多租户:保留最早的租户,将其余租户的 tenant_id 全部替换,然后删除多余租户。
|
||||
* 仅在 DEPLOYMENT_MODE=PRIVATE 时执行。
|
||||
*/
|
||||
private void migrate_v20260527_consolidate_private_tenants(Consumer<String> emit) {
|
||||
if (!deployProps.isPrivate()) {
|
||||
return;
|
||||
}
|
||||
final String id = "v20260527_consolidate_private_tenants";
|
||||
if (migrationApplied(id)) {
|
||||
emit.accept(" [已应用] " + id);
|
||||
return;
|
||||
}
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
// 1. 找到最早的租户
|
||||
String keepId;
|
||||
try (PreparedStatement ps = conn.prepareStatement(
|
||||
"SELECT id FROM t_tenant ORDER BY created_at ASC LIMIT 1");
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
if (!rs.next()) {
|
||||
emit.accept(" [跳过] " + id + ": 无租户数据");
|
||||
recordMigration(id, "私有化合并多租户(无数据)");
|
||||
return;
|
||||
}
|
||||
keepId = rs.getString("id");
|
||||
}
|
||||
|
||||
// 2. 统计需要迁移的租户数
|
||||
int totalTenants;
|
||||
try (PreparedStatement ps = conn.prepareStatement("SELECT COUNT(*) FROM t_tenant");
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
rs.next();
|
||||
totalTenants = rs.getInt(1);
|
||||
}
|
||||
|
||||
if (totalTenants <= 1) {
|
||||
emit.accept(" [跳过] " + id + ": 仅 " + totalTenants + " 个租户,无需合并");
|
||||
recordMigration(id, "私有化合并多租户(仅" + totalTenants + "个)");
|
||||
return;
|
||||
}
|
||||
|
||||
emit.accept(" [合并] 保留最早租户 " + keepId + ",共 " + totalTenants + " 个租户");
|
||||
|
||||
// 3. 替换所有表中的 tenant_id
|
||||
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] + " != ?")) {
|
||||
ps.setString(1, keepId);
|
||||
ps.setString(2, keepId);
|
||||
int rows = ps.executeUpdate();
|
||||
if (rows > 0) {
|
||||
emit.accept(" " + tbl[0] + ": 更新 " + rows + " 行");
|
||||
totalUpdated += rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 + " 个子账号");
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 重启核心 ────────────────────────────────────────────────────────────────
|
||||
|
||||
private void restartAndSelfUpdate(Consumer<String> emit, String composeFile) {
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户