61 lines
2.1 KiB
Python
61 lines
2.1 KiB
Python
from fastapi import APIRouter, Request, Query, Depends
|
|
from fastapi.responses import PlainTextResponse
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from app.database import get_db
|
|
from app.services.crypto import verify_url, decrypt_message
|
|
from app.services.wechat_client import sync_msg
|
|
from app.services.message_service import save_messages
|
|
from app.config import settings
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/webhook")
|
|
async def verify_callback(
|
|
msg_signature: str = Query(..., alias="msg_signature"),
|
|
timestamp: str = Query(...),
|
|
nonce: str = Query(...),
|
|
echostr: str = Query(...),
|
|
):
|
|
"""URL 验证:解密 echostr 并返回明文"""
|
|
try:
|
|
plain = verify_url(msg_signature, timestamp, nonce, echostr)
|
|
return PlainTextResponse(plain)
|
|
except Exception as e:
|
|
logger.error(f"URL 验证失败: {e}")
|
|
return PlainTextResponse("verification failed", status_code=400)
|
|
|
|
|
|
@router.post("/webhook")
|
|
async def receive_callback(
|
|
request: Request,
|
|
msg_signature: str = Query("", alias="msg_signature"),
|
|
timestamp: str = Query(""),
|
|
nonce: str = Query(""),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""接收消息回调:验证签名 → 解密 XML → 拉取消息 → 入库"""
|
|
try:
|
|
xml_body = await request.body()
|
|
xml_str = xml_body.decode("utf-8")
|
|
logger.info(f"收到回调, encrypt 前100字符: {xml_str[:100]}")
|
|
|
|
token, open_kfid = decrypt_message(xml_str, msg_signature, timestamp, nonce)
|
|
if not token or not open_kfid:
|
|
logger.warning("解密后 Token 或 OpenKfId 为空")
|
|
return PlainTextResponse("fail")
|
|
|
|
# 拉取消息
|
|
result = await sync_msg(open_kfid, token=token, limit=100)
|
|
msg_list = result.get("msg_list", [])
|
|
if msg_list:
|
|
saved = await save_messages(db, msg_list)
|
|
logger.info(f"回调拉取消息 {len(msg_list)} 条,新增入库 {saved} 条")
|
|
|
|
return PlainTextResponse("success")
|
|
except Exception as e:
|
|
logger.error(f"回调处理失败: {e}")
|
|
return PlainTextResponse("fail")
|