如何从数据库定时/实时生成图表所需CSV文件?代码故障排查
问题排查与代码修正
现有代码的核心问题
- 未处理异步Promise:
db.manager.findAndCount(User)是异步操作,未加await直接拼接,导致输出[object Promise]。 - 数据格式不符预期:使用
Date.now()输出时间戳,而非要求的YYYY-MM-DD日期格式;仅查询了用户数据,缺少文章、评论的统计。 - 文件操作逻辑错误:
fs.writeFile是异步方法,回调中返回fs.readFileSync无法正确响应客户端;未处理CSV表头,且每次写入仅追加单行,未考虑历史数据的维护。 - 无定时生成逻辑:仅在路由访问时触发生成,未实现每小时自动生成的需求。
修正后的完整代码
1. 实时生成CSV的路由处理
import { format } from 'date-fns'; // 或用原生Date方法:new Date().toISOString().split('T')[0] import fs from 'fs/promises'; // 用Promise版fs适配async/await import path from 'path'; import { CronJob } from 'cron'; // 安装依赖:npm install cron // 假设已导入对应的数据库模型 import { User, Article, Comment } from './models'; admin.get('/chart', async (req, res) => { try { // 并行查询所有统计数据,提升效率 const [userStats, articleStats, commentStats] = await Promise.all([ db.manager.findAndCount(User), db.manager.findAndCount(Article), db.manager.findAndCount(Comment) ]); // 格式化日期并提取统计数量 const today = format(new Date(), 'yyyy-MM-dd'); const userCount = userStats.count; const articleCount = articleStats.count; const commentCount = commentStats.count; // 构建CSV行数据 const csvRow = `${today},${userCount},${articleCount},${commentCount}`; const csvPath = path.join('assets/data.csv'); // 处理CSV文件:不存在则写入表头,存在则追加新行(避免重复当日数据) let fileContent = ''; try { await fs.access(csvPath); fileContent = await fs.readFile(csvPath, 'utf8'); // 检查当日数据是否已存在,防止重复写入 const rows = fileContent.split('\n').filter(row => row.trim()); const hasTodayData = rows.some(row => row.startsWith(today)); if (!hasTodayData) { fileContent += `\n${csvRow}`; } } catch { // 文件不存在,写入表头+首行数据 fileContent = '日期,日用户数,日文章数,日评论数\n' + csvRow; } // 写入文件并响应客户端 await fs.writeFile(csvPath, fileContent, 'utf8'); res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename=data.csv'); res.send(fileContent); } catch (err) { console.error('生成CSV失败:', err); res.status(500).send('生成CSV失败'); } });
2. 每小时定时生成CSV的逻辑
// 定义定时任务:每小时第0分钟执行一次 const hourlyCsvJob = new CronJob('0 * * * *', async () => { try { const [userStats, articleStats, commentStats] = await Promise.all([ db.manager.findAndCount(User), db.manager.findAndCount(Article), db.manager.findAndCount(Comment) ]); const today = format(new Date(), 'yyyy-MM-dd'); const userCount = userStats.count; const articleCount = articleStats.count; const commentCount = commentStats.count; const csvRow = `${today},${userCount},${articleCount},${commentCount}`; const csvPath = path.join('assets/data.csv'); let fileContent = ''; try { await fs.access(csvPath); fileContent = await fs.readFile(csvPath, 'utf8'); const rows = fileContent.split('\n').filter(row => row.trim()); const hasTodayData = rows.some(row => row.startsWith(today)); if (!hasTodayData) { fileContent += `\n${csvRow}`; await fs.writeFile(csvPath, fileContent, 'utf8'); } } catch { fileContent = '日期,日用户数,日文章数,日评论数\n' + csvRow; await fs.writeFile(csvPath, fileContent, 'utf8'); } console.log('定时生成CSV完成'); } catch (err) { console.error('定时生成CSV失败:', err); } }); // 启动定时任务 hourlyCsvJob.start();
关键修正点说明
- 异步处理优化:用
await等待数据库查询,Promise.all并行查询减少等待时间。 - 日期格式化:生成标准的
YYYY-MM-DD格式日期,匹配需求中的格式。 - 文件操作规范:使用
fs.promises替代回调API,逻辑更清晰;添加CSV表头,避免重复写入当日数据。 - 定时任务实现:借助
cron库配置每小时执行的任务,满足定时生成需求。
内容的提问来源于stack exchange,提问作者Free
相关产品推荐
相关产品推荐

