movies.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. """确定性搜片路由(D1):不经过 LLM,直接走 MovieService → TMDB。"""
  2. from typing import Optional
  3. from fastapi import APIRouter, Path, Query
  4. from ...models.schemas import MovieDetailResponse, MovieListResponse
  5. from ...services.movie_service import get_movie_service
  6. from ...utils.logger import get_logger
  7. router = APIRouter(prefix="/movies", tags=["Movies"])
  8. logger = get_logger("app.movies")
  9. @router.get(
  10. "/search",
  11. response_model=MovieListResponse,
  12. summary="文本搜索电影",
  13. description="对应 TMDB `GET /search/movie`,参数 `q` 必填。",
  14. )
  15. async def search_movies(
  16. q: str = Query(..., min_length=1, description="搜索关键词", examples=["盗梦空间"]),
  17. year: Optional[int] = Query(default=None, description="上映年份"),
  18. page: int = Query(default=1, ge=1, le=500, description="页码"),
  19. ):
  20. movies = get_movie_service().search(q=q, year=year, page=page)
  21. logger.info("search q=%r year=%s -> %d", q, year, len(movies))
  22. return MovieListResponse(
  23. success=True,
  24. message=f"搜索成功,共 {len(movies)} 条",
  25. data=movies,
  26. )
  27. @router.get(
  28. "/discover",
  29. response_model=MovieListResponse,
  30. summary="条件发现电影",
  31. description="对应 TMDB `GET /discover/movie`;`with_genres` 支持中文类型名或 id。",
  32. )
  33. async def discover_movies(
  34. with_genres: Optional[str] = Query(
  35. default=None,
  36. description="类型:名称或 id,逗号分隔,如 剧情,喜剧 或 18,35",
  37. examples=["科幻"],
  38. ),
  39. year: Optional[int] = Query(default=None, description="精确上映年"),
  40. year_gte: Optional[int] = Query(default=None, description="上映年起"),
  41. year_lte: Optional[int] = Query(default=None, description="上映年止"),
  42. max_runtime: Optional[int] = Query(
  43. default=None,
  44. ge=1,
  45. description="最大片长(分钟)→ with_runtime.lte",
  46. ),
  47. with_original_language: Optional[str] = Query(
  48. default=None,
  49. description="原始语言,如 zh / en / ja / ko",
  50. ),
  51. sort_by: str = Query(default="popularity.desc", description="排序字段"),
  52. page: int = Query(default=1, ge=1, le=500, description="页码"),
  53. ):
  54. movies = get_movie_service().discover(
  55. with_genres=with_genres,
  56. year=year,
  57. year_gte=year_gte,
  58. year_lte=year_lte,
  59. max_runtime=max_runtime,
  60. with_original_language=with_original_language,
  61. sort_by=sort_by,
  62. page=page,
  63. )
  64. logger.info("discover genres=%r -> %d", with_genres, len(movies))
  65. return MovieListResponse(
  66. success=True,
  67. message=f"发现成功,共 {len(movies)} 条",
  68. data=movies,
  69. )
  70. @router.get(
  71. "/{movie_id}",
  72. response_model=MovieDetailResponse,
  73. summary="电影详情",
  74. description="对应 TMDB `GET /movie/{movie_id}`,补全片长 runtime 等详情字段。",
  75. )
  76. async def get_movie_detail(
  77. movie_id: int = Path(..., ge=1, description="TMDB 电影 id", examples=[550]),
  78. ):
  79. movie = get_movie_service().get_detail(movie_id)
  80. logger.info("detail id=%s title=%r runtime=%s", movie_id, movie.title, movie.runtime)
  81. return MovieDetailResponse(
  82. success=True,
  83. message="查询成功",
  84. data=movie,
  85. )