"""Research Worker HTTP API(FastAPI)— 供 Go 后端调用 / 调试。 任务正常由 Go 后端写入 research_tasks 并 LPush 到 Redis 队列;本服务的后台线程消费队列。 这里另外提供状态查询与健康检查端点。 """ import json import uuid import threading from typing import Optional import psycopg import redis import uvicorn from fastapi import FastAPI, HTTPException from pydantic import BaseModel from config import config from db import get_task from worker import ResearchWorker app = FastAPI(title="Research Worker API", version="1.0.0") rdb = redis.from_url(config.REDIS_URL, decode_responses=True) class CreateTaskRequest(BaseModel): user_id: str topic: str app_id: Optional[str] = None config: dict = {} class TaskStatusResponse(BaseModel): task_id: str status: str progress: int status_message: Optional[str] = None error_message: Optional[str] = None @app.post("/api/tasks") def create_task(req: CreateTaskRequest): task_id = str(uuid.uuid4()) with psycopg.connect(config.DATABASE_URL) as conn: with conn.cursor() as cur: cur.execute( "INSERT INTO research_tasks (id, user_id, app_id, topic, config) " "VALUES (%s, %s, %s, %s, %s)", (task_id, req.user_id, req.app_id, req.topic, json.dumps(req.config)), ) conn.commit() rdb.lpush(config.TASK_QUEUE, json.dumps({"task_id": task_id})) return {"task_id": task_id, "status": "pending"} @app.get("/api/tasks/{task_id}", response_model=TaskStatusResponse) def get_task_status(task_id: str): cached = rdb.hgetall(f"{config.TASK_STATUS_PREFIX}{task_id}") if cached: return TaskStatusResponse( task_id=task_id, status=cached.get("status", "unknown"), progress=int(cached.get("progress", 0)), status_message=cached.get("message"), ) task = get_task(task_id) if not task: raise HTTPException(status_code=404, detail="任务不存在") return TaskStatusResponse( task_id=task_id, status=task["status"], progress=task["progress"], status_message=task.get("status_message"), error_message=task.get("error_message"), ) @app.get("/health") def health(): return {"status": "ok", "service": "research-worker"} def _start_worker(): ResearchWorker().start() if __name__ == "__main__": threading.Thread(target=_start_worker, daemon=True).start() uvicorn.run(app, host=config.HOST, port=config.PORT)