From 6361c94204ed7a93c64fc63d02c5a5104bb8cf88 Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Thu, 5 Mar 2026 21:31:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=97=A5=E6=8A=A5/=E5=91=A8=E6=8A=A5?= =?UTF-8?q?=E6=9C=88=E6=8A=A5=E5=88=86=E5=BC=80=E6=8E=A5=E6=94=B6=E4=BA=BA?= =?UTF-8?q?=20+=20=E4=BA=BA=E5=9D=87=E4=BA=A7=E5=87=BA=E5=8F=AA=E7=AE=97?= =?UTF-8?q?=E4=B8=AD=E6=9C=9F=E6=8F=90=E4=BA=A4=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 日报推送12人(主管+组长+制片+股东) - 周报/月报推送4人(仅股东+主管,含成本信息) - 人均日产出分母改为只算有中期提交的人,与产出口径一致 Co-Authored-By: Claude Opus 4.6 --- backend/.env | 4 +++- backend/config.py | 1 + backend/routers/dashboard.py | 4 +++- backend/routers/reports.py | 9 +++++---- backend/services/feishu_service.py | 11 ++++++----- backend/services/scheduler_service.py | 8 ++++++-- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/backend/.env b/backend/.env index 0700b61..a45c59c 100644 --- a/backend/.env +++ b/backend/.env @@ -14,5 +14,7 @@ ARK_BASE_URL=https://ark.cn-beijing.volces.com/api/v3 FEISHU_APP_ID=cli_a90478156bf85bd7 FEISHU_APP_SECRET=87N2nnx6Yv56TPjl2GraLdKOjFiGOSGp -# 报告接收人手机号(逗号分隔) +# 日报接收人手机号(主管+组长+制片+股东) +DAILY_REPORT_RECEIVERS=18002277047,13811803069,13636518028,13811126887,19521028015,13570527019,15920585849,17762840667,17798147128,13726331058,13751770010,18826166683 +# 周报/月报接收人手机号(含成本信息,仅股东+主管) REPORT_RECEIVERS=18002277047,13811803069,13636518028,13811126887 diff --git a/backend/config.py b/backend/config.py index c7184b5..546000d 100644 --- a/backend/config.py +++ b/backend/config.py @@ -36,4 +36,5 @@ FEISHU_APP_ID = os.getenv("FEISHU_APP_ID", "") FEISHU_APP_SECRET = os.getenv("FEISHU_APP_SECRET", "") # 报告接收人手机号 +DAILY_REPORT_RECEIVERS = [p.strip() for p in os.getenv("DAILY_REPORT_RECEIVERS", "").split(",") if p.strip()] REPORT_RECEIVERS = [p.strip() for p in os.getenv("REPORT_RECEIVERS", "").split(",") if p.strip()] diff --git a/backend/routers/dashboard.py b/backend/routers/dashboard.py index 6a44921..7272789 100644 --- a/backend/routers/dashboard.py +++ b/backend/routers/dashboard.py @@ -85,9 +85,11 @@ def get_dashboard( Submission.project_phase == PhaseGroup.PRODUCTION, ).scalar() or 0 - # 活跃人数 + # 活跃人数(只算有中期提交的人,与产出口径一致) active_users = db.query(Submission.user_id).filter( Submission.submit_date >= month_start, + Submission.submit_date <= today, + Submission.project_phase == PhaseGroup.PRODUCTION, ).distinct().count() working_days = max(1, (today - month_start).days + 1) avg_daily = round(monthly_secs / max(1, active_users) / working_days, 1) diff --git a/backend/routers/reports.py b/backend/routers/reports.py index 2fdfa21..77afef2 100644 --- a/backend/routers/reports.py +++ b/backend/routers/reports.py @@ -9,8 +9,8 @@ from auth import require_permission router = APIRouter(prefix="/api/reports", tags=["AI报告"]) -async def _push_card(card: dict, test_mobile: Optional[str] = None) -> dict: - """推送卡片,支持 test_mobile 只推单人""" +async def _push_card(card: dict, test_mobile: Optional[str] = None, receivers: list = None) -> dict: + """推送卡片,支持 test_mobile 只推单人,或指定 receivers 列表""" from services.feishu_service import feishu if test_mobile: user_id = await feishu.get_user_id_by_mobile(test_mobile) @@ -20,7 +20,7 @@ async def _push_card(card: dict, test_mobile: Optional[str] = None) -> dict: if ok: return {"success": [test_mobile], "failed": []} return {"success": [], "failed": [{"mobile": test_mobile, "reason": "发送失败"}]} - return await feishu.send_report_card_to_all(card) + return await feishu.send_report_card_to_all(card, receivers=receivers) @router.post("/daily") @@ -32,10 +32,11 @@ async def trigger_daily_report( """手动触发日报生成并推送飞书""" from services.report_service import generate_daily_report from services.feishu_service import build_daily_card + from config import DAILY_REPORT_RECEIVERS report = generate_daily_report(db) card = build_daily_card(report["title"], report["card_data"]) - push_result = await _push_card(card, test_mobile) + push_result = await _push_card(card, test_mobile, receivers=DAILY_REPORT_RECEIVERS) return { "message": "日报生成并推送完成", diff --git a/backend/services/feishu_service.py b/backend/services/feishu_service.py index 4ae215d..214f599 100644 --- a/backend/services/feishu_service.py +++ b/backend/services/feishu_service.py @@ -3,7 +3,7 @@ import time import json import logging import httpx -from config import FEISHU_APP_ID, FEISHU_APP_SECRET, REPORT_RECEIVERS +from config import FEISHU_APP_ID, FEISHU_APP_SECRET, REPORT_RECEIVERS, DAILY_REPORT_RECEIVERS logger = logging.getLogger(__name__) @@ -387,15 +387,16 @@ class FeishuService: } return await self.send_card(user_id, card) - async def send_report_card_to_all(self, card: dict) -> dict: - """给所有配置的接收人发送卡片报告""" + async def send_report_card_to_all(self, card: dict, receivers: list = None) -> dict: + """给指定接收人发送卡片报告,默认使用 REPORT_RECEIVERS""" results = {"success": [], "failed": []} + receiver_list = receivers or REPORT_RECEIVERS - if not REPORT_RECEIVERS: + if not receiver_list: logger.warning("未配置报告接收人") return results - for mobile in REPORT_RECEIVERS: + for mobile in receiver_list: user_id = await self.get_user_id_by_mobile(mobile) if not user_id: results["failed"].append({"mobile": mobile, "reason": "未找到用户"}) diff --git a/backend/services/scheduler_service.py b/backend/services/scheduler_service.py index eaa565d..361f587 100644 --- a/backend/services/scheduler_service.py +++ b/backend/services/scheduler_service.py @@ -17,6 +17,7 @@ async def _run_report_job(report_type: str): from services.feishu_service import ( feishu, build_daily_card, build_weekly_card, build_monthly_card, ) + from config import DAILY_REPORT_RECEIVERS, REPORT_RECEIVERS logger.info(f"[定时任务] 开始生成{report_type}...") db = SessionLocal() @@ -24,18 +25,21 @@ async def _run_report_job(report_type: str): if report_type == "日报": result = generate_daily_report(db) card = build_daily_card(result["title"], result["card_data"]) + receivers = DAILY_REPORT_RECEIVERS elif report_type == "周报": result = generate_weekly_report(db) card = build_weekly_card(result["title"], result["card_data"]) + receivers = REPORT_RECEIVERS elif report_type == "月报": result = generate_monthly_report(db) card = build_monthly_card(result["title"], result["card_data"]) + receivers = REPORT_RECEIVERS else: logger.error(f"未知报告类型: {report_type}") return - logger.info(f"[定时任务] {report_type}生成完成,开始推送飞书...") - push_result = await feishu.send_report_card_to_all(card) + logger.info(f"[定时任务] {report_type}生成完成,开始推送飞书({len(receivers)}人)...") + push_result = await feishu.send_report_card_to_all(card, receivers=receivers) logger.info(f"[定时任务] {report_type}推送完成: {push_result}") except Exception as e: