search.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """搜索接口路由。"""
  2. from __future__ import annotations
  3. import logging
  4. from fastapi import APIRouter, HTTPException, Query
  5. from app.schemas import SearchRequest, SearchResponse
  6. from app.services.cache import faq_cache
  7. from app.services.zendesk_client import ZendeskError, search_articles
  8. logger = logging.getLogger(__name__)
  9. router = APIRouter(tags=["search"])
  10. async def _do_search(req: SearchRequest) -> SearchResponse:
  11. """共享的搜索逻辑:使用内存中的 FAQ sec_ids 调用 Zendesk。"""
  12. snapshot = faq_cache.snapshot()
  13. sec_ids: list[int] = snapshot["sec_ids"]
  14. if not sec_ids:
  15. logger.warning("FAQ sec_ids 缓存为空,本次搜索不带 section 过滤")
  16. try:
  17. data = await search_articles(
  18. query=req.query,
  19. section_ids=sec_ids,
  20. locale=req.locale,
  21. page=req.page,
  22. per_page=req.per_page,
  23. )
  24. except ZendeskError as exc:
  25. raise HTTPException(status_code=502, detail=str(exc)) from exc
  26. return SearchResponse(
  27. success=True,
  28. query=req.query,
  29. count=int(data.get("count", 0)),
  30. page=req.page,
  31. per_page=req.per_page,
  32. next_page=data.get("next_page"),
  33. sec_ids_used=sec_ids,
  34. results=data.get("results", []),
  35. )
  36. @router.get("/search", response_model=SearchResponse, summary="按 QUERY 搜索 FAQ 文章")
  37. async def search_get(
  38. query: str = Query(..., min_length=1, description="搜索关键词"),
  39. locale: str | None = Query(default=None),
  40. page: int = Query(default=1, ge=1),
  41. per_page: int = Query(default=25, ge=1, le=100),
  42. ) -> SearchResponse:
  43. """GET 版本,便于浏览器直接测试。"""
  44. return await _do_search(
  45. SearchRequest(
  46. query=query, locale=locale, page=page, per_page=per_page
  47. )
  48. )
  49. @router.post("/search", response_model=SearchResponse, summary="按 QUERY 搜索 FAQ 文章 (POST)")
  50. async def search_post(req: SearchRequest) -> SearchResponse:
  51. """POST 版本,请求体:{"query": "...", "locale": "...", ...}。"""
  52. return await _do_search(req)