修改文件结构,统一文档格式
This commit is contained in:
225
backend/tools/verification/analyze-foreign-keys.js
Normal file
225
backend/tools/verification/analyze-foreign-keys.js
Normal 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 };
|
||||
19
backend/tools/verification/check-alerts-status.js
Normal file
19
backend/tools/verification/check-alerts-status.js
Normal 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();
|
||||
0
backend/tools/verification/check-animal-count.js
Normal file
0
backend/tools/verification/check-animal-count.js
Normal file
80
backend/tools/verification/check-current-data.js
Normal file
80
backend/tools/verification/check-current-data.js
Normal 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 };
|
||||
23
backend/tools/verification/check-devices.js
Normal file
23
backend/tools/verification/check-devices.js
Normal 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();
|
||||
}
|
||||
})();
|
||||
56
backend/tools/verification/check-farm-foreign-keys.js
Normal file
56
backend/tools/verification/check-farm-foreign-keys.js
Normal 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();
|
||||
28
backend/tools/verification/check-farms-id.js
Normal file
28
backend/tools/verification/check-farms-id.js
Normal 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();
|
||||
48
backend/tools/verification/check-farms-sql.js
Normal file
48
backend/tools/verification/check-farms-sql.js
Normal 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();
|
||||
69
backend/tools/verification/check-orphaned-foreign-keys.js
Normal file
69
backend/tools/verification/check-orphaned-foreign-keys.js
Normal 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();
|
||||
128
backend/tools/verification/check-primary-keys.js
Normal file
128
backend/tools/verification/check-primary-keys.js
Normal 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 };
|
||||
48
backend/tools/verification/check-sensor-data.js
Normal file
48
backend/tools/verification/check-sensor-data.js
Normal 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();
|
||||
}
|
||||
})();
|
||||
19
backend/tools/verification/check-sensor-table.js
Normal file
19
backend/tools/verification/check-sensor-table.js
Normal 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();
|
||||
47
backend/tools/verification/check-table-columns.js
Normal file
47
backend/tools/verification/check-table-columns.js
Normal 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();
|
||||
53
backend/tools/verification/check-table-structure.js
Normal file
53
backend/tools/verification/check-table-structure.js
Normal 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();
|
||||
50
backend/tools/verification/check-users.js
Normal file
50
backend/tools/verification/check-users.js
Normal 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();
|
||||
27
backend/tools/verification/check_password.js
Normal file
27
backend/tools/verification/check_password.js
Normal 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);
|
||||
});
|
||||
14
backend/tools/verification/check_users.js
Normal file
14
backend/tools/verification/check_users.js
Normal 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);
|
||||
});
|
||||
26
backend/tools/verification/count-data.js
Normal file
26
backend/tools/verification/count-data.js
Normal 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();
|
||||
32
backend/tools/verification/verify-data.js
Normal file
32
backend/tools/verification/verify-data.js
Normal 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();
|
||||
105
backend/tools/verification/verify-environment-data.js
Normal file
105
backend/tools/verification/verify-environment-data.js
Normal 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();
|
||||
110
backend/tools/verification/verify-farms-import.js
Normal file
110
backend/tools/verification/verify-farms-import.js
Normal 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();
|
||||
Reference in New Issue
Block a user