/** * 数据库工具脚本 * 用于创建迁移文件和种子数据文件 */ import { writeFileSync, mkdirSync, existsSync } from 'fs' import { resolve, dirname } from 'path' import { fileURLToPath } from 'url' // 获取当前文件目录 const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) const projectRoot = resolve(__dirname, '..') // 配置路径 const MIGRATIONS_DIR = 'src/infrastructure/database/migrations' const SEEDS_DIR = 'src/infrastructure/database/seeds' /** * 生成时间戳 * @returns {string} 格式: YYYYMMDDHHMMSS */ function generateTimestamp() { const now = new Date() const year = now.getFullYear() const month = String(now.getMonth() + 1).padStart(2, '0') const day = String(now.getDate()).padStart(2, '0') const hours = String(now.getHours()).padStart(2, '0') const minutes = String(now.getMinutes()).padStart(2, '0') const seconds = String(now.getSeconds()).padStart(2, '0') return `${year}${month}${day}${hours}${minutes}${seconds}` } /** * 创建迁移文件模板 * @param {string} migrationName 迁移名称 * @returns {string} 迁移文件内容 */ function createMigrationTemplate(migrationName) { return `/** * ${migrationName} 迁移文件 * @param { import("knex").Knex } knex * @returns { Promise } */ export const up = async knex => { // TODO: 在此处编写数据库结构变更逻辑 // 例如:创建表、添加列、创建索引等 /* return knex.schema.createTable('table_name', table => { table.increments('id').primary() table.string('name').notNullable() table.timestamps(true, true) }) */ } /** * @param { import("knex").Knex } knex * @returns { Promise } */ export const down = async knex => { // TODO: 在此处编写回滚逻辑 // 例如:删除表、删除列、删除索引等 /* return knex.schema.dropTable('table_name') */ } ` } /** * 创建种子数据文件模板 * @param {string} seedName 种子数据名称 * @returns {string} 种子数据文件内容 */ function createSeedTemplate(seedName) { return `/** * ${seedName} 种子数据 * @param { import("knex").Knex } knex * @returns { Promise } */ export const seed = async knex => { // 清空现有数据(可选) // await knex('table_name').del() // 插入种子数据 /* await knex('table_name').insert([ { name: '示例数据1', created_at: knex.fn.now(), updated_at: knex.fn.now() }, { name: '示例数据2', created_at: knex.fn.now(), updated_at: knex.fn.now() } ]) console.log('✅ ${seedName} seeded successfully!') */ } ` } /** * 创建迁移文件 * @param {string} migrationName 迁移名称 */ function createMigration(migrationName) { if (!migrationName) { console.error('❌ 请提供迁移名称') console.log('用法: bun run scripts/db-tools.js migrate ') process.exit(1) } // 确保目录存在 const migrationsPath = resolve(projectRoot, MIGRATIONS_DIR) if (!existsSync(migrationsPath)) { mkdirSync(migrationsPath, { recursive: true }) } // 生成文件名 const timestamp = generateTimestamp() const fileName = `${timestamp}_${migrationName}.mjs` const filePath = resolve(migrationsPath, fileName) // 检查文件是否已存在 if (existsSync(filePath)) { console.error(`❌ 迁移文件已存在: ${fileName}`) process.exit(1) } // 创建文件 const content = createMigrationTemplate(migrationName) writeFileSync(filePath, content, 'utf8') console.log(`✅ 迁移文件创建成功: ${fileName}`) console.log(`📁 路径: ${filePath}`) console.log(`📝 请编辑文件并实现 up() 和 down() 方法`) } /** * 创建种子数据文件 * @param {string} seedName 种子数据名称 */ function createSeed(seedName) { if (!seedName) { console.error('❌ 请提供种子数据名称') console.log('用法: bun run scripts/db-tools.js seed ') process.exit(1) } // 确保目录存在 const seedsPath = resolve(projectRoot, SEEDS_DIR) if (!existsSync(seedsPath)) { mkdirSync(seedsPath, { recursive: true }) } // 生成文件名 const timestamp = generateTimestamp() const fileName = `${timestamp}_${seedName}.mjs` const filePath = resolve(seedsPath, fileName) // 检查文件是否已存在 if (existsSync(filePath)) { console.error(`❌ 种子数据文件已存在: ${fileName}`) process.exit(1) } // 创建文件 const content = createSeedTemplate(seedName) writeFileSync(filePath, content, 'utf8') console.log(`✅ 种子数据文件创建成功: ${fileName}`) console.log(`📁 路径: ${filePath}`) console.log(`📝 请编辑文件并实现 seed() 方法`) } /** * 显示帮助信息 */ function showHelp() { console.log(` 🗄️ 数据库工具 - 迁移和种子数据管理 用法: bun run scripts/db-tools.js [options] 命令: migrate 创建新的迁移文件 seed 创建新的种子数据文件 help 显示帮助信息 示例: bun run scripts/db-tools.js migrate create_posts_table bun run scripts/db-tools.js seed posts_seed 💡 提示: - 迁移文件用于管理数据库结构变更 - 种子数据文件用于插入测试或初始数据 - 文件名会自动添加时间戳前缀 - 所有文件使用 ES 模块格式 (.mjs) `) } // 主函数 function main() { const args = process.argv.slice(2) const command = args[0] const name = args[1] switch (command) { case 'migrate': createMigration(name) break case 'seed': createSeed(name) break case 'help': case '--help': case '-h': showHelp() break default: console.error(`❌ 未知命令: ${command}`) showHelp() process.exit(1) } } // 运行主函数 main()