feat(selection): 补充策略执行结果查询链路
This commit is contained in:
@@ -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[]
|
||||
}
|
||||
Reference in New Issue
Block a user