feat: scheduler skips on-leave managers; AI summary includes leave data

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 13:10:36 +08:00
parent 56adc625c0
commit eb05a0420e
2 changed files with 48 additions and 4 deletions
+21 -1
View File
@@ -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"}
+27 -3
View File
@@ -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: