diff --git a/app/routers/wechat_notify_api.py b/app/routers/wechat_notify_api.py index befbc72..3813308 100644 --- a/app/routers/wechat_notify_api.py +++ b/app/routers/wechat_notify_api.py @@ -128,6 +128,11 @@ async def send_card(request: Request): if alarm_snapshot_key: presigned_url = _get_presigned_url(alarm_snapshot_key) + logger.info(f"群聊截图诊断: alarm={req.alarmId}, " + f"iot_snapshot={req.snapshotUrl!r}, " + f"db_snapshot={alarm_snapshot_key!r}, " + f"presigned_url={presigned_url[:80] if presigned_url else '(空)'}...") + # alarm_type: 用 alarm_type_code 避免"告警告警" actual_alarm_type = alarm_type_code or req.title diff --git a/app/services/notify_dispatch.py b/app/services/notify_dispatch.py index 78a3706..eeed834 100644 --- a/app/services/notify_dispatch.py +++ b/app/services/notify_dispatch.py @@ -144,10 +144,19 @@ async def process_alarm_notification(alarm_data: Dict): def _get_presigned_url(snapshot_url: str) -> str: - """将 COS object key 转为预签名 URL""" + """将 COS object key 或永久 URL 转为预签名 URL""" if not snapshot_url: return "" + # 如果是完整 COS 永久 URL,提取 object key 后重新生成预签名 URL if snapshot_url.startswith("http"): + object_key = _extract_cos_object_key(snapshot_url) + if object_key: + try: + from app.services.oss_storage import get_oss_storage + return get_oss_storage().get_presigned_url(object_key) + except Exception as e: + logger.warning(f"从COS URL生成预签名失败: {e}") + # 无法提取 key,返回原 URL(可能是外部 URL) return snapshot_url try: from app.services.oss_storage import get_oss_storage @@ -157,6 +166,37 @@ def _get_presigned_url(snapshot_url: str) -> str: return snapshot_url +def _extract_cos_object_key(url: str) -> str: + """从 COS 永久 URL 中提取 object key + + 支持格式: + - https://{bucket}.cos.{region}.myqcloud.com/{key} + - https://cos.{region}.myqcloud.com/{bucket}/{key} + """ + try: + from urllib.parse import urlparse + parsed = urlparse(url) + host = parsed.hostname or "" + path = parsed.path.lstrip("/") + + if not path or "myqcloud.com" not in host: + return "" + + # 格式1: {bucket}.cos.{region}.myqcloud.com/{key} + if ".cos." in host and host.endswith(".myqcloud.com"): + return path + + # 格式2: cos.{region}.myqcloud.com/{bucket}/{key} + if host.startswith("cos.") and host.endswith(".myqcloud.com"): + parts = path.split("/", 1) + if len(parts) == 2: + return parts[1] + + return "" + except Exception: + return "" + + def _save_vlm_result(alarm_id: str, vlm_result: Dict): """将 VLM 复核结果写入 alarm_llm_analysis 表""" db = get_session() diff --git a/app/services/wechat_service.py b/app/services/wechat_service.py index 103625c..5d512d4 100644 --- a/app/services/wechat_service.py +++ b/app/services/wechat_service.py @@ -218,9 +218,12 @@ class WeChatService: async def upload_media_from_url(self, image_url: str) -> Optional[str]: """从 URL 下载图片后上传到企微,返回 media_id""" + logger.info(f"企微截图上传: 开始下载 url={image_url[:100]}...") image_data = await self._download_image(image_url) if not image_data: + logger.warning(f"企微截图上传: 下载失败,无法上传 url={image_url[:100]}") return None + logger.info(f"企微截图上传: 下载成功,size={len(image_data)} bytes,开始上传到企微") return await self.upload_media(image_data) # ==================== 个人消息:按钮交互型模板卡片 ====================