HaniBlindBox/server/scripts/migrate_admins.js
2026-01-02 05:18:05 +08:00

190 lines
6.4 KiB
JavaScript

/**
* 管理员数据迁移脚本 - Node.js
* Feature: database-migration, Property 2: 数据记录数一致性
* Validates: Requirements 10.1
*
* 源表: MySQL admin (3 条记录)
* 目标表: SQL Server admins
*
* 注意: 管理日志表(admin_login_logs, admin_operation_logs)仅保留结构,不迁移数据
*/
const mysql = require('mysql2/promise');
const sql = require('mssql');
// MySQL 配置
const mysqlConfig = {
host: '192.168.195.16',
port: 1887,
user: 'root',
password: 'Dbt@com@123',
database: 'youdas',
charset: 'utf8mb4'
};
// SQL Server 配置
const sqlServerConfig = {
server: '192.168.195.15',
port: 1433,
user: 'sa',
password: 'Dbt@com@123',
database: 'honey_box',
options: {
encrypt: false,
trustServerCertificate: true
}
};
// Unix时间戳转换为 SQL Server DATETIME2 格式
function unixToDatetime(timestamp) {
if (!timestamp || timestamp === 0) {
return null;
}
const date = new Date(timestamp * 1000);
return date.toISOString().slice(0, 23).replace('T', ' ');
}
// 转义SQL字符串
function escapeString(str) {
if (str === null || str === undefined) return 'NULL';
return "N'" + String(str).replace(/'/g, "''") + "'";
}
// 格式化日期时间
function formatDatetime(dt) {
if (!dt) return 'NULL';
return "'" + dt + "'";
}
// 获取已迁移的管理员ID列表
async function getMigratedIds(pool) {
const result = await pool.request().query('SELECT id FROM admins');
return new Set(result.recordset.map(r => r.id));
}
// 插入单条管理员数据
async function insertAdmin(pool, admin) {
const createdAt = unixToDatetime(admin.addtime) || new Date().toISOString().slice(0, 23).replace('T', ' ');
const updatedAt = unixToDatetime(admin.update_time) || createdAt;
const getTime = unixToDatetime(admin.get_time) || createdAt;
const sqlStatement = `
SET IDENTITY_INSERT admins ON;
INSERT INTO admins (
id, username, nickname, password, qid, status, get_time,
random, token, admin_id, created_at, updated_at
) VALUES (
${admin.id},
${escapeString(admin.username)},
${escapeString(admin.nickname)},
${escapeString(admin.password)},
${admin.qid || 'NULL'},
${admin.status || 0},
${formatDatetime(getTime)},
${escapeString(admin.random)},
${escapeString(admin.token)},
${admin.admin_id || 0},
${formatDatetime(createdAt)},
${formatDatetime(updatedAt)}
);
SET IDENTITY_INSERT admins OFF;`;
await pool.request().batch(sqlStatement);
}
async function main() {
console.log('========================================');
console.log('管理员数据迁移脚本 - Node.js');
console.log('========================================\n');
let mysqlConn = null;
let sqlPool = null;
try {
// 连接 MySQL
console.log('正在连接 MySQL...');
mysqlConn = await mysql.createConnection(mysqlConfig);
console.log('MySQL 连接成功\n');
// 连接 SQL Server
console.log('正在连接 SQL Server...');
sqlPool = await sql.connect(sqlServerConfig);
console.log('SQL Server 连接成功\n');
// 获取已迁移的ID
console.log('正在获取已迁移的管理员ID...');
const migratedIds = await getMigratedIds(sqlPool);
console.log(`已迁移管理员数: ${migratedIds.size}\n`);
// 从 MySQL 获取所有管理员数据
console.log('正在从 MySQL 读取管理员数据...');
const [rows] = await mysqlConn.execute(`
SELECT id, username, nickname, password, qid, status, get_time,
random, token, admin_id, addtime, update_time
FROM admin
ORDER BY id
`);
console.log(`MySQL 管理员总数: ${rows.length}\n`);
// 过滤出未迁移的管理员
const adminsToMigrate = rows.filter(admin => !migratedIds.has(admin.id));
console.log(`待迁移管理员数: ${adminsToMigrate.length}\n`);
if (adminsToMigrate.length === 0) {
console.log('所有管理员数据已迁移完成!');
} else {
// 逐条迁移(数据量小,无需批量)
let totalInserted = 0;
for (const admin of adminsToMigrate) {
try {
await insertAdmin(sqlPool, admin);
totalInserted++;
console.log(`已迁移管理员: ${admin.id} - ${admin.username}`);
} catch (err) {
console.error(`插入管理员 ${admin.id} 失败:`, err.message);
}
}
console.log(`\n迁移完成!共插入 ${totalInserted} 条记录`);
}
// 验证迁移结果
console.log('\n========================================');
console.log('迁移结果验证');
console.log('========================================');
const [mysqlCount] = await mysqlConn.execute('SELECT COUNT(*) as count FROM admin');
const sqlResult = await sqlPool.request().query('SELECT COUNT(*) as count FROM admins');
console.log(`MySQL admin 表记录数: ${mysqlCount[0].count}`);
console.log(`SQL Server admins 表记录数: ${sqlResult.recordset[0].count}`);
if (mysqlCount[0].count === sqlResult.recordset[0].count) {
console.log('\n✅ 数据迁移完成,记录数一致!');
} else {
console.log(`\n⚠️ 记录数不一致,差异: ${mysqlCount[0].count - sqlResult.recordset[0].count}`);
}
// 显示迁移后的数据
console.log('\n========================================');
console.log('迁移后的管理员数据');
console.log('========================================');
const adminsResult = await sqlPool.request().query('SELECT id, username, nickname, status FROM admins ORDER BY id');
for (const admin of adminsResult.recordset) {
console.log(`ID: ${admin.id}, 用户名: ${admin.username}, 昵称: ${admin.nickname}, 状态: ${admin.status}`);
}
} catch (err) {
console.error('迁移过程中发生错误:', err);
process.exit(1);
} finally {
// 关闭连接
if (mysqlConn) await mysqlConn.end();
if (sqlPool) await sqlPool.close();
}
}
main();