feat: 添加 NotificationService 通知服务 + 测试
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
from app.config import settings
|
||||||
|
from app.wechat.client import WeChatClient
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationService:
|
||||||
|
def __init__(self):
|
||||||
|
self.client = WeChatClient()
|
||||||
|
|
||||||
|
async def send(self, announcements: List[Dict[str, Any]]) -> int:
|
||||||
|
if not settings.wechat_enabled:
|
||||||
|
return 0
|
||||||
|
if not announcements:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
sent = 0
|
||||||
|
for ann in announcements:
|
||||||
|
try:
|
||||||
|
title = ann.get("title", "")
|
||||||
|
if len(title) > 128:
|
||||||
|
title = title[:125] + "..."
|
||||||
|
|
||||||
|
purchase_name = ann.get("purchase_name", "")
|
||||||
|
if len(purchase_name) > 25:
|
||||||
|
purchase_name = purchase_name[:22] + "..."
|
||||||
|
|
||||||
|
pub_date = ann.get("publish_date")
|
||||||
|
time_str = pub_date.strftime("%Y-%m-%d %H:%M") if pub_date else "时间未知"
|
||||||
|
|
||||||
|
source_name = ann.get("source_name", "")
|
||||||
|
|
||||||
|
description = (
|
||||||
|
f'<div style="font-size: 14px; margin-top: 8px;">'
|
||||||
|
f'{source_name} | {purchase_name} | {time_str}'
|
||||||
|
f'</div>'
|
||||||
|
)
|
||||||
|
|
||||||
|
url = ann.get("content_url", "")
|
||||||
|
|
||||||
|
if await self.client.send_textcard(title, description, url):
|
||||||
|
sent += 1
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return sent
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import pytest
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
from app.services.notification_service import NotificationService
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_notification_send():
|
||||||
|
svc = NotificationService()
|
||||||
|
announcements = [
|
||||||
|
{
|
||||||
|
"title": "测试公告",
|
||||||
|
"publish_date": None,
|
||||||
|
"purchase_name": "测试单位",
|
||||||
|
"content_url": "https://x.com/1",
|
||||||
|
"source_code": "test",
|
||||||
|
"source_name": "测试来源",
|
||||||
|
"announcement_type": "purchase",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
with patch.object(svc.client, "send_textcard", AsyncMock(return_value=True)):
|
||||||
|
count = await svc.send(announcements)
|
||||||
|
assert count == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_notification_send_empty():
|
||||||
|
svc = NotificationService()
|
||||||
|
count = await svc.send([])
|
||||||
|
assert count == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_notification_send_disabled():
|
||||||
|
svc = NotificationService()
|
||||||
|
announcements = [{"title": "test"}]
|
||||||
|
|
||||||
|
with patch.object(svc.client, "send_textcard", AsyncMock(return_value=False)):
|
||||||
|
count = await svc.send(announcements)
|
||||||
|
assert count == 0
|
||||||
Reference in New Issue
Block a user