feat(scheduler): 新增多种报表推送功能并重构调度器代码

- 新增考勤汇总、风险进度、工作日报、工作周报等报表推送功能
- 重构调度器中的报表推送逻辑,提取通用的 deliver_report 函数
- 添加新的定时任务配置项用于各种报表推送
- 实现飞书推送配置检查函数 _feishu_delivery_configured
- 将重复的报表推送代码抽取为可复用的函数模式
```
This commit is contained in:
2026-07-13 09:47:29 +08:00
parent f8020cab56
commit 267b01b9f4
12 changed files with 722 additions and 191 deletions

View File

@@ -19,6 +19,7 @@ from app.modules.business.models import (
WorkTask,
)
from app.modules.reports.constants import (
ReportPushType,
ReportResponseKey,
ReportTitle,
)
@@ -65,6 +66,7 @@ class ReportSummaryMixin:
]
return {
ReportResponseKey.TITLE: ReportTitle.DAILY_BRIEF,
ReportResponseKey.REPORT_TYPE: ReportPushType.DAILY_BRIEF,
ReportResponseKey.LINES: lines,
ReportResponseKey.CONTENT: "\n".join(lines),
}
@@ -93,6 +95,28 @@ class ReportSummaryMixin:
lines.append(f" - 超预算:{item.get('code')} {item.get('name')}")
return {
ReportResponseKey.TITLE: ReportTitle.PROJECT_WEEKLY,
ReportResponseKey.REPORT_TYPE: ReportPushType.PROJECT_WEEKLY,
ReportResponseKey.LINES: lines,
ReportResponseKey.CONTENT: "\n".join(lines),
}
def risk_progress(self) -> dict[str, Any]:
"""Build a standalone project risk progress report."""
summary = self.risks.summary()
lines = [
f"- 综合风险等级:{summary[RiskSummaryKey.RISK_LEVEL]}",
f"- 风险分:{summary[RiskSummaryKey.RISK_SCORE]}",
f"- 逾期任务:{len(summary[RiskSummaryKey.OVERDUE_TASKS])}",
f"- 延期项目:{len(summary[RiskSummaryKey.DELAYED_PROJECTS])}",
f"- 超预算项目:{len(summary[RiskSummaryKey.OVER_BUDGET_PROJECTS])}",
f"- 资金风险账户:{len(summary[RiskSummaryKey.FUND_RISKS])}",
f"- 供应商风险:{len(summary[RiskSummaryKey.SUPPLIER_RISKS])}",
f"- 打开风险事件:{len(summary[RiskSummaryKey.OPEN_EVENTS])}",
]
return {
ReportResponseKey.TITLE: ReportTitle.RISK_PROGRESS,
ReportResponseKey.REPORT_TYPE: ReportPushType.RISK_PROGRESS,
ReportResponseKey.LINES: lines,
ReportResponseKey.CONTENT: "\n".join(lines),
}
@@ -122,6 +146,7 @@ class ReportSummaryMixin:
lines.append(f"- {status}{count}")
return {
ReportResponseKey.TITLE: ReportTitle.ATTENDANCE_SUMMARY,
ReportResponseKey.REPORT_TYPE: ReportPushType.ATTENDANCE_SUMMARY,
ReportResponseKey.WORK_DATE: target_date.isoformat(),
ReportResponseKey.TOTAL: total,
ReportResponseKey.ABNORMAL_TOTAL: abnormal_total,

View File

@@ -30,6 +30,7 @@ from app.modules.events.constants import (
)
from app.modules.events.services import EventService
from app.modules.reports.constants import (
ReportPushType,
ReportResponseKey,
ReportTitle,
ReportType,
@@ -41,6 +42,34 @@ from app.modules.reports.services.common import _json_safe, _next_code
class ReportWorkReportMixin:
def work_daily_report(
self,
reporter: str = ActorValue.SCHEDULER,
actor: str = ActorValue.SCHEDULER,
) -> dict[str, Any]:
"""Build a transient work daily report for Feishu delivery."""
return self._transient_work_report(
report_type=ReportType.DAILY,
push_type=ReportPushType.WORK_DAILY,
reporter=reporter,
actor=actor,
)
def work_weekly_report(
self,
reporter: str = ActorValue.SCHEDULER,
actor: str = ActorValue.SCHEDULER,
) -> dict[str, Any]:
"""Build a transient work weekly report for Feishu delivery."""
return self._transient_work_report(
report_type=ReportType.WEEKLY,
push_type=ReportPushType.WORK_WEEKLY,
reporter=reporter,
actor=actor,
)
def generate_work_report(
self,
report_type: str = ReportType.DAILY,
@@ -119,6 +148,23 @@ class ReportWorkReportMixin:
return {ReportResponseKey.REPORT: report, ReportResponseKey.DATA: record_data}
def _transient_work_report(
self,
report_type: str,
push_type: str,
reporter: str,
actor: str,
) -> dict[str, Any]:
result = self.generate_work_report(
report_type=report_type,
reporter=reporter,
persist=False,
actor=actor,
)
report = dict(result[ReportResponseKey.REPORT])
report[ReportResponseKey.REPORT_TYPE] = push_type
return report
def _resolve_period(
self,
report_type: str,