feat(job): D2 全市场选股 Job 化(kind=selection 异步)

- Job kind=selection:executor 分支用 SelectionService(spec=SelectionQuery),结果
  落 result_json、自动归档 Experiment(summary:as_of + 选出 N);/api/jobs 与
  /api/experiments 的 result 解码支持 SelectionResult
- POST /api/selections/jobs:提交 SelectionQuery 为异步 Job(BackgroundTasks/subprocess)
- Web 选股页新增「异步(全市场)」按钮:提交 Job → waitJob 轮询结果(解决同步 60s+)
- tests/test_selection_job.py:executor 执行归档(SelectionResult/Experiment kind)、
  API 提交→轮询→结果与实验列表;全量 pytest + tsc 通过
This commit is contained in:
Simon
2026-09-09 07:43:35 +08:00
parent ed54096331
commit 861a4051ca
6 changed files with 211 additions and 13 deletions
+25 -3
View File
@@ -9,18 +9,20 @@ GET /api/selections 历史选股元数据(可过滤 as_of/method)
from __future__ import annotations
from datetime import date
from datetime import date, datetime
from typing import Annotated
from fastapi import APIRouter, HTTPException, Query
from fastapi import APIRouter, BackgroundTasks, HTTPException, Query
from pydantic import BaseModel
from app.api.deps import (
DbSession,
JobRepoDep,
SelectionRepoDep,
SelectionServiceDep,
)
from app.application.services.job_executor import new_id
from app.application.services.job_executor import new_id, run_job_background
from app.domain.entities.research import JobRecord, JobStatus
from app.domain.entities.selection import SelectionMeta, SelectionQuery, SelectionResult
router = APIRouter(prefix="/selections", tags=["selections"])
@@ -31,6 +33,26 @@ class SelectionRun(BaseModel):
result: SelectionResult
@router.post("/jobs", summary="提交全市场/长任务选股为异步 Job")
def submit_selection_job(
query: SelectionQuery,
background: BackgroundTasks,
session: DbSession,
job_repo: JobRepoDep,
) -> dict:
job = JobRecord(
id=new_id("JOB"),
kind="selection",
spec_json=query.model_dump_json(),
status=JobStatus.QUEUED,
created_at=datetime.now(),
)
job_repo.create(job)
session.commit()
background.add_task(run_job_background, job.id)
return {"job_id": job.id, "status": job.status}
@router.post("", response_model=SelectionRun, summary="执行一次选股(同步)并落库")
def run_selection(
query: SelectionQuery,