This commit is contained in:
2026-09-04 10:00:35 +08:00
parent 2735ff1fd9
commit 76b422320b
14 changed files with 611 additions and 218 deletions

View File

@@ -12,6 +12,7 @@ import type {
PreviewResponse,
ScreenerQueryItem,
ScreenerRunRequest,
ScreenerStreamEvent,
ScreenerRunResponse,
ScreenerSyncRequest,
ScreenerSyncStatus,
@@ -120,10 +121,38 @@ export async function postEventBacktest(req: EventBacktestRequest): Promise<Even
}
}
export async function runScreener(req: ScreenerRunRequest): Promise<ScreenerRunResponse> {
const res = await apiFetch('/api/screener/run', { method: 'POST', body: JSON.stringify(req) });
/** 选股流式执行:逐行回调 NDJSON 事件(阶段/进度/解析条件),最终返回完整结果。
* signal 中止后(含读流中途)以 AbortError 拒绝,由调用方决定如何呈现。 */
export async function runScreener(
req: ScreenerRunRequest,
onEvent?: (ev: ScreenerStreamEvent) => void,
signal?: AbortSignal,
): Promise<ScreenerRunResponse> {
const res = await apiFetch('/api/screener/run', { method: 'POST', body: JSON.stringify(req), signal });
if (!res.ok) throw new ApiError(await readError(res, `选股失败 (HTTP ${res.status})`), res.status);
return (await res.json()) as ScreenerRunResponse;
if (!res.body) throw new ApiError('当前环境不支持流式响应', 0);
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buf = '';
let result: ScreenerRunResponse | null = null;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
let nl: number;
while ((nl = buf.indexOf('\n')) >= 0) {
const line = buf.slice(0, nl).trim();
buf = buf.slice(nl + 1);
if (!line) continue;
const ev = JSON.parse(line) as ScreenerStreamEvent;
if (ev.type === 'error') throw new ApiError(ev.message, ev.code ?? 0);
if (ev.type === 'result') result = ev.result;
onEvent?.(ev);
}
}
if (!result) throw new ApiError('选股响应中断(未收到结果事件)', 0);
return result;
}
export async function startScreenerSync(req: ScreenerSyncRequest = {}): Promise<ScreenerSyncStatus> {