You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从数据库定时/实时生成图表所需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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:21:36