feat(selection): 补充策略执行结果查询链路

This commit is contained in:
yuxuanhui
2026-08-09 09:34:46 +08:00
parent e9d06df5de
commit 9c1a1eac23
31 changed files with 3566 additions and 14 deletions
@@ -0,0 +1,39 @@
import { requestJson } from "@/shared/api/request-json"
import type {
SelectionResults,
SelectionRunAccepted,
SelectionRunRequest,
SelectionStrategy,
} from "./selection.types"
export function getSelectionResults(
strategy: SelectionStrategy,
targetTradeDate?: string,
signal?: AbortSignal,
) {
const params = new URLSearchParams({ strategy })
if (targetTradeDate) params.set("target_trade_date", targetTradeDate)
return requestJson<SelectionResults>(
`/api/v1/selection/results?${params.toString()}`,
{ signal },
)
}
export function getSelectionRun(runId: string, signal?: AbortSignal) {
return requestJson<SelectionResults>(`/api/v1/selection/runs/${runId}`, {
signal,
})
}
export function triggerSelectionRun(
request: SelectionRunRequest,
signal?: AbortSignal,
) {
return requestJson<SelectionRunAccepted>("/api/v1/selection/runs", {
body: JSON.stringify(request),
headers: { "Content-Type": "application/json" },
method: "POST",
signal,
})
}
@@ -0,0 +1,79 @@
import {
useMutation,
useQuery,
useQueryClient,
type QueryClient,
} from "@tanstack/react-query"
import { useEffect } from "react"
import {
getSelectionResults,
getSelectionRun,
triggerSelectionRun,
} from "./selection.api"
import type { SelectionRunRequest, SelectionStrategy } from "./selection.types"
export const selectionResultsQueryKey = (
strategy: SelectionStrategy,
targetTradeDate?: string,
) => ["selection", "results", strategy, targetTradeDate] as const
export const selectionRunQueryKey = (runId: string) =>
["selection", "runs", runId] as const
export function useSelectionResults(
strategy: SelectionStrategy,
targetTradeDate?: string,
) {
return useQuery({
queryFn: ({ signal }) =>
getSelectionResults(strategy, targetTradeDate, signal),
queryKey: selectionResultsQueryKey(strategy, targetTradeDate),
})
}
export function useSelectionRun(runId: string | null) {
const queryClient = useQueryClient()
const query = useQuery({
enabled: Boolean(runId),
queryFn: ({ signal }) => getSelectionRun(runId ?? "", signal),
queryKey: selectionRunQueryKey(runId ?? "none"),
refetchInterval: (query) =>
query.state.data?.status === "running" ? 1500 : false,
})
useEffect(() => {
const result = query.data
if (!runId || !result || result.status === "running") return
void invalidateSelectionResults(queryClient, result.strategy)
}, [query.data, queryClient, runId])
return query
}
export function useTriggerSelectionRun() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({
request,
signal,
}: {
request: SelectionRunRequest
signal?: AbortSignal
}) => triggerSelectionRun(request, signal),
onSuccess: (_accepted, { request }) => {
void invalidateSelectionResults(queryClient, request.strategy)
},
})
}
export function invalidateSelectionResults(
queryClient: QueryClient,
strategy: SelectionStrategy,
) {
return queryClient.invalidateQueries({
queryKey: ["selection", "results", strategy],
})
}
@@ -0,0 +1,55 @@
export type SelectionStrategy = "zhixing_b1"
export type SelectionRunStatus =
"no_data" | "running" | "success" | "partial_success" | "failed"
export interface SelectionRunRequest {
strategy: SelectionStrategy
target_trade_date: string
rerun: boolean
}
export interface SelectionRunAccepted {
run_id: string
strategy: SelectionStrategy
target_trade_date: string
status: "running"
}
export interface SelectionSignal {
ts_code: string
name: string
target_trade_date: string
strategy: SelectionStrategy
category: string
close: number
details: Record<string, number | string | null>
}
export interface SelectionFailure {
ts_code: string
name: string
status: string
reason: string | null
}
export interface SelectionResults {
strategy: SelectionStrategy
target_trade_date: string | null
run_id: string | null
market_sync_batch_id: string | null
status: SelectionRunStatus
target_count: number
eligible_count: number
evaluated_count: number
selected_stock_count: number
signal_count: number
failed_count: number
coverage: number
error_type: string | null
error_message: string | null
created_at: string | null
finished_at: string | null
failures: SelectionFailure[]
signals: SelectionSignal[]
}