修改文件结构,统一文档格式
This commit is contained in:
@@ -306,6 +306,55 @@ const options = {
|
||||
description: '更新时间'
|
||||
}
|
||||
}
|
||||
},
|
||||
FarmInput: {
|
||||
type: 'object',
|
||||
required: ['name', 'type', 'location'],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: '养殖场名称'
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: '养殖场类型'
|
||||
},
|
||||
location: {
|
||||
type: 'object',
|
||||
required: ['latitude', 'longitude'],
|
||||
properties: {
|
||||
latitude: {
|
||||
type: 'number',
|
||||
format: 'float',
|
||||
description: '纬度'
|
||||
},
|
||||
longitude: {
|
||||
type: 'number',
|
||||
format: 'float',
|
||||
description: '经度'
|
||||
}
|
||||
},
|
||||
description: '地理位置'
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
description: '详细地址'
|
||||
},
|
||||
contact: {
|
||||
type: 'string',
|
||||
description: '联系人'
|
||||
},
|
||||
phone: {
|
||||
type: 'string',
|
||||
description: '联系电话'
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
enum: ['active', 'inactive', 'maintenance'],
|
||||
description: '养殖场状态',
|
||||
default: 'active'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ const userController = require('../controllers/userController');
|
||||
* - id
|
||||
* - username
|
||||
* - email
|
||||
* - password
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
@@ -30,10 +31,36 @@ const userController = require('../controllers/userController');
|
||||
* email:
|
||||
* type: string
|
||||
* description: 邮箱地址
|
||||
* password:
|
||||
* type: string
|
||||
* description: 密码(加密存储)
|
||||
* phone:
|
||||
* type: string
|
||||
* description: 手机号码
|
||||
* avatar:
|
||||
* type: string
|
||||
* description: 头像URL
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [active, inactive, suspended]
|
||||
* description: 用户状态
|
||||
* createdAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: 创建时间
|
||||
* updatedAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: 更新时间
|
||||
* example:
|
||||
* id: 1
|
||||
* username: "john_doe"
|
||||
* email: "john@example.com"
|
||||
* phone: "13800138000"
|
||||
* avatar: "/uploads/avatars/default.png"
|
||||
* status: "active"
|
||||
* createdAt: "2024-01-01T00:00:00.000Z"
|
||||
* updatedAt: "2024-01-01T00:00:00.000Z"
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/**
|
||||
* 数据库初始化脚本
|
||||
* @file init-db.js
|
||||
* @description 初始化数据库结构和基础数据
|
||||
*/
|
||||
const { sequelize, syncModels } = require('../models');
|
||||
const { User, Role, UserRole } = require('../models');
|
||||
const bcrypt = require('bcrypt');
|
||||
const migrationManager = require('./migration-manager');
|
||||
const seedManager = require('./seed-manager');
|
||||
|
||||
async function initDb() {
|
||||
try {
|
||||
console.log('开始初始化数据库...');
|
||||
|
||||
// 测试数据库连接
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 创建迁移表
|
||||
await migrationManager.createMigrationTable();
|
||||
console.log('迁移表创建成功');
|
||||
|
||||
// 创建种子表
|
||||
await seedManager.createSeedTable();
|
||||
console.log('种子表创建成功');
|
||||
|
||||
// 运行待处理的迁移
|
||||
await migrationManager.runPendingMigrations();
|
||||
console.log('迁移完成');
|
||||
|
||||
// 运行种子数据
|
||||
await seedManager.runAllSeeds();
|
||||
console.log('种子数据应用完成');
|
||||
|
||||
// 同步模型(确保所有模型都已同步到数据库)
|
||||
await syncModels({ alter: true });
|
||||
console.log('模型同步完成');
|
||||
|
||||
// 检查是否有管理员用户
|
||||
const adminUser = await User.findOne({ where: { username: 'admin' } });
|
||||
|
||||
// 如果有管理员用户,检查密码是否为123456的哈希值
|
||||
if (adminUser) {
|
||||
// 检查密码是否为123456的哈希值
|
||||
const isCorrectPassword = await adminUser.validPassword('123456');
|
||||
|
||||
// 如果密码不是123456的哈希值,则更新密码
|
||||
if (!isCorrectPassword) {
|
||||
adminUser.password = await bcrypt.hash('123456', 10);
|
||||
await adminUser.save();
|
||||
console.log('管理员密码已重置为123456');
|
||||
} else {
|
||||
console.log('管理员密码已是123456');
|
||||
}
|
||||
|
||||
// 确保管理员有admin角色
|
||||
const adminRole = await Role.findOne({ where: { name: 'admin' } });
|
||||
if (adminRole) {
|
||||
const hasAdminRole = await adminUser.hasRole('admin');
|
||||
if (!hasAdminRole) {
|
||||
await adminUser.assignRole(adminRole.id);
|
||||
console.log('已为管理员分配admin角色');
|
||||
} else {
|
||||
console.log('管理员已有admin角色');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果没有管理员用户,则创建一个
|
||||
const newAdmin = await User.create({
|
||||
username: 'admin',
|
||||
email: 'admin@example.com',
|
||||
password: await bcrypt.hash('123456', 10)
|
||||
});
|
||||
console.log('管理员用户已创建,用户名: admin,密码: 123456');
|
||||
|
||||
// 为新管理员分配admin角色
|
||||
const adminRole = await Role.findOne({ where: { name: 'admin' } });
|
||||
if (adminRole) {
|
||||
await newAdmin.assignRole(adminRole.id);
|
||||
console.log('已为新管理员分配admin角色');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('数据库初始化完成');
|
||||
|
||||
// 关闭数据库连接
|
||||
await sequelize.close();
|
||||
console.log('数据库连接已关闭');
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('数据库初始化失败:', error);
|
||||
|
||||
// 尝试关闭数据库连接
|
||||
try {
|
||||
await sequelize.close();
|
||||
console.log('数据库连接已关闭');
|
||||
} catch (closeError) {
|
||||
console.error('关闭数据库连接失败:', closeError);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本,则执行初始化
|
||||
if (require.main === module) {
|
||||
initDb();
|
||||
}
|
||||
|
||||
module.exports = initDb;
|
||||
@@ -1,315 +0,0 @@
|
||||
/**
|
||||
* 数据库迁移管理器
|
||||
* @file migration-manager.js
|
||||
* @description 管理数据库迁移,支持版本控制和回滚
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { sequelize } = require('../config/database-simple');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
|
||||
// 迁移文件目录
|
||||
const MIGRATIONS_DIR = path.join(__dirname, '../migrations');
|
||||
|
||||
// 确保迁移目录存在
|
||||
if (!fs.existsSync(MIGRATIONS_DIR)) {
|
||||
fs.mkdirSync(MIGRATIONS_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// 创建迁移表(如果不存在)
|
||||
async function createMigrationTable() {
|
||||
await sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`);
|
||||
}
|
||||
|
||||
// 获取已应用的迁移
|
||||
async function getAppliedMigrations() {
|
||||
await createMigrationTable();
|
||||
|
||||
const migrations = await sequelize.query(
|
||||
'SELECT name FROM migrations ORDER BY id ASC',
|
||||
{ type: QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
return migrations.map(migration => migration.name);
|
||||
}
|
||||
|
||||
// 获取所有迁移文件
|
||||
function getAllMigrations() {
|
||||
return fs.readdirSync(MIGRATIONS_DIR)
|
||||
.filter(file => file.endsWith('.js'))
|
||||
.sort(); // 按文件名排序,通常是时间戳前缀
|
||||
}
|
||||
|
||||
// 应用迁移
|
||||
async function applyMigration(migrationName) {
|
||||
const migration = require(path.join(MIGRATIONS_DIR, migrationName));
|
||||
|
||||
console.log(`正在应用迁移: ${migrationName}`);
|
||||
|
||||
// 开始事务
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
// 执行迁移的 up 方法
|
||||
await migration.up(sequelize.getQueryInterface(), sequelize);
|
||||
|
||||
// 记录迁移已应用
|
||||
await sequelize.query(
|
||||
'INSERT INTO migrations (name) VALUES (:name)',
|
||||
{
|
||||
replacements: { name: migrationName },
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
// 提交事务
|
||||
await transaction.commit();
|
||||
console.log(`迁移应用成功: ${migrationName}`);
|
||||
} catch (error) {
|
||||
// 回滚事务
|
||||
await transaction.rollback();
|
||||
console.error(`迁移应用失败: ${migrationName}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 回滚迁移
|
||||
async function revertMigration(migrationName) {
|
||||
const migration = require(path.join(MIGRATIONS_DIR, migrationName));
|
||||
|
||||
console.log(`正在回滚迁移: ${migrationName}`);
|
||||
|
||||
// 开始事务
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
// 执行迁移的 down 方法
|
||||
await migration.down(sequelize.getQueryInterface(), sequelize);
|
||||
|
||||
// 删除迁移记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM migrations WHERE name = :name',
|
||||
{
|
||||
replacements: { name: migrationName },
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
// 提交事务
|
||||
await transaction.commit();
|
||||
console.log(`迁移回滚成功: ${migrationName}`);
|
||||
} catch (error) {
|
||||
// 回滚事务
|
||||
await transaction.rollback();
|
||||
console.error(`迁移回滚失败: ${migrationName}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 运行待处理的迁移
|
||||
async function runPendingMigrations() {
|
||||
const appliedMigrations = await getAppliedMigrations();
|
||||
const allMigrations = getAllMigrations();
|
||||
|
||||
// 找出未应用的迁移
|
||||
const pendingMigrations = allMigrations.filter(
|
||||
migration => !appliedMigrations.includes(migration)
|
||||
);
|
||||
|
||||
if (pendingMigrations.length === 0) {
|
||||
console.log('没有待处理的迁移');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`发现 ${pendingMigrations.length} 个待处理的迁移`);
|
||||
|
||||
// 按顺序应用每个待处理的迁移
|
||||
for (const migration of pendingMigrations) {
|
||||
await applyMigration(migration);
|
||||
}
|
||||
|
||||
console.log('所有待处理的迁移已应用');
|
||||
}
|
||||
|
||||
// 回滚最近的迁移
|
||||
async function rollbackLastMigration() {
|
||||
const appliedMigrations = await getAppliedMigrations();
|
||||
|
||||
if (appliedMigrations.length === 0) {
|
||||
console.log('没有可回滚的迁移');
|
||||
return;
|
||||
}
|
||||
|
||||
const lastMigration = appliedMigrations[appliedMigrations.length - 1];
|
||||
await revertMigration(lastMigration);
|
||||
}
|
||||
|
||||
// 回滚到特定迁移
|
||||
async function rollbackToMigration(targetMigration) {
|
||||
const appliedMigrations = await getAppliedMigrations();
|
||||
|
||||
if (appliedMigrations.length === 0) {
|
||||
console.log('没有可回滚的迁移');
|
||||
return;
|
||||
}
|
||||
|
||||
const targetIndex = appliedMigrations.indexOf(targetMigration);
|
||||
|
||||
if (targetIndex === -1) {
|
||||
console.error(`目标迁移 ${targetMigration} 未找到或未应用`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 从最新的迁移开始,回滚到目标迁移之后的所有迁移
|
||||
const migrationsToRollback = appliedMigrations.slice(targetIndex + 1).reverse();
|
||||
|
||||
for (const migration of migrationsToRollback) {
|
||||
await revertMigration(migration);
|
||||
}
|
||||
|
||||
console.log(`已回滚到迁移: ${targetMigration}`);
|
||||
}
|
||||
|
||||
// 创建新的迁移文件
|
||||
function createMigration(name) {
|
||||
const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '').split('.')[0];
|
||||
const fileName = `${timestamp}_${name}.js`;
|
||||
const filePath = path.join(MIGRATIONS_DIR, fileName);
|
||||
|
||||
const template = `/**
|
||||
* 迁移: ${name}
|
||||
* 创建时间: ${new Date().toISOString()}
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
// 在此处添加迁移代码(创建表、添加列等)
|
||||
// 例如:
|
||||
// await queryInterface.createTable('users', {
|
||||
// id: {
|
||||
// type: Sequelize.INTEGER,
|
||||
// primaryKey: true,
|
||||
// autoIncrement: true
|
||||
// },
|
||||
// name: Sequelize.STRING,
|
||||
// createdAt: {
|
||||
// type: Sequelize.DATE,
|
||||
// defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
|
||||
// field: 'created_at'
|
||||
// },
|
||||
// updatedAt: {
|
||||
// type: Sequelize.DATE,
|
||||
// defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
|
||||
// field: 'updated_at'
|
||||
// }
|
||||
// });
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
// 在此处添加回滚代码(删除表、删除列等)
|
||||
// 例如:
|
||||
// await queryInterface.dropTable('users');
|
||||
}
|
||||
};
|
||||
`;
|
||||
|
||||
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);
|
||||
}
|
||||
createMigration(args[1]);
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
case 'migrate':
|
||||
await runPendingMigrations();
|
||||
break;
|
||||
|
||||
case 'down':
|
||||
case 'rollback':
|
||||
await rollbackLastMigration();
|
||||
break;
|
||||
|
||||
case 'to':
|
||||
if (!args[1]) {
|
||||
console.error('请提供目标迁移名称');
|
||||
process.exit(1);
|
||||
}
|
||||
await rollbackToMigration(args[1]);
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
const applied = await getAppliedMigrations();
|
||||
const all = getAllMigrations();
|
||||
|
||||
console.log('已应用的迁移:');
|
||||
applied.forEach(m => console.log(` - ${m}`));
|
||||
|
||||
console.log('\n待处理的迁移:');
|
||||
all.filter(m => !applied.includes(m))
|
||||
.forEach(m => console.log(` - ${m}`));
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`
|
||||
数据库迁移管理器
|
||||
|
||||
用法:
|
||||
node migration-manager.js <命令> [参数]
|
||||
|
||||
命令:
|
||||
create <name> 创建新的迁移文件
|
||||
up, migrate 应用所有待处理的迁移
|
||||
down, rollback 回滚最近的一次迁移
|
||||
to <migration> 回滚到指定的迁移(不包括该迁移)
|
||||
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 = {
|
||||
createMigrationTable,
|
||||
getAppliedMigrations,
|
||||
getAllMigrations,
|
||||
applyMigration,
|
||||
revertMigration,
|
||||
runPendingMigrations,
|
||||
rollbackLastMigration,
|
||||
rollbackToMigration,
|
||||
createMigration
|
||||
};
|
||||
@@ -1,282 +0,0 @@
|
||||
/**
|
||||
* 数据库种子数据管理器
|
||||
* @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
|
||||
};
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* 数据库连接测试脚本
|
||||
* @file test-connection.js
|
||||
* @description 测试数据库连接、连接池状态和查询性能
|
||||
*/
|
||||
const { sequelize } = require('../models');
|
||||
const { User } = require('../models');
|
||||
const dbPool = require('../config/database-pool');
|
||||
const queryOptimizer = require('../config/query-optimizer');
|
||||
const dbMonitor = require('../config/db-monitor');
|
||||
|
||||
async function testConnection() {
|
||||
try {
|
||||
console.log('开始测试数据库连接...');
|
||||
|
||||
// 测试数据库连接
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 获取连接池状态
|
||||
const poolStatus = await dbPool.getPoolStatus();
|
||||
console.log('连接池状态:', JSON.stringify(poolStatus, null, 2));
|
||||
|
||||
// 获取数据库状态
|
||||
const dbStatus = await queryOptimizer.getDatabaseStatus();
|
||||
console.log('数据库状态:', JSON.stringify(dbStatus, null, 2));
|
||||
|
||||
// 查询用户数量
|
||||
console.time('用户查询');
|
||||
const userCount = await User.count();
|
||||
console.timeEnd('用户查询');
|
||||
console.log(`当前用户数量: ${userCount}`);
|
||||
|
||||
// 执行查询分析
|
||||
const userQuery = User.findAll();
|
||||
const explainResult = await queryOptimizer.explainQuery(userQuery);
|
||||
console.log('查询分析结果:', JSON.stringify(explainResult, null, 2));
|
||||
|
||||
// 获取表信息
|
||||
const tableInfo = await queryOptimizer.getTableInfo('users');
|
||||
console.log('用户表信息:', JSON.stringify(tableInfo, null, 2));
|
||||
|
||||
// 获取索引信息
|
||||
const indexInfo = await queryOptimizer.getIndexInfo('users');
|
||||
console.log('用户表索引:', JSON.stringify(indexInfo, null, 2));
|
||||
|
||||
// 监控连接状态
|
||||
const connectionStatus = await dbMonitor.checkConnectionStatus();
|
||||
console.log('连接监控状态:', JSON.stringify(connectionStatus, null, 2));
|
||||
|
||||
// 关闭数据库连接
|
||||
await sequelize.close();
|
||||
console.log('数据库连接已关闭');
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('数据库连接测试失败:', error);
|
||||
|
||||
// 尝试关闭数据库连接
|
||||
try {
|
||||
await sequelize.close();
|
||||
console.log('数据库连接已关闭');
|
||||
} catch (closeError) {
|
||||
console.error('关闭数据库连接失败:', closeError);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本,则执行测试
|
||||
if (require.main === module) {
|
||||
testConnection();
|
||||
}
|
||||
|
||||
module.exports = testConnection;
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* 百度地图API测试脚本
|
||||
* 用于测试百度地图API服务是否正常工作
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const axios = require('axios');
|
||||
|
||||
// 百度地图API密钥
|
||||
const BAIDU_MAP_AK = process.env.BAIDU_MAP_AK || 'your_baidu_map_ak';
|
||||
|
||||
// 测试地理编码API
|
||||
async function testGeocode() {
|
||||
try {
|
||||
console.log('测试地理编码API...');
|
||||
const address = '宁夏回族自治区银川市兴庆区北京东路';
|
||||
|
||||
const response = await axios.get('http://api.map.baidu.com/geocoding/v3', {
|
||||
params: {
|
||||
address,
|
||||
output: 'json',
|
||||
ak: BAIDU_MAP_AK
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.status === 0) {
|
||||
console.log('地理编码成功:');
|
||||
console.log(`地址: ${address}`);
|
||||
console.log(`经度: ${response.data.result.location.lng}`);
|
||||
console.log(`纬度: ${response.data.result.location.lat}`);
|
||||
return true;
|
||||
} else {
|
||||
console.error('地理编码失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('地理编码测试错误:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试逆地理编码API
|
||||
async function testReverseGeocode() {
|
||||
try {
|
||||
console.log('\n测试逆地理编码API...');
|
||||
// 银川市中心坐标
|
||||
const lat = 38.4864;
|
||||
const lng = 106.2324;
|
||||
|
||||
const response = await axios.get('http://api.map.baidu.com/reverse_geocoding/v3', {
|
||||
params: {
|
||||
location: `${lat},${lng}`,
|
||||
output: 'json',
|
||||
ak: BAIDU_MAP_AK
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.status === 0) {
|
||||
console.log('逆地理编码成功:');
|
||||
console.log(`经度: ${lng}, 纬度: ${lat}`);
|
||||
console.log(`地址: ${response.data.result.formatted_address}`);
|
||||
return true;
|
||||
} else {
|
||||
console.error('逆地理编码失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('逆地理编码测试错误:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试IP定位API
|
||||
async function testIpLocation() {
|
||||
try {
|
||||
console.log('\n测试IP定位API...');
|
||||
|
||||
const response = await axios.get('http://api.map.baidu.com/location/ip', {
|
||||
params: {
|
||||
ak: BAIDU_MAP_AK,
|
||||
coor: 'bd09ll'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.status === 0) {
|
||||
console.log('IP定位成功:');
|
||||
console.log(`地址: ${response.data.content.address}`);
|
||||
console.log(`经度: ${response.data.content.point.x}`);
|
||||
console.log(`纬度: ${response.data.content.point.y}`);
|
||||
return true;
|
||||
} else {
|
||||
console.error('IP定位失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('IP定位测试错误:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 运行所有测试
|
||||
async function runTests() {
|
||||
console.log('===== 百度地图API测试 =====');
|
||||
console.log(`使用的API密钥: ${BAIDU_MAP_AK}`);
|
||||
|
||||
if (BAIDU_MAP_AK === 'your_baidu_map_ak' || BAIDU_MAP_AK === 'your_baidu_map_ak_here') {
|
||||
console.warn('警告: 您正在使用默认API密钥,请在.env文件中设置有效的BAIDU_MAP_AK');
|
||||
}
|
||||
|
||||
console.log('\n开始测试...');
|
||||
|
||||
const geocodeResult = await testGeocode();
|
||||
const reverseGeocodeResult = await testReverseGeocode();
|
||||
const ipLocationResult = await testIpLocation();
|
||||
|
||||
console.log('\n===== 测试结果汇总 =====');
|
||||
console.log(`地理编码API: ${geocodeResult ? '✅ 通过' : '❌ 失败'}`);
|
||||
console.log(`逆地理编码API: ${reverseGeocodeResult ? '✅ 通过' : '❌ 失败'}`);
|
||||
console.log(`IP定位API: ${ipLocationResult ? '✅ 通过' : '❌ 失败'}`);
|
||||
|
||||
const allPassed = geocodeResult && reverseGeocodeResult && ipLocationResult;
|
||||
console.log(`\n总体结果: ${allPassed ? '✅ 所有测试通过' : '❌ 部分测试失败'}`);
|
||||
|
||||
if (!allPassed) {
|
||||
console.log('\n可能的问题:');
|
||||
console.log('1. API密钥无效或未正确设置');
|
||||
console.log('2. 网络连接问题');
|
||||
console.log('3. 百度地图API服务暂时不可用');
|
||||
console.log('\n解决方案:');
|
||||
console.log('1. 检查.env文件中的BAIDU_MAP_AK设置');
|
||||
console.log('2. 确保您的网络可以访问百度地图API');
|
||||
console.log('3. 稍后再试');
|
||||
}
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
runTests().catch(error => {
|
||||
console.error('测试执行错误:', error);
|
||||
});
|
||||
Reference in New Issue
Block a user