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;