282 lines
6.9 KiB
JavaScript
282 lines
6.9 KiB
JavaScript
/**
|
||
* 数据库种子数据管理器
|
||
* @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
|
||
}; |