From c4e8a3b79065e83bd2d23bd2e6d8a7ef71f20e69 Mon Sep 17 00:00:00 2001 From: v6ole Date: Fri, 26 Jun 2026 15:34:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=8C=E6=B6=88=E6=81=AF=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=20=E2=80=94=20=E5=82=AC=E5=8A=9E=E7=94=A8=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=8D=A1=E7=89=87=20+=20=E5=85=AC=E5=91=8A=E7=94=A8?= =?UTF-8?q?=20Markdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scheduler.py: - 未填报经理 → 模板卡片消息(标题+描述+「去填报」按钮) - 支局长/领导 → Markdown 每日填报汇总(markdown) - 文本消息作为降级方案 wecom.py API: - remind: 模板卡片优先 → 降级文本 - announcement: Markdown 优先 → 降级文本 Co-Authored-By: Claude --- backend/app/api/wecom.py | 22 ++++++++++---- backend/app/services/scheduler.py | 49 +++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/backend/app/api/wecom.py b/backend/app/api/wecom.py index d7ee472..3ba3277 100644 --- a/backend/app/api/wecom.py +++ b/backend/app/api/wecom.py @@ -201,9 +201,17 @@ async def send_reminder( wecom_ids = [r[0] for r in result.all() if r[0]] content = data.message or "📋 请及时完成今日拜访记录填报。" - success = await wecom_client.send_text_message(wecom_ids, content) + success = await wecom_client.send_template_card( + user_ids=wecom_ids, + title="📋 填报提醒", + desc=content, + url="https://qj.dhdx.fun/m", + btn_text="去填报", + ) + if not success: + success = await wecom_client.send_text_message(wecom_ids, content) - return {"success": success, "sent_to": len(wecom_ids)} + return {"success": success, "sent_to": len(wecom_ids), "rich": True} @router.post("/announcement") @@ -212,14 +220,16 @@ async def send_announcement( current_user: dict = Depends(require_director), db: AsyncSession = Depends(get_db), ): - """Director sends an announcement to all team members.""" + """Director sends an announcement to all team members (markdown).""" result = await db.execute(select(User.wecom_userid).where(User.wecom_userid.isnot(None))) wecom_ids = [r[0] for r in result.all()] - content = f"📢 支局长公告\n\n{data.content}" - success = await wecom_client.send_text_message(wecom_ids, content) + markdown = f"## 📢 支局长公告\n\n{data.content}" + success = await wecom_client.send_markdown_message(markdown) + if not success: + success = await wecom_client.send_text_message(wecom_ids, f"📢 支局长公告\n\n{data.content}") - return {"success": success, "sent_to": len(wecom_ids)} + return {"success": success, "sent_to": len(wecom_ids), "rich": True} @router.post("/test-message") diff --git a/backend/app/services/scheduler.py b/backend/app/services/scheduler.py index a2d8c02..da3387b 100644 --- a/backend/app/services/scheduler.py +++ b/backend/app/services/scheduler.py @@ -21,6 +21,9 @@ async def check_daily_reporting(db: AsyncSession) -> dict: ) managers = result.scalars().all() + if not managers: + return {"status": "ok", "date": str(today), "total_managers": 0, "reported": 0, "not_reported": 0} + # Get managers who have reported today reported_visits = await db.execute( select(Visit.manager_id).where(Visit.visit_date == today) @@ -40,21 +43,43 @@ async def check_daily_reporting(db: AsyncSession) -> dict: else: not_reported.append(m) - # Send markdown message to not-reported managers + # 1. Send template card to not-reported managers (tap to open app) if not_reported: - names = "、".join(m.name for m in not_reported) - content = ( - f"## 📋 今日填报提醒\n\n" - f"> 日期:{today}\n" - f"> 已填报:{len(reported_names)} 人\n" - f"> 未填报:**{len(not_reported)} 人**\n\n" - f"以下同事尚未提交今日拜访记录:\n" - + "".join(f"- **{m.name}**\n" for m in not_reported) - + f"\n请尽快完成今日拜访填报 🙏" - ) user_ids = [m.wecom_userid for m in not_reported if m.wecom_userid] if user_ids: - await wecom_client.send_text_message(user_ids, content) + desc = f"{today} | 已填报 {len(reported_names)}/{len(managers)} 人" + success = await wecom_client.send_template_card( + user_ids=user_ids, + title="📋 今日填报提醒", + desc=desc, + url="https://qj.dhdx.fun/m", + btn_text="去填报", + ) + # Fallback to text + if not success: + names_text = "、".join(m.name for m in not_reported) + await wecom_client.send_text_message( + user_ids, + f"📋 今日填报提醒\n\n{desc}\n未填报:{names_text}\n\n请尽快完成填报 🙏\nhttps://qj.dhdx.fun/m", + ) + + # 2. Send summary to director + directors = await db.execute( + select(User).where(User.role.in_(["director", "leader"]), User.wecom_userid.isnot(None)) + ) + for d in directors.scalars().all(): + if managers: + pct = len(reported_map) / len(managers) * 100 + summary = ( + f"## 📊 今日填报汇总\n\n" + f"> 日期:{today}\n" + f"> 填报率:**{pct:.0f}%** ({len(reported_map)}/{len(managers)})\n\n" + ) + if not_reported: + summary += "**未填报:**\n" + "".join(f"- {m.name}\n" for m in not_reported) + else: + summary += "✅ 全体已完成今日填报" + await wecom_client.send_markdown_message(summary) # broadcast for director visibility return { "status": "ok",