修改文件结构,统一文档格式
This commit is contained in:
36
backend/tools/data-management/cleanup-temp-tables.js
Normal file
36
backend/tools/data-management/cleanup-temp-tables.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 清理临时表
|
||||
* @file cleanup-temp-tables.js
|
||||
*/
|
||||
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
|
||||
async function cleanupTempTables() {
|
||||
try {
|
||||
console.log('清理临时表...');
|
||||
|
||||
const tables = await sequelize.query(
|
||||
"SHOW TABLES LIKE '%_temp_reorder'",
|
||||
{ type: QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
for (const table of tables) {
|
||||
const tableName = Object.values(table)[0];
|
||||
console.log('删除临时表:', tableName);
|
||||
await sequelize.query(`DROP TABLE ${tableName}`);
|
||||
}
|
||||
|
||||
console.log('清理完成');
|
||||
} catch (error) {
|
||||
console.error('清理失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
cleanupTempTables();
|
||||
}
|
||||
|
||||
module.exports = { cleanupTempTables };
|
||||
156
backend/tools/data-management/create-environment-schedule.js
Normal file
156
backend/tools/data-management/create-environment-schedule.js
Normal file
@@ -0,0 +1,156 @@
|
||||
const sequelize = require('./config/database');
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
// 定义环境监测时刻表模型
|
||||
const EnvironmentSchedule = sequelize.define('EnvironmentSchedule', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
farm_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '农场ID'
|
||||
},
|
||||
device_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '设备ID'
|
||||
},
|
||||
schedule_time: {
|
||||
type: DataTypes.TIME,
|
||||
allowNull: false,
|
||||
comment: '监测时刻(HH:MM:SS)'
|
||||
},
|
||||
temperature: {
|
||||
type: DataTypes.DECIMAL(5, 2),
|
||||
allowNull: true,
|
||||
comment: '温度值(摄氏度)'
|
||||
},
|
||||
humidity: {
|
||||
type: DataTypes.DECIMAL(5, 2),
|
||||
allowNull: true,
|
||||
comment: '湿度值(百分比)'
|
||||
},
|
||||
monitoring_date: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
comment: '监测日期'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'inactive', 'maintenance'),
|
||||
defaultValue: 'active',
|
||||
comment: '监测状态'
|
||||
},
|
||||
notes: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息'
|
||||
}
|
||||
}, {
|
||||
tableName: 'environment_schedules',
|
||||
timestamps: true,
|
||||
indexes: [
|
||||
{
|
||||
fields: ['farm_id', 'monitoring_date', 'schedule_time']
|
||||
},
|
||||
{
|
||||
fields: ['device_id']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
async function createEnvironmentScheduleTable() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 创建表
|
||||
await EnvironmentSchedule.sync({ force: true });
|
||||
console.log('环境监测时刻表创建成功');
|
||||
|
||||
// 生成示例数据
|
||||
const scheduleData = [];
|
||||
const today = new Date();
|
||||
const schedules = [
|
||||
'06:00:00', '08:00:00', '10:00:00', '12:00:00',
|
||||
'14:00:00', '16:00:00', '18:00:00', '20:00:00'
|
||||
];
|
||||
|
||||
// 为过去7天生成数据
|
||||
for (let day = 0; day < 7; day++) {
|
||||
const monitoringDate = new Date(today);
|
||||
monitoringDate.setDate(today.getDate() - day);
|
||||
|
||||
schedules.forEach(time => {
|
||||
// 农场1的数据
|
||||
scheduleData.push({
|
||||
farm_id: 1,
|
||||
device_id: 1,
|
||||
schedule_time: time,
|
||||
temperature: (18 + Math.random() * 15).toFixed(2), // 18-33度
|
||||
humidity: (45 + Math.random() * 35).toFixed(2), // 45-80%
|
||||
monitoring_date: monitoringDate.toISOString().split('T')[0],
|
||||
status: 'active',
|
||||
notes: `定时监测数据 - ${time}`
|
||||
});
|
||||
|
||||
// 农场2的数据(如果存在)
|
||||
scheduleData.push({
|
||||
farm_id: 2,
|
||||
device_id: 2,
|
||||
schedule_time: time,
|
||||
temperature: (16 + Math.random() * 18).toFixed(2), // 16-34度
|
||||
humidity: (40 + Math.random() * 40).toFixed(2), // 40-80%
|
||||
monitoring_date: monitoringDate.toISOString().split('T')[0],
|
||||
status: 'active',
|
||||
notes: `定时监测数据 - ${time}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 批量插入数据
|
||||
await EnvironmentSchedule.bulkCreate(scheduleData);
|
||||
console.log(`成功插入 ${scheduleData.length} 条环境监测时刻数据`);
|
||||
|
||||
// 验证数据
|
||||
const totalCount = await EnvironmentSchedule.count();
|
||||
console.log(`环境监测时刻表总记录数: ${totalCount}`);
|
||||
|
||||
const todayCount = await EnvironmentSchedule.count({
|
||||
where: {
|
||||
monitoring_date: today.toISOString().split('T')[0]
|
||||
}
|
||||
});
|
||||
console.log(`今日监测记录数: ${todayCount}`);
|
||||
|
||||
// 显示部分数据样例
|
||||
const sampleData = await EnvironmentSchedule.findAll({
|
||||
limit: 5,
|
||||
order: [['monitoring_date', 'DESC'], ['schedule_time', 'ASC']]
|
||||
});
|
||||
|
||||
console.log('\n数据样例:');
|
||||
sampleData.forEach(record => {
|
||||
console.log(`日期: ${record.monitoring_date}, 时间: ${record.schedule_time}, 温度: ${record.temperature}°C, 湿度: ${record.humidity}%`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建环境监测时刻表失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 导出模型和创建函数
|
||||
module.exports = {
|
||||
EnvironmentSchedule,
|
||||
createEnvironmentScheduleTable
|
||||
};
|
||||
|
||||
// 如果直接运行此文件,则执行创建操作
|
||||
if (require.main === module) {
|
||||
createEnvironmentScheduleTable();
|
||||
}
|
||||
69
backend/tools/data-management/create-sensor-data.js
Normal file
69
backend/tools/data-management/create-sensor-data.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const { SensorData } = require('./models');
|
||||
const sequelize = require('./config/database');
|
||||
|
||||
async function createSensorData() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 创建过去24小时的温度和湿度数据
|
||||
const now = new Date();
|
||||
const sensorDataList = [];
|
||||
|
||||
// 生成过去24小时的数据,每小时一条记录
|
||||
for (let i = 23; i >= 0; i--) {
|
||||
const timestamp = new Date(now.getTime() - i * 60 * 60 * 1000);
|
||||
|
||||
// 温度数据 (20-30度之间随机)
|
||||
const temperature = 20 + Math.random() * 10;
|
||||
sensorDataList.push({
|
||||
device_id: 1,
|
||||
farm_id: 1,
|
||||
sensor_type: 'temperature',
|
||||
value: parseFloat(temperature.toFixed(1)),
|
||||
unit: '°C',
|
||||
timestamp: timestamp,
|
||||
created_at: timestamp,
|
||||
updated_at: timestamp
|
||||
});
|
||||
|
||||
// 湿度数据 (50-80%之间随机)
|
||||
const humidity = 50 + Math.random() * 30;
|
||||
sensorDataList.push({
|
||||
device_id: 1,
|
||||
farm_id: 1,
|
||||
sensor_type: 'humidity',
|
||||
value: parseFloat(humidity.toFixed(1)),
|
||||
unit: '%',
|
||||
timestamp: timestamp,
|
||||
created_at: timestamp,
|
||||
updated_at: timestamp
|
||||
});
|
||||
}
|
||||
|
||||
// 批量插入数据
|
||||
await SensorData.bulkCreate(sensorDataList);
|
||||
console.log(`成功创建 ${sensorDataList.length} 条传感器数据`);
|
||||
|
||||
// 验证数据
|
||||
const count = await SensorData.count();
|
||||
console.log(`传感器数据总数: ${count}`);
|
||||
|
||||
const temperatureCount = await SensorData.count({
|
||||
where: { sensor_type: 'temperature' }
|
||||
});
|
||||
console.log(`温度数据条数: ${temperatureCount}`);
|
||||
|
||||
const humidityCount = await SensorData.count({
|
||||
where: { sensor_type: 'humidity' }
|
||||
});
|
||||
console.log(`湿度数据条数: ${humidityCount}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建传感器数据失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
createSensorData();
|
||||
@@ -0,0 +1,121 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// 数据库配置
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: process.env.DB_PORT || 3306,
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'nxxmdata'
|
||||
};
|
||||
|
||||
async function createEnvironmentScheduleTable() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
// 创建数据库连接
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 创建环境监测时刻表
|
||||
const createTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS environment_schedules (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
farm_id INT NOT NULL COMMENT '农场ID',
|
||||
device_id INT NOT NULL COMMENT '设备ID',
|
||||
schedule_time TIME NOT NULL COMMENT '监测时刻(HH:MM:SS)',
|
||||
temperature DECIMAL(5,2) NULL COMMENT '温度值(摄氏度)',
|
||||
humidity DECIMAL(5,2) NULL COMMENT '湿度值(百分比)',
|
||||
monitoring_date DATE NOT NULL DEFAULT (CURRENT_DATE) COMMENT '监测日期',
|
||||
status ENUM('active', 'inactive', 'maintenance') DEFAULT 'active' COMMENT '监测状态',
|
||||
notes TEXT NULL COMMENT '备注信息',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_farm_date_time (farm_id, monitoring_date, schedule_time),
|
||||
INDEX idx_device (device_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='环境监测时刻表';
|
||||
`;
|
||||
|
||||
await connection.execute(createTableSQL);
|
||||
console.log('环境监测时刻表创建成功');
|
||||
|
||||
// 生成示例数据
|
||||
const schedules = [
|
||||
'06:00:00', '08:00:00', '10:00:00', '12:00:00',
|
||||
'14:00:00', '16:00:00', '18:00:00', '20:00:00'
|
||||
];
|
||||
|
||||
const insertSQL = `
|
||||
INSERT INTO environment_schedules
|
||||
(farm_id, device_id, schedule_time, temperature, humidity, monitoring_date, status, notes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
let totalInserted = 0;
|
||||
const today = new Date();
|
||||
|
||||
// 为过去7天生成数据
|
||||
for (let day = 0; day < 7; day++) {
|
||||
const monitoringDate = new Date(today);
|
||||
monitoringDate.setDate(today.getDate() - day);
|
||||
const dateStr = monitoringDate.toISOString().split('T')[0];
|
||||
|
||||
for (const time of schedules) {
|
||||
// 农场1的数据
|
||||
const temp1 = (18 + Math.random() * 15).toFixed(2);
|
||||
const humidity1 = (45 + Math.random() * 35).toFixed(2);
|
||||
|
||||
await connection.execute(insertSQL, [
|
||||
1, 1, time, temp1, humidity1, dateStr, 'active', `定时监测数据 - ${time}`
|
||||
]);
|
||||
totalInserted++;
|
||||
|
||||
// 农场2的数据
|
||||
const temp2 = (16 + Math.random() * 18).toFixed(2);
|
||||
const humidity2 = (40 + Math.random() * 40).toFixed(2);
|
||||
|
||||
await connection.execute(insertSQL, [
|
||||
2, 2, time, temp2, humidity2, dateStr, 'active', `定时监测数据 - ${time}`
|
||||
]);
|
||||
totalInserted++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`成功插入 ${totalInserted} 条环境监测时刻数据`);
|
||||
|
||||
// 验证数据
|
||||
const [countResult] = await connection.execute(
|
||||
'SELECT COUNT(*) as total FROM environment_schedules'
|
||||
);
|
||||
console.log(`环境监测时刻表总记录数: ${countResult[0].total}`);
|
||||
|
||||
const [todayResult] = await connection.execute(
|
||||
'SELECT COUNT(*) as today_count FROM environment_schedules WHERE monitoring_date = CURDATE()'
|
||||
);
|
||||
console.log(`今日监测记录数: ${todayResult[0].today_count}`);
|
||||
|
||||
// 显示部分数据样例
|
||||
const [sampleData] = await connection.execute(`
|
||||
SELECT monitoring_date, schedule_time, temperature, humidity
|
||||
FROM environment_schedules
|
||||
ORDER BY monitoring_date DESC, schedule_time ASC
|
||||
LIMIT 5
|
||||
`);
|
||||
|
||||
console.log('\n数据样例:');
|
||||
sampleData.forEach(record => {
|
||||
console.log(`日期: ${record.monitoring_date}, 时间: ${record.schedule_time}, 温度: ${record.temperature}°C, 湿度: ${record.humidity}%`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建环境监测时刻表失败:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行创建操作
|
||||
createEnvironmentScheduleTable();
|
||||
@@ -0,0 +1,171 @@
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const path = require('path');
|
||||
|
||||
// SQLite数据库文件路径
|
||||
const dbPath = path.join(__dirname, 'environment_schedule.db');
|
||||
|
||||
async function createEnvironmentScheduleTable() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 创建或连接到SQLite数据库
|
||||
const db = new sqlite3.Database(dbPath, (err) => {
|
||||
if (err) {
|
||||
console.error('数据库连接失败:', err.message);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
console.log('SQLite数据库连接成功');
|
||||
});
|
||||
|
||||
// 创建环境监测时刻表
|
||||
const createTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS environment_schedules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
farm_id INTEGER NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
schedule_time TEXT NOT NULL,
|
||||
temperature REAL,
|
||||
humidity REAL,
|
||||
monitoring_date TEXT NOT NULL,
|
||||
status TEXT DEFAULT 'active',
|
||||
notes TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`;
|
||||
|
||||
db.run(createTableSQL, (err) => {
|
||||
if (err) {
|
||||
console.error('创建表失败:', err.message);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
console.log('环境监测时刻表创建成功');
|
||||
|
||||
// 生成示例数据
|
||||
const schedules = [
|
||||
'06:00:00', '08:00:00', '10:00:00', '12:00:00',
|
||||
'14:00:00', '16:00:00', '18:00:00', '20:00:00'
|
||||
];
|
||||
|
||||
const insertSQL = `
|
||||
INSERT INTO environment_schedules
|
||||
(farm_id, device_id, schedule_time, temperature, humidity, monitoring_date, status, notes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
let totalInserted = 0;
|
||||
const today = new Date();
|
||||
const insertPromises = [];
|
||||
|
||||
// 为过去7天生成数据
|
||||
for (let day = 0; day < 7; day++) {
|
||||
const monitoringDate = new Date(today);
|
||||
monitoringDate.setDate(today.getDate() - day);
|
||||
const dateStr = monitoringDate.toISOString().split('T')[0];
|
||||
|
||||
for (const time of schedules) {
|
||||
// 农场1的数据
|
||||
const temp1 = (18 + Math.random() * 15).toFixed(2);
|
||||
const humidity1 = (45 + Math.random() * 35).toFixed(2);
|
||||
|
||||
insertPromises.push(new Promise((resolve, reject) => {
|
||||
db.run(insertSQL, [
|
||||
1, 1, time, temp1, humidity1, dateStr, 'active', `定时监测数据 - ${time}`
|
||||
], function(err) {
|
||||
if (err) reject(err);
|
||||
else {
|
||||
totalInserted++;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
// 农场2的数据
|
||||
const temp2 = (16 + Math.random() * 18).toFixed(2);
|
||||
const humidity2 = (40 + Math.random() * 40).toFixed(2);
|
||||
|
||||
insertPromises.push(new Promise((resolve, reject) => {
|
||||
db.run(insertSQL, [
|
||||
2, 2, time, temp2, humidity2, dateStr, 'active', `定时监测数据 - ${time}`
|
||||
], function(err) {
|
||||
if (err) reject(err);
|
||||
else {
|
||||
totalInserted++;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// 等待所有插入操作完成
|
||||
Promise.all(insertPromises)
|
||||
.then(() => {
|
||||
console.log(`成功插入 ${totalInserted} 条环境监测时刻数据`);
|
||||
|
||||
// 验证数据
|
||||
db.get('SELECT COUNT(*) as total FROM environment_schedules', (err, row) => {
|
||||
if (err) {
|
||||
console.error('查询总数失败:', err.message);
|
||||
} else {
|
||||
console.log(`环境监测时刻表总记录数: ${row.total}`);
|
||||
}
|
||||
|
||||
// 查询今日数据
|
||||
const todayStr = today.toISOString().split('T')[0];
|
||||
db.get(
|
||||
'SELECT COUNT(*) as today_count FROM environment_schedules WHERE monitoring_date = ?',
|
||||
[todayStr],
|
||||
(err, row) => {
|
||||
if (err) {
|
||||
console.error('查询今日数据失败:', err.message);
|
||||
} else {
|
||||
console.log(`今日监测记录数: ${row.today_count}`);
|
||||
}
|
||||
|
||||
// 显示部分数据样例
|
||||
db.all(`
|
||||
SELECT monitoring_date, schedule_time, temperature, humidity
|
||||
FROM environment_schedules
|
||||
ORDER BY monitoring_date DESC, schedule_time ASC
|
||||
LIMIT 5
|
||||
`, (err, rows) => {
|
||||
if (err) {
|
||||
console.error('查询样例数据失败:', err.message);
|
||||
} else {
|
||||
console.log('\n数据样例:');
|
||||
rows.forEach(record => {
|
||||
console.log(`日期: ${record.monitoring_date}, 时间: ${record.schedule_time}, 温度: ${record.temperature}°C, 湿度: ${record.humidity}%`);
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
db.close((err) => {
|
||||
if (err) {
|
||||
console.error('关闭数据库失败:', err.message);
|
||||
} else {
|
||||
console.log('数据库连接已关闭');
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('插入数据失败:', err.message);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 执行创建操作
|
||||
createEnvironmentScheduleTable()
|
||||
.then(() => {
|
||||
console.log('环境监测时刻表创建完成');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('操作失败:', err.message);
|
||||
});
|
||||
71
backend/tools/data-management/create-test-sensor-data.js
Normal file
71
backend/tools/data-management/create-test-sensor-data.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const { SensorData } = require('./models');
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('开始创建测试传感器数据...');
|
||||
|
||||
// 清除现有数据
|
||||
await SensorData.destroy({ where: {} });
|
||||
console.log('已清除现有传感器数据');
|
||||
|
||||
// 创建过去24小时的测试数据
|
||||
const testData = [];
|
||||
const now = new Date();
|
||||
|
||||
// 生成24小时的数据,每小时一个数据点
|
||||
for (let i = 23; i >= 0; i--) {
|
||||
const time = new Date(now.getTime() - i * 60 * 60 * 1000);
|
||||
|
||||
// 温度数据 (20-30度之间波动)
|
||||
testData.push({
|
||||
device_id: 2,
|
||||
farm_id: 1,
|
||||
sensor_type: 'temperature',
|
||||
value: 20 + Math.random() * 10,
|
||||
unit: '°C',
|
||||
status: 'normal',
|
||||
recorded_at: time
|
||||
});
|
||||
|
||||
// 湿度数据 (50-80%之间波动)
|
||||
testData.push({
|
||||
device_id: 3,
|
||||
farm_id: 1,
|
||||
sensor_type: 'humidity',
|
||||
value: 50 + Math.random() * 30,
|
||||
unit: '%',
|
||||
status: 'normal',
|
||||
recorded_at: time
|
||||
});
|
||||
}
|
||||
|
||||
// 批量插入数据
|
||||
await SensorData.bulkCreate(testData);
|
||||
console.log(`成功创建 ${testData.length} 条测试传感器数据`);
|
||||
|
||||
// 验证数据
|
||||
const temperatureCount = await SensorData.count({ where: { sensor_type: 'temperature' } });
|
||||
const humidityCount = await SensorData.count({ where: { sensor_type: 'humidity' } });
|
||||
|
||||
console.log(`温度数据条数: ${temperatureCount}`);
|
||||
console.log(`湿度数据条数: ${humidityCount}`);
|
||||
|
||||
// 显示最新的几条数据
|
||||
const latestData = await SensorData.findAll({
|
||||
limit: 5,
|
||||
order: [['recorded_at', 'DESC']],
|
||||
attributes: ['sensor_type', 'value', 'unit', 'recorded_at']
|
||||
});
|
||||
|
||||
console.log('\n最新的5条数据:');
|
||||
latestData.forEach(data => {
|
||||
console.log(`${data.sensor_type}: ${data.value}${data.unit} at ${data.recorded_at}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建测试数据时出错:', error);
|
||||
} finally {
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
48
backend/tools/data-management/create-test-user.js
Normal file
48
backend/tools/data-management/create-test-user.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const bcrypt = require('bcrypt');
|
||||
const { User } = require('./models');
|
||||
|
||||
async function createTestUser() {
|
||||
try {
|
||||
console.log('连接数据库...');
|
||||
|
||||
// 手动创建用户,直接设置加密密码
|
||||
const hashedPassword = await bcrypt.hash('123456', 10);
|
||||
console.log('生成的密码哈希:', hashedPassword);
|
||||
|
||||
// 删除现有的testuser3(如果存在)
|
||||
await User.destroy({
|
||||
where: { username: 'testuser3' }
|
||||
});
|
||||
|
||||
// 直接插入数据库,绕过模型钩子
|
||||
const user = await User.create({
|
||||
username: 'testuser3',
|
||||
email: 'test3@example.com',
|
||||
password: hashedPassword,
|
||||
status: 'active'
|
||||
}, {
|
||||
hooks: false // 禁用钩子
|
||||
});
|
||||
|
||||
console.log('用户创建成功:', {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const isValid = await bcrypt.compare('123456', user.password);
|
||||
console.log('密码验证结果:', isValid);
|
||||
|
||||
// 使用模型方法验证
|
||||
const isValidModel = await user.validPassword('123456');
|
||||
console.log('模型方法验证结果:', isValidModel);
|
||||
|
||||
} catch (error) {
|
||||
console.error('错误:', error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
createTestUser();
|
||||
115
backend/tools/data-management/fix-location-data.js
Normal file
115
backend/tools/data-management/fix-location-data.js
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 修复数据库中的location字段格式
|
||||
* 将JSON字符串转换为JSON对象
|
||||
* @file fix-location-data.js
|
||||
*/
|
||||
|
||||
const { Farm } = require('./models');
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
|
||||
async function fixLocationData() {
|
||||
try {
|
||||
console.log('连接数据库...');
|
||||
await sequelize.authenticate();
|
||||
|
||||
console.log('\n查询所有养殖场记录...');
|
||||
const farms = await Farm.findAll({
|
||||
attributes: ['id', 'name', 'location']
|
||||
});
|
||||
|
||||
console.log(`找到 ${farms.length} 条记录`);
|
||||
|
||||
let fixedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const farm of farms) {
|
||||
try {
|
||||
console.log(`\n处理 ID: ${farm.id}, 名称: ${farm.name}`);
|
||||
console.log(`原始 location: ${JSON.stringify(farm.location)}`);
|
||||
console.log(`location 类型: ${typeof farm.location}`);
|
||||
|
||||
let needsUpdate = false;
|
||||
let newLocation = {};
|
||||
|
||||
// 检查location字段的格式
|
||||
if (typeof farm.location === 'string') {
|
||||
// 如果是字符串,尝试解析为JSON
|
||||
try {
|
||||
newLocation = JSON.parse(farm.location);
|
||||
needsUpdate = true;
|
||||
console.log(` ✅ 解析JSON字符串成功: ${JSON.stringify(newLocation)}`);
|
||||
} catch (parseError) {
|
||||
console.log(` ❌ 解析JSON字符串失败: ${parseError.message}`);
|
||||
errorCount++;
|
||||
continue;
|
||||
}
|
||||
} else if (farm.location && typeof farm.location === 'object') {
|
||||
// 如果已经是对象,检查是否需要格式化
|
||||
newLocation = farm.location;
|
||||
console.log(` ✅ 已经是对象格式`);
|
||||
} else {
|
||||
// 如果为空或其他类型,设置为空对象
|
||||
newLocation = {};
|
||||
needsUpdate = true;
|
||||
console.log(` ⚠️ 设置为空对象`);
|
||||
}
|
||||
|
||||
// 验证经纬度数据
|
||||
if (newLocation.lng !== undefined) {
|
||||
newLocation.lng = parseFloat(newLocation.lng);
|
||||
}
|
||||
if (newLocation.lat !== undefined) {
|
||||
newLocation.lat = parseFloat(newLocation.lat);
|
||||
}
|
||||
|
||||
// 更新数据库记录
|
||||
if (needsUpdate) {
|
||||
await farm.update({ location: newLocation });
|
||||
console.log(` ✅ 更新成功: ${JSON.stringify(newLocation)}`);
|
||||
fixedCount++;
|
||||
} else {
|
||||
console.log(` ⏭️ 无需更新`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(` ❌ 处理记录 ${farm.id} 时出错:`, error.message);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log(`修复完成!`);
|
||||
console.log(`总记录数: ${farms.length}`);
|
||||
console.log(`修复成功: ${fixedCount}`);
|
||||
console.log(`修复失败: ${errorCount}`);
|
||||
console.log(`无需修复: ${farms.length - fixedCount - errorCount}`);
|
||||
|
||||
// 验证修复结果
|
||||
console.log('\n验证修复结果...');
|
||||
const verifyFarms = await Farm.findAll({
|
||||
attributes: ['id', 'name', 'location'],
|
||||
limit: 5
|
||||
});
|
||||
|
||||
verifyFarms.forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Location类型: ${typeof farm.location}, 值: ${JSON.stringify(farm.location)}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('修复失败:', error.message);
|
||||
if (error.sql) {
|
||||
console.error('SQL:', error.sql);
|
||||
}
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行修复
|
||||
if (require.main === module) {
|
||||
console.log('开始修复数据库中的location字段格式...');
|
||||
fixLocationData();
|
||||
}
|
||||
|
||||
module.exports = { fixLocationData };
|
||||
23
backend/tools/data-management/fix-migration-types.js
Normal file
23
backend/tools/data-management/fix-migration-types.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const migrationFile = path.join(__dirname, 'migrations/20230101000000_initial_schema.js');
|
||||
|
||||
// 读取迁移文件
|
||||
let content = fs.readFileSync(migrationFile, 'utf8');
|
||||
|
||||
// 替换所有的 Sequelize. 为 DataTypes.
|
||||
content = content.replace(/Sequelize\.STRING/g, 'DataTypes.STRING');
|
||||
content = content.replace(/Sequelize\.INTEGER/g, 'DataTypes.INTEGER');
|
||||
content = content.replace(/Sequelize\.TEXT/g, 'DataTypes.TEXT');
|
||||
content = content.replace(/Sequelize\.DECIMAL/g, 'DataTypes.DECIMAL');
|
||||
content = content.replace(/Sequelize\.ENUM/g, 'DataTypes.ENUM');
|
||||
content = content.replace(/Sequelize\.DATE/g, 'DataTypes.DATE');
|
||||
|
||||
// 修复literal函数
|
||||
content = content.replace(/DataTypes\.literal/g, 'literal');
|
||||
|
||||
// 写入修复后的文件
|
||||
fs.writeFileSync(migrationFile, content, 'utf8');
|
||||
|
||||
console.log('✅ 迁移文件数据类型已修复');
|
||||
95
backend/tools/data-management/fix-orphaned-foreign-keys.js
Normal file
95
backend/tools/data-management/fix-orphaned-foreign-keys.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
|
||||
async function fixOrphanedForeignKeys() {
|
||||
try {
|
||||
console.log('修复孤立外键数据...');
|
||||
|
||||
// 首先检查是否已存在ID为12的农场
|
||||
const [existingFarm] = await sequelize.query(
|
||||
'SELECT id FROM farms WHERE id = 12'
|
||||
);
|
||||
|
||||
if (existingFarm.length === 0) {
|
||||
console.log('创建ID为12的农场记录...');
|
||||
|
||||
// 创建ID为12的农场记录
|
||||
await sequelize.query(`
|
||||
INSERT INTO farms (id, name, type, location, address, contact, phone, status, created_at, updated_at)
|
||||
VALUES (
|
||||
12,
|
||||
'西部牧场',
|
||||
'综合养殖场',
|
||||
'{"lat":36.0611,"lng":103.8343}',
|
||||
'兰州市城关区西部路12号',
|
||||
'李十二',
|
||||
'13800138012',
|
||||
'active',
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
console.log('✓ 成功创建ID为12的农场记录');
|
||||
} else {
|
||||
console.log('ID为12的农场记录已存在');
|
||||
}
|
||||
|
||||
// 验证修复结果
|
||||
const [devicesCount] = await sequelize.query(
|
||||
'SELECT COUNT(*) as count FROM devices WHERE farm_id = 12'
|
||||
);
|
||||
|
||||
const [alertsCount] = await sequelize.query(
|
||||
'SELECT COUNT(*) as count FROM alerts WHERE farm_id = 12'
|
||||
);
|
||||
|
||||
const [animalsCount] = await sequelize.query(
|
||||
'SELECT COUNT(*) as count FROM animals WHERE farm_id = 12'
|
||||
);
|
||||
|
||||
console.log('\n修复后的数据统计:');
|
||||
console.log('farm_id=12的devices记录:', devicesCount[0].count);
|
||||
console.log('farm_id=12的alerts记录:', alertsCount[0].count);
|
||||
console.log('farm_id=12的animals记录:', animalsCount[0].count);
|
||||
|
||||
// 检查外键完整性
|
||||
const [orphanedDevices] = await sequelize.query(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM devices d
|
||||
LEFT JOIN farms f ON d.farm_id = f.id
|
||||
WHERE f.id IS NULL
|
||||
`);
|
||||
|
||||
const [orphanedAlerts] = await sequelize.query(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM alerts a
|
||||
LEFT JOIN farms f ON a.farm_id = f.id
|
||||
WHERE f.id IS NULL
|
||||
`);
|
||||
|
||||
const [orphanedAnimals] = await sequelize.query(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM animals an
|
||||
LEFT JOIN farms f ON an.farm_id = f.id
|
||||
WHERE f.id IS NULL
|
||||
`);
|
||||
|
||||
console.log('\n外键完整性检查:');
|
||||
console.log('孤立的devices记录:', orphanedDevices[0].count);
|
||||
console.log('孤立的alerts记录:', orphanedAlerts[0].count);
|
||||
console.log('孤立的animals记录:', orphanedAnimals[0].count);
|
||||
|
||||
if (orphanedDevices[0].count === 0 && orphanedAlerts[0].count === 0 && orphanedAnimals[0].count === 0) {
|
||||
console.log('\n✓ 外键完整性修复成功!');
|
||||
} else {
|
||||
console.log('\n⚠ 仍存在孤立外键记录,需要进一步检查');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('修复失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
fixOrphanedForeignKeys();
|
||||
35
backend/tools/data-management/fix_admin_password.js
Normal file
35
backend/tools/data-management/fix_admin_password.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const { User } = require('./models');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
async function fixAdminPassword() {
|
||||
try {
|
||||
// 查找 admin 用户
|
||||
const admin = await User.findOne({ where: { username: 'admin' } });
|
||||
|
||||
if (!admin) {
|
||||
console.log('未找到 admin 用户');
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成正确的密码哈希
|
||||
const hashedPassword = await bcrypt.hash('123456', 10);
|
||||
|
||||
// 更新密码
|
||||
await admin.update({ password: hashedPassword });
|
||||
|
||||
console.log('Admin 密码已更新为正确的哈希值');
|
||||
console.log('用户名: admin');
|
||||
console.log('密码: 123456');
|
||||
|
||||
// 验证密码
|
||||
const isValid = await bcrypt.compare('123456', hashedPassword);
|
||||
console.log('密码验证:', isValid ? '成功' : '失败');
|
||||
|
||||
} catch (error) {
|
||||
console.error('修复密码失败:', error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
fixAdminPassword();
|
||||
272
backend/tools/data-management/generate-test-data.js
Normal file
272
backend/tools/data-management/generate-test-data.js
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* 生成产品管理和订单管理测试数据
|
||||
* @file generate-test-data.js
|
||||
* @description 为产品和订单模块生成测试数据并插入数据库
|
||||
*/
|
||||
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
const { Product, Order, OrderItem, User } = require('./models');
|
||||
|
||||
// 产品测试数据
|
||||
const productData = [
|
||||
{
|
||||
name: '有机苹果',
|
||||
description: '新鲜有机苹果,来自山东烟台,口感甜脆,营养丰富',
|
||||
price: 1200, // 12.00元,以分为单位
|
||||
stock: 500,
|
||||
image_url: '/images/products/apple.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '优质大米',
|
||||
description: '东北优质大米,粒粒饱满,口感香甜,5kg装',
|
||||
price: 3500, // 35.00元
|
||||
stock: 200,
|
||||
image_url: '/images/products/rice.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '新鲜鸡蛋',
|
||||
description: '农场散养鸡蛋,30枚装,营养价值高',
|
||||
price: 2800, // 28.00元
|
||||
stock: 150,
|
||||
image_url: '/images/products/eggs.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '有机蔬菜礼盒',
|
||||
description: '精选有机蔬菜组合,包含白菜、萝卜、青菜等',
|
||||
price: 4500, // 45.00元
|
||||
stock: 80,
|
||||
image_url: '/images/products/vegetable-box.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '纯天然蜂蜜',
|
||||
description: '纯天然野花蜜,500g装,无添加剂',
|
||||
price: 6800, // 68.00元
|
||||
stock: 120,
|
||||
image_url: '/images/products/honey.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '有机牛奶',
|
||||
description: '有机牧场牛奶,1L装,营养丰富',
|
||||
price: 1800, // 18.00元
|
||||
stock: 300,
|
||||
image_url: '/images/products/milk.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '农家土鸡',
|
||||
description: '农家散养土鸡,约2kg,肉质鲜美',
|
||||
price: 8500, // 85.00元
|
||||
stock: 50,
|
||||
image_url: '/images/products/chicken.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '新鲜玉米',
|
||||
description: '甜玉米,10根装,口感甜嫩',
|
||||
price: 1500, // 15.00元
|
||||
stock: 200,
|
||||
image_url: '/images/products/corn.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '有机胡萝卜',
|
||||
description: '有机胡萝卜,2kg装,营养丰富',
|
||||
price: 1200, // 12.00元
|
||||
stock: 180,
|
||||
image_url: '/images/products/carrot.jpg',
|
||||
is_active: true
|
||||
},
|
||||
{
|
||||
name: '精品茶叶',
|
||||
description: '高山绿茶,250g装,香气清雅',
|
||||
price: 12800, // 128.00元
|
||||
stock: 60,
|
||||
image_url: '/images/products/tea.jpg',
|
||||
is_active: true
|
||||
}
|
||||
];
|
||||
|
||||
// 生成随机订单数据的辅助函数
|
||||
function generateRandomOrders(users, products, orderCount = 20) {
|
||||
const orders = [];
|
||||
const orderStatuses = ['pending', 'processing', 'shipped', 'delivered', 'cancelled'];
|
||||
const paymentStatuses = ['unpaid', 'paid', 'refunded'];
|
||||
const addresses = [
|
||||
'北京市朝阳区建国路88号',
|
||||
'上海市浦东新区陆家嘴环路1000号',
|
||||
'广州市天河区珠江新城花城大道123号',
|
||||
'深圳市南山区科技园南区456号',
|
||||
'杭州市西湖区文三路789号',
|
||||
'成都市锦江区春熙路321号',
|
||||
'武汉市江汉区中山大道654号',
|
||||
'西安市雁塔区高新路987号'
|
||||
];
|
||||
|
||||
for (let i = 0; i < orderCount; i++) {
|
||||
const user = users[Math.floor(Math.random() * users.length)];
|
||||
const status = orderStatuses[Math.floor(Math.random() * orderStatuses.length)];
|
||||
const paymentStatus = paymentStatuses[Math.floor(Math.random() * paymentStatuses.length)];
|
||||
const address = addresses[Math.floor(Math.random() * addresses.length)];
|
||||
|
||||
// 创建订单基本信息
|
||||
const order = {
|
||||
user_id: user.id,
|
||||
total_amount: 0, // 稍后计算
|
||||
status: status,
|
||||
payment_status: paymentStatus,
|
||||
shipping_address: address,
|
||||
created_at: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000) // 最近30天内的随机时间
|
||||
};
|
||||
|
||||
// 为每个订单生成1-5个订单项
|
||||
const itemCount = Math.floor(Math.random() * 5) + 1;
|
||||
const orderItems = [];
|
||||
let totalAmount = 0;
|
||||
|
||||
for (let j = 0; j < itemCount; j++) {
|
||||
const product = products[Math.floor(Math.random() * products.length)];
|
||||
const quantity = Math.floor(Math.random() * 3) + 1; // 1-3个
|
||||
const price = product.price;
|
||||
|
||||
orderItems.push({
|
||||
product_id: product.id,
|
||||
quantity: quantity,
|
||||
price: price
|
||||
});
|
||||
|
||||
totalAmount += price * quantity;
|
||||
}
|
||||
|
||||
order.total_amount = totalAmount;
|
||||
order.items = orderItems;
|
||||
orders.push(order);
|
||||
}
|
||||
|
||||
return orders;
|
||||
}
|
||||
|
||||
// 主函数:生成并插入测试数据
|
||||
async function generateTestData() {
|
||||
try {
|
||||
console.log('开始生成测试数据...');
|
||||
|
||||
// 连接数据库
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 获取现有用户
|
||||
const users = await User.findAll();
|
||||
if (users.length === 0) {
|
||||
console.log('警告:数据库中没有用户数据,请先创建用户');
|
||||
return;
|
||||
}
|
||||
console.log(`找到 ${users.length} 个用户`);
|
||||
|
||||
// 清理现有的测试数据(可选)
|
||||
console.log('清理现有测试数据...');
|
||||
await OrderItem.destroy({ where: {} });
|
||||
await Order.destroy({ where: {} });
|
||||
await Product.destroy({ where: {} });
|
||||
console.log('清理完成');
|
||||
|
||||
// 插入产品数据
|
||||
console.log('插入产品数据...');
|
||||
const createdProducts = await Product.bulkCreate(productData);
|
||||
console.log(`成功插入 ${createdProducts.length} 个产品`);
|
||||
|
||||
// 生成订单数据
|
||||
console.log('生成订单数据...');
|
||||
const orderData = generateRandomOrders(users, createdProducts, 25);
|
||||
|
||||
// 使用事务插入订单和订单项
|
||||
console.log('插入订单和订单项数据...');
|
||||
let orderCount = 0;
|
||||
let orderItemCount = 0;
|
||||
|
||||
for (const orderInfo of orderData) {
|
||||
await sequelize.transaction(async (t) => {
|
||||
// 创建订单
|
||||
const order = await Order.create({
|
||||
user_id: orderInfo.user_id,
|
||||
total_amount: orderInfo.total_amount,
|
||||
status: orderInfo.status,
|
||||
payment_status: orderInfo.payment_status,
|
||||
shipping_address: orderInfo.shipping_address,
|
||||
created_at: orderInfo.created_at
|
||||
}, { transaction: t });
|
||||
|
||||
// 创建订单项
|
||||
const orderItems = orderInfo.items.map(item => ({
|
||||
order_id: order.id,
|
||||
product_id: item.product_id,
|
||||
quantity: item.quantity,
|
||||
price: item.price
|
||||
}));
|
||||
|
||||
await OrderItem.bulkCreate(orderItems, { transaction: t });
|
||||
|
||||
orderCount++;
|
||||
orderItemCount += orderItems.length;
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`成功插入 ${orderCount} 个订单`);
|
||||
console.log(`成功插入 ${orderItemCount} 个订单项`);
|
||||
|
||||
// 显示统计信息
|
||||
console.log('\n=== 数据统计 ===');
|
||||
const productCount = await Product.count();
|
||||
const totalOrderCount = await Order.count();
|
||||
const totalOrderItemCount = await OrderItem.count();
|
||||
|
||||
console.log(`产品总数: ${productCount}`);
|
||||
console.log(`订单总数: ${totalOrderCount}`);
|
||||
console.log(`订单项总数: ${totalOrderItemCount}`);
|
||||
|
||||
// 显示一些示例数据
|
||||
console.log('\n=== 示例产品 ===');
|
||||
const sampleProducts = await Product.findAll({ limit: 3 });
|
||||
sampleProducts.forEach(product => {
|
||||
console.log(`${product.name} - ¥${(product.price / 100).toFixed(2)} - 库存: ${product.stock}`);
|
||||
});
|
||||
|
||||
console.log('\n=== 示例订单 ===');
|
||||
const sampleOrders = await Order.findAll({
|
||||
limit: 3,
|
||||
include: [
|
||||
{ model: User, as: 'user', attributes: ['username'] },
|
||||
{
|
||||
model: OrderItem,
|
||||
as: 'orderItems',
|
||||
include: [{ model: Product, as: 'product', attributes: ['name'] }]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
sampleOrders.forEach(order => {
|
||||
console.log(`订单 #${order.id} - 用户: ${order.user.username} - 总额: ¥${(order.total_amount / 100).toFixed(2)} - 状态: ${order.status}`);
|
||||
order.orderItems.forEach(item => {
|
||||
console.log(` - ${item.product.name} x ${item.quantity}`);
|
||||
});
|
||||
});
|
||||
|
||||
console.log('\n测试数据生成完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成测试数据失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 运行脚本
|
||||
if (require.main === module) {
|
||||
generateTestData();
|
||||
}
|
||||
|
||||
module.exports = { generateTestData, productData };
|
||||
133
backend/tools/data-management/import-alerts-sensors.js
Normal file
133
backend/tools/data-management/import-alerts-sensors.js
Normal file
@@ -0,0 +1,133 @@
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
|
||||
async function importAlertsAndSensors() {
|
||||
try {
|
||||
console.log('开始导入预警和传感器数据...');
|
||||
|
||||
// 连接数据库
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 获取所有设备
|
||||
const devices = await sequelize.query('SELECT id, farm_id, type FROM devices', { type: QueryTypes.SELECT });
|
||||
console.log(`获取到 ${devices.length} 个设备`);
|
||||
|
||||
// 1. 插入预警数据
|
||||
const alertData = [];
|
||||
const alertTypes = ['温度异常', '湿度异常', '设备故障', '动物健康', '饲料不足', '水源问题'];
|
||||
const alertLevels = ['low', 'medium', 'high', 'critical'];
|
||||
const alertStatuses = ['active', 'acknowledged', 'resolved'];
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const device = devices[Math.floor(Math.random() * devices.length)];
|
||||
const type = alertTypes[Math.floor(Math.random() * alertTypes.length)];
|
||||
const level = alertLevels[Math.floor(Math.random() * alertLevels.length)];
|
||||
const status = alertStatuses[Math.floor(Math.random() * alertStatuses.length)];
|
||||
const createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000); // 过去30天内
|
||||
|
||||
alertData.push({
|
||||
type: type,
|
||||
level: level,
|
||||
message: `设备 ${device.type} 发生 ${type},需要及时处理`,
|
||||
status: status,
|
||||
farm_id: device.farm_id,
|
||||
device_id: device.id,
|
||||
resolved_at: status === 'resolved' ? new Date(createdAt.getTime() + Math.random() * 7 * 24 * 60 * 60 * 1000) : null,
|
||||
resolved_by: status === 'resolved' ? 1 : null,
|
||||
resolution_notes: status === 'resolved' ? '问题已解决,设备运行正常' : null,
|
||||
created_at: createdAt,
|
||||
updated_at: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
if (alertData.length > 0) {
|
||||
await sequelize.query(
|
||||
`INSERT INTO alerts (type, level, message, status, farm_id, device_id, resolved_at, resolved_by, resolution_notes, created_at, updated_at) VALUES ${alertData.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ')}`,
|
||||
{
|
||||
replacements: alertData.flatMap(alert => [
|
||||
alert.type, alert.level, alert.message, alert.status, alert.farm_id,
|
||||
alert.device_id, alert.resolved_at, alert.resolved_by, alert.resolution_notes,
|
||||
alert.created_at, alert.updated_at
|
||||
]),
|
||||
type: QueryTypes.INSERT
|
||||
}
|
||||
);
|
||||
console.log('预警数据导入成功');
|
||||
}
|
||||
|
||||
// 2. 插入传感器数据
|
||||
const sensorData = [];
|
||||
|
||||
// 为每个设备生成过去7天的传感器数据
|
||||
for (const device of devices) {
|
||||
for (let day = 0; day < 7; day++) {
|
||||
for (let hour = 0; hour < 24; hour += 2) { // 每2小时一条记录
|
||||
const timestamp = new Date();
|
||||
timestamp.setDate(timestamp.getDate() - day);
|
||||
timestamp.setHours(hour, 0, 0, 0);
|
||||
|
||||
// 为每个设备生成多个传感器数据记录
|
||||
const sensorTypes = [
|
||||
{ type: 'temperature', unit: '°C', min: 10, max: 30 },
|
||||
{ type: 'humidity', unit: '%', min: 30, max: 80 },
|
||||
{ type: 'light_intensity', unit: 'lux', min: 0, max: 1000 },
|
||||
{ type: 'air_quality', unit: 'AQI', min: 50, max: 100 },
|
||||
{ type: 'noise_level', unit: 'dB', min: 20, max: 50 },
|
||||
{ type: 'co2_level', unit: 'ppm', min: 300, max: 800 }
|
||||
];
|
||||
|
||||
for (const sensor of sensorTypes) {
|
||||
const value = Math.round((Math.random() * (sensor.max - sensor.min) + sensor.min) * 10) / 10;
|
||||
sensorData.push({
|
||||
device_id: device.id,
|
||||
farm_id: device.farm_id,
|
||||
sensor_type: sensor.type,
|
||||
value: value,
|
||||
unit: sensor.unit,
|
||||
status: Math.random() > 0.9 ? (Math.random() > 0.5 ? 'warning' : 'error') : 'normal',
|
||||
recorded_at: timestamp,
|
||||
created_at: timestamp,
|
||||
updated_at: timestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分批插入传感器数据(每次100条)
|
||||
const batchSize = 100;
|
||||
for (let i = 0; i < sensorData.length; i += batchSize) {
|
||||
const batch = sensorData.slice(i, i + batchSize);
|
||||
|
||||
await sequelize.query(
|
||||
`INSERT INTO sensor_data (device_id, farm_id, sensor_type, value, unit, status, recorded_at, created_at, updated_at) VALUES ${batch.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ')}`,
|
||||
{
|
||||
replacements: batch.flatMap(sensor => [
|
||||
sensor.device_id, sensor.farm_id, sensor.sensor_type, sensor.value, sensor.unit,
|
||||
sensor.status, sensor.recorded_at, sensor.created_at, sensor.updated_at
|
||||
]),
|
||||
type: QueryTypes.INSERT
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`传感器数据批次 ${Math.floor(i / batchSize) + 1} 导入成功 (${batch.length} 条记录)`);
|
||||
}
|
||||
|
||||
// 3. 验证导入结果
|
||||
const [alertCount] = await sequelize.query('SELECT COUNT(*) as count FROM alerts', { type: QueryTypes.SELECT });
|
||||
const [sensorCount] = await sequelize.query('SELECT COUNT(*) as count FROM sensor_data', { type: QueryTypes.SELECT });
|
||||
|
||||
console.log('\n=== 预警和传感器数据导入完成 ===');
|
||||
console.log(`预警总数: ${alertCount.count}`);
|
||||
console.log(`传感器数据总数: ${sensorCount.count}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('数据导入失败:', error.message);
|
||||
console.error('详细错误:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
importAlertsAndSensors();
|
||||
165
backend/tools/data-management/import-data.js
Normal file
165
backend/tools/data-management/import-data.js
Normal file
@@ -0,0 +1,165 @@
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
|
||||
async function importData() {
|
||||
try {
|
||||
console.log('开始导入数据...');
|
||||
|
||||
// 连接数据库
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 1. 插入养殖场数据
|
||||
const farmData = [
|
||||
{
|
||||
name: '东方养殖场',
|
||||
type: 'pig',
|
||||
location: JSON.stringify({ lat: 39.9042, lng: 116.4074, address: '北京市朝阳区' }),
|
||||
address: '北京市朝阳区东三环北路',
|
||||
contact: '张三',
|
||||
phone: '13800138001',
|
||||
status: 'active',
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
},
|
||||
{
|
||||
name: '西部牧场',
|
||||
type: 'cattle',
|
||||
location: JSON.stringify({ lat: 30.5728, lng: 104.0668, address: '四川省成都市' }),
|
||||
address: '四川省成都市高新区天府大道',
|
||||
contact: '李四',
|
||||
phone: '13800138002',
|
||||
status: 'active',
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
},
|
||||
{
|
||||
name: '南方羊场',
|
||||
type: 'sheep',
|
||||
location: JSON.stringify({ lat: 23.1291, lng: 113.2644, address: '广东省广州市' }),
|
||||
address: '广东省广州市天河区珠江新城',
|
||||
contact: '王五',
|
||||
phone: '13800138003',
|
||||
status: 'active',
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
}
|
||||
];
|
||||
|
||||
await sequelize.query(
|
||||
`INSERT INTO farms (name, type, location, address, contact, phone, status, created_at, updated_at) VALUES ${farmData.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ')}`,
|
||||
{
|
||||
replacements: farmData.flatMap(farm => [
|
||||
farm.name, farm.type, farm.location, farm.address, farm.contact,
|
||||
farm.phone, farm.status, farm.created_at, farm.updated_at
|
||||
]),
|
||||
type: QueryTypes.INSERT
|
||||
}
|
||||
);
|
||||
console.log('养殖场数据导入成功');
|
||||
|
||||
// 获取刚插入的养殖场ID
|
||||
const farms = await sequelize.query('SELECT id, name, type FROM farms WHERE name IN ("东方养殖场", "西部牧场", "南方羊场")', { type: QueryTypes.SELECT });
|
||||
console.log('获取到养殖场:', farms);
|
||||
|
||||
// 2. 插入动物数据
|
||||
const animalData = [];
|
||||
const animalTypes = {
|
||||
'pig': ['猪', '母猪', '仔猪'],
|
||||
'cattle': ['肉牛', '奶牛', '小牛'],
|
||||
'sheep': ['山羊', '绵羊', '羔羊']
|
||||
};
|
||||
|
||||
for (const farm of farms) {
|
||||
const types = animalTypes[farm.type] || ['未知'];
|
||||
for (const type of types) {
|
||||
animalData.push({
|
||||
type: type,
|
||||
count: Math.floor(Math.random() * 100) + 50,
|
||||
farm_id: farm.id,
|
||||
health_status: Math.random() > 0.2 ? 'healthy' : 'sick',
|
||||
last_inspection: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000),
|
||||
notes: `${type}群体健康状况良好`,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (animalData.length > 0) {
|
||||
await sequelize.query(
|
||||
`INSERT INTO animals (type, count, farm_id, health_status, last_inspection, notes, created_at, updated_at) VALUES ${animalData.map(() => '(?, ?, ?, ?, ?, ?, ?, ?)').join(', ')}`,
|
||||
{
|
||||
replacements: animalData.flatMap(animal => [
|
||||
animal.type, animal.count, animal.farm_id, animal.health_status,
|
||||
animal.last_inspection, animal.notes, animal.created_at, animal.updated_at
|
||||
]),
|
||||
type: QueryTypes.INSERT
|
||||
}
|
||||
);
|
||||
console.log('动物数据导入成功');
|
||||
}
|
||||
|
||||
// 3. 插入设备数据
|
||||
const deviceData = [];
|
||||
const deviceTypes = ['温度传感器', '湿度传感器', '摄像头', '喂食器', '饮水器'];
|
||||
const deviceStatuses = ['online', 'offline', 'maintenance'];
|
||||
|
||||
for (const farm of farms) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const type = deviceTypes[Math.floor(Math.random() * deviceTypes.length)];
|
||||
const status = deviceStatuses[Math.floor(Math.random() * deviceStatuses.length)];
|
||||
|
||||
deviceData.push({
|
||||
name: `${type}_${farm.name}_${String(i + 1).padStart(3, '0')}`,
|
||||
type: type,
|
||||
status: status,
|
||||
farm_id: farm.id,
|
||||
last_maintenance: new Date(Date.now() - Math.random() * 90 * 24 * 60 * 60 * 1000),
|
||||
installation_date: new Date(Date.now() - Math.random() * 730 * 24 * 60 * 60 * 1000),
|
||||
metrics: JSON.stringify({
|
||||
temperature: Math.round((Math.random() * 15 + 15) * 10) / 10,
|
||||
humidity: Math.round((Math.random() * 40 + 40) * 10) / 10,
|
||||
battery: Math.round(Math.random() * 100),
|
||||
signal_strength: Math.round(Math.random() * 100)
|
||||
}),
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceData.length > 0) {
|
||||
await sequelize.query(
|
||||
`INSERT INTO devices (name, type, status, farm_id, last_maintenance, installation_date, metrics, created_at, updated_at) VALUES ${deviceData.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ')}`,
|
||||
{
|
||||
replacements: deviceData.flatMap(device => [
|
||||
device.name, device.type, device.status, device.farm_id,
|
||||
device.last_maintenance, device.installation_date, device.metrics,
|
||||
device.created_at, device.updated_at
|
||||
]),
|
||||
type: QueryTypes.INSERT
|
||||
}
|
||||
);
|
||||
console.log('设备数据导入成功');
|
||||
}
|
||||
|
||||
// 4. 验证导入结果
|
||||
const [farmCount] = await sequelize.query('SELECT COUNT(*) as count FROM farms', { type: QueryTypes.SELECT });
|
||||
const [animalCount] = await sequelize.query('SELECT COUNT(*) as count FROM animals', { type: QueryTypes.SELECT });
|
||||
const [deviceCount] = await sequelize.query('SELECT COUNT(*) as count FROM devices', { type: QueryTypes.SELECT });
|
||||
|
||||
console.log('\n=== 数据导入完成 ===');
|
||||
console.log(`养殖场总数: ${farmCount.count}`);
|
||||
console.log(`动物总数: ${animalCount.count}`);
|
||||
console.log(`设备总数: ${deviceCount.count}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('数据导入失败:', error.message);
|
||||
console.error('详细错误:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
importData();
|
||||
164
backend/tools/data-management/import-farms-static-data.js
Normal file
164
backend/tools/data-management/import-farms-static-data.js
Normal file
@@ -0,0 +1,164 @@
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
const { Farm } = require('./models');
|
||||
|
||||
/**
|
||||
* 导入farms静态数据到数据库
|
||||
* 包含API路由中的静态数据和种子文件中的详细数据
|
||||
*/
|
||||
async function importFarmsStaticData() {
|
||||
try {
|
||||
console.log('开始导入farms静态数据到数据库...');
|
||||
|
||||
// 检查当前farms表状态
|
||||
const existingFarms = await Farm.findAll();
|
||||
console.log(`当前farms表中有 ${existingFarms.length} 条记录`);
|
||||
|
||||
// API路由中的静态数据
|
||||
const apiStaticData = [
|
||||
{ id: 1, name: '宁夏农场1', location: '银川市' },
|
||||
{ id: 2, name: '宁夏农场2', location: '石嘴山市' },
|
||||
{ id: 3, name: '宁夏农场3', location: '吴忠市' }
|
||||
];
|
||||
|
||||
// 种子文件中的详细数据
|
||||
const seedData = [
|
||||
{
|
||||
name: '阳光农场',
|
||||
type: '养猪场',
|
||||
location: JSON.stringify({ lat: 39.9042, lng: 116.4074 }),
|
||||
address: '北京市朝阳区农场路1号',
|
||||
contact: '张三',
|
||||
phone: '13800138001',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '绿野牧场',
|
||||
type: '养牛场',
|
||||
location: JSON.stringify({ lat: 31.2304, lng: 121.4737 }),
|
||||
address: '上海市浦东新区牧场路2号',
|
||||
contact: '李四',
|
||||
phone: '13800138002',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '山谷羊场',
|
||||
type: '养羊场',
|
||||
location: JSON.stringify({ lat: 23.1291, lng: 113.2644 }),
|
||||
address: '广州市天河区山谷路3号',
|
||||
contact: '王五',
|
||||
phone: '13800138003',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '蓝天养鸡场',
|
||||
type: '养鸡场',
|
||||
location: JSON.stringify({ lat: 30.2741, lng: 120.1551 }),
|
||||
address: '杭州市西湖区蓝天路4号',
|
||||
contact: '赵六',
|
||||
phone: '13800138004',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '金山养鸭场',
|
||||
type: '养鸭场',
|
||||
location: JSON.stringify({ lat: 36.0611, lng: 103.8343 }),
|
||||
address: '兰州市城关区金山路5号',
|
||||
contact: '孙七',
|
||||
phone: '13800138005',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '银河渔场',
|
||||
type: '渔场',
|
||||
location: JSON.stringify({ lat: 29.5647, lng: 106.5507 }),
|
||||
address: '重庆市渝中区银河路6号',
|
||||
contact: '周八',
|
||||
phone: '13800138006',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '星空牧场',
|
||||
type: '综合养殖场',
|
||||
location: JSON.stringify({ lat: 34.3416, lng: 108.9398 }),
|
||||
address: '西安市雁塔区星空路7号',
|
||||
contact: '吴九',
|
||||
phone: '13800138007',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '彩虹农庄',
|
||||
type: '有机农场',
|
||||
location: JSON.stringify({ lat: 25.0478, lng: 102.7123 }),
|
||||
address: '昆明市五华区彩虹路8号',
|
||||
contact: '郑十',
|
||||
phone: '13800138008',
|
||||
status: 'active'
|
||||
}
|
||||
];
|
||||
|
||||
// 合并API静态数据和种子数据
|
||||
const allFarmsData = [];
|
||||
|
||||
// 首先添加API静态数据(转换为完整格式)
|
||||
for (const apiData of apiStaticData) {
|
||||
allFarmsData.push({
|
||||
name: apiData.name,
|
||||
type: '综合农场', // 默认类型
|
||||
location: JSON.stringify({ lat: 38.4872, lng: 106.2309 }), // 宁夏地区坐标
|
||||
address: `宁夏回族自治区${apiData.location}`,
|
||||
contact: '管理员',
|
||||
phone: '400-000-0000',
|
||||
status: 'active'
|
||||
});
|
||||
}
|
||||
|
||||
// 然后添加种子数据
|
||||
allFarmsData.push(...seedData);
|
||||
|
||||
console.log(`准备导入 ${allFarmsData.length} 条farms数据`);
|
||||
|
||||
// 开始事务
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
// 清空现有数据
|
||||
await Farm.destroy({ where: {}, transaction });
|
||||
console.log('已清空现有farms数据');
|
||||
|
||||
// 重置自增ID
|
||||
await sequelize.query('ALTER TABLE farms AUTO_INCREMENT = 1', { transaction });
|
||||
console.log('已重置farms表自增ID');
|
||||
|
||||
// 批量插入新数据
|
||||
const createdFarms = await Farm.bulkCreate(allFarmsData, { transaction });
|
||||
console.log(`成功插入 ${createdFarms.length} 条farms数据`);
|
||||
|
||||
// 提交事务
|
||||
await transaction.commit();
|
||||
|
||||
// 验证导入结果
|
||||
const finalFarms = await Farm.findAll({ order: [['id', 'ASC']] });
|
||||
console.log('\n导入后的farms数据:');
|
||||
finalFarms.forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Name: ${farm.name}, Type: ${farm.type}, Location: ${farm.address}`);
|
||||
});
|
||||
|
||||
console.log(`\n✅ 成功导入 ${finalFarms.length} 条farms静态数据到数据库`);
|
||||
|
||||
} catch (error) {
|
||||
// 回滚事务
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 导入farms静态数据失败:', error.message);
|
||||
console.error('错误详情:', error);
|
||||
} finally {
|
||||
// 关闭数据库连接
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 执行导入
|
||||
importFarmsStaticData();
|
||||
124
backend/tools/data-management/insert-environment-data.js
Normal file
124
backend/tools/data-management/insert-environment-data.js
Normal file
@@ -0,0 +1,124 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
require('dotenv').config();
|
||||
|
||||
async function insertEnvironmentData() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
// 创建数据库连接
|
||||
connection = await mysql.createConnection({
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME
|
||||
});
|
||||
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 创建环境监测时刻表
|
||||
const createTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS environment_schedules (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
farm_id INT NOT NULL,
|
||||
device_id VARCHAR(50),
|
||||
schedule_time TIME NOT NULL,
|
||||
temperature DECIMAL(5,2),
|
||||
humidity DECIMAL(5,2),
|
||||
monitoring_date DATE NOT NULL,
|
||||
status ENUM('active', 'inactive', 'maintenance') DEFAULT 'active',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_farm_date (farm_id, monitoring_date),
|
||||
INDEX idx_schedule_time (schedule_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
`;
|
||||
|
||||
await connection.execute(createTableSQL);
|
||||
console.log('环境监测时刻表创建成功');
|
||||
|
||||
// 生成过去7天的数据
|
||||
const scheduleData = [];
|
||||
const currentDate = new Date();
|
||||
|
||||
// 每天的监测时间点
|
||||
const scheduleTimes = ['06:00:00', '12:00:00', '18:00:00', '22:00:00'];
|
||||
|
||||
for (let day = 0; day < 7; day++) {
|
||||
const date = new Date(currentDate);
|
||||
date.setDate(date.getDate() - day);
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
|
||||
for (const time of scheduleTimes) {
|
||||
// 农场1的数据
|
||||
scheduleData.push([
|
||||
1, // farm_id
|
||||
'TEMP_001', // device_id
|
||||
time,
|
||||
(20 + Math.random() * 15).toFixed(2), // 温度 20-35°C
|
||||
(40 + Math.random() * 40).toFixed(2), // 湿度 40-80%
|
||||
dateStr,
|
||||
'active',
|
||||
`农场1 ${dateStr} ${time} 环境监测数据`
|
||||
]);
|
||||
|
||||
// 农场2的数据
|
||||
scheduleData.push([
|
||||
2, // farm_id
|
||||
'TEMP_002', // device_id
|
||||
time,
|
||||
(18 + Math.random() * 17).toFixed(2), // 温度 18-35°C
|
||||
(35 + Math.random() * 45).toFixed(2), // 湿度 35-80%
|
||||
dateStr,
|
||||
'active',
|
||||
`农场2 ${dateStr} ${time} 环境监测数据`
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量插入数据
|
||||
const insertSQL = `
|
||||
INSERT INTO environment_schedules
|
||||
(farm_id, device_id, schedule_time, temperature, humidity, monitoring_date, status, notes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
for (const data of scheduleData) {
|
||||
await connection.execute(insertSQL, data);
|
||||
}
|
||||
|
||||
console.log(`成功插入 ${scheduleData.length} 条环境监测数据`);
|
||||
|
||||
// 验证插入的数据
|
||||
const [rows] = await connection.execute(
|
||||
'SELECT COUNT(*) as count FROM environment_schedules'
|
||||
);
|
||||
console.log(`环境监测表总记录数: ${rows[0].count}`);
|
||||
|
||||
// 显示最近的几条记录
|
||||
const [recentRows] = await connection.execute(
|
||||
`SELECT farm_id, device_id, schedule_time, temperature, humidity,
|
||||
monitoring_date, status
|
||||
FROM environment_schedules
|
||||
ORDER BY monitoring_date DESC, schedule_time DESC
|
||||
LIMIT 5`
|
||||
);
|
||||
|
||||
console.log('\n最近的环境监测记录:');
|
||||
recentRows.forEach(row => {
|
||||
console.log(`农场${row.farm_id} | ${row.device_id} | ${row.monitoring_date} ${row.schedule_time} | 温度:${row.temperature}°C | 湿度:${row.humidity}% | 状态:${row.status}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('\n数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 运行脚本
|
||||
insertEnvironmentData();
|
||||
105
backend/tools/data-management/reorder-farms-id.js
Normal file
105
backend/tools/data-management/reorder-farms-id.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const { Farm, Animal, Device, Alert } = require('./models');
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
|
||||
async function reorderFarmsId() {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
console.log('开始重新排序farms表ID...');
|
||||
|
||||
// 1. 获取所有farms数据,按当前ID排序
|
||||
const farms = await Farm.findAll({
|
||||
order: [['id', 'ASC']],
|
||||
transaction
|
||||
});
|
||||
|
||||
console.log(`找到 ${farms.length} 个养殖场`);
|
||||
|
||||
// 2. 创建ID映射表
|
||||
const idMapping = {};
|
||||
farms.forEach((farm, index) => {
|
||||
const oldId = farm.id;
|
||||
const newId = index + 1;
|
||||
idMapping[oldId] = newId;
|
||||
console.log(`养殖场 "${farm.name}": ${oldId} -> ${newId}`);
|
||||
});
|
||||
|
||||
// 3. 临时禁用外键约束
|
||||
await sequelize.query('SET FOREIGN_KEY_CHECKS = 0', { transaction });
|
||||
|
||||
// 4. 创建临时表存储farms数据
|
||||
await sequelize.query(`
|
||||
CREATE TEMPORARY TABLE farms_temp AS
|
||||
SELECT * FROM farms ORDER BY id ASC
|
||||
`, { transaction });
|
||||
|
||||
// 5. 清空farms表
|
||||
await sequelize.query('DELETE FROM farms', { transaction });
|
||||
|
||||
// 6. 重置自增ID
|
||||
await sequelize.query('ALTER TABLE farms AUTO_INCREMENT = 1', { transaction });
|
||||
|
||||
// 7. 按新顺序插入数据
|
||||
for (let i = 0; i < farms.length; i++) {
|
||||
const farm = farms[i];
|
||||
const newId = i + 1;
|
||||
|
||||
await sequelize.query(`
|
||||
INSERT INTO farms (id, name, type, location, address, contact, phone, status, created_at, updated_at)
|
||||
SELECT ${newId}, name, type, location, address, contact, phone, status, created_at, updated_at
|
||||
FROM farms_temp WHERE id = ${farm.id}
|
||||
`, { transaction });
|
||||
}
|
||||
|
||||
// 8. 更新animals表的farm_id
|
||||
console.log('\n更新animals表的farm_id...');
|
||||
for (const [oldId, newId] of Object.entries(idMapping)) {
|
||||
const result = await sequelize.query(
|
||||
'UPDATE animals SET farm_id = ? WHERE farm_id = ?',
|
||||
{ replacements: [newId, oldId], transaction }
|
||||
);
|
||||
console.log(`Animals: farm_id ${oldId} -> ${newId}, 影响 ${result[0].affectedRows || 0} 行`);
|
||||
}
|
||||
|
||||
// 9. 更新devices表的farm_id
|
||||
console.log('\n更新devices表的farm_id...');
|
||||
for (const [oldId, newId] of Object.entries(idMapping)) {
|
||||
const result = await sequelize.query(
|
||||
'UPDATE devices SET farm_id = ? WHERE farm_id = ?',
|
||||
{ replacements: [newId, oldId], transaction }
|
||||
);
|
||||
console.log(`Devices: farm_id ${oldId} -> ${newId}, 影响 ${result[0].affectedRows || 0} 行`);
|
||||
}
|
||||
|
||||
// 10. 更新alerts表的farm_id
|
||||
console.log('\n更新alerts表的farm_id...');
|
||||
for (const [oldId, newId] of Object.entries(idMapping)) {
|
||||
const result = await sequelize.query(
|
||||
'UPDATE alerts SET farm_id = ? WHERE farm_id = ?',
|
||||
{ replacements: [newId, oldId], transaction }
|
||||
);
|
||||
console.log(`Alerts: farm_id ${oldId} -> ${newId}, 影响 ${result[0].affectedRows || 0} 行`);
|
||||
}
|
||||
|
||||
// 11. 重新启用外键约束
|
||||
await sequelize.query('SET FOREIGN_KEY_CHECKS = 1', { transaction });
|
||||
|
||||
// 12. 验证数据完整性
|
||||
console.log('\n验证数据完整性...');
|
||||
const newFarms = await Farm.findAll({ order: [['id', 'ASC']], transaction });
|
||||
console.log('更新后的farms表:');
|
||||
newFarms.forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
console.log('\n✅ farms表ID重新排序完成!');
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('❌ 重新排序失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
reorderFarmsId().catch(console.error);
|
||||
340
backend/tools/data-management/reorder-primary-keys.js
Normal file
340
backend/tools/data-management/reorder-primary-keys.js
Normal file
@@ -0,0 +1,340 @@
|
||||
/**
|
||||
* 重新排序数据库中所有表的主键ID
|
||||
* @file reorder-primary-keys.js
|
||||
* @description 将所有表的主键ID重新排序,从1开始升序,同时更新外键引用
|
||||
*/
|
||||
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
|
||||
// 根据依赖关系确定的处理顺序(被引用的表优先)
|
||||
const TABLE_ORDER = [
|
||||
'roles',
|
||||
'users',
|
||||
'farms',
|
||||
'devices',
|
||||
'products',
|
||||
'animals',
|
||||
'alerts',
|
||||
'orders',
|
||||
'sensor_data',
|
||||
'order_items',
|
||||
'user_roles',
|
||||
'seeds'
|
||||
];
|
||||
|
||||
// 外键映射关系
|
||||
const FOREIGN_KEY_MAPPINGS = {
|
||||
'animals': [{ column: 'farm_id', referencedTable: 'farms' }],
|
||||
'alerts': [
|
||||
{ column: 'device_id', referencedTable: 'devices' },
|
||||
{ column: 'farm_id', referencedTable: 'farms' }
|
||||
],
|
||||
'devices': [{ column: 'farm_id', referencedTable: 'farms' }],
|
||||
'order_items': [
|
||||
{ column: 'order_id', referencedTable: 'orders' },
|
||||
{ column: 'product_id', referencedTable: 'products' }
|
||||
],
|
||||
'orders': [{ column: 'user_id', referencedTable: 'users' }],
|
||||
'sensor_data': [
|
||||
{ column: 'device_id', referencedTable: 'devices' },
|
||||
{ column: 'farm_id', referencedTable: 'farms' }
|
||||
],
|
||||
'user_roles': [
|
||||
{ column: 'role_id', referencedTable: 'roles' },
|
||||
{ column: 'user_id', referencedTable: 'users' }
|
||||
]
|
||||
};
|
||||
|
||||
class IDReorderManager {
|
||||
constructor() {
|
||||
this.idMappings = new Map(); // 存储旧ID到新ID的映射
|
||||
this.transaction = null;
|
||||
}
|
||||
|
||||
async reorderAllTables() {
|
||||
this.transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
console.log('=== 开始重新排序所有表的主键ID ===\n');
|
||||
|
||||
// 临时禁用外键检查
|
||||
await sequelize.query('SET FOREIGN_KEY_CHECKS = 0', { transaction: this.transaction });
|
||||
|
||||
// 按顺序处理每个表
|
||||
for (const tableName of TABLE_ORDER) {
|
||||
await this.reorderTableIDs(tableName);
|
||||
}
|
||||
|
||||
// 重新启用外键检查
|
||||
await sequelize.query('SET FOREIGN_KEY_CHECKS = 1', { transaction: this.transaction });
|
||||
|
||||
// 验证数据完整性
|
||||
await this.verifyIntegrity();
|
||||
|
||||
await this.transaction.commit();
|
||||
console.log('\n✅ 所有表的ID重新排序完成!');
|
||||
|
||||
} catch (error) {
|
||||
await this.transaction.rollback();
|
||||
console.error('❌ ID重新排序失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async reorderTableIDs(tableName) {
|
||||
try {
|
||||
console.log(`\n🔄 处理表: ${tableName}`);
|
||||
|
||||
// 检查表是否存在且有数据
|
||||
const tableExists = await this.checkTableExists(tableName);
|
||||
if (!tableExists) {
|
||||
console.log(`⚠️ 表 ${tableName} 不存在,跳过`);
|
||||
return;
|
||||
}
|
||||
|
||||
const recordCount = await this.getRecordCount(tableName);
|
||||
if (recordCount === 0) {
|
||||
console.log(`ℹ️ 表 ${tableName} 无数据,跳过`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(` 记录数: ${recordCount}`);
|
||||
|
||||
// 获取当前所有记录(按ID排序)
|
||||
const records = await sequelize.query(
|
||||
`SELECT * FROM ${tableName} ORDER BY id`,
|
||||
{ type: QueryTypes.SELECT, transaction: this.transaction }
|
||||
);
|
||||
|
||||
if (records.length === 0) {
|
||||
console.log(` 无记录需要处理`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建ID映射
|
||||
const oldToNewIdMap = new Map();
|
||||
records.forEach((record, index) => {
|
||||
const oldId = record.id;
|
||||
const newId = index + 1;
|
||||
oldToNewIdMap.set(oldId, newId);
|
||||
});
|
||||
|
||||
// 存储映射供其他表使用
|
||||
this.idMappings.set(tableName, oldToNewIdMap);
|
||||
|
||||
// 检查是否需要重新排序
|
||||
const needsReorder = records.some((record, index) => record.id !== index + 1);
|
||||
|
||||
if (!needsReorder) {
|
||||
console.log(` ✅ ID已经是连续的,无需重新排序`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(` 🔧 重新排序ID: ${records[0].id}-${records[records.length-1].id} -> 1-${records.length}`);
|
||||
|
||||
// 更新外键引用(如果有的话)
|
||||
await this.updateForeignKeyReferences(tableName, records);
|
||||
|
||||
// 创建临时表
|
||||
const tempTableName = `${tableName}_temp_reorder`;
|
||||
await this.createTempTable(tableName, tempTableName);
|
||||
|
||||
// 将数据插入临时表(使用新ID)
|
||||
await this.insertDataWithNewIDs(tableName, tempTableName, records, oldToNewIdMap);
|
||||
|
||||
// 删除原表数据
|
||||
await sequelize.query(
|
||||
`DELETE FROM ${tableName}`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
|
||||
// 重置自增ID
|
||||
await sequelize.query(
|
||||
`ALTER TABLE ${tableName} AUTO_INCREMENT = 1`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
|
||||
// 将临时表数据复制回原表
|
||||
await this.copyDataFromTempTable(tableName, tempTableName);
|
||||
|
||||
// 删除临时表
|
||||
await sequelize.query(
|
||||
`DROP TABLE ${tempTableName}`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
|
||||
console.log(` ✅ 表 ${tableName} ID重新排序完成`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ 处理表 ${tableName} 失败:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async checkTableExists(tableName) {
|
||||
const result = await sequelize.query(
|
||||
`SELECT COUNT(*) as count FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE() AND table_name = '${tableName}'`,
|
||||
{ type: QueryTypes.SELECT, transaction: this.transaction }
|
||||
);
|
||||
return result[0].count > 0;
|
||||
}
|
||||
|
||||
async getRecordCount(tableName) {
|
||||
const result = await sequelize.query(
|
||||
`SELECT COUNT(*) as count FROM ${tableName}`,
|
||||
{ type: QueryTypes.SELECT, transaction: this.transaction }
|
||||
);
|
||||
return parseInt(result[0].count);
|
||||
}
|
||||
|
||||
async updateForeignKeyReferences(tableName, records) {
|
||||
const foreignKeys = FOREIGN_KEY_MAPPINGS[tableName];
|
||||
if (!foreignKeys) return;
|
||||
|
||||
console.log(` 🔗 更新外键引用...`);
|
||||
|
||||
for (const fk of foreignKeys) {
|
||||
const referencedTableMapping = this.idMappings.get(fk.referencedTable);
|
||||
if (!referencedTableMapping) {
|
||||
console.log(` ⚠️ 引用表 ${fk.referencedTable} 的映射不存在,跳过外键 ${fk.column}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 更新外键值
|
||||
for (const record of records) {
|
||||
const oldForeignKeyValue = record[fk.column];
|
||||
if (oldForeignKeyValue && referencedTableMapping.has(oldForeignKeyValue)) {
|
||||
const newForeignKeyValue = referencedTableMapping.get(oldForeignKeyValue);
|
||||
record[fk.column] = newForeignKeyValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createTempTable(originalTable, tempTable) {
|
||||
// 先删除临时表(如果存在)
|
||||
try {
|
||||
await sequelize.query(
|
||||
`DROP TABLE IF EXISTS ${tempTable}`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
} catch (error) {
|
||||
// 忽略删除错误
|
||||
}
|
||||
|
||||
await sequelize.query(
|
||||
`CREATE TABLE ${tempTable} LIKE ${originalTable}`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
}
|
||||
|
||||
async insertDataWithNewIDs(originalTable, tempTable, records, idMapping) {
|
||||
if (records.length === 0) return;
|
||||
|
||||
// 获取表结构
|
||||
const columns = await sequelize.query(
|
||||
`SELECT COLUMN_NAME FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = '${originalTable}'
|
||||
ORDER BY ordinal_position`,
|
||||
{ type: QueryTypes.SELECT, transaction: this.transaction }
|
||||
);
|
||||
|
||||
const columnNames = columns.map(col => col.COLUMN_NAME);
|
||||
|
||||
// 批量插入数据
|
||||
for (let i = 0; i < records.length; i += 100) {
|
||||
const batch = records.slice(i, i + 100);
|
||||
const values = batch.map((record, batchIndex) => {
|
||||
const newId = i + batchIndex + 1;
|
||||
const values = columnNames.map(col => {
|
||||
if (col === 'id') {
|
||||
return newId;
|
||||
}
|
||||
const value = record[col];
|
||||
if (value === null || value === undefined) {
|
||||
return 'NULL';
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return `'${value.replace(/'/g, "''")}'`;
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return `'${value.toISOString().slice(0, 19).replace('T', ' ')}'`;
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return `(${values.join(', ')})`;
|
||||
});
|
||||
|
||||
await sequelize.query(
|
||||
`INSERT INTO ${tempTable} (${columnNames.join(', ')}) VALUES ${values.join(', ')}`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async copyDataFromTempTable(originalTable, tempTable) {
|
||||
await sequelize.query(
|
||||
`INSERT INTO ${originalTable} SELECT * FROM ${tempTable}`,
|
||||
{ transaction: this.transaction }
|
||||
);
|
||||
}
|
||||
|
||||
async verifyIntegrity() {
|
||||
console.log('\n🔍 验证数据完整性...');
|
||||
|
||||
for (const [tableName, foreignKeys] of Object.entries(FOREIGN_KEY_MAPPINGS)) {
|
||||
if (!foreignKeys) continue;
|
||||
|
||||
for (const fk of foreignKeys) {
|
||||
try {
|
||||
const result = await sequelize.query(`
|
||||
SELECT COUNT(*) as invalid_count
|
||||
FROM ${tableName} t
|
||||
WHERE t.${fk.column} IS NOT NULL
|
||||
AND t.${fk.column} NOT IN (
|
||||
SELECT id FROM ${fk.referencedTable} WHERE id IS NOT NULL
|
||||
)
|
||||
`, { type: QueryTypes.SELECT, transaction: this.transaction });
|
||||
|
||||
const invalidCount = parseInt(result[0].invalid_count);
|
||||
if (invalidCount > 0) {
|
||||
throw new Error(`表 ${tableName}.${fk.column} 有 ${invalidCount} 个无效的外键引用`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ 完整性检查失败: ${tableName}.${fk.column}`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 数据完整性验证通过');
|
||||
}
|
||||
}
|
||||
|
||||
async function reorderPrimaryKeys() {
|
||||
const manager = new IDReorderManager();
|
||||
await manager.reorderAllTables();
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
reorderPrimaryKeys()
|
||||
.then(() => {
|
||||
console.log('\n🎉 主键ID重新排序完成!');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('💥 主键ID重新排序失败:', error);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => {
|
||||
sequelize.close();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { reorderPrimaryKeys, IDReorderManager };
|
||||
44
backend/tools/data-management/reset_admin_password.js
Normal file
44
backend/tools/data-management/reset_admin_password.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const { User } = require('./models');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
async function resetAdminPassword() {
|
||||
try {
|
||||
// 查找 admin 用户
|
||||
const admin = await User.findOne({ where: { username: 'admin' } });
|
||||
|
||||
if (!admin) {
|
||||
console.log('未找到 admin 用户');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('重置前的密码哈希:', admin.password);
|
||||
|
||||
// 直接生成新的哈希并更新
|
||||
const newPassword = '123456';
|
||||
const newHash = await bcrypt.hash(newPassword, 10);
|
||||
|
||||
// 直接更新数据库,绕过模型钩子
|
||||
await User.update(
|
||||
{ password: newHash },
|
||||
{ where: { username: 'admin' } }
|
||||
);
|
||||
|
||||
console.log('新的密码哈希:', newHash);
|
||||
|
||||
// 验证新密码
|
||||
const isValid = await bcrypt.compare(newPassword, newHash);
|
||||
console.log('新密码验证:', isValid ? '成功' : '失败');
|
||||
|
||||
// 重新查询用户验证
|
||||
const updatedAdmin = await User.findOne({ where: { username: 'admin' } });
|
||||
const finalCheck = await bcrypt.compare(newPassword, updatedAdmin.password);
|
||||
console.log('数据库中的密码验证:', finalCheck ? '成功' : '失败');
|
||||
|
||||
} catch (error) {
|
||||
console.error('重置密码失败:', error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
resetAdminPassword();
|
||||
41
backend/tools/data-management/restore-farms-data.js
Normal file
41
backend/tools/data-management/restore-farms-data.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
|
||||
async function restoreFarmsData() {
|
||||
try {
|
||||
console.log('恢复farms数据...');
|
||||
|
||||
// 清空现有数据
|
||||
await sequelize.query('DELETE FROM farms');
|
||||
await sequelize.query('ALTER TABLE farms AUTO_INCREMENT = 1');
|
||||
|
||||
// 插入农场数据
|
||||
await sequelize.query(`
|
||||
INSERT INTO farms (name, type, location, address, contact, phone, status, created_at, updated_at) VALUES
|
||||
('阳光农场', '养猪场', JSON_OBJECT('lat', 39.9042, 'lng', 116.4074), '北京市朝阳区农场路1号', '张三', '13800138001', 'active', NOW(), NOW()),
|
||||
('绿野牧场', '养牛场', JSON_OBJECT('lat', 31.2304, 'lng', 121.4737), '上海市浦东新区牧场路2号', '李四', '13800138002', 'active', NOW(), NOW()),
|
||||
('山谷羊场', '养羊场', JSON_OBJECT('lat', 23.1291, 'lng', 113.2644), '广州市天河区山谷路3号', '王五', '13800138003', 'active', NOW(), NOW()),
|
||||
('蓝天养鸡场', '养鸡场', JSON_OBJECT('lat', 30.5728, 'lng', 104.0668), '成都市锦江区蓝天路4号', '赵六', '13800138004', 'active', NOW(), NOW()),
|
||||
('金山养鸭场', '养鸭场', JSON_OBJECT('lat', 36.0611, 'lng', 120.3785), '青岛市市南区金山路5号', '钱七', '13800138005', 'active', NOW(), NOW()),
|
||||
('银河渔场', '渔场', JSON_OBJECT('lat', 22.3193, 'lng', 114.1694), '深圳市福田区银河路6号', '孙八', '13800138006', 'active', NOW(), NOW()),
|
||||
('星空牧场', '综合农场', JSON_OBJECT('lat', 29.5630, 'lng', 106.5516), '重庆市渝中区星空路7号', '周九', '13800138007', 'active', NOW(), NOW()),
|
||||
('彩虹农庄', '有机农场', JSON_OBJECT('lat', 34.3416, 'lng', 108.9398), '西安市雁塔区彩虹路8号', '吴十', '13800138008', 'active', NOW(), NOW()),
|
||||
('东方养殖场', '养猪场', JSON_OBJECT('lat', 26.0745, 'lng', 119.2965), '福州市鼓楼区东方路9号', '郑一', '13800138009', 'active', NOW(), NOW()),
|
||||
('西部牧场', '养牛场', JSON_OBJECT('lat', 43.8256, 'lng', 87.6168), '乌鲁木齐市天山区西部路10号', '王二', '13800138010', 'active', NOW(), NOW()),
|
||||
('南方羊场', '养羊场', JSON_OBJECT('lat', 25.0478, 'lng', 102.7123), '昆明市五华区南方路11号', '李三', '13800138011', 'active', NOW(), NOW())
|
||||
`);
|
||||
|
||||
// 验证数据
|
||||
const farms = await sequelize.query('SELECT id, name FROM farms ORDER BY id ASC');
|
||||
console.log('恢复的farms数据:');
|
||||
farms[0].forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
|
||||
});
|
||||
|
||||
console.log(`\n✅ 成功恢复 ${farms[0].length} 个养殖场数据`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 恢复数据失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
restoreFarmsData();
|
||||
161
backend/tools/data-management/simple-reorder-farms.js
Normal file
161
backend/tools/data-management/simple-reorder-farms.js
Normal file
@@ -0,0 +1,161 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
async function simpleReorderFarms() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
// 创建数据库连接
|
||||
connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: 'root123',
|
||||
database: 'nxxmdata'
|
||||
});
|
||||
|
||||
console.log('连接数据库成功');
|
||||
|
||||
// 1. 检查当前farms数据
|
||||
const [farms] = await connection.execute('SELECT * FROM farms ORDER BY id ASC');
|
||||
console.log(`找到 ${farms.length} 个养殖场:`);
|
||||
farms.forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
|
||||
});
|
||||
|
||||
if (farms.length === 0) {
|
||||
console.log('farms表为空,先恢复数据...');
|
||||
|
||||
// 插入基础数据
|
||||
await connection.execute(`
|
||||
INSERT INTO farms (name, type, location, address, contact, phone, status, created_at, updated_at) VALUES
|
||||
('蓝天养鸡场', '养鸡场', '{}', '成都市锦江区蓝天路4号', '赵六', '13800138004', 'active', NOW(), NOW()),
|
||||
('金山养鸭场', '养鸭场', '{}', '青岛市市南区金山路5号', '钱七', '13800138005', 'active', NOW(), NOW()),
|
||||
('银河渔场', '渔场', '{}', '深圳市福田区银河路6号', '孙八', '13800138006', 'active', NOW(), NOW()),
|
||||
('星空牧场', '综合农场', '{}', '重庆市渝中区星空路7号', '周九', '13800138007', 'active', NOW(), NOW()),
|
||||
('彩虹农庄', '有机农场', '{}', '西安市雁塔区彩虹路8号', '吴十', '13800138008', 'active', NOW(), NOW()),
|
||||
('东方养殖场', '养猪场', '{}', '福州市鼓楼区东方路9号', '郑一', '13800138009', 'active', NOW(), NOW()),
|
||||
('西部牧场', '养牛场', '{}', '乌鲁木齐市天山区西部路10号', '王二', '13800138010', 'active', NOW(), NOW()),
|
||||
('南方羊场', '养羊场', '{}', '昆明市五华区南方路11号', '李三', '13800138011', 'active', NOW(), NOW())
|
||||
`);
|
||||
|
||||
console.log('数据恢复完成');
|
||||
|
||||
// 重新获取数据
|
||||
const [newFarms] = await connection.execute('SELECT * FROM farms ORDER BY id ASC');
|
||||
console.log('恢复后的数据:');
|
||||
newFarms.forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 开始事务
|
||||
await connection.beginTransaction();
|
||||
|
||||
// 3. 禁用外键检查
|
||||
await connection.execute('SET FOREIGN_KEY_CHECKS = 0');
|
||||
|
||||
// 4. 创建ID映射
|
||||
const idMapping = {};
|
||||
farms.forEach((farm, index) => {
|
||||
idMapping[farm.id] = index + 1;
|
||||
});
|
||||
|
||||
console.log('\nID映射:');
|
||||
Object.entries(idMapping).forEach(([oldId, newId]) => {
|
||||
console.log(`${oldId} -> ${newId}`);
|
||||
});
|
||||
|
||||
// 5. 更新farms表ID
|
||||
console.log('\n更新farms表ID...');
|
||||
for (let i = 0; i < farms.length; i++) {
|
||||
const farm = farms[i];
|
||||
const newId = i + 1;
|
||||
|
||||
if (farm.id !== newId) {
|
||||
// 先更新为临时ID避免冲突
|
||||
const tempId = 1000 + newId;
|
||||
await connection.execute(
|
||||
'UPDATE farms SET id = ? WHERE id = ?',
|
||||
[tempId, farm.id]
|
||||
);
|
||||
console.log(`临时更新: ${farm.id} -> ${tempId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 更新为最终ID
|
||||
for (let i = 0; i < farms.length; i++) {
|
||||
const newId = i + 1;
|
||||
const tempId = 1000 + newId;
|
||||
|
||||
await connection.execute(
|
||||
'UPDATE farms SET id = ? WHERE id = ?',
|
||||
[newId, tempId]
|
||||
);
|
||||
console.log(`最终更新: ${tempId} -> ${newId}`);
|
||||
}
|
||||
|
||||
// 7. 更新相关表的外键
|
||||
console.log('\n更新外键...');
|
||||
|
||||
// 更新animals表
|
||||
for (const [oldId, newId] of Object.entries(idMapping)) {
|
||||
if (oldId !== newId.toString()) {
|
||||
const [result] = await connection.execute(
|
||||
'UPDATE animals SET farm_id = ? WHERE farm_id = ?',
|
||||
[newId, oldId]
|
||||
);
|
||||
console.log(`Animals: farm_id ${oldId} -> ${newId}, 影响 ${result.affectedRows} 行`);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新devices表
|
||||
for (const [oldId, newId] of Object.entries(idMapping)) {
|
||||
if (oldId !== newId.toString()) {
|
||||
const [result] = await connection.execute(
|
||||
'UPDATE devices SET farm_id = ? WHERE farm_id = ?',
|
||||
[newId, oldId]
|
||||
);
|
||||
console.log(`Devices: farm_id ${oldId} -> ${newId}, 影响 ${result.affectedRows} 行`);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新alerts表
|
||||
for (const [oldId, newId] of Object.entries(idMapping)) {
|
||||
if (oldId !== newId.toString()) {
|
||||
const [result] = await connection.execute(
|
||||
'UPDATE alerts SET farm_id = ? WHERE farm_id = ?',
|
||||
[newId, oldId]
|
||||
);
|
||||
console.log(`Alerts: farm_id ${oldId} -> ${newId}, 影响 ${result.affectedRows} 行`);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 重新启用外键检查
|
||||
await connection.execute('SET FOREIGN_KEY_CHECKS = 1');
|
||||
|
||||
// 9. 提交事务
|
||||
await connection.commit();
|
||||
|
||||
// 10. 验证结果
|
||||
const [finalFarms] = await connection.execute('SELECT * FROM farms ORDER BY id ASC');
|
||||
console.log('\n最终结果:');
|
||||
finalFarms.forEach(farm => {
|
||||
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
|
||||
});
|
||||
|
||||
console.log('\n✅ farms表ID重新排序完成!');
|
||||
|
||||
} catch (error) {
|
||||
if (connection) {
|
||||
await connection.rollback();
|
||||
}
|
||||
console.error('❌ 操作失败:', error.message);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
simpleReorderFarms();
|
||||
88
backend/tools/data-management/update-device-status.js
Normal file
88
backend/tools/data-management/update-device-status.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 更新设备状态脚本
|
||||
* 将设备状态更新为online、maintenance、offline三种状态
|
||||
*/
|
||||
|
||||
const { Device } = require('./models');
|
||||
|
||||
async function updateDeviceStatus() {
|
||||
try {
|
||||
console.log('开始更新设备状态...');
|
||||
|
||||
// 获取所有设备
|
||||
const devices = await Device.findAll();
|
||||
console.log(`找到 ${devices.length} 个设备`);
|
||||
|
||||
// 计算每种状态的设备数量
|
||||
const totalDevices = devices.length;
|
||||
const onlineCount = Math.floor(totalDevices * 0.6); // 60% 在线
|
||||
const maintenanceCount = Math.floor(totalDevices * 0.15); // 15% 维护
|
||||
const offlineCount = totalDevices - onlineCount - maintenanceCount; // 剩余离线
|
||||
|
||||
console.log(`计划分配: ${onlineCount} 在线, ${maintenanceCount} 维护, ${offlineCount} 离线`);
|
||||
|
||||
// 随机打乱设备数组
|
||||
const shuffledDevices = devices.sort(() => Math.random() - 0.5);
|
||||
|
||||
// 批量更新设备状态
|
||||
const updates = [];
|
||||
|
||||
// 设置在线设备
|
||||
for (let i = 0; i < onlineCount; i++) {
|
||||
updates.push(
|
||||
Device.update(
|
||||
{ status: 'online' },
|
||||
{ where: { id: shuffledDevices[i].id } }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 设置维护设备
|
||||
for (let i = onlineCount; i < onlineCount + maintenanceCount; i++) {
|
||||
updates.push(
|
||||
Device.update(
|
||||
{ status: 'maintenance' },
|
||||
{ where: { id: shuffledDevices[i].id } }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 设置离线设备
|
||||
for (let i = onlineCount + maintenanceCount; i < totalDevices; i++) {
|
||||
updates.push(
|
||||
Device.update(
|
||||
{ status: 'offline' },
|
||||
{ where: { id: shuffledDevices[i].id } }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 执行所有更新
|
||||
await Promise.all(updates);
|
||||
|
||||
// 验证更新结果
|
||||
const statusCount = await Device.findAll({
|
||||
attributes: [
|
||||
'status',
|
||||
[Device.sequelize.fn('COUNT', Device.sequelize.col('status')), 'count']
|
||||
],
|
||||
group: ['status'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
console.log('\n更新完成!当前设备状态分布:');
|
||||
statusCount.forEach(item => {
|
||||
console.log(`${item.status}: ${item.count} 个设备`);
|
||||
});
|
||||
|
||||
console.log('\n设备状态更新成功!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新设备状态失败:', error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
updateDeviceStatus();
|
||||
44
backend/tools/data-management/update-health-status-enum.js
Normal file
44
backend/tools/data-management/update-health-status-enum.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 更新动物健康状态枚举值
|
||||
* 添加 'treatment' 状态到现有的 ENUM 类型
|
||||
*/
|
||||
|
||||
const { sequelize } = require('./config/database-simple');
|
||||
|
||||
async function updateHealthStatusEnum() {
|
||||
try {
|
||||
console.log('开始更新 health_status 枚举值...');
|
||||
|
||||
// 更新 ENUM 类型,添加 'treatment' 选项
|
||||
await sequelize.query(`
|
||||
ALTER TABLE animals
|
||||
MODIFY COLUMN health_status
|
||||
ENUM('healthy', 'sick', 'quarantine', 'treatment')
|
||||
DEFAULT 'healthy'
|
||||
`);
|
||||
|
||||
console.log('health_status 枚举值更新成功!');
|
||||
console.log('现在支持的状态: healthy, sick, quarantine, treatment');
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新 health_status 枚举值失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
updateHealthStatusEnum()
|
||||
.then(() => {
|
||||
console.log('数据库更新完成');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('数据库更新失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = updateHealthStatusEnum;
|
||||
Reference in New Issue
Block a user