Files
aijianhua/backend/routes/identifications.js

355 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const dbConnector = require('../utils/dbConnector');
const { asyncHandler } = require('../middlewares/errorHandler');
const router = express.Router();
// 配置multer用于文件上传
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = path.join(__dirname, '../uploads/identifications');
// 确保上传目录存在
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, 'identification-' + uniqueSuffix + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: 10 * 1024 * 1024, // 10MB限制
},
fileFilter: (req, file, cb) => {
// 只允许图片文件
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('只支持图片文件'), false);
}
}
});
/**
* 获取识别历史记录
*/
router.get('/', asyncHandler(async (req, res) => {
const { page = 1, limit = 10 } = req.query;
const userId = req.user.id;
const offset = (page - 1) * limit;
// 查询识别记录
const identifications = await dbConnector.query(
`SELECT
id, user_id, image_url, result, confidence, created_at
FROM identifications
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?`,
[userId, parseInt(limit), offset]
);
// 查询总数
const totalResult = await dbConnector.query(
'SELECT COUNT(*) as total FROM identifications WHERE user_id = ?',
[userId]
);
res.json({
code: 200,
message: '获取成功',
data: {
identifications,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total: totalResult[0].total,
pages: Math.ceil(totalResult[0].total / limit)
}
}
});
}));
/**
* 获取单条识别记录详情
*/
router.get('/:id', asyncHandler(async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const identifications = await dbConnector.query(
`SELECT
id, user_id, image_url, result, confidence, created_at
FROM identifications
WHERE id = ? AND user_id = ?`,
[id, userId]
);
if (identifications.length === 0) {
return res.status(404).json({
code: 404,
message: '识别记录不存在',
data: null
});
}
res.json({
code: 200,
message: '获取成功',
data: identifications[0]
});
}));
/**
* 花卉识别接口
*/
router.post('/identify', upload.single('image'), asyncHandler(async (req, res) => {
const userId = req.user.id;
if (!req.file) {
return res.status(400).json({
code: 400,
message: '请上传图片文件',
data: null
});
}
// 这里应该调用AI识别服务
// 由于AI识别服务需要额外配置这里先模拟识别结果
const imageUrl = `/uploads/identifications/${req.file.filename}`;
// 模拟AI识别结果实际项目中应该调用真实的AI服务
const mockResults = [
{ name: '玫瑰', confidence: 0.95, scientificName: 'Rosa rugosa', description: '玫瑰是蔷薇科蔷薇属的植物,具有浓郁的芳香和美丽的花朵。' },
{ name: '百合', confidence: 0.87, scientificName: 'Lilium brownii', description: '百合是百合科百合属的植物,象征纯洁和高雅。' },
{ name: '菊花', confidence: 0.82, scientificName: 'Chrysanthemum morifolium', description: '菊花是菊科菊属的植物,具有很高的观赏和药用价值。' }
];
// 选择置信度最高的结果
const bestResult = mockResults[0];
// 保存识别记录到数据库
const result = await dbConnector.query(
'INSERT INTO identifications (user_id, image_url, result, confidence) VALUES (?, ?, ?, ?)',
[userId, imageUrl, JSON.stringify(mockResults), bestResult.confidence]
);
res.json({
code: 200,
message: '识别成功',
data: {
identification_id: result.insertId,
image_url: imageUrl,
results: mockResults,
best_result: bestResult,
created_at: new Date().toISOString()
}
});
}));
/**
* 批量识别历史记录
*/
router.get('/batch/history', asyncHandler(async (req, res) => {
const { start_date, end_date, min_confidence = 0.7 } = req.query;
const userId = req.user.id;
let whereClause = 'WHERE user_id = ? AND confidence >= ?';
let queryParams = [userId, parseFloat(min_confidence)];
if (start_date) {
whereClause += ' AND created_at >= ?';
queryParams.push(start_date);
}
if (end_date) {
whereClause += ' AND created_at <= ?';
queryParams.push(end_date + ' 23:59:59');
}
const identifications = await dbConnector.query(
`SELECT
id, image_url, result, confidence, created_at,
DATE(created_at) as identify_date
FROM identifications
${whereClause}
ORDER BY created_at DESC`,
queryParams
);
// 按日期分组
const groupedByDate = {};
identifications.forEach(record => {
const date = record.identify_date;
if (!groupedByDate[date]) {
groupedByDate[date] = [];
}
groupedByDate[date].push(record);
});
res.json({
code: 200,
message: '获取成功',
data: {
total: identifications.length,
by_date: groupedByDate,
statistics: {
total_count: identifications.length,
avg_confidence: identifications.reduce((sum, item) => sum + item.confidence, 0) / identifications.length,
date_range: {
start: identifications.length > 0 ? identifications[identifications.length - 1].identify_date : null,
end: identifications.length > 0 ? identifications[0].identify_date : null
}
}
}
});
}));
/**
* 识别统计信息
*/
router.get('/stats/summary', asyncHandler(async (req, res) => {
const userId = req.user.id;
// 总识别次数
const totalCountResult = await dbConnector.query(
'SELECT COUNT(*) as total FROM identifications WHERE user_id = ?',
[userId]
);
// 平均置信度
const avgConfidenceResult = await dbConnector.query(
'SELECT AVG(confidence) as avg_confidence FROM identifications WHERE user_id = ?',
[userId]
);
// 最近识别时间
const lastIdentificationResult = await dbConnector.query(
'SELECT created_at FROM identifications WHERE user_id = ? ORDER BY created_at DESC LIMIT 1',
[userId]
);
// 识别最多的花卉类型需要解析result字段
const allIdentifications = await dbConnector.query(
'SELECT result FROM identifications WHERE user_id = ?',
[userId]
);
const flowerCounts = {};
allIdentifications.forEach(item => {
try {
const results = JSON.parse(item.result);
if (results && results.length > 0) {
const bestResult = results[0];
flowerCounts[bestResult.name] = (flowerCounts[bestResult.name] || 0) + 1;
}
} catch (e) {
console.error('解析识别结果失败:', e);
}
});
// 找出识别最多的花卉
let mostIdentifiedFlower = null;
let maxCount = 0;
for (const [flower, count] of Object.entries(flowerCounts)) {
if (count > maxCount) {
mostIdentifiedFlower = flower;
maxCount = count;
}
}
res.json({
code: 200,
message: '获取成功',
data: {
total_count: totalCountResult[0].total,
avg_confidence: avgConfidenceResult[0].avg_confidence || 0,
last_identification: lastIdentificationResult[0] ? lastIdentificationResult[0].created_at : null,
most_identified_flower: mostIdentifiedFlower,
flower_counts: flowerCounts,
weekly_trend: await getWeeklyTrend(userId)
}
});
}));
/**
* 获取周趋势数据
*/
async function getWeeklyTrend(userId) {
const weeklyData = await dbConnector.query(
`SELECT
DATE(created_at) as date,
COUNT(*) as count,
AVG(confidence) as avg_confidence
FROM identifications
WHERE user_id = ? AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(created_at)
ORDER BY date DESC`,
[userId]
);
return weeklyData;
}
/**
* 删除识别记录
*/
router.delete('/:id', asyncHandler(async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
// 先获取记录信息以删除图片文件
const identification = await dbConnector.query(
'SELECT image_url FROM identifications WHERE id = ? AND user_id = ?',
[id, userId]
);
if (identification.length === 0) {
return res.status(404).json({
code: 404,
message: '识别记录不存在',
data: null
});
}
// 删除图片文件
if (identification[0].image_url) {
const imagePath = path.join(__dirname, '../', identification[0].image_url);
if (fs.existsSync(imagePath)) {
fs.unlinkSync(imagePath);
}
}
// 删除数据库记录
const result = await dbConnector.query(
'DELETE FROM identifications WHERE id = ? AND user_id = ?',
[id, userId]
);
if (result.affectedRows === 0) {
return res.status(404).json({
code: 404,
message: '识别记录不存在',
data: null
});
}
res.json({
code: 200,
message: '删除成功',
data: null
});
}));
module.exports = router;