From eb05a0420e88880bb258a65d0b1b13ab28e6a070 Mon Sep 17 00:00:00 2001 From: v6ole Date: Sun, 12 Jul 2026 13:10:36 +0800 Subject: [PATCH] feat: scheduler skips on-leave managers; AI summary includes leave data Co-Authored-By: Claude --- backend/app/services/ai_summary.py | 22 +++++++++++++++++++++- backend/app/services/scheduler.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/backend/app/services/ai_summary.py b/backend/app/services/ai_summary.py index c0ccc85..fdab587 100644 --- a/backend/app/services/ai_summary.py +++ b/backend/app/services/ai_summary.py @@ -41,6 +41,7 @@ def build_summary_prompt( light_board: dict, period: str, reference_date: str, + leaves: list | None = None, ) -> str: """Build the user prompt with structured visit data for the LLM.""" @@ -140,6 +141,20 @@ def build_summary_prompt( lvd = rc.get("last_visit_date") or "从未" data_block += f" - {rc['customer_name']}(上次拜访: {lvd})\n" + # ── Leave data ── + if leaves: + data_block += "\n## 本周请假情况\n" + for lv in leaves: + data_block += ( + f"- {lv.get('manager_name', '未知')} ({lv.get('leave_type', '请假')}): " + f"{lv.get('start_date', '')} ~ {lv.get('end_date', '')} " + f"({lv.get('days', 0)}天)\n" + ) + if not leaves: + data_block += "无请假记录\n" + else: + data_block += "\n## 本周请假情况\n无请假记录\n" + return data_block @@ -220,7 +235,12 @@ async def generate_summary( db=db, user_id=user_id, role=role, reference_date=ref, ) light_board = await get_light_board(db, ref, user_id, role) - user_prompt = build_summary_prompt(weekly_report, light_board, period, str(ref)) + from app.services.leaves import get_leave_overview + leave_overview = await get_leave_overview(db, str(user_id), role, ref) + user_prompt = build_summary_prompt( + weekly_report, light_board, period, str(ref), + leaves=leave_overview.get("leave_list", []), + ) # Call LLM headers = {"Content-Type": "application/json"} diff --git a/backend/app/services/scheduler.py b/backend/app/services/scheduler.py index bca593b..5a40788 100644 --- a/backend/app/services/scheduler.py +++ b/backend/app/services/scheduler.py @@ -1,9 +1,10 @@ from datetime import date, datetime -from sqlalchemy import select, func +from sqlalchemy import select, func, and_ from sqlalchemy.ext.asyncio import AsyncSession from app.models.visit import Visit from app.models.daily_note import DailyNote from app.models.user import User +from app.models.leave import Leave from app.models.work_plan import WorkPlan from app.models.customer import Customer from app.services.wecom import wecom_client @@ -23,6 +24,15 @@ async def check_daily_reporting(db: AsyncSession) -> dict: ) managers = result.scalars().all() + # ── Exclude managers on leave today ── + leaves_today = await db.execute( + select(Leave.manager_id).where(and_( + Leave.start_date <= today, + Leave.end_date >= today, + )) + ) + on_leave_ids = {str(uid) for uid, in leaves_today.all()} + # Get managers who have reported today reported_visits = await db.execute( select(Visit.manager_id).where(Visit.visit_date == today) @@ -36,7 +46,11 @@ async def check_daily_reporting(db: AsyncSession) -> dict: not_reported = [] reported_names = [] + on_leave_names = [] for m in managers: + if str(m.id) in on_leave_ids: + on_leave_names.append(m.name) + continue # skip — exempt from reporting if str(m.id) in reported_map: reported_names.append(m.name) else: @@ -68,12 +82,22 @@ async def check_daily_reporting(db: AsyncSession) -> dict: ) for d in directors.scalars().all(): if managers: - pct = len(reported_map) / len(managers) * 100 + effective_total = len(managers) - len(on_leave_names) + if effective_total > 0: + pct = len(reported_map) / effective_total * 100 + else: + pct = 100.0 + leave_note = "" + if on_leave_names: + leave_note = f"\n> 请假中(已豁免):{len(on_leave_names)}人\n" + leave_note += "".join(f"- {n} (请假)\n" for n in on_leave_names) summary = ( f"## 📊 今日填报汇总\n\n" f"> 日期:{today}\n" - f"> 填报率:**{pct:.0f}%** ({len(reported_map)}/{len(managers)})\n\n" + f"> 填报率:**{pct:.0f}%** ({len(reported_map)}/{effective_total})\n" ) + if leave_note: + summary += leave_note + "\n" if not_reported: summary += "**未填报:**\n" + "".join(f"- {m.name}\n" for m in not_reported) else: