diff --git a/app/services/daily_report_service.py b/app/services/daily_report_service.py index d9938b9..edf427c 100644 --- a/app/services/daily_report_service.py +++ b/app/services/daily_report_service.py @@ -28,7 +28,6 @@ CLEANING_TYPE_NAMES = { } WEEKDAY_NAMES = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] -REPORT_COVER_IMAGE_URL = "https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0" def _format_duration(minutes: float) -> str: @@ -317,19 +316,27 @@ def _build_preview_text(report: Dict) -> str: return "\n".join(lines) -def _build_report_news(report: Dict) -> Dict: +def _build_report_textcard(report: Dict) -> Dict: summary = report["summary"] click_url = settings.wechat.service_base_url or "https://work.weixin.qq.com" - description = ( - f"{report['overview']}\n" - f"首响 {summary['avg_resp']}|完结 {summary['avg_close']}|误报率 {summary['false_alarm_rate']}\n" - f"高发区域:{report['tops']['areas']}" - ) + description_lines = [ + f"
{report['subtitle']}
", + f"
昨日新增 {summary['yesterday_total']} 条({report['change_str']})
", + f"
昨日完成 {summary['completed_count']} 条|当前待处理 {summary['backlog_count']}
", + f"
安保 {summary['security_count']} 条|保洁 {summary['clean_count']} 条
", + f"
平均首响 {summary['avg_resp']}|平均完结 {summary['avg_close']}
", + f"
误报率 {summary['false_alarm_rate']}|遗留待处理 {summary['carry_over_count']} 条
", + "
重点风险
", + f"
安保高发:{report['tops']['alarm_types']}
", + f"
高发区域:{report['tops']['areas']}
", + f"
高发摄像头:{report['tops']['cameras']}
", + ] + if report["top_overdue"]: + description_lines.append(f"
优先跟进:{report['top_overdue'][0]}
") return { "title": report["title"], - "description": description, + "description": "".join(description_lines), "url": click_url, - "picurl": REPORT_COVER_IMAGE_URL, } @@ -340,11 +347,11 @@ async def generate_daily_report() -> Optional[str]: return _build_preview_text(report) -async def generate_daily_report_news() -> Optional[Dict]: +async def generate_daily_report_textcard() -> Optional[Dict]: report = await _build_daily_report_data() if not report: return None - return _build_report_news(report) + return _build_report_textcard(report) async def _send_daily_report(): @@ -356,31 +363,25 @@ async def _send_daily_report(): return try: - news = await generate_daily_report_news() + card = await generate_daily_report_textcard() preview = await generate_daily_report() - if not news or not preview: + if not card or not preview: logger.info("日报生成内容为空,跳过发送") return wechat_svc = get_wechat_service() - ok = await wechat_svc.send_group_news( + ok = await wechat_svc.send_group_textcard( chat_id=chat_id, - title=news["title"], - description=news["description"], - url=news["url"], - picurl=news["picurl"], + title=card["title"], + description=card["description"], + url=card["url"], ) if ok: - await asyncio.sleep(1) - detail_ok = await wechat_svc.send_group_markdown(chat_id, preview) - if detail_ok: - logger.info("日报图文+摘要已发送到企微群聊") - else: - logger.warning("日报图文已发送,但摘要markdown发送失败") + logger.info("日报文本卡片已发送到企微群聊") else: fallback_ok = await wechat_svc.send_group_markdown(chat_id, preview) if fallback_ok: - logger.info("日报图文发送失败,已降级为markdown发送") + logger.info("日报文本卡片发送失败,已降级为markdown发送") else: logger.error("日报发送失败") except Exception: diff --git a/app/services/wechat_service.py b/app/services/wechat_service.py index 034cdf4..b16b92b 100644 --- a/app/services/wechat_service.py +++ b/app/services/wechat_service.py @@ -588,6 +588,35 @@ class WeChatService: logger.error(f"发送群聊markdown异常: {e}") return False + async def send_group_textcard(self, chat_id: str, title: str, description: str, url: str = "") -> bool: + """发送 textcard 消息到群聊,适合单条摘要展示。""" + if not self._enabled: + return False + try: + access_token = await self._get_access_token() + msg = { + "chatid": chat_id, + "msgtype": "textcard", + "textcard": { + "title": title, + "description": description, + "url": url or "https://work.weixin.qq.com", + "btntxt": "查看详情", + }, + } + api_url = f"https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token={access_token}" + async with httpx.AsyncClient(timeout=10) as client: + resp = await client.post(api_url, json=msg) + data = resp.json() + if data.get("errcode") != 0: + logger.error(f"群聊文本卡片发送失败: {data}") + return False + logger.info(f"群聊文本卡片已发送: chatid={chat_id}") + return True + except Exception as e: + logger.error(f"发送群聊文本卡片异常: {e}") + return False + async def send_group_image(self, chat_id: str, media_id: str) -> bool: """发送图片消息到群聊""" if not self._enabled: