main.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from fastapi import FastAPI, HTTPException, Request
  2. from fastapi.responses import JSONResponse
  3. from fastapi.middleware.cors import CORSMiddleware
  4. from utils.params import DecodeContentParam, PatternContentParam, TopicSearchParam
  5. from dotenv import load_dotenv, find_dotenv
  6. from typing import List, Dict, Any, Optional
  7. from tasks.decode import begin_decode_task
  8. from tasks.detail import get_decode_detail_by_task_id
  9. from tasks.pattern import begin_pattern_task
  10. from tasks.topic_search import search_topics
  11. from loguru import logger
  12. import sys
  13. logger.add(sink=sys.stderr, level="ERROR", backtrace=True, diagnose=True)
  14. # 响应消息映射
  15. RESPONSE_MSG_MAP = {
  16. 0: "success",
  17. 1002: "视频不存在",
  18. 2001: "解构/聚类任务创建失败",
  19. -1: "failed",
  20. 404: "任务不存在"
  21. }
  22. load_dotenv(find_dotenv(), override=False)
  23. app = FastAPI()
  24. app.add_middleware(
  25. CORSMiddleware,
  26. allow_origins=["*"],
  27. allow_credentials=True,
  28. allow_methods=["*"],
  29. allow_headers=["*"],
  30. )
  31. @app.exception_handler(HTTPException)
  32. async def http_exception_handler(request: Request, exc: HTTPException):
  33. """统一处理 HTTPException,保证返回结构与其它接口一致"""
  34. msg = RESPONSE_MSG_MAP.get(exc.status_code, "failed")
  35. content = {
  36. "code": exc.status_code,
  37. "msg": msg,
  38. "data": None,
  39. }
  40. # 将异常 detail 写入 reason,便于排查问题
  41. if exc.detail:
  42. content["reason"] = str(exc.detail)
  43. return JSONResponse(status_code=200, content=content)
  44. def _build_api_response(
  45. code: int,
  46. data: Any = None,
  47. reason: Optional[str] = None
  48. ) -> JSONResponse:
  49. """构建统一的API响应"""
  50. msg = RESPONSE_MSG_MAP.get(code, "failed")
  51. content = {
  52. "code": code,
  53. "msg": msg,
  54. "data": data
  55. }
  56. # 失败时添加 reason 字段
  57. if code != 0 and reason:
  58. content["reason"] = reason
  59. return JSONResponse(status_code=200, content=content)
  60. @app.post("/api/v1/content/tasks/decode")
  61. def decode_content(param: DecodeContentParam):
  62. """创建解构任务"""
  63. res = begin_decode_task(param)
  64. code = res.get("code", -1)
  65. task_id = res.get("task_id")
  66. reason = res.get("reason", "")
  67. return _build_api_response(
  68. code=code,
  69. data={"task_id": task_id} if task_id else None,
  70. reason=reason
  71. )
  72. @app.get("/api/v1/content/tasks/{taskId}")
  73. def get_task_detail(taskId: str):
  74. """获取任务详情"""
  75. result = get_decode_detail_by_task_id(taskId)
  76. # 任务不存在
  77. if result is None:
  78. return _build_api_response(code=404, data=None)
  79. # 直接返回结果(已经包含 code、msg、data、reason)
  80. return JSONResponse(status_code=200, content=result)
  81. @app.post("/api/v1/content/tasks/pattern")
  82. def pattern_content(param: PatternContentParam):
  83. """创建聚类任务"""
  84. res = begin_pattern_task(param)
  85. code = res.get("code", -1)
  86. task_id = res.get("task_id")
  87. reason = res.get("reason", "")
  88. return _build_api_response(
  89. code=code,
  90. data={"task_id": task_id} if task_id else None,
  91. reason=reason
  92. )
  93. @app.post("/api/v1/content/topics/search")
  94. def search_content_topics(param: TopicSearchParam):
  95. """视频选题检索:根据关键词在解构结果中匹配,返回匹配度最高的 top5"""
  96. results = search_topics(param)
  97. return _build_api_response(code=0, data=results)