feat(database): 新增 push 和 license 操作日志表
- 在 tenant-service 的系统更新服务中添加新的数据库迁移方法 - 为 push-service 创建 PushSchemaMigrationService 并实现数据库迁移逻辑 - 为 license-service 创建 LicenseSchemaMigrationService 并实现数据库迁移逻辑 - 创建 push_operation_log 表用于记录推送服务操作日志 - 创建 license_operation_log 表用于记录授权服务操作日志 - 实现数据库迁移记录表 _schema_migrations 以跟踪迁移状态 - 添加迁移验证和错误处理机制确保迁移过程可靠性
这个提交包含在:
父节点
73dd4814f2
当前提交
db986808f2
@ -0,0 +1,113 @@
|
||||
package com.xuqm.license.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Service
|
||||
public class LicenseSchemaMigrationService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LicenseSchemaMigrationService.class);
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
public LicenseSchemaMigrationService(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onStartup() {
|
||||
runSchemaMigrations(msg -> log.info("[license-migration] {}", msg));
|
||||
}
|
||||
|
||||
public void runSchemaMigrations(Consumer<String> emit) {
|
||||
emit.accept("检查数据库迁移...");
|
||||
try {
|
||||
ensureMigrationsTable();
|
||||
} catch (Exception e) {
|
||||
emit.accept("[警告] 无法初始化迁移记录表: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
migrate_v20260527_create_license_operation_log(emit);
|
||||
|
||||
emit.accept("数据库迁移检查完成");
|
||||
}
|
||||
|
||||
private void ensureMigrationsTable() throws Exception {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("""
|
||||
CREATE TABLE IF NOT EXISTS _schema_migrations (
|
||||
id VARCHAR(128) NOT NULL PRIMARY KEY,
|
||||
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
description VARCHAR(255)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean migrationApplied(String id) {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(
|
||||
"SELECT COUNT(*) FROM _schema_migrations WHERE id = ?")) {
|
||||
ps.setString(1, id);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
return rs.next() && rs.getInt(1) > 0;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("check migration {} failed: {}", id, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void recordMigration(String id, String description) {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(
|
||||
"INSERT IGNORE INTO _schema_migrations (id, description) VALUES (?, ?)")) {
|
||||
ps.setString(1, id);
|
||||
ps.setString(2, description);
|
||||
ps.executeUpdate();
|
||||
} catch (Exception e) {
|
||||
log.warn("record migration {} failed: {}", id, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── 各版本迁移 ──────────────────────────────────────────────────────────────
|
||||
|
||||
private void migrate_v20260527_create_license_operation_log(Consumer<String> emit) {
|
||||
final String id = "v20260527_create_license_operation_log";
|
||||
if (migrationApplied(id)) {
|
||||
emit.accept("[已应用] " + id);
|
||||
return;
|
||||
}
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("""
|
||||
CREATE TABLE IF NOT EXISTS license_operation_log (
|
||||
id VARCHAR(36) NOT NULL PRIMARY KEY,
|
||||
app_key VARCHAR(64) NOT NULL,
|
||||
operator VARCHAR(128) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
resource_type VARCHAR(64) NOT NULL,
|
||||
resource_id VARCHAR(128),
|
||||
summary VARCHAR(255),
|
||||
detail_json TEXT,
|
||||
created_at DATETIME NOT NULL,
|
||||
INDEX idx_license_op_log_app_time (app_key, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""");
|
||||
emit.accept("[已迁移] " + id + ": 创建 license_operation_log 表");
|
||||
recordMigration(id, "创建授权操作日志表");
|
||||
} catch (Exception e) {
|
||||
emit.accept("[错误] " + id + ": " + e.getMessage());
|
||||
log.error("migration {} failed", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
package com.xuqm.push.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Service
|
||||
public class PushSchemaMigrationService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PushSchemaMigrationService.class);
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
public PushSchemaMigrationService(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onStartup() {
|
||||
runSchemaMigrations(msg -> log.info("[push-migration] {}", msg));
|
||||
}
|
||||
|
||||
public void runSchemaMigrations(Consumer<String> emit) {
|
||||
emit.accept("检查数据库迁移...");
|
||||
try {
|
||||
ensureMigrationsTable();
|
||||
} catch (Exception e) {
|
||||
emit.accept("[警告] 无法初始化迁移记录表: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
migrate_v20260527_create_push_operation_log(emit);
|
||||
|
||||
emit.accept("数据库迁移检查完成");
|
||||
}
|
||||
|
||||
private void ensureMigrationsTable() throws Exception {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("""
|
||||
CREATE TABLE IF NOT EXISTS _schema_migrations (
|
||||
id VARCHAR(128) NOT NULL PRIMARY KEY,
|
||||
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
description VARCHAR(255)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean migrationApplied(String id) {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(
|
||||
"SELECT COUNT(*) FROM _schema_migrations WHERE id = ?")) {
|
||||
ps.setString(1, id);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
return rs.next() && rs.getInt(1) > 0;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("check migration {} failed: {}", id, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void recordMigration(String id, String description) {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(
|
||||
"INSERT IGNORE INTO _schema_migrations (id, description) VALUES (?, ?)")) {
|
||||
ps.setString(1, id);
|
||||
ps.setString(2, description);
|
||||
ps.executeUpdate();
|
||||
} catch (Exception e) {
|
||||
log.warn("record migration {} failed: {}", id, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── 各版本迁移 ──────────────────────────────────────────────────────────────
|
||||
|
||||
private void migrate_v20260527_create_push_operation_log(Consumer<String> emit) {
|
||||
final String id = "v20260527_create_push_operation_log";
|
||||
if (migrationApplied(id)) {
|
||||
emit.accept("[已应用] " + id);
|
||||
return;
|
||||
}
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("""
|
||||
CREATE TABLE IF NOT EXISTS push_operation_log (
|
||||
id VARCHAR(36) NOT NULL PRIMARY KEY,
|
||||
app_key VARCHAR(64) NOT NULL,
|
||||
operator VARCHAR(128) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
resource_type VARCHAR(64) NOT NULL,
|
||||
resource_id VARCHAR(128),
|
||||
summary VARCHAR(255),
|
||||
detail TEXT,
|
||||
created_at DATETIME NOT NULL,
|
||||
INDEX idx_push_op_log_app_time (app_key, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
""");
|
||||
emit.accept("[已迁移] " + id + ": 创建 push_operation_log 表");
|
||||
recordMigration(id, "创建推送操作日志表");
|
||||
} catch (Exception e) {
|
||||
emit.accept("[错误] " + id + ": " + e.getMessage());
|
||||
log.error("migration {} failed", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -408,6 +408,7 @@ public class SystemUpdateService {
|
||||
}
|
||||
|
||||
migrate_v20260101_drop_device_id_unique_index(emit);
|
||||
migrate_v20260527_push_license_operation_logs(emit);
|
||||
// 新版本迁移在此追加,例如:
|
||||
// migrate_v20260601_add_app_extra_column(emit);
|
||||
|
||||
@ -494,6 +495,20 @@ public class SystemUpdateService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* push-service 和 license-service 新增操作日志表(push_operation_log / license_operation_log)。
|
||||
* 实际表结构由各服务的 SchemaMigrationService 在各自数据库中创建,此处仅记录版本标记。
|
||||
*/
|
||||
private void migrate_v20260527_push_license_operation_logs(Consumer<String> emit) {
|
||||
final String id = "v20260527_push_license_operation_logs";
|
||||
if (migrationApplied(id)) {
|
||||
emit.accept(" [已应用] " + id);
|
||||
return;
|
||||
}
|
||||
emit.accept(" [已迁移] " + id + ": push/license 操作日志表已由各服务自行创建");
|
||||
recordMigration(id, "push-service 和 license-service 新增操作日志表");
|
||||
}
|
||||
|
||||
// ── 重启核心 ────────────────────────────────────────────────────────────────
|
||||
|
||||
private void restartAndSelfUpdate(Consumer<String> emit, String composeFile) {
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户