diff --git a/sanguo_api/routes.py b/sanguo_api/routes.py index 9ff5767..134b8e5 100644 --- a/sanguo_api/routes.py +++ b/sanguo_api/routes.py @@ -324,14 +324,50 @@ def factor_report(task_id: str, factor: str, token: str = Query(...)): @router.get("/task", dependencies=[Depends(verify_token)]) def list_tasks(type: str | None = None, status: str | None = None): - """List historical tasks (from the results DB).""" + """List tasks: 内存中 running/pending + DB done/failed(历史)。 + + running/pending 任务还没存 DB(done 才 save_result),光查 DB 看不到刚提交的 + → 任务中心(GitHub Actions 模式)必须合并内存 active + DB 已完成。 + """ from sanguo_backtest.result_store import list_results orch = get_orchestrator() - items = [] + active: list[dict] = [] + seen: set[str] = set() + + # 1) 内存 active 任务 (running/pending,未存 DB) + for tid, task in list(getattr(orch.pool, "_tasks", {}).items()): + st = task.status.value if hasattr(task.status, "value") else str(task.status) + if st not in ("running", "pending"): + continue + if status and st != status: + continue + ttype = getattr(task, "task_type", "") or "" + if type and ttype != type: + continue + spec = getattr(orch, "_pending", {}).get(tid, {}) or {} + strat = spec.get("strategy", "") + if not strat and spec.get("strategy_class"): + strat = getattr(spec["strategy_class"], "__name__", str(spec["strategy_class"])) + active.append({ + "id": 0, + "task_id": tid, + "type": ttype, + "status": st, + "strategy": strat, + "symbol": spec.get("symbol", "") or spec.get("benchmark", ""), + "start": spec.get("start", ""), + "end": spec.get("end", ""), + }) + seen.add(tid) + + # 2) DB 已持久化任务 (done/failed) + db_items: list[dict] = [] for r in list_results(type_filter=type, db_path=orch.db_path): + if r.task_id in seen: + continue if status and r.status != status: continue - items.append({ + db_items.append({ "id": r.id, "task_id": r.task_id, "type": r.type, @@ -341,8 +377,10 @@ def list_tasks(type: str | None = None, status: str | None = None): "start": r.start, "end": r.end, }) - items.reverse() # newest first - return {"tasks": items} + + active.reverse() # 最新提交的 running 在最上 + db_items.reverse() # DB newest first + return {"tasks": active + db_items} @router.get("/task/{task_id}/optimization-results", dependencies=[Depends(verify_token)])