/** * 数据库种子数据管理器 * @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 创建新的种子文件 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 };