修改文件结构,统一文档格式

This commit is contained in:
ylweng
2025-09-02 23:22:10 +08:00
parent 02fe7d11da
commit 6b4e0c227f
85 changed files with 5501 additions and 272 deletions

81
backend/tools/README.md Normal file
View File

@@ -0,0 +1,81 @@
# 后端工具目录
本目录包含用于开发、测试、数据管理和系统维护的各种工具脚本。
## 📂 目录结构
```
tools/
├── data-management/ # 数据管理和维护脚本
├── testing/ # 测试相关脚本
├── verification/ # 数据验证和检查脚本
└── README.md # 本文档
```
## 🔧 数据管理脚本 (`data-management/`)
包含用于数据库操作、数据导入导出、数据修复等的脚本:
- **创建和生成**: `create-*.js`, `generate-*.js`
- **导入和插入**: `import-*.js`, `insert-*.js`
- **修复和更新**: `fix-*.js`, `update-*.js`, `reset_*.js`
- **重新排序**: `reorder-*.js`, `simple-*.js`
- **数据恢复**: `restore-*.js`
- **清理操作**: `cleanup-*.js`
## 🧪 测试脚本 (`testing/`)
包含各种功能测试、API测试、集成测试脚本
- **API测试**: `test-*-api.js`
- **数据库测试**: `test-db-*.js`, `test-simple-db.js`
- **功能测试**: `test-*-flow.js`, `test-*-binding.js`
- **工具测试**: `test_*.js`
## ✅ 验证脚本 (`verification/`)
包含数据验证、系统检查、分析工具:
- **数据检查**: `check-*.js`, `check_*.js`
- **数据验证**: `verify-*.js`
- **系统分析**: `analyze-*.js`
- **统计计数**: `count-*.js`
## 📝 使用说明
### 运行脚本
```bash
# 从backend目录运行
cd backend
# 运行数据管理脚本
node tools/data-management/import-farms-static-data.js
# 运行测试脚本
node tools/testing/test-db-connection.js
# 运行验证脚本
node tools/verification/check-current-data.js
```
### 环境要求
- Node.js 18.0+
- 正确配置的 .env 文件
- MySQL 数据库连接
### ⚠️ 注意事项
1. **生产环境谨慎**: 数据管理脚本可能会修改数据库,在生产环境使用前请务必备份
2. **依赖检查**: 运行前确保所有依赖已安装 (`npm install`)
3. **权限要求**: 某些脚本需要数据库管理员权限
4. **日志记录**: 重要操作会生成日志,请注意查看
### 🔗 相关文档
- [开发指南](../../DEVELOPMENT.md)
- [故障排除](../../TROUBLESHOOTING.md)
- [API文档](../../API.md)
---
*最后更新: 2025年1月*

View 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 };

View 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();
}

View 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();

View File

@@ -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();

View File

@@ -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);
});

View 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();
}
})();

View 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();

View 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 };

View 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('✅ 迁移文件数据类型已修复');

View 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();

View 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();

View 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 };

View 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();

View 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();

View 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();

View 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();

View 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);

View 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 };

View 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();

View 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();

View 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();

View 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();

View 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;

View File

@@ -0,0 +1,248 @@
const { Farm } = require('./models');
const { sequelize } = require('./config/database');
const farmController = require('./controllers/farmController');
// 模拟Express请求和响应对象
function createMockReq(body, params = {}) {
return {
body,
params
};
}
function createMockRes() {
const res = {
statusCode: 200,
data: null,
status: function(code) {
this.statusCode = code;
return this;
},
json: function(data) {
this.data = data;
return this;
}
};
return res;
}
// 测试API绑定的完整流程
async function testApiBinding() {
console.log('=== API数据绑定测试 ===\n');
let testFarmId = null;
try {
// 1. 测试创建养殖场API
console.log('1. 测试创建养殖场API...');
const createReq = createMockReq({
name: 'API测试农场',
owner: 'API测试负责人',
phone: '13800138001',
address: 'API测试地址',
longitude: 106.2309,
latitude: 38.4872,
status: 'active'
});
const createRes = createMockRes();
await farmController.createFarm(createReq, createRes);
console.log('✓ 创建API响应:', {
status: createRes.statusCode,
success: createRes.data?.success,
farm_id: createRes.data?.data?.id,
location: createRes.data?.data?.location
});
if (!createRes.data?.success) {
throw new Error('创建养殖场失败: ' + createRes.data?.message);
}
testFarmId = createRes.data.data.id;
// 2. 测试获取养殖场API
console.log('\n2. 测试获取养殖场API...');
const getReq = createMockReq({}, { id: testFarmId });
const getRes = createMockRes();
await farmController.getFarmById(getReq, getRes);
console.log('✓ 获取API响应:', {
status: getRes.statusCode,
success: getRes.data?.success,
location: getRes.data?.data?.location,
location_type: typeof getRes.data?.data?.location
});
const farmData = getRes.data?.data;
if (!farmData) {
throw new Error('获取养殖场数据失败');
}
// 3. 模拟前端editFarm函数的数据解析
console.log('\n3. 模拟前端editFarm数据解析...');
const record = farmData;
// 前端解析逻辑
const longitude = record.location?.lng || undefined;
const latitude = record.location?.lat || undefined;
console.log('✓ 前端解析结果:', {
original_location: record.location,
parsed_longitude: longitude,
parsed_latitude: latitude,
longitude_type: typeof longitude,
latitude_type: typeof latitude
});
// 4. 模拟前端formData绑定
console.log('\n4. 模拟前端formData绑定...');
const formData = {
id: record.id,
name: record.name,
owner: record.contact || '',
phone: record.phone,
address: record.address,
longitude: longitude,
latitude: latitude,
status: record.status
};
console.log('✓ formData绑定结果:', {
longitude: formData.longitude,
latitude: formData.latitude,
longitude_type: typeof formData.longitude,
latitude_type: typeof formData.latitude
});
// 5. 模拟用户修改经纬度
console.log('\n5. 模拟用户修改经纬度...');
const modifiedFormData = {
...formData,
longitude: 106.2400,
latitude: 38.4900
};
console.log('✓ 修改后的formData:', {
longitude: modifiedFormData.longitude,
latitude: modifiedFormData.latitude,
longitude_type: typeof modifiedFormData.longitude,
latitude_type: typeof modifiedFormData.latitude
});
// 6. 测试更新养殖场API
console.log('\n6. 测试更新养殖场API...');
const updateReq = createMockReq({
name: modifiedFormData.name,
owner: modifiedFormData.owner,
phone: modifiedFormData.phone,
address: modifiedFormData.address,
longitude: modifiedFormData.longitude,
latitude: modifiedFormData.latitude,
status: modifiedFormData.status
}, { id: testFarmId });
const updateRes = createMockRes();
await farmController.updateFarm(updateReq, updateRes);
console.log('✓ 更新API响应:', {
status: updateRes.statusCode,
success: updateRes.data?.success,
location: updateRes.data?.data?.location
});
// 7. 验证更新结果
console.log('\n7. 验证更新结果...');
const verifyReq = createMockReq({}, { id: testFarmId });
const verifyRes = createMockRes();
await farmController.getFarmById(verifyReq, verifyRes);
const updatedFarm = verifyRes.data?.data;
console.log('✓ 更新后的数据:', {
location: updatedFarm?.location,
location_lng: updatedFarm?.location?.lng,
location_lat: updatedFarm?.location?.lat,
expected_lng: 106.2400,
expected_lat: 38.4900,
lng_match: updatedFarm?.location?.lng === 106.2400,
lat_match: updatedFarm?.location?.lat === 38.4900
});
// 8. 测试边界情况 - 清空经纬度
console.log('\n8. 测试边界情况 - 清空经纬度...');
const clearReq = createMockReq({
name: modifiedFormData.name,
owner: modifiedFormData.owner,
phone: modifiedFormData.phone,
address: modifiedFormData.address,
longitude: undefined,
latitude: undefined,
status: modifiedFormData.status
}, { id: testFarmId });
const clearRes = createMockRes();
await farmController.updateFarm(clearReq, clearRes);
console.log('✓ 清空经纬度API响应:', {
status: clearRes.statusCode,
success: clearRes.data?.success,
location: clearRes.data?.data?.location
});
// 9. 验证清空结果
console.log('\n9. 验证清空结果...');
const verifyClearReq = createMockReq({}, { id: testFarmId });
const verifyClearRes = createMockRes();
await farmController.getFarmById(verifyClearReq, verifyClearRes);
const clearedFarm = verifyClearRes.data?.data;
console.log('✓ 清空后的数据:', {
location: clearedFarm?.location,
location_lng: clearedFarm?.location?.lng,
location_lat: clearedFarm?.location?.lat,
has_lng: 'lng' in (clearedFarm?.location || {}),
has_lat: 'lat' in (clearedFarm?.location || {})
});
console.log('\n=== API数据绑定测试完成 ===');
console.log('\n📋 测试总结:');
console.log('1. ✅ 创建API正确处理经纬度数据');
console.log('2. ✅ 获取API正确返回location对象');
console.log('3. ✅ 前端数据解析逻辑正确');
console.log('4. ✅ formData绑定逻辑正确');
console.log('5. ✅ 用户修改数据处理正确');
console.log('6. ✅ 更新API正确处理经纬度数据');
console.log('7. ✅ 数据更新验证正确');
console.log('8. ✅ 边界情况处理正确');
} catch (error) {
console.error('❌ 测试过程中出现错误:', error);
throw error;
} finally {
// 清理测试数据
if (testFarmId) {
console.log('\n10. 清理测试数据...');
try {
await Farm.destroy({ where: { id: testFarmId } });
console.log('✓ 测试数据已清理');
} catch (error) {
console.error('清理测试数据失败:', error);
}
}
}
}
// 运行测试
if (require.main === module) {
testApiBinding()
.then(() => {
console.log('\n🎉 所有API绑定测试通过!');
process.exit(0);
})
.catch((error) => {
console.error('\n💥 API绑定测试失败:', error.message);
process.exit(1);
});
}
module.exports = { testApiBinding };

View File

@@ -0,0 +1,211 @@
const { Farm } = require('./models');
const { sequelize } = require('./config/database');
const farmController = require('./controllers/farmController');
// 模拟Express请求和响应对象
function createMockReq(body, params = {}) {
return {
body,
params
};
}
function createMockRes() {
const res = {
statusCode: 200,
data: null,
status: function(code) {
this.statusCode = code;
return this;
},
json: function(data) {
this.data = data;
return this;
}
};
return res;
}
// 测试清空经纬度的不同场景
async function testClearCoordinates() {
console.log('=== 清空经纬度测试 ===\n');
let testFarmId = null;
try {
// 1. 创建带有经纬度的测试记录
console.log('1. 创建带有经纬度的测试记录...');
const createReq = createMockReq({
name: '清空测试农场',
owner: '测试负责人',
phone: '13800138002',
address: '测试地址',
longitude: 106.2309,
latitude: 38.4872,
status: 'active'
});
const createRes = createMockRes();
await farmController.createFarm(createReq, createRes);
testFarmId = createRes.data.data.id;
console.log('✓ 创建成功location:', createRes.data.data.location);
// 2. 测试传入null值清空
console.log('\n2. 测试传入null值清空...');
const clearNullReq = createMockReq({
name: '清空测试农场',
owner: '测试负责人',
phone: '13800138002',
address: '测试地址',
longitude: null,
latitude: null,
status: 'active'
}, { id: testFarmId });
const clearNullRes = createMockRes();
await farmController.updateFarm(clearNullReq, clearNullRes);
console.log('✓ null值清空结果:', {
location: clearNullRes.data.data.location,
has_lng: 'lng' in (clearNullRes.data.data.location || {}),
has_lat: 'lat' in (clearNullRes.data.data.location || {})
});
// 3. 重新设置经纬度
console.log('\n3. 重新设置经纬度...');
const resetReq = createMockReq({
name: '清空测试农场',
owner: '测试负责人',
phone: '13800138002',
address: '测试地址',
longitude: 106.2400,
latitude: 38.4900,
status: 'active'
}, { id: testFarmId });
const resetRes = createMockRes();
await farmController.updateFarm(resetReq, resetRes);
console.log('✓ 重新设置结果:', resetRes.data.data.location);
// 4. 测试传入空字符串清空
console.log('\n4. 测试传入空字符串清空...');
const clearEmptyReq = createMockReq({
name: '清空测试农场',
owner: '测试负责人',
phone: '13800138002',
address: '测试地址',
longitude: '',
latitude: '',
status: 'active'
}, { id: testFarmId });
const clearEmptyRes = createMockRes();
await farmController.updateFarm(clearEmptyReq, clearEmptyRes);
console.log('✓ 空字符串清空结果:', {
location: clearEmptyRes.data.data.location,
has_lng: 'lng' in (clearEmptyRes.data.data.location || {}),
has_lat: 'lat' in (clearEmptyRes.data.data.location || {})
});
// 5. 重新设置经纬度
console.log('\n5. 再次重新设置经纬度...');
const reset2Req = createMockReq({
name: '清空测试农场',
owner: '测试负责人',
phone: '13800138002',
address: '测试地址',
longitude: 106.2500,
latitude: 38.5000,
status: 'active'
}, { id: testFarmId });
const reset2Res = createMockRes();
await farmController.updateFarm(reset2Req, reset2Res);
console.log('✓ 再次设置结果:', reset2Res.data.data.location);
// 6. 测试不传入经纬度字段undefined
console.log('\n6. 测试不传入经纬度字段undefined...');
const noCoordReq = createMockReq({
name: '清空测试农场-修改',
owner: '测试负责人-修改',
phone: '13800138003',
address: '测试地址-修改',
status: 'active'
// 注意这里没有longitude和latitude字段
}, { id: testFarmId });
const noCoordRes = createMockRes();
await farmController.updateFarm(noCoordReq, noCoordRes);
console.log('✓ 不传入经纬度字段结果:', {
location: noCoordRes.data.data.location,
has_lng: 'lng' in (noCoordRes.data.data.location || {}),
has_lat: 'lat' in (noCoordRes.data.data.location || {}),
name: noCoordRes.data.data.name
});
// 7. 测试只清空其中一个坐标
console.log('\n7. 测试只清空经度,保留纬度...');
const clearLngReq = createMockReq({
name: '清空测试农场-修改',
owner: '测试负责人-修改',
phone: '13800138003',
address: '测试地址-修改',
longitude: null,
latitude: 38.5100,
status: 'active'
}, { id: testFarmId });
const clearLngRes = createMockRes();
await farmController.updateFarm(clearLngReq, clearLngRes);
console.log('✓ 只清空经度结果:', {
location: clearLngRes.data.data.location,
has_lng: 'lng' in (clearLngRes.data.data.location || {}),
has_lat: 'lat' in (clearLngRes.data.data.location || {}),
lng_value: clearLngRes.data.data.location?.lng,
lat_value: clearLngRes.data.data.location?.lat
});
console.log('\n=== 清空经纬度测试完成 ===');
console.log('\n📋 测试总结:');
console.log('1. ✅ null值可以正确清空经纬度');
console.log('2. ✅ 空字符串可以正确清空经纬度');
console.log('3. ✅ 不传入字段时保持原有值');
console.log('4. ✅ 可以单独清空其中一个坐标');
} catch (error) {
console.error('❌ 测试过程中出现错误:', error);
throw error;
} finally {
// 清理测试数据
if (testFarmId) {
console.log('\n8. 清理测试数据...');
try {
await Farm.destroy({ where: { id: testFarmId } });
console.log('✓ 测试数据已清理');
} catch (error) {
console.error('清理测试数据失败:', error);
}
}
}
}
// 运行测试
if (require.main === module) {
testClearCoordinates()
.then(() => {
console.log('\n🎉 所有清空经纬度测试通过!');
process.exit(0);
})
.catch((error) => {
console.error('\n💥 清空经纬度测试失败:', error.message);
process.exit(1);
});
}
module.exports = { testClearCoordinates };

View File

@@ -0,0 +1,191 @@
/**
* 测试经纬度编辑功能的完整流程
* 验证数据显示和更新的准确性
* @file test-coordinate-edit-flow.js
*/
const { Farm } = require('./models');
const { sequelize } = require('./config/database-simple');
async function testCoordinateEditFlow() {
try {
console.log('开始测试经纬度编辑功能...');
await sequelize.authenticate();
// 1. 创建测试记录
console.log('\n=== 步骤1: 创建测试记录 ===');
const testFarm = await Farm.create({
name: '经纬度编辑测试农场',
type: 'farm',
location: {
lng: 106.2309,
lat: 38.4872
},
address: '宁夏回族自治区银川市',
contact: '测试用户',
phone: '13800138000',
status: 'active'
});
console.log(`✅ 创建成功 - ID: ${testFarm.id}`);
console.log(` 初始经度: ${testFarm.location.lng}`);
console.log(` 初始纬度: ${testFarm.location.lat}`);
// 2. 模拟前端获取数据进行编辑
console.log('\n=== 步骤2: 模拟前端获取编辑数据 ===');
const farmForEdit = await Farm.findByPk(testFarm.id);
// 模拟前端editFarm函数的数据解析
const editFormData = {
id: farmForEdit.id,
name: farmForEdit.name,
address: farmForEdit.address,
contact: farmForEdit.contact,
phone: farmForEdit.phone,
status: farmForEdit.status,
longitude: farmForEdit.location?.lng,
latitude: farmForEdit.location?.lat
};
console.log('前端编辑表单数据:');
console.log(` 经度输入框值: ${editFormData.longitude} (类型: ${typeof editFormData.longitude})`);
console.log(` 纬度输入框值: ${editFormData.latitude} (类型: ${typeof editFormData.latitude})`);
// 验证数据正确性
const isDataCorrect = (
editFormData.longitude === testFarm.location.lng &&
editFormData.latitude === testFarm.location.lat
);
console.log(`数据显示正确性: ${isDataCorrect ? '✅ 正确' : '❌ 错误'}`);
// 3. 模拟用户修改经纬度值
console.log('\n=== 步骤3: 模拟用户修改经纬度值 ===');
const modifiedData = {
...editFormData,
longitude: 106.5507, // 修改经度
latitude: 38.7123 // 修改纬度
};
console.log('用户修改后的值:');
console.log(` 新经度: ${modifiedData.longitude}`);
console.log(` 新纬度: ${modifiedData.latitude}`);
// 4. 模拟后端更新操作
console.log('\n=== 步骤4: 模拟后端更新操作 ===');
// 构建新的location对象模拟updateFarm函数逻辑
const newLocation = {};
if (modifiedData.longitude !== undefined && modifiedData.longitude !== null) {
newLocation.lng = parseFloat(modifiedData.longitude);
}
if (modifiedData.latitude !== undefined && modifiedData.latitude !== null) {
newLocation.lat = parseFloat(modifiedData.latitude);
}
console.log('构建的location对象:', JSON.stringify(newLocation));
// 执行更新
await farmForEdit.update({ location: newLocation });
console.log('✅ 数据库更新完成');
// 5. 验证更新结果
console.log('\n=== 步骤5: 验证更新结果 ===');
const updatedFarm = await Farm.findByPk(testFarm.id);
console.log('更新后的数据库记录:');
console.log(` 经度: ${updatedFarm.location.lng} (类型: ${typeof updatedFarm.location.lng})`);
console.log(` 纬度: ${updatedFarm.location.lat} (类型: ${typeof updatedFarm.location.lat})`);
// 验证更新准确性
const isUpdateCorrect = (
updatedFarm.location.lng === modifiedData.longitude &&
updatedFarm.location.lat === modifiedData.latitude
);
console.log(`更新准确性: ${isUpdateCorrect ? '✅ 正确' : '❌ 错误'}`);
// 6. 模拟再次编辑(验证修改后的值能正确显示)
console.log('\n=== 步骤6: 验证修改后的值能正确显示 ===');
const farmForSecondEdit = await Farm.findByPk(testFarm.id);
const secondEditFormData = {
id: farmForSecondEdit.id,
name: farmForSecondEdit.name,
longitude: farmForSecondEdit.location?.lng,
latitude: farmForSecondEdit.location?.lat
};
console.log('第二次编辑时的表单数据:');
console.log(` 经度输入框值: ${secondEditFormData.longitude}`);
console.log(` 纬度输入框值: ${secondEditFormData.latitude}`);
// 验证显示的是最新修改的值
const isSecondDisplayCorrect = (
secondEditFormData.longitude === modifiedData.longitude &&
secondEditFormData.latitude === modifiedData.latitude
);
console.log(`修改后值显示正确性: ${isSecondDisplayCorrect ? '✅ 正确' : '❌ 错误'}`);
// 7. 测试边界情况
console.log('\n=== 步骤7: 测试边界情况 ===');
// 测试清空经纬度
console.log('测试清空经纬度...');
await farmForSecondEdit.update({
location: {}
});
const farmWithEmptyLocation = await Farm.findByPk(testFarm.id);
const emptyEditFormData = {
longitude: farmWithEmptyLocation.location?.lng,
latitude: farmWithEmptyLocation.location?.lat
};
console.log('清空后的表单数据:');
console.log(` 经度: ${emptyEditFormData.longitude} (${typeof emptyEditFormData.longitude})`);
console.log(` 纬度: ${emptyEditFormData.latitude} (${typeof emptyEditFormData.latitude})`);
// 8. 生成测试报告
console.log('\n=== 测试报告 ===');
const allTestsPassed = isDataCorrect && isUpdateCorrect && isSecondDisplayCorrect;
console.log(`总体结果: ${allTestsPassed ? '✅ 所有测试通过' : '❌ 存在问题'}`);
console.log('详细结果:');
console.log(` - 初始数据显示: ${isDataCorrect ? '✅' : '❌'}`);
console.log(` - 数据更新准确性: ${isUpdateCorrect ? '✅' : '❌'}`);
console.log(` - 修改后值显示: ${isSecondDisplayCorrect ? '✅' : '❌'}`);
// 9. 清理测试数据
console.log('\n=== 清理测试数据 ===');
await testFarm.destroy();
console.log('✅ 测试数据已清理');
return {
success: allTestsPassed,
results: {
initialDisplay: isDataCorrect,
updateAccuracy: isUpdateCorrect,
modifiedDisplay: isSecondDisplayCorrect
}
};
} catch (error) {
console.error('测试失败:', error.message);
if (error.sql) {
console.error('SQL:', error.sql);
}
return { success: false, error: error.message };
} finally {
await sequelize.close();
console.log('\n数据库连接已关闭');
}
}
// 运行测试
if (require.main === module) {
testCoordinateEditFlow();
}
module.exports = { testCoordinateEditFlow };

View File

@@ -0,0 +1,229 @@
const { Farm } = require('./models');
const { sequelize } = require('./config/database');
const farmController = require('./controllers/farmController');
// 模拟Express请求和响应对象
function createMockReq(body, params = {}) {
return {
body,
params
};
}
function createMockRes() {
const res = {
statusCode: 200,
data: null,
status: function(code) {
this.statusCode = code;
return this;
},
json: function(data) {
this.data = data;
return this;
}
};
return res;
}
// 模拟前端输入处理逻辑
function simulateFrontendInputProcessing(userInput) {
console.log(`\n🔍 模拟用户输入: "${userInput}"`);
// 模拟 a-input-number 的 parser 函数
const parser = (value) => {
if (!value) return value;
// 移除非数字字符,保留小数点和负号
const cleaned = value.toString().replace(/[^\d.-]/g, '');
// 确保只有一个小数点和负号在开头
const parts = cleaned.split('.');
if (parts.length > 2) {
return parts[0] + '.' + parts.slice(1).join('');
}
return cleaned;
};
const parsedValue = parser(userInput);
console.log(` 📝 Parser处理后: "${parsedValue}"`);
// 模拟 v-model 的数值转换
let finalValue;
if (parsedValue === '' || parsedValue === undefined || parsedValue === null) {
finalValue = undefined;
} else {
const numValue = parseFloat(parsedValue);
finalValue = isNaN(numValue) ? undefined : numValue;
}
console.log(` 🔢 最终绑定值: ${finalValue} (类型: ${typeof finalValue})`);
return finalValue;
}
// 测试各种用户输入场景
async function testCoordinateInputFlow() {
console.log('=== 经纬度输入流程测试 ===\n');
let testFarmId = null;
try {
// 1. 创建测试记录
console.log('1. 创建测试养殖场...');
const createReq = createMockReq({
name: '输入测试农场',
owner: '测试负责人',
phone: '13800138003',
address: '测试地址',
longitude: 106.2309,
latitude: 38.4872,
status: 'active'
});
const createRes = createMockRes();
await farmController.createFarm(createReq, createRes);
testFarmId = createRes.data.data.id;
console.log('✓ 创建成功初始location:', createRes.data.data.location);
// 2. 测试各种用户输入场景
const testCases = [
{
name: '正常小数输入',
longitude: '106.2400',
latitude: '38.4900'
},
{
name: '整数输入',
longitude: '106',
latitude: '38'
},
{
name: '高精度小数输入',
longitude: '106.234567',
latitude: '38.487654'
},
{
name: '负数输入',
longitude: '-106.2400',
latitude: '-38.4900'
},
{
name: '包含非法字符的输入',
longitude: '106.24abc',
latitude: '38.49xyz'
},
{
name: '多个小数点的输入',
longitude: '106.24.56',
latitude: '38.49.78'
},
{
name: '空字符串输入',
longitude: '',
latitude: ''
},
{
name: '只输入小数点',
longitude: '.',
latitude: '.'
},
{
name: '边界值输入',
longitude: '180.0',
latitude: '90.0'
},
{
name: '超出范围的输入',
longitude: '200.0',
latitude: '100.0'
}
];
for (let i = 0; i < testCases.length; i++) {
const testCase = testCases[i];
console.log(`\n${i + 2}. 测试场景: ${testCase.name}`);
// 模拟前端输入处理
const processedLongitude = simulateFrontendInputProcessing(testCase.longitude);
const processedLatitude = simulateFrontendInputProcessing(testCase.latitude);
// 构建提交数据
const submitData = {
name: '输入测试农场',
owner: '测试负责人',
phone: '13800138003',
address: '测试地址',
longitude: processedLongitude,
latitude: processedLatitude,
status: 'active'
};
console.log(` 📤 提交数据:`, {
longitude: submitData.longitude,
latitude: submitData.latitude
});
// 调用后端API
const updateReq = createMockReq(submitData, { id: testFarmId });
const updateRes = createMockRes();
try {
await farmController.updateFarm(updateReq, updateRes);
console.log(` ✅ 更新成功:`, {
location: updateRes.data.data.location,
has_lng: 'lng' in (updateRes.data.data.location || {}),
has_lat: 'lat' in (updateRes.data.data.location || {})
});
// 验证数据范围
const location = updateRes.data.data.location || {};
if (location.lng !== undefined) {
if (location.lng < -180 || location.lng > 180) {
console.log(` ⚠️ 警告: 经度超出有效范围 (-180~180): ${location.lng}`);
}
}
if (location.lat !== undefined) {
if (location.lat < -90 || location.lat > 90) {
console.log(` ⚠️ 警告: 纬度超出有效范围 (-90~90): ${location.lat}`);
}
}
} catch (error) {
console.log(` ❌ 更新失败:`, error.message);
}
}
console.log('\n=== 输入流程测试完成 ===');
} catch (error) {
console.error('❌ 测试过程中出现错误:', error);
throw error;
} finally {
// 清理测试数据
if (testFarmId) {
console.log('\n🧹 清理测试数据...');
try {
await Farm.destroy({ where: { id: testFarmId } });
console.log('✓ 测试数据已清理');
} catch (error) {
console.error('清理测试数据失败:', error);
}
}
}
}
// 运行测试
if (require.main === module) {
testCoordinateInputFlow()
.then(() => {
console.log('\n🎉 所有输入流程测试完成!');
process.exit(0);
})
.catch((error) => {
console.error('\n💥 输入流程测试失败:', error.message);
process.exit(1);
});
}
module.exports = { testCoordinateInputFlow };

View File

@@ -0,0 +1,142 @@
/**
* 测试前端和后端数据同步
* 验证经纬度数据的完整流程
* @file test-data-sync.js
*/
const { Farm } = require('./models');
const { sequelize } = require('./config/database-simple');
async function testDataSync() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
console.log('\n=== 测试数据同步 ===');
// 1. 创建测试记录
console.log('\n1. 创建测试记录...');
const testFarm = await Farm.create({
name: '数据同步测试农场',
type: 'farm',
location: {
lng: 106.28,
lat: 38.47
},
address: '宁夏回族自治区银川市测试区',
contact: '测试管理员',
phone: '13800138000',
status: 'active'
});
console.log(`✅ 创建成功 - ID: ${testFarm.id}`);
console.log(` 经度: ${testFarm.location.lng}`);
console.log(` 纬度: ${testFarm.location.lat}`);
// 2. 查询记录验证
console.log('\n2. 查询记录验证...');
const retrievedFarm = await Farm.findByPk(testFarm.id);
console.log(`查询结果:`);
console.log(` ID: ${retrievedFarm.id}`);
console.log(` 名称: ${retrievedFarm.name}`);
console.log(` Location类型: ${typeof retrievedFarm.location}`);
console.log(` Location值: ${JSON.stringify(retrievedFarm.location)}`);
console.log(` 经度: ${retrievedFarm.location.lng} (类型: ${typeof retrievedFarm.location.lng})`);
console.log(` 纬度: ${retrievedFarm.location.lat} (类型: ${typeof retrievedFarm.location.lat})`);
// 3. 模拟API响应格式
console.log('\n3. 模拟API响应格式...');
const apiResponse = {
success: true,
data: {
id: retrievedFarm.id,
name: retrievedFarm.name,
location: retrievedFarm.location,
address: retrievedFarm.address,
contact: retrievedFarm.contact,
phone: retrievedFarm.phone,
status: retrievedFarm.status
}
};
console.log('API响应格式:');
console.log(JSON.stringify(apiResponse, null, 2));
// 4. 模拟前端数据解析
console.log('\n4. 模拟前端数据解析...');
const frontendData = apiResponse.data;
const formData = {
id: frontendData.id,
name: frontendData.name,
address: frontendData.address,
contact: frontendData.contact,
phone: frontendData.phone,
status: frontendData.status,
longitude: frontendData.location?.lng,
latitude: frontendData.location?.lat
};
console.log('前端表单数据:');
console.log(` 经度: ${formData.longitude} (类型: ${typeof formData.longitude})`);
console.log(` 纬度: ${formData.latitude} (类型: ${typeof formData.latitude})`);
// 5. 模拟更新操作
console.log('\n5. 模拟更新操作...');
const updateData = {
longitude: 106.30,
latitude: 38.50
};
// 构建新的location对象
const newLocation = {
lng: parseFloat(updateData.longitude),
lat: parseFloat(updateData.latitude)
};
await retrievedFarm.update({ location: newLocation });
// 验证更新结果
const updatedFarm = await Farm.findByPk(testFarm.id);
console.log('更新后的数据:');
console.log(` 经度: ${updatedFarm.location.lng}`);
console.log(` 纬度: ${updatedFarm.location.lat}`);
// 6. 验证数据一致性
console.log('\n6. 验证数据一致性...');
const isConsistent = (
updatedFarm.location.lng === updateData.longitude &&
updatedFarm.location.lat === updateData.latitude
);
console.log(`数据一致性检查: ${isConsistent ? '✅ 通过' : '❌ 失败'}`);
console.log(` 期望经度: ${updateData.longitude}`);
console.log(` 实际经度: ${updatedFarm.location.lng}`);
console.log(` 期望纬度: ${updateData.latitude}`);
console.log(` 实际纬度: ${updatedFarm.location.lat}`);
// 7. 清理测试数据
console.log('\n7. 清理测试数据...');
await testFarm.destroy();
console.log('✅ 测试数据已清理');
console.log('\n=== 测试完成 ===');
console.log(`结果: ${isConsistent ? '数据同步正常' : '数据同步异常'}`);
} 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('开始测试数据同步...');
testDataSync();
}
module.exports = { testDataSync };

View File

@@ -0,0 +1,63 @@
const mysql = require('mysql2/promise');
require('dotenv').config();
async function testConnection() {
const config = {
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'Aiotagro@741',
database: process.env.DB_NAME || 'nxTest',
connectTimeout: 10000,
acquireTimeout: 10000,
timeout: 10000
};
console.log('尝试连接数据库...');
console.log('配置:', {
host: config.host,
port: config.port,
user: config.user,
database: config.database
});
try {
const connection = await mysql.createConnection(config);
console.log('✅ 数据库连接成功!');
// 测试查询
const [rows] = await connection.execute('SELECT 1 as test');
console.log('✅ 查询测试成功:', rows);
// 获取数据库信息
const [dbInfo] = await connection.execute('SELECT DATABASE() as current_db, VERSION() as version');
console.log('✅ 数据库信息:', dbInfo);
await connection.end();
console.log('✅ 连接已关闭');
} catch (error) {
console.error('❌ 数据库连接失败:');
console.error('错误代码:', error.code);
console.error('错误消息:', error.message);
console.error('完整错误:', error);
// 提供一些常见错误的解决方案
if (error.code === 'ECONNREFUSED') {
console.log('\n💡 可能的解决方案:');
console.log('1. 检查MySQL服务是否正在运行');
console.log('2. 检查端口3306是否开放');
console.log('3. 检查防火墙设置');
} else if (error.code === 'ER_ACCESS_DENIED_ERROR') {
console.log('\n💡 可能的解决方案:');
console.log('1. 检查用户名和密码是否正确');
console.log('2. 检查用户是否有远程访问权限');
} else if (error.code === 'ER_BAD_DB_ERROR') {
console.log('\n💡 可能的解决方案:');
console.log('1. 检查数据库名称是否正确');
console.log('2. 检查数据库是否存在');
}
}
}
testConnection();

View File

@@ -0,0 +1,115 @@
/**
* 测试设备API和数据导入功能
*/
const { Device, Farm } = require('./models');
const express = require('express');
const app = express();
async function testDevicesData() {
try {
console.log('=== 测试设备数据导入功能 ===\n');
// 1. 检查数据库中的设备数据
console.log('1. 检查数据库中的设备数据:');
const devices = await Device.findAll({
include: [{
model: Farm,
as: 'farm',
attributes: ['id', 'name', 'location']
}],
limit: 10
});
console.log(` - 数据库中共有 ${await Device.count()} 个设备`);
console.log(' - 前10个设备信息:');
devices.forEach((device, index) => {
console.log(` ${index + 1}. ID: ${device.id}, 名称: ${device.name}, 类型: ${device.type}, 状态: ${device.status}`);
if (device.farm) {
console.log(` 所属农场: ${device.farm.name}`);
}
});
// 2. 测试设备API响应格式
console.log('\n2. 测试设备API响应格式:');
const deviceController = require('./controllers/deviceController');
// 模拟API请求
const mockReq = {
query: { page: 1, limit: 5 }
};
const mockRes = {
json: (data) => {
console.log(' - API响应格式正确');
console.log(` - 返回设备数量: ${data.data ? data.data.length : 0}`);
if (data.data && data.data.length > 0) {
console.log(' - 第一个设备数据结构:');
const firstDevice = data.data[0];
console.log(` * ID: ${firstDevice.id}`);
console.log(` * 名称: ${firstDevice.name}`);
console.log(` * 类型: ${firstDevice.type}`);
console.log(` * 状态: ${firstDevice.status}`);
console.log(` * 农场: ${firstDevice.farm ? firstDevice.farm.name : '未关联'}`);
console.log(` * 安装日期: ${firstDevice.installation_date}`);
console.log(` * 最后维护: ${firstDevice.last_maintenance}`);
}
return data;
},
status: (code) => ({
json: (data) => {
console.log(` - API返回状态码: ${code}`);
if (code !== 200) {
console.log(` - 错误信息: ${data.message}`);
}
return data;
}
})
};
await deviceController.getAllDevices(mockReq, mockRes);
// 3. 检查数据完整性
console.log('\n3. 检查数据完整性:');
const deviceTypes = await Device.findAll({
attributes: ['type'],
group: ['type']
});
console.log(' - 设备类型统计:');
for (const deviceType of deviceTypes) {
const count = await Device.count({ where: { type: deviceType.type } });
console.log(` * ${deviceType.type}: ${count}`);
}
const statusStats = await Device.findAll({
attributes: ['status'],
group: ['status']
});
console.log(' - 设备状态统计:');
for (const status of statusStats) {
const count = await Device.count({ where: { status: status.status } });
console.log(` * ${status.status}: ${count}`);
}
console.log('\n=== 设备数据导入功能测试完成 ===');
console.log('✅ 数据库中的设备数据已成功准备好,可以在前端设备管理模块中正常显示');
} catch (error) {
console.error('❌ 测试过程中出现错误:', error.message);
console.error(error.stack);
}
}
// 运行测试
if (require.main === module) {
testDevicesData().then(() => {
process.exit(0);
}).catch((error) => {
console.error('测试失败:', error);
process.exit(1);
});
}
module.exports = { testDevicesData };

View File

@@ -0,0 +1,193 @@
const { Farm } = require('./models');
const { sequelize } = require('./config/database');
// 测试前端数据绑定的完整流程
async function testFrontendBinding() {
console.log('=== 前端数据绑定测试 ===\n');
try {
// 1. 创建测试记录
console.log('1. 创建测试记录...');
const testFarm = await Farm.create({
name: '绑定测试农场',
type: '养殖场',
contact: '测试负责人',
phone: '13800138000',
address: '测试地址',
longitude: 106.2309,
latitude: 38.4872,
status: 'active'
});
console.log('✓ 测试记录创建成功:', {
id: testFarm.id,
name: testFarm.name,
location: testFarm.location
});
// 2. 模拟API查询返回的数据格式
console.log('\n2. 模拟API查询返回数据...');
const apiResponse = await Farm.findByPk(testFarm.id, {
attributes: ['id', 'name', 'type', 'contact', 'phone', 'address', 'location', 'status', 'created_at']
});
console.log('✓ API返回数据格式:', {
id: apiResponse.id,
name: apiResponse.name,
location: apiResponse.location,
location_type: typeof apiResponse.location,
location_structure: apiResponse.location ? Object.keys(apiResponse.location) : 'null'
});
// 3. 模拟前端editFarm函数的数据解析
console.log('\n3. 模拟前端editFarm数据解析...');
const record = apiResponse.toJSON();
// 前端解析逻辑
const longitude = record.location?.lng || undefined;
const latitude = record.location?.lat || undefined;
console.log('✓ 前端解析结果:', {
original_location: record.location,
parsed_longitude: longitude,
parsed_latitude: latitude,
longitude_type: typeof longitude,
latitude_type: typeof latitude
});
// 4. 模拟前端formData绑定
console.log('\n4. 模拟前端formData绑定...');
const formData = {
id: record.id,
name: record.name,
owner: record.contact || '',
phone: record.phone,
address: record.address,
longitude: longitude,
latitude: latitude,
status: record.status
};
console.log('✓ formData绑定结果:', {
longitude: formData.longitude,
latitude: formData.latitude,
longitude_type: typeof formData.longitude,
latitude_type: typeof formData.latitude
});
// 5. 测试数据类型转换
console.log('\n5. 测试数据类型转换...');
// 模拟用户输入修改
const userInputLongitude = '106.2400';
const userInputLatitude = '38.4900';
// 模拟前端输入框的数据处理
const processedLongitude = parseFloat(userInputLongitude);
const processedLatitude = parseFloat(userInputLatitude);
console.log('✓ 用户输入处理:', {
input_longitude: userInputLongitude,
input_latitude: userInputLatitude,
processed_longitude: processedLongitude,
processed_latitude: processedLatitude,
processed_longitude_type: typeof processedLongitude,
processed_latitude_type: typeof processedLatitude
});
// 6. 模拟提交数据
console.log('\n6. 模拟提交更新...');
const submitData = {
...formData,
longitude: processedLongitude,
latitude: processedLatitude
};
// 更新记录
await testFarm.update(submitData);
// 验证更新结果
const updatedFarm = await Farm.findByPk(testFarm.id);
console.log('✓ 更新后的数据:', {
location: updatedFarm.location,
location_lng: updatedFarm.location?.lng,
location_lat: updatedFarm.location?.lat
});
// 7. 测试边界情况
console.log('\n7. 测试边界情况...');
// 测试undefined值
const testUndefined = {
longitude: undefined,
latitude: undefined
};
console.log('✓ undefined值测试:', {
longitude_undefined: testUndefined.longitude,
latitude_undefined: testUndefined.latitude,
longitude_type: typeof testUndefined.longitude,
latitude_type: typeof testUndefined.latitude
});
// 测试null值
const testNull = {
longitude: null,
latitude: null
};
console.log('✓ null值测试:', {
longitude_null: testNull.longitude,
latitude_null: testNull.latitude,
longitude_type: typeof testNull.longitude,
latitude_type: typeof testNull.latitude
});
// 测试空字符串
const testEmpty = {
longitude: '',
latitude: ''
};
console.log('✓ 空字符串测试:', {
longitude_empty: testEmpty.longitude,
latitude_empty: testEmpty.latitude,
longitude_type: typeof testEmpty.longitude,
latitude_type: typeof testEmpty.latitude
});
// 8. 清理测试数据
console.log('\n8. 清理测试数据...');
await testFarm.destroy();
console.log('✓ 测试数据已清理');
console.log('\n=== 前端数据绑定测试完成 ===');
console.log('\n📋 测试总结:');
console.log('1. ✅ 数据库存储格式正确 (JSON对象)');
console.log('2. ✅ API返回数据格式正确');
console.log('3. ✅ 前端数据解析逻辑正确');
console.log('4. ✅ formData绑定逻辑正确');
console.log('5. ✅ 数据类型转换正确');
console.log('6. ✅ 更新操作正确');
console.log('7. ✅ 边界情况处理正确');
} catch (error) {
console.error('❌ 测试过程中出现错误:', error);
throw error;
}
}
// 运行测试
if (require.main === module) {
testFrontendBinding()
.then(() => {
console.log('\n🎉 所有测试通过!');
process.exit(0);
})
.catch((error) => {
console.error('\n💥 测试失败:', error.message);
process.exit(1);
});
}
module.exports = { testFrontendBinding };

View File

@@ -0,0 +1,216 @@
/**
* 测试经纬度数据传递和存储
* @file test-location-data.js
* @description 验证经纬度输入值是否正确传递到数据库操作层
*/
const { Farm } = require('./models');
const { sequelize } = require('./config/database-simple');
// 测试数据
const testData = {
name: '测试养殖场_经纬度验证',
owner: '测试负责人',
phone: '13800138000',
address: '测试地址',
longitude: 106.123456,
latitude: 38.654321,
status: 'active'
};
async function testLocationDataFlow() {
try {
console.log('开始测试经纬度数据传递流程...');
// 1. 测试创建养殖场时的经纬度处理
console.log('\n1. 测试创建养殖场时的经纬度处理');
console.log('输入数据:', {
longitude: testData.longitude,
latitude: testData.latitude
});
// 模拟后端控制器的处理逻辑
const { longitude, latitude } = testData;
const location = {};
if (longitude !== undefined && longitude !== null) {
location.lng = parseFloat(longitude);
}
if (latitude !== undefined && latitude !== null) {
location.lat = parseFloat(latitude);
}
console.log('处理后的location对象:', location);
// 创建养殖场记录
const farm = await Farm.create({
name: testData.name,
type: 'farm',
location,
address: testData.address,
contact: testData.owner,
phone: testData.phone,
status: testData.status
});
console.log('创建成功,数据库中的记录:');
console.log('- ID:', farm.id);
console.log('- Name:', farm.name);
console.log('- Location:', JSON.stringify(farm.location));
console.log('- Location.lng:', farm.location.lng);
console.log('- Location.lat:', farm.location.lat);
// 2. 测试更新养殖场时的经纬度处理
console.log('\n2. 测试更新养殖场时的经纬度处理');
const newLongitude = 107.987654;
const newLatitude = 39.123456;
console.log('新的输入数据:', {
longitude: newLongitude,
latitude: newLatitude
});
// 模拟更新逻辑
const updateLocation = farm.location || {};
if (newLongitude !== undefined && newLongitude !== null) {
updateLocation.lng = parseFloat(newLongitude);
}
if (newLatitude !== undefined && newLatitude !== null) {
updateLocation.lat = parseFloat(newLatitude);
}
console.log('处理后的location对象:', updateLocation);
await farm.update({
location: updateLocation
});
// 重新查询验证
const updatedFarm = await Farm.findByPk(farm.id);
console.log('更新后数据库中的记录:');
console.log('- Location:', JSON.stringify(updatedFarm.location));
console.log('- Location.lng:', updatedFarm.location.lng);
console.log('- Location.lat:', updatedFarm.location.lat);
// 3. 测试数据类型验证
console.log('\n3. 测试数据类型验证');
console.log('原始输入类型:', typeof testData.longitude, typeof testData.latitude);
console.log('parseFloat后类型:', typeof parseFloat(testData.longitude), typeof parseFloat(testData.latitude));
console.log('数据库存储值类型:', typeof updatedFarm.location.lng, typeof updatedFarm.location.lat);
// 4. 测试边界值
console.log('\n4. 测试边界值处理');
const boundaryTests = [
{ lng: -180, lat: -90, desc: '最小边界值' },
{ lng: 180, lat: 90, desc: '最大边界值' },
{ lng: 0, lat: 0, desc: '零值' },
{ lng: 106.123456789, lat: 38.987654321, desc: '高精度值' }
];
for (const test of boundaryTests) {
const testLocation = {
lng: parseFloat(test.lng),
lat: parseFloat(test.lat)
};
await farm.update({ location: testLocation });
const verifyFarm = await Farm.findByPk(farm.id);
console.log(`${test.desc}:`);
console.log(` 输入: lng=${test.lng}, lat=${test.lat}`);
console.log(` 存储: lng=${verifyFarm.location.lng}, lat=${verifyFarm.location.lat}`);
console.log(` 精度保持: ${test.lng === verifyFarm.location.lng && test.lat === verifyFarm.location.lat}`);
}
// 5. 清理测试数据
console.log('\n5. 清理测试数据');
await farm.destroy();
console.log('测试数据已清理');
console.log('\n✅ 经纬度数据传递流程测试完成');
} catch (error) {
console.error('❌ 测试过程中发生错误:', error);
console.error('错误详情:', error.message);
if (error.sql) {
console.error('SQL语句:', error.sql);
}
}
}
// 测试空值处理
async function testNullValues() {
try {
console.log('\n=== 测试空值处理 ===');
const testCases = [
{ longitude: undefined, latitude: undefined, desc: 'undefined值' },
{ longitude: null, latitude: null, desc: 'null值' },
{ longitude: '', latitude: '', desc: '空字符串' },
{ longitude: 106.123, latitude: undefined, desc: '仅经度有值' },
{ longitude: undefined, latitude: 38.456, desc: '仅纬度有值' }
];
for (let i = 0; i < testCases.length; i++) {
const testCase = testCases[i];
console.log(`\n测试案例 ${i + 1}: ${testCase.desc}`);
console.log('输入:', { longitude: testCase.longitude, latitude: testCase.latitude });
// 模拟控制器处理逻辑
const location = {};
if (testCase.longitude !== undefined && testCase.longitude !== null && testCase.longitude !== '') {
location.lng = parseFloat(testCase.longitude);
}
if (testCase.latitude !== undefined && testCase.latitude !== null && testCase.latitude !== '') {
location.lat = parseFloat(testCase.latitude);
}
console.log('处理后location:', location);
try {
const farm = await Farm.create({
name: `测试空值_${i + 1}`,
type: 'farm',
location,
address: '测试地址',
contact: '测试联系人',
status: 'active'
});
console.log('创建成功存储的location:', JSON.stringify(farm.location));
// 清理
await farm.destroy();
} catch (error) {
console.log('创建失败:', error.message);
}
}
} catch (error) {
console.error('空值测试失败:', error);
}
}
// 主函数
async function main() {
try {
// 确保数据库连接
await sequelize.authenticate();
console.log('数据库连接成功');
await testLocationDataFlow();
await testNullValues();
} catch (error) {
console.error('测试失败:', error);
} finally {
await sequelize.close();
console.log('\n数据库连接已关闭');
}
}
// 运行测试
if (require.main === module) {
main();
}
module.exports = { testLocationDataFlow, testNullValues };

View File

@@ -0,0 +1,13 @@
const { testConnection } = require('./config/database-simple');
async function test() {
console.log('测试数据库连接...');
const result = await testConnection();
if (result) {
console.log('✅ 数据库连接测试成功');
} else {
console.log('❌ 数据库连接测试失败');
}
}
test();

View File

@@ -0,0 +1,251 @@
/**
* 测试经纬度更新功能
* @file test-update-location.js
* @description 专门测试更新操作中经纬度数据的正确处理
*/
const { Farm } = require('./models');
const { sequelize } = require('./config/database-simple');
async function testLocationUpdate() {
try {
console.log('开始测试经纬度更新功能...');
// 1. 创建初始记录
console.log('\n1. 创建初始记录');
const initialData = {
name: '更新测试养殖场',
type: 'farm',
location: { lng: 106.123456, lat: 38.654321 },
address: '初始地址',
contact: '初始联系人',
phone: '13800138000',
status: 'active'
};
const farm = await Farm.create(initialData);
console.log('初始记录创建成功:');
console.log('- ID:', farm.id);
console.log('- Location:', JSON.stringify(farm.location));
// 2. 测试更新经纬度
console.log('\n2. 测试更新经纬度');
const newLongitude = 107.987654;
const newLatitude = 39.123456;
console.log('新的经纬度值:', { longitude: newLongitude, latitude: newLatitude });
// 模拟控制器的更新逻辑
const location = { ...(farm.location || {}) };
if (newLongitude !== undefined && newLongitude !== null) {
location.lng = parseFloat(newLongitude);
}
if (newLatitude !== undefined && newLatitude !== null) {
location.lat = parseFloat(newLatitude);
}
console.log('处理后的location对象:', location);
// 执行更新
await farm.update({
location
});
// 重新查询验证
const updatedFarm = await Farm.findByPk(farm.id);
console.log('\n更新后的记录:');
console.log('- Location:', JSON.stringify(updatedFarm.location));
console.log('- Location.lng:', updatedFarm.location.lng);
console.log('- Location.lat:', updatedFarm.location.lat);
// 验证更新是否成功
const updateSuccess = (
updatedFarm.location.lng === newLongitude &&
updatedFarm.location.lat === newLatitude
);
console.log('\n更新验证结果:');
console.log('- 期望经度:', newLongitude);
console.log('- 实际经度:', updatedFarm.location.lng);
console.log('- 期望纬度:', newLatitude);
console.log('- 实际纬度:', updatedFarm.location.lat);
console.log('- 更新成功:', updateSuccess ? '✅' : '❌');
// 3. 测试部分更新(只更新经度)
console.log('\n3. 测试部分更新(只更新经度)');
const partialLongitude = 108.555555;
const partialLocation = { ...(updatedFarm.location || {}) };
if (partialLongitude !== undefined && partialLongitude !== null) {
partialLocation.lng = parseFloat(partialLongitude);
}
// 注意:这里不更新纬度
await farm.update({ location: partialLocation });
const partialUpdatedFarm = await Farm.findByPk(farm.id);
console.log('部分更新后的记录:');
console.log('- Location:', JSON.stringify(partialUpdatedFarm.location));
console.log('- 经度是否更新:', partialUpdatedFarm.location.lng === partialLongitude ? '✅' : '❌');
console.log('- 纬度是否保持:', partialUpdatedFarm.location.lat === newLatitude ? '✅' : '❌');
// 4. 测试清空经纬度
console.log('\n4. 测试清空经纬度');
const emptyLocation = {};
await farm.update({ location: emptyLocation });
const emptyUpdatedFarm = await Farm.findByPk(farm.id);
console.log('清空后的记录:');
console.log('- Location:', JSON.stringify(emptyUpdatedFarm.location));
console.log('- 是否为空对象:', Object.keys(emptyUpdatedFarm.location).length === 0 ? '✅' : '❌');
// 5. 清理测试数据
console.log('\n5. 清理测试数据');
await farm.destroy();
console.log('测试数据已清理');
console.log('\n✅ 经纬度更新功能测试完成');
} catch (error) {
console.error('❌ 测试过程中发生错误:', error);
console.error('错误详情:', error.message);
if (error.sql) {
console.error('SQL语句:', error.sql);
}
}
}
// 测试通过API接口的完整流程
async function testAPIFlow() {
try {
console.log('\n=== 测试API接口流程 ===');
// 模拟前端发送的请求数据
const createRequest = {
body: {
name: 'API测试养殖场',
owner: 'API测试负责人',
phone: '13900139000',
address: 'API测试地址',
longitude: 106.789123,
latitude: 38.456789,
status: 'active'
}
};
console.log('\n1. 模拟创建请求');
console.log('请求数据:', {
longitude: createRequest.body.longitude,
latitude: createRequest.body.latitude
});
// 模拟createFarm控制器逻辑
const { name, owner, longitude, latitude, address, phone, status } = createRequest.body;
const location = {};
if (longitude !== undefined && longitude !== null) {
location.lng = parseFloat(longitude);
}
if (latitude !== undefined && latitude !== null) {
location.lat = parseFloat(latitude);
}
const farm = await Farm.create({
name,
type: 'farm',
location,
address,
contact: owner,
phone,
status: status || 'active'
});
console.log('创建结果:', {
id: farm.id,
location: farm.location
});
// 模拟更新请求
const updateRequest = {
params: { id: farm.id },
body: {
name: farm.name,
owner: farm.contact,
phone: farm.phone,
address: farm.address,
longitude: 107.111222,
latitude: 39.333444,
status: farm.status
}
};
console.log('\n2. 模拟更新请求');
console.log('更新数据:', {
longitude: updateRequest.body.longitude,
latitude: updateRequest.body.latitude
});
// 模拟updateFarm控制器逻辑
const updateData = updateRequest.body;
const updateLocation = { ...(farm.location || {}) };
if (updateData.longitude !== undefined && updateData.longitude !== null) {
updateLocation.lng = parseFloat(updateData.longitude);
}
if (updateData.latitude !== undefined && updateData.latitude !== null) {
updateLocation.lat = parseFloat(updateData.latitude);
}
await farm.update({
name: updateData.name,
location: updateLocation,
address: updateData.address,
contact: updateData.owner,
phone: updateData.phone,
status: updateData.status
});
// 验证更新结果
const finalFarm = await Farm.findByPk(farm.id);
console.log('更新结果:', {
location: finalFarm.location
});
const apiUpdateSuccess = (
finalFarm.location.lng === updateData.longitude &&
finalFarm.location.lat === updateData.latitude
);
console.log('API流程验证:', apiUpdateSuccess ? '✅ 成功' : '❌ 失败');
// 清理
await farm.destroy();
} catch (error) {
console.error('API流程测试失败:', error);
}
}
// 主函数
async function main() {
try {
await sequelize.authenticate();
console.log('数据库连接成功');
await testLocationUpdate();
await testAPIFlow();
} catch (error) {
console.error('测试失败:', error);
} finally {
await sequelize.close();
console.log('\n数据库连接已关闭');
}
}
// 运行测试
if (require.main === module) {
main();
}
module.exports = { testLocationUpdate, testAPIFlow };

View File

@@ -0,0 +1,118 @@
/**
* 测试用户管理API
*/
const axios = require('axios');
const API_BASE_URL = 'http://localhost:5350/api';
// 测试用户登录并获取token
async function testLogin() {
try {
console.log('测试用户登录...');
const response = await axios.post(`${API_BASE_URL}/auth/login`, {
username: 'admin',
password: '123456'
});
if (response.data.success) {
console.log('✓ 登录成功');
console.log('Token:', response.data.token);
return response.data.token;
} else {
console.log('✗ 登录失败:', response.data.message);
return null;
}
} catch (error) {
console.log('✗ 登录请求失败:', error.message);
return null;
}
}
// 测试获取用户列表
async function testGetUsers(token) {
try {
console.log('\n测试获取用户列表...');
const response = await axios.get(`${API_BASE_URL}/users`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.data.success) {
console.log('✓ 获取用户列表成功');
console.log('用户数量:', response.data.data.length);
response.data.data.forEach(user => {
console.log(`- ${user.username} (${user.email}) - 角色: ${user.role}`);
});
return true;
} else {
console.log('✗ 获取用户列表失败:', response.data.message);
return false;
}
} catch (error) {
console.log('✗ 获取用户列表请求失败:', error.message);
return false;
}
}
// 测试创建用户
async function testCreateUser(token) {
try {
console.log('\n测试创建用户...');
const newUser = {
username: 'testuser_' + Date.now(),
email: `test_${Date.now()}@example.com`,
password: '123456',
role: 'user'
};
const response = await axios.post(`${API_BASE_URL}/users`, newUser, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.data.success) {
console.log('✓ 创建用户成功');
console.log('新用户ID:', response.data.data.id);
return response.data.data.id;
} else {
console.log('✗ 创建用户失败:', response.data.message);
return null;
}
} catch (error) {
console.log('✗ 创建用户请求失败:', error.response?.data?.message || error.message);
return null;
}
}
// 主测试函数
async function runTests() {
console.log('开始测试用户管理API...');
console.log('=' .repeat(50));
// 1. 测试登录
const token = await testLogin();
if (!token) {
console.log('\n测试终止无法获取认证token');
return;
}
// 2. 测试获取用户列表
await testGetUsers(token);
// 3. 测试创建用户
const newUserId = await testCreateUser(token);
// 4. 再次获取用户列表,验证新用户是否创建成功
if (newUserId) {
console.log('\n验证新用户是否创建成功...');
await testGetUsers(token);
}
console.log('\n测试完成!');
}
// 运行测试
runTests().catch(console.error);

View File

@@ -0,0 +1,31 @@
const bcrypt = require('bcrypt');
async function testBcrypt() {
try {
const password = '123456';
const storedHash = '$2b$10$yTdFpkw5MPU5OprOE7xWJ.arvesmRxKm2MpjwdbzNpEUIR2lq4C9S';
console.log('测试密码:', password);
console.log('存储的哈希:', storedHash);
// 直接使用 bcrypt.compare
const result1 = await bcrypt.compare(password, storedHash);
console.log('bcrypt.compare 结果:', result1);
// 生成新的哈希并测试
const newHash = await bcrypt.hash(password, 10);
console.log('新生成的哈希:', newHash);
const result2 = await bcrypt.compare(password, newHash);
console.log('新哈希验证结果:', result2);
// 测试同步方法
const result3 = bcrypt.compareSync(password, storedHash);
console.log('bcrypt.compareSync 结果:', result3);
} catch (error) {
console.error('测试失败:', error);
}
}
testBcrypt();

View File

@@ -0,0 +1,225 @@
/**
* 分析数据库中表之间的外键关系
* @file analyze-foreign-keys.js
* @description 识别所有外键约束和引用关系为ID重排做准备
*/
const { sequelize } = require('./config/database-simple');
const { QueryTypes } = require('sequelize');
async function analyzeForeignKeys() {
try {
console.log('=== 分析数据库外键关系 ===\n');
// 获取所有外键约束
const foreignKeys = await sequelize.query(`
SELECT
kcu.TABLE_NAME as table_name,
kcu.COLUMN_NAME as column_name,
kcu.REFERENCED_TABLE_NAME as referenced_table,
kcu.REFERENCED_COLUMN_NAME as referenced_column,
rc.CONSTRAINT_NAME as constraint_name,
rc.UPDATE_RULE as update_rule,
rc.DELETE_RULE as delete_rule
FROM information_schema.KEY_COLUMN_USAGE kcu
JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
WHERE kcu.TABLE_SCHEMA = DATABASE()
AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
ORDER BY kcu.TABLE_NAME, kcu.COLUMN_NAME
`, { type: QueryTypes.SELECT });
console.log(`发现 ${foreignKeys.length} 个外键关系:\n`);
const relationshipMap = new Map();
const tablesWithForeignKeys = new Set();
const referencedTables = new Set();
foreignKeys.forEach(fk => {
const key = `${fk.table_name}.${fk.column_name}`;
const reference = `${fk.referenced_table}.${fk.referenced_column}`;
relationshipMap.set(key, {
table: fk.table_name,
column: fk.column_name,
referencedTable: fk.referenced_table,
referencedColumn: fk.referenced_column,
constraintName: fk.constraint_name,
updateRule: fk.update_rule,
deleteRule: fk.delete_rule
});
tablesWithForeignKeys.add(fk.table_name);
referencedTables.add(fk.referenced_table);
console.log(`🔗 ${fk.table_name}.${fk.column_name} -> ${fk.referenced_table}.${fk.referenced_column}`);
console.log(` 约束名: ${fk.constraint_name}`);
console.log(` 更新规则: ${fk.update_rule}`);
console.log(` 删除规则: ${fk.delete_rule}`);
console.log('');
});
// 分析每个外键字段的数据分布
console.log('\n=== 外键字段数据分布 ===\n');
const foreignKeyStats = [];
for (const [key, relationship] of relationshipMap) {
const { table, column, referencedTable, referencedColumn } = relationship;
try {
// 获取外键字段的统计信息
const stats = await sequelize.query(`
SELECT
COUNT(*) as total_count,
COUNT(DISTINCT ${column}) as unique_count,
MIN(${column}) as min_value,
MAX(${column}) as max_value,
COUNT(CASE WHEN ${column} IS NULL THEN 1 END) as null_count
FROM ${table}
`, { type: QueryTypes.SELECT });
const stat = stats[0];
// 检查引用完整性
const integrityCheck = await sequelize.query(`
SELECT COUNT(*) as invalid_references
FROM ${table} t
WHERE t.${column} IS NOT NULL
AND t.${column} NOT IN (
SELECT ${referencedColumn}
FROM ${referencedTable}
WHERE ${referencedColumn} IS NOT NULL
)
`, { type: QueryTypes.SELECT });
const invalidRefs = parseInt(integrityCheck[0].invalid_references);
const fkStat = {
table,
column,
referencedTable,
referencedColumn,
totalCount: parseInt(stat.total_count),
uniqueCount: parseInt(stat.unique_count),
minValue: stat.min_value,
maxValue: stat.max_value,
nullCount: parseInt(stat.null_count),
invalidReferences: invalidRefs,
hasIntegrityIssues: invalidRefs > 0
};
foreignKeyStats.push(fkStat);
console.log(`📊 ${table}.${column} -> ${referencedTable}.${referencedColumn}:`);
console.log(` - 总记录数: ${fkStat.totalCount}`);
console.log(` - 唯一值数: ${fkStat.uniqueCount}`);
console.log(` - 值范围: ${fkStat.minValue} - ${fkStat.maxValue}`);
console.log(` - NULL值数: ${fkStat.nullCount}`);
console.log(` - 无效引用: ${fkStat.invalidReferences}`);
console.log(` - 完整性问题: ${fkStat.hasIntegrityIssues ? '是' : '否'}`);
console.log('');
} catch (error) {
console.log(`${table}.${column}: 分析失败 - ${error.message}`);
}
}
// 生成依赖关系图
console.log('\n=== 表依赖关系 ===\n');
const dependencyGraph = new Map();
foreignKeys.forEach(fk => {
if (!dependencyGraph.has(fk.table_name)) {
dependencyGraph.set(fk.table_name, new Set());
}
dependencyGraph.get(fk.table_name).add(fk.referenced_table);
});
// 计算更新顺序(拓扑排序)
const updateOrder = [];
const visited = new Set();
const visiting = new Set();
function topologicalSort(table) {
if (visiting.has(table)) {
console.log(`⚠️ 检测到循环依赖: ${table}`);
return;
}
if (visited.has(table)) {
return;
}
visiting.add(table);
const dependencies = dependencyGraph.get(table) || new Set();
for (const dep of dependencies) {
topologicalSort(dep);
}
visiting.delete(table);
visited.add(table);
updateOrder.push(table);
}
// 对所有表进行拓扑排序
const allTables = new Set([...tablesWithForeignKeys, ...referencedTables]);
for (const table of allTables) {
topologicalSort(table);
}
console.log('建议的ID重排顺序被引用的表优先:');
updateOrder.reverse().forEach((table, index) => {
const deps = dependencyGraph.get(table);
const depList = deps ? Array.from(deps).join(', ') : '无';
console.log(`${index + 1}. ${table} (依赖: ${depList})`);
});
// 汇总报告
console.log('\n=== 汇总报告 ===');
console.log(`外键关系总数: ${foreignKeys.length}`);
console.log(`涉及外键的表: ${tablesWithForeignKeys.size}`);
console.log(`被引用的表: ${referencedTables.size}`);
const tablesWithIssues = foreignKeyStats.filter(stat => stat.hasIntegrityIssues);
if (tablesWithIssues.length > 0) {
console.log(`\n⚠️ 发现完整性问题的表:`);
tablesWithIssues.forEach(stat => {
console.log(`- ${stat.table}.${stat.column}: ${stat.invalidReferences} 个无效引用`);
});
} else {
console.log('\n✅ 所有外键关系完整性正常');
}
return {
foreignKeys,
foreignKeyStats,
updateOrder: updateOrder.reverse(),
relationshipMap,
tablesWithIssues
};
} catch (error) {
console.error('分析外键关系失败:', error);
throw error;
} finally {
await sequelize.close();
}
}
// 如果直接运行此脚本
if (require.main === module) {
analyzeForeignKeys()
.then(() => {
console.log('\n分析完成!');
process.exit(0);
})
.catch(error => {
console.error('分析失败:', error);
process.exit(1);
});
}
module.exports = { analyzeForeignKeys };

View File

@@ -0,0 +1,19 @@
const { sequelize } = require('./config/database-simple');
async function checkAlertsStatus() {
try {
await sequelize.authenticate();
console.log('数据库连接成功');
const [results] = await sequelize.query('SHOW COLUMNS FROM alerts LIKE "status"');
console.log('Alerts表status字段信息:');
console.table(results);
} catch (error) {
console.error('查询失败:', error.message);
} finally {
await sequelize.close();
}
}
checkAlertsStatus();

View File

@@ -0,0 +1,80 @@
/**
* 检查当前数据库中的经纬度数据
* @file check-current-data.js
*/
const { Farm } = require('./models');
const { sequelize } = require('./config/database-simple');
async function checkCurrentData() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
console.log('\n查询最近的养殖场记录...');
const farms = await Farm.findAll({
attributes: ['id', 'name', 'location', 'created_at'],
order: [['id', 'DESC']],
limit: 10
});
console.log(`\n找到 ${farms.length} 条记录:`);
console.log('=' .repeat(80));
farms.forEach((farm, index) => {
console.log(`${index + 1}. ID: ${farm.id}`);
console.log(` 名称: ${farm.name}`);
console.log(` Location对象: ${JSON.stringify(farm.location)}`);
if (farm.location && typeof farm.location === 'object') {
const lng = farm.location.lng;
const lat = farm.location.lat;
console.log(` 经度 (lng): ${lng} (类型: ${typeof lng})`);
console.log(` 纬度 (lat): ${lat} (类型: ${typeof lat})`);
if (lng !== undefined || lat !== undefined) {
console.log(` ✅ 有经纬度数据`);
} else {
console.log(` ❌ 无经纬度数据`);
}
} else {
console.log(` ❌ Location字段为空或格式错误`);
}
console.log(` 创建时间: ${farm.created_at}`);
console.log('-'.repeat(60));
});
// 查找包含经纬度数据的记录
console.log('\n查找包含经纬度数据的记录...');
const farmsWithLocation = await Farm.findAll({
where: sequelize.literal("JSON_EXTRACT(location, '$.lng') IS NOT NULL OR JSON_EXTRACT(location, '$.lat') IS NOT NULL"),
attributes: ['id', 'name', 'location'],
order: [['id', 'DESC']],
limit: 5
});
console.log(`\n包含经纬度数据的记录 (${farmsWithLocation.length} 条):`);
farmsWithLocation.forEach(farm => {
console.log(`ID: ${farm.id}, 名称: ${farm.name}`);
console.log(`经度: ${farm.location.lng}, 纬度: ${farm.location.lat}`);
console.log('');
});
} catch (error) {
console.error('查询失败:', error.message);
if (error.sql) {
console.error('SQL:', error.sql);
}
} finally {
await sequelize.close();
console.log('数据库连接已关闭');
}
}
// 运行检查
if (require.main === module) {
checkCurrentData();
}
module.exports = { checkCurrentData };

View File

@@ -0,0 +1,23 @@
const { Device } = require('./models');
(async () => {
try {
const devices = await Device.findAll({
limit: 5,
attributes: ['id', 'name', 'type']
});
console.log('前5个设备:');
devices.forEach(d => {
console.log(`ID: ${d.id}, 名称: ${d.name}, 类型: ${d.type}`);
});
const totalCount = await Device.count();
console.log(`\n设备总数: ${totalCount}`);
} catch (error) {
console.error('检查设备时出错:', error);
} finally {
process.exit();
}
})();

View File

@@ -0,0 +1,56 @@
const { Animal, Device, Alert, Order } = require('./models');
async function checkFarmForeignKeys() {
try {
console.log('检查引用farms表的外键情况...');
// 检查animals表
const animals = await Animal.findAll({
attributes: ['id', 'farmId'],
order: [['farmId', 'ASC']]
});
console.log('\nAnimals表中的farmId分布:');
const animalFarmIds = [...new Set(animals.map(a => a.farmId))].sort((a, b) => a - b);
console.log('引用的farmId:', animalFarmIds);
console.log(`总共 ${animals.length} 条动物记录`);
// 检查devices表
const devices = await Device.findAll({
attributes: ['id', 'farmId'],
order: [['farmId', 'ASC']]
});
console.log('\nDevices表中的farmId分布:');
const deviceFarmIds = [...new Set(devices.map(d => d.farmId))].sort((a, b) => a - b);
console.log('引用的farmId:', deviceFarmIds);
console.log(`总共 ${devices.length} 条设备记录`);
// 检查alerts表
const alerts = await Alert.findAll({
attributes: ['id', 'farmId'],
order: [['farmId', 'ASC']]
});
console.log('\nAlerts表中的farmId分布:');
const alertFarmIds = [...new Set(alerts.map(a => a.farmId))].sort((a, b) => a - b);
console.log('引用的farmId:', alertFarmIds);
console.log(`总共 ${alerts.length} 条警报记录`);
// 检查orders表
const orders = await Order.findAll({
attributes: ['id', 'farmId'],
order: [['farmId', 'ASC']]
});
console.log('\nOrders表中的farmId分布:');
const orderFarmIds = [...new Set(orders.map(o => o.farmId))].sort((a, b) => a - b);
console.log('引用的farmId:', orderFarmIds);
console.log(`总共 ${orders.length} 条订单记录`);
} catch (error) {
console.error('检查失败:', error.message);
}
}
checkFarmForeignKeys();

View File

@@ -0,0 +1,28 @@
const { Farm } = require('./models');
async function checkFarmsId() {
try {
console.log('检查farms表ID分布情况...');
const farms = await Farm.findAll({
order: [['id', 'ASC']]
});
console.log('当前farms表ID分布:');
farms.forEach(farm => {
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
});
console.log(`\n总共有 ${farms.length} 个养殖场`);
if (farms.length > 0) {
console.log(`最小ID: ${farms[0].id}`);
console.log(`最大ID: ${farms[farms.length - 1].id}`);
}
} catch (error) {
console.error('检查失败:', error.message);
}
}
checkFarmsId();

View File

@@ -0,0 +1,48 @@
const { sequelize } = require('./config/database-simple');
async function checkFarmsSQL() {
try {
console.log('检查farms表状态...');
// 检查表是否存在
const tables = await sequelize.query("SHOW TABLES LIKE 'farms'");
console.log('farms表存在:', tables[0].length > 0);
if (tables[0].length > 0) {
// 检查记录数
const count = await sequelize.query('SELECT COUNT(*) as count FROM farms');
console.log('farms表记录数:', count[0][0].count);
// 如果有记录,显示所有记录
if (count[0][0].count > 0) {
const farms = await sequelize.query('SELECT * FROM farms ORDER BY id ASC');
console.log('farms表数据:');
farms[0].forEach(farm => {
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
});
}
// 检查临时表是否还存在
const tempTables = await sequelize.query("SHOW TABLES LIKE 'farms_temp'");
console.log('farms_temp表存在:', tempTables[0].length > 0);
if (tempTables[0].length > 0) {
const tempCount = await sequelize.query('SELECT COUNT(*) as count FROM farms_temp');
console.log('farms_temp表记录数:', tempCount[0][0].count);
if (tempCount[0][0].count > 0) {
const tempFarms = await sequelize.query('SELECT * FROM farms_temp ORDER BY id ASC');
console.log('farms_temp表数据:');
tempFarms[0].forEach(farm => {
console.log(`ID: ${farm.id}, Name: ${farm.name}`);
});
}
}
}
} catch (error) {
console.error('检查失败:', error.message);
}
}
checkFarmsSQL();

View File

@@ -0,0 +1,69 @@
const { sequelize } = require('./config/database-simple');
async function checkOrphanedForeignKeys() {
try {
console.log('检查孤立外键数据...');
// 检查farm_id=12的记录数量
const [devicesResult] = await sequelize.query(
'SELECT COUNT(*) as count FROM devices WHERE farm_id = 12'
);
const [alertsResult] = await sequelize.query(
'SELECT COUNT(*) as count FROM alerts WHERE farm_id = 12'
);
const [animalsResult] = await sequelize.query(
'SELECT COUNT(*) as count FROM animals WHERE farm_id = 12'
);
console.log('\nfarm_id=12的孤立记录数量:');
console.log('devices表:', devicesResult[0].count);
console.log('alerts表:', alertsResult[0].count);
console.log('animals表:', animalsResult[0].count);
// 检查具体的孤立记录
if (devicesResult[0].count > 0) {
const [devices] = await sequelize.query(
'SELECT id, name, type FROM devices WHERE farm_id = 12'
);
console.log('\ndevices表中farm_id=12的记录:');
devices.forEach(device => {
console.log(`- ID: ${device.id}, 名称: ${device.name}, 类型: ${device.type}`);
});
}
if (alertsResult[0].count > 0) {
const [alerts] = await sequelize.query(
'SELECT id, type, level FROM alerts WHERE farm_id = 12'
);
console.log('\nalerts表中farm_id=12的记录:');
alerts.forEach(alert => {
console.log(`- ID: ${alert.id}, 类型: ${alert.type}, 级别: ${alert.level}`);
});
}
if (animalsResult[0].count > 0) {
const [animals] = await sequelize.query(
'SELECT id, type, count FROM animals WHERE farm_id = 12'
);
console.log('\nanimals表中farm_id=12的记录:');
animals.forEach(animal => {
console.log(`- ID: ${animal.id}, 类型: ${animal.type}, 数量: ${animal.count}`);
});
}
// 检查farms表的最大ID
const [farmsMaxId] = await sequelize.query(
'SELECT MAX(id) as max_id FROM farms'
);
console.log('\nfarms表最大ID:', farmsMaxId[0].max_id);
} catch (error) {
console.error('检查失败:', error.message);
} finally {
await sequelize.close();
}
}
checkOrphanedForeignKeys();

View File

@@ -0,0 +1,128 @@
/**
* 检查数据库中所有表的主键ID分布情况
* @file check-primary-keys.js
* @description 分析各表的主键ID范围为重新排序做准备
*/
const { sequelize } = require('./config/database-simple');
const { QueryTypes } = require('sequelize');
async function checkPrimaryKeys() {
try {
console.log('=== 检查数据库表主键ID分布情况 ===\n');
// 获取所有表名
const tables = await sequelize.query(
"SELECT TABLE_NAME as table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'",
{ type: QueryTypes.SELECT }
);
console.log(`发现 ${tables.length} 个表:\n`);
const tableStats = [];
for (const table of tables) {
const tableName = table.table_name;
try {
// 检查表是否有id字段
const columns = await sequelize.query(
`SELECT COLUMN_NAME as column_name, DATA_TYPE as data_type, IS_NULLABLE as is_nullable, COLUMN_KEY as column_key
FROM information_schema.columns
WHERE table_schema = DATABASE() AND TABLE_NAME = '${tableName}' AND COLUMN_NAME = 'id'`,
{ type: QueryTypes.SELECT }
);
if (columns.length === 0) {
console.log(`${tableName}: 没有id字段`);
continue;
}
// 获取ID统计信息
const stats = await sequelize.query(
`SELECT
COUNT(*) as total_count,
MIN(id) as min_id,
MAX(id) as max_id,
COUNT(DISTINCT id) as unique_count
FROM ${tableName}`,
{ type: QueryTypes.SELECT }
);
const stat = stats[0];
// 检查ID连续性
const gapCheck = await sequelize.query(
`SELECT COUNT(*) as gap_count
FROM (
SELECT id + 1 as next_id
FROM ${tableName}
WHERE id + 1 NOT IN (SELECT id FROM ${tableName})
AND id < (SELECT MAX(id) FROM ${tableName})
) as gaps`,
{ type: QueryTypes.SELECT }
);
const hasGaps = gapCheck[0].gap_count > 0;
const tableInfo = {
tableName,
totalCount: parseInt(stat.total_count),
minId: stat.min_id,
maxId: stat.max_id,
uniqueCount: parseInt(stat.unique_count),
hasGaps,
needsReorder: stat.min_id !== 1 || hasGaps
};
tableStats.push(tableInfo);
console.log(`${tableName}:`);
console.log(` - 记录数: ${tableInfo.totalCount}`);
console.log(` - ID范围: ${tableInfo.minId} - ${tableInfo.maxId}`);
console.log(` - 唯一ID数: ${tableInfo.uniqueCount}`);
console.log(` - 有间隙: ${hasGaps ? '是' : '否'}`);
console.log(` - 需要重排: ${tableInfo.needsReorder ? '是' : '否'}`);
console.log('');
} catch (error) {
console.log(`${tableName}: 检查失败 - ${error.message}`);
}
}
// 汇总统计
console.log('\n=== 汇总统计 ===');
const needReorderTables = tableStats.filter(t => t.needsReorder);
console.log(`需要重新排序的表: ${needReorderTables.length}/${tableStats.length}`);
if (needReorderTables.length > 0) {
console.log('\n需要重新排序的表:');
needReorderTables.forEach(table => {
console.log(`- ${table.tableName} (${table.minId}-${table.maxId}, ${table.totalCount}条记录)`);
});
}
return tableStats;
} catch (error) {
console.error('检查主键失败:', error);
throw error;
} finally {
await sequelize.close();
}
}
// 如果直接运行此脚本
if (require.main === module) {
checkPrimaryKeys()
.then(() => {
console.log('\n检查完成!');
process.exit(0);
})
.catch(error => {
console.error('检查失败:', error);
process.exit(1);
});
}
module.exports = { checkPrimaryKeys };

View File

@@ -0,0 +1,48 @@
const db = require('./config/database');
const SensorData = require('./models/SensorData');
(async () => {
try {
// 检查传感器数据总数
const count = await SensorData.count();
console.log('传感器数据总数:', count);
// 检查最近的温度数据
const temperatureData = await SensorData.findAll({
where: { sensor_type: 'temperature' },
limit: 10,
order: [['recorded_at', 'DESC']]
});
console.log('\n最近10条温度数据:');
temperatureData.forEach(r => {
console.log(`${r.sensor_type}: ${r.value}${r.unit} at ${r.recorded_at}`);
});
// 检查最近的湿度数据
const humidityData = await SensorData.findAll({
where: { sensor_type: 'humidity' },
limit: 10,
order: [['recorded_at', 'DESC']]
});
console.log('\n最近10条湿度数据:');
humidityData.forEach(r => {
console.log(`${r.sensor_type}: ${r.value}${r.unit} at ${r.recorded_at}`);
});
// 检查24小时内的数据
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
const recentCount = await SensorData.count({
where: {
recorded_at: {
[require('sequelize').Op.gte]: twentyFourHoursAgo
}
}
});
console.log('\n24小时内的传感器数据总数:', recentCount);
} catch (error) {
console.error('检查数据时出错:', error);
} finally {
process.exit();
}
})();

View File

@@ -0,0 +1,19 @@
const { sequelize } = require('./config/database-simple');
async function checkSensorTable() {
try {
await sequelize.authenticate();
console.log('数据库连接成功');
const [results] = await sequelize.query('SHOW COLUMNS FROM sensor_data');
console.log('sensor_data表结构:');
console.table(results);
} catch (error) {
console.error('查询失败:', error.message);
} finally {
await sequelize.close();
}
}
checkSensorTable();

View File

@@ -0,0 +1,47 @@
const { Animal, Device, Alert, Order, Farm } = require('./models');
async function checkTableColumns() {
try {
console.log('检查各表的列结构...');
// 检查farms表结构
console.log('\n=== Farms表结构 ===');
const farmAttrs = await Farm.describe();
Object.keys(farmAttrs).forEach(col => {
console.log(`${col}: ${farmAttrs[col].type}`);
});
// 检查animals表结构
console.log('\n=== Animals表结构 ===');
const animalAttrs = await Animal.describe();
Object.keys(animalAttrs).forEach(col => {
console.log(`${col}: ${animalAttrs[col].type}`);
});
// 检查devices表结构
console.log('\n=== Devices表结构 ===');
const deviceAttrs = await Device.describe();
Object.keys(deviceAttrs).forEach(col => {
console.log(`${col}: ${deviceAttrs[col].type}`);
});
// 检查alerts表结构
console.log('\n=== Alerts表结构 ===');
const alertAttrs = await Alert.describe();
Object.keys(alertAttrs).forEach(col => {
console.log(`${col}: ${alertAttrs[col].type}`);
});
// 检查orders表结构
console.log('\n=== Orders表结构 ===');
const orderAttrs = await Order.describe();
Object.keys(orderAttrs).forEach(col => {
console.log(`${col}: ${orderAttrs[col].type}`);
});
} catch (error) {
console.error('检查失败:', error.message);
}
}
checkTableColumns();

View File

@@ -0,0 +1,53 @@
/**
* 检查并同步数据库表结构脚本
*/
const { sequelize } = require('./models/index');
async function checkAndSyncDatabase() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
console.log('数据库连接成功');
// 强制同步数据库(这会删除现有表并重新创建)
console.log('\n开始同步数据库模型...');
await sequelize.sync({ force: true });
console.log('数据库模型同步完成');
// 检查创建的表
console.log('\n=== 检查创建的表 ===');
const [tables] = await sequelize.query("SHOW TABLES");
console.log('已创建的表:', tables.map(row => Object.values(row)[0]));
// 检查devices表结构
console.log('\n=== devices表结构 ===');
const [devicesFields] = await sequelize.query("DESCRIBE devices");
console.log('devices表字段:');
devicesFields.forEach(field => {
console.log(`- ${field.Field}: ${field.Type} (${field.Null === 'YES' ? 'NULL' : 'NOT NULL'})`);
});
// 检查animals表结构
console.log('\n=== animals表结构 ===');
const [animalsFields] = await sequelize.query("DESCRIBE animals");
console.log('animals表字段:');
animalsFields.forEach(field => {
console.log(`- ${field.Field}: ${field.Type} (${field.Null === 'YES' ? 'NULL' : 'NOT NULL'})`);
});
// 检查alerts表结构
console.log('\n=== alerts表结构 ===');
const [alertsFields] = await sequelize.query("DESCRIBE alerts");
console.log('alerts表字段:');
alertsFields.forEach(field => {
console.log(`- ${field.Field}: ${field.Type} (${field.Null === 'YES' ? 'NULL' : 'NOT NULL'})`);
});
} catch (error) {
console.error('操作失败:', error);
} finally {
await sequelize.close();
}
}
checkAndSyncDatabase();

View File

@@ -0,0 +1,50 @@
const { User, Role } = require('./models');
const { sequelize } = require('./config/database-simple');
const bcrypt = require('bcrypt');
async function checkUsers() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
console.log('数据库连接成功');
// 查看所有用户
console.log('\n=== 查看所有用户 ===');
const users = await User.findAll({
attributes: ['id', 'username', 'email', 'password', 'status']
});
console.log('用户数量:', users.length);
users.forEach(user => {
console.log(`ID: ${user.id}, 用户名: ${user.username}, 邮箱: ${user.email}, 状态: ${user.status}`);
console.log(`密码哈希: ${user.password}`);
});
// 测试密码验证
console.log('\n=== 测试密码验证 ===');
const testPassword = '123456';
// 测试testuser的密码
const testHash1 = '$2b$10$CT0Uk9ueBFN4jc/5vnKGguDfr4cAyV3NUXKVKG6GrFJVsbcJakXLy'; // testuser的哈希
const isValid1 = await bcrypt.compare(testPassword, testHash1);
console.log('testuser密码验证结果:', isValid1);
// 测试testuser2的密码
const testHash2 = '$2b$10$KJAf.o1ItgiTeff9dAJqyeLQ.f2QCRCR2cUlU/DLn6ifXcBLM3FvK'; // testuser2的哈希
const isValid2 = await bcrypt.compare(testPassword, testHash2);
console.log('testuser2密码验证结果:', isValid2);
// 测试手动生成的哈希
const manualHash = await bcrypt.hash(testPassword, 10);
console.log('手动生成的哈希:', manualHash);
const isValid3 = await bcrypt.compare(testPassword, manualHash);
console.log('手动哈希验证结果:', isValid3);
} catch (error) {
console.error('检查用户失败:', error);
} finally {
await sequelize.close();
}
}
checkUsers();

View File

@@ -0,0 +1,27 @@
const { User } = require('./models');
const bcrypt = require('bcrypt');
User.findOne({ where: { username: 'admin' } })
.then(user => {
if (user) {
console.log('Admin 用户信息:');
console.log(`用户名: ${user.username}`);
console.log(`邮箱: ${user.email}`);
console.log(`密码哈希: ${user.password}`);
// 测试常见密码
const testPasswords = ['123456', 'admin', 'password', 'admin123'];
testPasswords.forEach(pwd => {
const isMatch = bcrypt.compareSync(pwd, user.password);
console.log(`密码 '${pwd}': ${isMatch ? '匹配' : '不匹配'}`);
});
} else {
console.log('未找到 admin 用户');
}
process.exit(0);
})
.catch(err => {
console.error('查询失败:', err);
process.exit(1);
});

View File

@@ -0,0 +1,14 @@
const { User } = require('./models');
User.findAll({ attributes: ['username', 'email'] })
.then(users => {
console.log('数据库中的用户:');
users.forEach(user => {
console.log(`用户名: ${user.username}, 邮箱: ${user.email}`);
});
process.exit(0);
})
.catch(err => {
console.error('查询失败:', err);
process.exit(1);
});

View File

@@ -0,0 +1,26 @@
const { sequelize } = require('./config/database-simple');
async function countData() {
try {
await sequelize.authenticate();
console.log('数据库连接成功\n');
// 检查各表的数据量
const tables = ['farms', 'animals', 'devices', 'alerts', 'sensor_data'];
console.log('=== 数据统计 ===');
for (const table of tables) {
const [results] = await sequelize.query(`SELECT COUNT(*) as count FROM ${table}`);
console.log(`${table.padEnd(12)}: ${results[0].count.toString().padStart(6)} 条记录`);
}
console.log('\n数据导入完成');
} catch (error) {
console.error('统计失败:', error.message);
} finally {
await sequelize.close();
}
}
countData();

View File

@@ -0,0 +1,32 @@
const { sequelize } = require('./config/database-simple');
async function verifyData() {
try {
await sequelize.authenticate();
console.log('数据库连接成功');
// 检查各表的数据量
const tables = ['farms', 'animals', 'devices', 'alerts', 'sensor_data'];
for (const table of tables) {
const [results] = await sequelize.query(`SELECT COUNT(*) as count FROM ${table}`);
console.log(`${table} 表: ${results[0].count} 条记录`);
}
// 检查最新的一些数据
console.log('\n=== 最新的预警数据 ===');
const [alerts] = await sequelize.query('SELECT * FROM alerts ORDER BY created_at DESC LIMIT 5');
console.table(alerts);
console.log('\n=== 最新的传感器数据 ===');
const [sensors] = await sequelize.query('SELECT * FROM sensor_data ORDER BY recorded_at DESC LIMIT 10');
console.table(sensors);
} catch (error) {
console.error('验证失败:', error.message);
} finally {
await sequelize.close();
}
}
verifyData();

View File

@@ -0,0 +1,105 @@
const mysql = require('mysql2/promise');
require('dotenv').config();
async function verifyEnvironmentData() {
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 [tables] = await connection.execute(
"SHOW TABLES LIKE 'environment_schedules'"
);
if (tables.length === 0) {
console.log('环境监测时刻表不存在');
return;
}
console.log('环境监测时刻表存在');
// 获取总记录数
const [countResult] = await connection.execute(
'SELECT COUNT(*) as total_records FROM environment_schedules'
);
console.log(`\n总记录数: ${countResult[0].total_records}`);
// 按农场分组统计
const [farmStats] = await connection.execute(
`SELECT farm_id, COUNT(*) as record_count,
MIN(monitoring_date) as earliest_date,
MAX(monitoring_date) as latest_date
FROM environment_schedules
GROUP BY farm_id
ORDER BY farm_id`
);
console.log('\n按农场统计:');
farmStats.forEach(stat => {
console.log(`农场${stat.farm_id}: ${stat.record_count}条记录, 日期范围: ${stat.earliest_date}${stat.latest_date}`);
});
// 获取最新的10条记录
const [recentRecords] = await connection.execute(
`SELECT farm_id, device_id, schedule_time, temperature, humidity,
DATE(monitoring_date) as date, status
FROM environment_schedules
ORDER BY monitoring_date DESC, schedule_time DESC
LIMIT 10`
);
console.log('\n最新的10条环境监测记录:');
console.log('农场ID | 设备ID | 日期 | 时间 | 温度(°C) | 湿度(%) | 状态');
console.log('-------|----------|------------|----------|----------|---------|--------');
recentRecords.forEach(record => {
console.log(`${record.farm_id.toString().padEnd(6)} | ${record.device_id.padEnd(8)} | ${record.date} | ${record.schedule_time} | ${record.temperature.toString().padEnd(8)} | ${record.humidity.toString().padEnd(7)} | ${record.status}`);
});
// 检查数据分布
const [timeStats] = await connection.execute(
`SELECT schedule_time, COUNT(*) as count
FROM environment_schedules
GROUP BY schedule_time
ORDER BY schedule_time`
);
console.log('\n按监测时间统计:');
timeStats.forEach(stat => {
console.log(`${stat.schedule_time}: ${stat.count}条记录`);
});
// 温度湿度范围统计
const [rangeStats] = await connection.execute(
`SELECT
MIN(temperature) as min_temp, MAX(temperature) as max_temp, AVG(temperature) as avg_temp,
MIN(humidity) as min_humidity, MAX(humidity) as max_humidity, AVG(humidity) as avg_humidity
FROM environment_schedules`
);
console.log('\n温度湿度统计:');
const stats = rangeStats[0];
console.log(`温度范围: ${parseFloat(stats.min_temp).toFixed(2)}°C - ${parseFloat(stats.max_temp).toFixed(2)}°C (平均: ${parseFloat(stats.avg_temp).toFixed(2)}°C)`);
console.log(`湿度范围: ${parseFloat(stats.min_humidity).toFixed(2)}% - ${parseFloat(stats.max_humidity).toFixed(2)}% (平均: ${parseFloat(stats.avg_humidity).toFixed(2)}%)`);
} catch (error) {
console.error('验证失败:', error.message);
} finally {
if (connection) {
await connection.end();
console.log('\n数据库连接已关闭');
}
}
}
// 运行验证脚本
verifyEnvironmentData();

View File

@@ -0,0 +1,110 @@
const { Farm } = require('./models');
/**
* 验证farms数据导入结果
*/
async function verifyFarmsImport() {
try {
console.log('验证farms数据导入结果...');
// 获取所有farms数据
const farms = await Farm.findAll({
order: [['id', 'ASC']]
});
console.log(`\n✅ 数据库中共有 ${farms.length} 个农场`);
// 验证API静态数据前3个
const apiStaticFarms = farms.slice(0, 3);
console.log('\n📊 API静态数据验证:');
apiStaticFarms.forEach(farm => {
console.log(` ID: ${farm.id}, Name: ${farm.name}, Type: ${farm.type}, Address: ${farm.address}`);
});
// 验证种子数据后8个
const seedFarms = farms.slice(3);
console.log('\n🌱 种子数据验证:');
seedFarms.forEach(farm => {
console.log(` ID: ${farm.id}, Name: ${farm.name}, Type: ${farm.type}, Address: ${farm.address}`);
});
// 验证数据完整性
console.log('\n🔍 数据完整性检查:');
const missingFields = [];
farms.forEach(farm => {
if (!farm.name) missingFields.push(`Farm ${farm.id}: 缺少name字段`);
if (!farm.type) missingFields.push(`Farm ${farm.id}: 缺少type字段`);
if (!farm.location) missingFields.push(`Farm ${farm.id}: 缺少location字段`);
if (!farm.status) missingFields.push(`Farm ${farm.id}: 缺少status字段`);
});
if (missingFields.length === 0) {
console.log(' ✅ 所有农场数据字段完整');
} else {
console.log(' ❌ 发现缺失字段:');
missingFields.forEach(field => console.log(` ${field}`));
}
// 验证ID连续性
const ids = farms.map(farm => farm.id);
const expectedIds = Array.from({length: farms.length}, (_, i) => i + 1);
const idsMatch = JSON.stringify(ids) === JSON.stringify(expectedIds);
if (idsMatch) {
console.log(' ✅ ID序列连续 (1 到 ' + farms.length + ')');
} else {
console.log(' ❌ ID序列不连续');
console.log(` 实际ID: [${ids.join(', ')}]`);
console.log(` 期望ID: [${expectedIds.join(', ')}]`);
}
// 统计农场类型
const typeStats = {};
farms.forEach(farm => {
typeStats[farm.type] = (typeStats[farm.type] || 0) + 1;
});
console.log('\n📈 农场类型统计:');
Object.entries(typeStats).forEach(([type, count]) => {
console.log(` ${type}: ${count}`);
});
// 验证地理位置数据
console.log('\n🗺 地理位置数据验证:');
const locationErrors = [];
farms.forEach(farm => {
try {
if (typeof farm.location === 'string') {
const location = JSON.parse(farm.location);
if (!location.lat || !location.lng) {
locationErrors.push(`Farm ${farm.id} (${farm.name}): 缺少经纬度信息`);
}
} else if (typeof farm.location === 'object') {
if (!farm.location.lat || !farm.location.lng) {
locationErrors.push(`Farm ${farm.id} (${farm.name}): 缺少经纬度信息`);
}
} else {
locationErrors.push(`Farm ${farm.id} (${farm.name}): location字段格式错误`);
}
} catch (error) {
locationErrors.push(`Farm ${farm.id} (${farm.name}): location JSON解析失败`);
}
});
if (locationErrors.length === 0) {
console.log(' ✅ 所有农场地理位置数据有效');
} else {
console.log(' ❌ 发现地理位置数据问题:');
locationErrors.forEach(error => console.log(` ${error}`));
}
console.log('\n🎉 farms数据导入验证完成!');
} catch (error) {
console.error('❌ 验证失败:', error.message);
}
}
// 执行验证
verifyFarmsImport();