From 523105650d5fa2e76eeaa1bd90e5c63e13f2756a Mon Sep 17 00:00:00 2001 From: claude_dev Date: Thu, 13 Aug 2026 08:52:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(api):=20GET=20/task=20=E5=86=85=E5=AD=98?= =?UTF-8?q?=E5=A4=B1=E5=90=8E=20DB=20fallback=20=E8=BF=94=20status(?= =?UTF-8?q?=E6=B2=BB=E5=9B=9E=E6=B5=8B=20done=20=E5=90=8E=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E8=BD=AE=E8=AF=A2=20404=20=E6=AD=BB=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E5=8D=A1=E7=AD=89=E5=BE=85)=20[nas]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sanguo_api/routes.py | 21 +++++++++++-- tests/api/test_task_status_db_fallback.py | 37 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/api/test_task_status_db_fallback.py diff --git a/sanguo_api/routes.py b/sanguo_api/routes.py index 06ad2c7..9ff5767 100644 --- a/sanguo_api/routes.py +++ b/sanguo_api/routes.py @@ -131,10 +131,27 @@ async def submit_factor(req: FactorAnalysisRequest): @router.get("/task/{task_id}", dependencies=[Depends(verify_token)]) def get_status(task_id: str): """Get task status""" - s = get_orchestrator().get_status(task_id) + orch = get_orchestrator() + s = orch.get_status(task_id) if s is None: + # 内存池无此 task(容器重启/已完成被清)→查 DB;有持久化结果则返其 status, + # 否则真不存在。治:done task 内存清后 GET /task 404 → 前端轮询死循环卡等待, + # 但结果其实在 DB(历史能查)。CTA/组合回测 done 后内存失均受益。 + try: + from sanguo_backtest.result_store import load_result_by_task_id + if orch.db_path: + r = load_result_by_task_id(task_id, orch.db_path) + if r is not None: + return { + "task_id": task_id, + "status": r.status or "done", + "stage": "", + "error_msg": None, + } + except Exception: + pass raise HTTPException(status_code=404, detail="task not found") - pool = get_orchestrator().pool + pool = orch.pool stage = pool.get_stage(task_id) task = pool.get_task(task_id) return { diff --git a/tests/api/test_task_status_db_fallback.py b/tests/api/test_task_status_db_fallback.py new file mode 100644 index 0000000..d4803e1 --- /dev/null +++ b/tests/api/test_task_status_db_fallback.py @@ -0,0 +1,37 @@ +"""GET /task/{id} 内存失后 DB fallback (治 done task 内存清后前端轮询 404 死循环卡等待)。""" +from fastapi.testclient import TestClient + +from sanguo_api.app import create_app +from sanguo_api.auth import set_jwt_config, create_token +from sanguo_backtest.result_store import BacktestResult, save_result + + +def _auth_client(tmp_path): + set_jwt_config("test_secret_dbfb", 60) + db = str(tmp_path / "r.db") + app = create_app(db_path=db) + c = TestClient(app) + token = create_token("admin") + return c, {"Authorization": f"Bearer {token}"}, db + + +def test_get_status_returns_done_when_only_in_db(tmp_path): + """内存池无(容器重启/清理)但 DB 有 done 结果 → 返回 done,不 404。""" + c, h, db = _auth_client(tmp_path) + save_result( + BacktestResult( + task_id="cta_dbfb", type="cta", status="done", strategy="DoubleMa", + symbol="600519.SH", params={}, start="2024-01-02", end="2024-03-29", + statistics={}, equity_curve=None, trades=None, + ), + db_path=db, + ) + r = c.get("/api/v1/task/cta_dbfb", headers=h) + assert r.status_code == 200 + assert r.json()["status"] == "done" + + +def test_get_status_404_when_neither_memory_nor_db(tmp_path): + c, h, _db = _auth_client(tmp_path) + r = c.get("/api/v1/task/nonexistent_xyz", headers=h) + assert r.status_code == 404