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:
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
import { useEffect, useState } from "react";
|
||||
import { apiGet, apiPost } from "@/lib/api";
|
||||
import { waitJob } from "@/lib/jobs";
|
||||
import type {
|
||||
FactorMeta,
|
||||
SelectionCondition,
|
||||
@@ -53,6 +54,7 @@ export default function SelectionPage() {
|
||||
const [result, setResult] = useState<SelectionResult | null>(null);
|
||||
const [selectionId, setSelectionId] = useState("");
|
||||
const [running, setRunning] = useState(false);
|
||||
const [asyncBusy, setAsyncBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [history, setHistory] = useState<SelectionMeta[] | null>(null);
|
||||
|
||||
@@ -111,6 +113,28 @@ export default function SelectionPage() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 异步(全市场等长任务):提交 Job 后台执行并轮询(解决同步 60s+)。 */
|
||||
async function runAsync() {
|
||||
setAsyncBusy(true);
|
||||
setError("");
|
||||
setResult(null);
|
||||
try {
|
||||
const { job_id } = await apiPost<{ job_id: string }>("/selections/jobs", buildQuery());
|
||||
const out = await waitJob<SelectionResult>(job_id);
|
||||
if (out.status === "success" && out.result) {
|
||||
setSelectionId(job_id);
|
||||
setResult(out.result);
|
||||
apiGet<SelectionMeta[]>("/selections?limit=8").then(setHistory).catch(() => undefined);
|
||||
} else {
|
||||
setError(`任务${out.status}${out.error ? `:${out.error}` : ""}`);
|
||||
}
|
||||
} catch (e) {
|
||||
setError((e as Error).message);
|
||||
} finally {
|
||||
setAsyncBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function openHistory(id: string) {
|
||||
setError("");
|
||||
try {
|
||||
@@ -279,9 +303,12 @@ export default function SelectionPage() {
|
||||
)}
|
||||
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<Btn variant="primary" icon="play" loading={running} disabled={running} onClick={run}>
|
||||
<Btn variant="primary" icon="play" loading={running} disabled={running || asyncBusy} onClick={run}>
|
||||
{running ? "筛选中…" : "执行选股"}
|
||||
</Btn>
|
||||
<Btn variant="ghost" icon="clock" loading={asyncBusy} disabled={running || asyncBusy} onClick={runAsync}>
|
||||
{asyncBusy ? "后台执行中…" : "异步(全市场)"}
|
||||
</Btn>
|
||||
</div>
|
||||
{error ? (
|
||||
<div style={{ marginTop: 12 }}>
|
||||
|
||||
Reference in New Issue
Block a user