mirror of
https://ghfast.top/https://github.com/aeroxw/easy_tdx_max.git
synced 2026-09-12 16:54:20 +08:00
fix(web-ui): 拼音搜索索引改为按需加载,避免首次构建阻塞行情请求
问题:新机器首次打开回测/寻优页时,组件挂载即触发 /security/search-index, 后端要走全量 get_security_list_all(几十次 TDX 协议往返,几十秒)。 而 AsyncTdxClient 是单连接 + _execute_lock,索引构建期间独占连接, 把 /bars 行情请求和寻优请求全部堵在后面,表现为点「一键寻优」后 等好久才出现第一根 bars 请求。 修复(三招组合): - useStockSearch: 挂载即拉 → 改为显式 ensureLoaded(),只在首次聚焦 输入框时才触发,确保「打开页面直接点取行情/寻优」的核心路径不被拖累 - fetchSearchIndex: 加 AbortController 15s 超时,避免长时间独占连接 - 静默失败降级:索引拉取失败只记 loadError 显示小 ⚠,不弹错不阻塞
This commit is contained in:
+12
-4
@@ -104,11 +104,19 @@ export async function fetchBars(
|
||||
}
|
||||
|
||||
/** 拉取股票搜索索引(code/name/initials,约 5000 条,~150KB)。
|
||||
* 前端 useStockSearch 会模块级缓存,整个会话只拉一次。 */
|
||||
* 前端 useStockSearch 会模块级缓存,整个会话只拉一次。
|
||||
* 超时 15 秒放弃——后端首次构建索引要走全量 get_security_list_all(几十秒),
|
||||
* 超时后放弃可避免长时间独占共享连接、阻塞 /bars 行情请求。 */
|
||||
export async function fetchSearchIndex(): Promise<StockSearchIndex> {
|
||||
const resp = await fetch(`${BASE}/security/search-index`)
|
||||
if (!resp.ok) await throwError(resp)
|
||||
return (await resp.json()) as StockSearchIndex
|
||||
const controller = new AbortController()
|
||||
const timer = setTimeout(() => controller.abort(), 15_000)
|
||||
try {
|
||||
const resp = await fetch(`${BASE}/security/search-index`, { signal: controller.signal })
|
||||
if (!resp.ok) await throwError(resp)
|
||||
return (await resp.json()) as StockSearchIndex
|
||||
} finally {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
|
||||
/** 把后端 bars 的单条记录归一化为统一 Bar(datetime 字段)。 */
|
||||
|
||||
@@ -22,7 +22,7 @@ const emit = defineEmits<{
|
||||
confirm: [code: string]
|
||||
}>()
|
||||
|
||||
const { ready, loadError, search } = useStockSearch()
|
||||
const { ready, loadError, ensureLoaded, search } = useStockSearch()
|
||||
|
||||
// 输入框文本(可能是代码片段、拼音、中文)。与 code 解耦:
|
||||
// code 是最终选定的 6 位代码,inputText 是用户正在敲的内容
|
||||
@@ -117,6 +117,9 @@ function onBlur() {
|
||||
}
|
||||
|
||||
function onFocus() {
|
||||
// 首次聚焦输入框才触发索引拉取(按需加载,避免挂载即拉阻塞行情请求)。
|
||||
// 详见 useStockSearch.ts 顶部注释。
|
||||
ensureLoaded()
|
||||
// 聚焦时若已有输入且非完整代码,重新展示建议
|
||||
nextTick(() => {
|
||||
if (inputText.value.trim() && !isFullCode.value && suggestions.value.length > 0) {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// 股票搜索 composable:模块级缓存搜索索引 + 按代码/名字/声母三路过滤。
|
||||
// 索引整会话只拉一次(~150KB / 5000 条),后续过滤纯本地计算(<5ms)。
|
||||
//
|
||||
// ⚠️ 重要:索引构建是"按需触发"——只在用户首次聚焦搜索输入框时才拉取,
|
||||
// 不在组件挂载时自动拉。原因是后端首次构建索引要走全量 get_security_list_all
|
||||
// (几十次 TDX 协议往返,几十秒),而 AsyncTdxClient 是单连接 + _execute_lock,
|
||||
// 索引构建期间会独占连接、阻塞所有 /bars 行情请求。按需触发确保"打开页面
|
||||
// 直接点取行情/寻优"的核心路径不被搜索索引拖累。
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -40,25 +46,31 @@ async function ensureIndex(): Promise<StockSearchEntry[]> {
|
||||
export interface UseStockSearch {
|
||||
/** 索引是否已加载就绪 */
|
||||
ready: ReturnType<typeof ref<boolean>>
|
||||
/** 加载错误信息(空串表示无错) */
|
||||
/** 加载错误信息(空串表示无错;搜索不可用时静默降级,不阻塞主流程) */
|
||||
loadError: ReturnType<typeof ref<string>>
|
||||
/** 按输入过滤,返回最多 limit 条(默认 30) */
|
||||
/** 显式触发索引加载(首次聚焦输入框时调用)。静默失败,不抛错。 */
|
||||
ensureLoaded: () => void
|
||||
/** 按输入过滤,返回最多 limit 条(默认 30)。索引未就绪时返回空数组。 */
|
||||
search: (query: string, limit?: number) => Promise<StockSearchEntry[]>
|
||||
}
|
||||
|
||||
/** 股票搜索:懒加载索引 + 本地三路过滤。 */
|
||||
/** 股票搜索:按需加载索引 + 本地三路过滤。 */
|
||||
export function useStockSearch(): UseStockSearch {
|
||||
const ready = ref(false)
|
||||
const loadError = ref('')
|
||||
|
||||
// 首次调用即触发后台拉取(不阻塞,失败记错)
|
||||
ensureIndex()
|
||||
.then(() => {
|
||||
ready.value = true
|
||||
})
|
||||
.catch((e) => {
|
||||
loadError.value = formatError(e)
|
||||
})
|
||||
function ensureLoaded() {
|
||||
if (cachedIndex || loadPromise) return
|
||||
ensureIndex()
|
||||
.then(() => {
|
||||
ready.value = true
|
||||
})
|
||||
.catch((e) => {
|
||||
// 静默失败:搜索是附加功能,不能因为索引拉不到而打扰用户。
|
||||
// 只记 loadError 供输入框显示一个小 ⚠ 图标,不弹错、不阻塞。
|
||||
loadError.value = formatError(e)
|
||||
})
|
||||
}
|
||||
|
||||
async function search(query: string, limit = 30): Promise<StockSearchEntry[]> {
|
||||
const q = query.trim().toLowerCase()
|
||||
@@ -74,5 +86,5 @@ export function useStockSearch(): UseStockSearch {
|
||||
return out
|
||||
}
|
||||
|
||||
return { ready, loadError, search }
|
||||
return { ready, loadError, ensureLoaded, search }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user