Files
nxxmdata/scripts/seed-manager.js
2025-09-01 02:42:03 +08:00

282 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 数据库种子数据管理器
* @file seed-manager.js
* @description 管理数据库种子数据,用于初始化和测试
*/
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const { sequelize } = require('../config/database-simple');
const { QueryTypes } = require('sequelize');
// 种子文件目录
const SEEDS_DIR = path.join(__dirname, '../seeds');
// 确保种子目录存在
if (!fs.existsSync(SEEDS_DIR)) {
fs.mkdirSync(SEEDS_DIR, { recursive: true });
}
// 创建种子记录表(如果不存在)
async function createSeedTable() {
await sequelize.query(`
CREATE TABLE IF NOT EXISTS seeds (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`);
}
// 获取已应用的种子
async function getAppliedSeeds() {
await createSeedTable();
const seeds = await sequelize.query(
'SELECT name FROM seeds ORDER BY id ASC',
{ type: QueryTypes.SELECT }
);
return seeds.map(seed => seed.name);
}
// 获取所有种子文件
function getAllSeeds() {
return fs.readdirSync(SEEDS_DIR)
.filter(file => file.endsWith('.js'))
.sort(); // 按文件名排序
}
// 应用种子数据
async function applySeed(seedName) {
const seed = require(path.join(SEEDS_DIR, seedName));
console.log(`正在应用种子数据: ${seedName}`);
// 开始事务
const transaction = await sequelize.transaction();
try {
// 执行种子的 up 方法
await seed.up(sequelize.getQueryInterface(), sequelize);
// 记录种子已应用
await sequelize.query(
'INSERT INTO seeds (name) VALUES (:name)',
{
replacements: { name: seedName },
transaction
}
);
// 提交事务
await transaction.commit();
console.log(`种子数据应用成功: ${seedName}`);
} catch (error) {
// 回滚事务
await transaction.rollback();
console.error(`种子数据应用失败: ${seedName}`, error);
throw error;
}
}
// 回滚种子数据
async function revertSeed(seedName) {
const seed = require(path.join(SEEDS_DIR, seedName));
console.log(`正在回滚种子数据: ${seedName}`);
// 开始事务
const transaction = await sequelize.transaction();
try {
// 执行种子的 down 方法
await seed.down(sequelize.getQueryInterface(), sequelize);
// 删除种子记录
await sequelize.query(
'DELETE FROM seeds WHERE name = :name',
{
replacements: { name: seedName },
transaction
}
);
// 提交事务
await transaction.commit();
console.log(`种子数据回滚成功: ${seedName}`);
} catch (error) {
// 回滚事务
await transaction.rollback();
console.error(`种子数据回滚失败: ${seedName}`, error);
throw error;
}
}
// 运行所有种子数据
async function runAllSeeds() {
const appliedSeeds = await getAppliedSeeds();
const allSeeds = getAllSeeds();
// 找出未应用的种子
const pendingSeeds = allSeeds.filter(
seed => !appliedSeeds.includes(seed)
);
if (pendingSeeds.length === 0) {
console.log('没有待处理的种子数据');
return;
}
console.log(`发现 ${pendingSeeds.length} 个待处理的种子数据`);
// 按顺序应用每个待处理的种子
for (const seed of pendingSeeds) {
await applySeed(seed);
}
console.log('所有待处理的种子数据已应用');
}
// 回滚所有种子数据
async function revertAllSeeds() {
const appliedSeeds = await getAppliedSeeds();
if (appliedSeeds.length === 0) {
console.log('没有可回滚的种子数据');
return;
}
// 从最新的种子开始,回滚所有种子
const seedsToRevert = [...appliedSeeds].reverse();
for (const seed of seedsToRevert) {
await revertSeed(seed);
}
console.log('所有种子数据已回滚');
}
// 创建新的种子文件
function createSeed(name) {
const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '').split('.')[0];
const fileName = `${timestamp}_${name}.js`;
const filePath = path.join(SEEDS_DIR, fileName);
const template = `/**
* 种子数据: ${name}
* 创建时间: ${new Date().toISOString()}
*/
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
// 在此处添加种子数据
// 例如:
// await queryInterface.bulkInsert('users', [
// {
// username: 'admin',
// email: 'admin@example.com',
// password: '$2b$10$rVHMOB./a2mFmE4EEdI3QO4f./bN3LYb.dpDvtX9gRUM9gNwspj1a', // 123456
// created_at: new Date(),
// updated_at: new Date()
// },
// {
// username: 'user',
// email: 'user@example.com',
// password: '$2b$10$rVHMOB./a2mFmE4EEdI3QO4f./bN3LYb.dpDvtX9gRUM9gNwspj1a', // 123456
// created_at: new Date(),
// updated_at: new Date()
// }
// ]);
},
down: async (queryInterface, Sequelize) => {
// 在此处添加回滚代码
// 例如:
// await queryInterface.bulkDelete('users', null, {});
}
};
`;
fs.writeFileSync(filePath, template);
console.log(`已创建种子文件: ${fileName}`);
return fileName;
}
// 命令行接口
async function main() {
const args = process.argv.slice(2);
const command = args[0];
try {
switch (command) {
case 'create':
if (!args[1]) {
console.error('请提供种子名称');
process.exit(1);
}
createSeed(args[1]);
break;
case 'run':
await runAllSeeds();
break;
case 'revert':
await revertAllSeeds();
break;
case 'status':
const applied = await getAppliedSeeds();
const all = getAllSeeds();
console.log('已应用的种子数据:');
applied.forEach(s => console.log(` - ${s}`));
console.log('\n待处理的种子数据:');
all.filter(s => !applied.includes(s))
.forEach(s => console.log(` - ${s}`));
break;
default:
console.log(`
数据库种子数据管理器
用法:
node seed-manager.js <命令> [参数]
命令:
create <name> 创建新的种子文件
run 应用所有待处理的种子数据
revert 回滚所有种子数据
status 显示种子数据状态
`);
}
} catch (error) {
console.error('种子数据操作失败:', error);
process.exit(1);
} finally {
// 关闭数据库连接
await sequelize.close();
}
}
// 如果直接运行此脚本则执行main函数
if (require.main === module) {
main().catch(err => {
console.error('未处理的错误:', err);
process.exit(1);
});
}
module.exports = {
createSeedTable,
getAppliedSeeds,
getAllSeeds,
applySeed,
revertSeed,
runAllSeeds,
revertAllSeeds,
createSeed
};