From c4d307136a26ff88df303e44be4712c8cbc4c785 Mon Sep 17 00:00:00 2001 From: xiaji Date: Fri, 9 Jan 2026 20:05:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(report):=20=E6=B7=BB=E5=8A=A0=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E7=94=9F=E6=88=90PDF=E6=8A=A5=E5=91=8A=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E9=87=8D=E6=9E=84=E9=82=AE=E4=BB=B6=E5=8F=91?= =?UTF-8?q?=E9=80=81=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将PDF生成逻辑从邮件发送任务中分离,新增独立定时任务 更新README文档说明PDF生成配置和使用方法 --- README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ core/tasks.py | 41 +++++++++++++++++++------- 2 files changed, 111 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 763e479..892f646 100644 --- a/README.md +++ b/README.md @@ -423,6 +423,87 @@ def setup_daily_report_task(): - 配置监控系统(如 Prometheus + Grafana) - 启用 Celery 监控工具(如 Flower) +## 定时生成PDF文件配置 + +除了定时发送邮件外,系统还支持单独定时生成PDF文件,便于在服务器上保存历史报告。 + +### 1. 配置定时生成PDF任务 + +#### 方式一:通过 Django 管理后台(推荐) + +1. 登录 Django 管理后台(/houtai) +2. 找到 **Periodic tasks**(周期性任务) +3. 点击 **Add** 按钮添加新任务 +4. 配置任务: + - **Name**: 任务名称(如:每日PDF报告生成) + - **Task (registered)**: 选择 `core.tasks.generate_daily_pdf_report` + - **Interval**: 设置执行间隔(如:每天) + - **Enabled**: 勾选启用 +5. 点击 **Save** 保存 + +#### 方式二:通过代码配置 + +在 `core/tasks.py` 中添加定时任务配置: + +```python +from celery import shared_task +from django_celery_beat.models import PeriodicTask, IntervalSchedule + +# 创建或更新PDF生成定时任务 +def setup_daily_pdf_task(): + # 创建每天执行的间隔 + schedule, created = IntervalSchedule.objects.get_or_create( + every=1, + period=IntervalSchedule.DAYS, + ) + + # 创建或更新定时任务 + task, created = PeriodicTask.objects.update_or_create( + name='Daily PDF Generation Task', + defaults={ + 'interval': schedule, + 'task': 'core.tasks.generate_daily_pdf_report', + }, + ) + return task +``` + +然后在 Django 启动时调用此函数(如在 `core/apps.py` 中)。 + +### 2. PDF文件存储位置 + +生成的PDF文件默认存储在项目的 `reports` 目录下,命名格式为 `report_YYYY-MM-DD.pdf`。 + +可以在 `settings.py` 中修改存储位置: + +```python +# Reports files configuration +REPORTS_URL = '/reports/' +REPORTS_ROOT = BASE_DIR / 'reports' # 可以修改为其他路径 +``` + +### 3. 验证PDF生成任务 + +- 查看 Celery 日志:`tail -f /var/log/celery/worker.log` +- 检查PDF文件是否生成:`ls -la /path/to/diary-family/reports/` +- 在 Django 管理后台查看任务执行记录 + +### 4. 常见问题排查 + +1. **PDF生成失败** + - 检查 WeasyPrint 库是否正确安装:`pip list | grep weasyprint` + - 检查系统字体是否完整:PDF生成需要系统字体支持 + - 查看 Celery 日志中的错误信息 + +2. **任务执行但没有生成文件** + - 检查 `REPORTS_ROOT` 目录权限:确保 Celery 进程有写入权限 + - 检查磁盘空间是否充足 + - 查看 Celery 日志中的详细信息 + +3. **PDF内容不完整** + - 检查模板文件 `core/templates/core/report_pdf.html` 是否完整 + - 确保所有依赖的 CSS 和图像资源都能正常访问 + ## Celery 监控(可选) 使用 Flower 监控 Celery 任务: diff --git a/core/tasks.py b/core/tasks.py index dac66d0..69c470e 100644 --- a/core/tasks.py +++ b/core/tasks.py @@ -32,9 +32,9 @@ from .models import ( ) @shared_task -def send_daily_report(): - """发送每日报告""" - logger.info("开始执行每日报告发送任务") +def generate_daily_pdf_report(): + """生成每日PDF报告""" + logger.info("开始执行每日PDF报告生成任务") # 检查WeasyPrint是否可用 if not is_weasyprint_available(): @@ -44,14 +44,6 @@ def send_daily_report(): today = timezone.now().date() today_str = today.strftime('%Y-%m-%d') - # 获取系统配置 - config = SystemConfig.get_config() - - # 检查邮件配置是否完整 - if not all([config.smtp_server, config.smtp_username, config.smtp_password, config.recipient_email]): - logger.error("邮件配置不完整,无法发送邮件") - return False - # 生成报告数据 report_date = today yesterday = report_date - timedelta(days=1) @@ -92,9 +84,32 @@ def send_daily_report(): from weasyprint import HTML HTML(string=html_string).write_pdf(pdf_path) logger.info(f"PDF报告生成成功: {pdf_path}") + return True except Exception as e: logger.error(f"PDF报告生成失败: {str(e)}") return False + +@shared_task +def send_daily_report(): + """发送每日报告""" + logger.info("开始执行每日报告发送任务") + + # 先生成PDF报告 + pdf_generated = generate_daily_pdf_report() + if not pdf_generated: + logger.error("PDF报告生成失败,无法发送邮件") + return False + + today = timezone.now().date() + today_str = today.strftime('%Y-%m-%d') + + # 获取系统配置 + config = SystemConfig.get_config() + + # 检查邮件配置是否完整 + if not all([config.smtp_server, config.smtp_username, config.smtp_password, config.recipient_email]): + logger.error("邮件配置不完整,无法发送邮件") + return False # 发送邮件 subject = f"家庭日报 - {today_str}" @@ -102,6 +117,10 @@ def send_daily_report(): from_email = config.smtp_username recipient_list = [config.recipient_email] + # PDF文件路径 + pdf_file = f"report_{today_str}.pdf" + pdf_path = os.path.join(settings.REPORTS_ROOT, pdf_file) + try: email = EmailMessage( subject=subject,