From 9a4621c8de33cb345e8a35b6bdb3920dc8140c09 Mon Sep 17 00:00:00 2001 From: shy3130 <415333856@qq.com> Date: Sun, 30 Aug 2026 19:19:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(onboarding):=20=E8=83=BD=E5=8A=9B=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E6=AD=A5=E9=AA=A4=E8=BF=9B=E5=85=A5=E6=97=B6=E6=8C=89?= =?UTF-8?q?=20tickflow-fuyao-stocksdk=20=E4=BC=98=E5=85=88=E7=BA=A7?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=AE=BE=E7=BD=AE=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Onboarding.tsx | 74 ++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Onboarding.tsx b/frontend/src/pages/Onboarding.tsx index 0979ed5..be2f838 100644 --- a/frontend/src/pages/Onboarding.tsx +++ b/frontend/src/pages/Onboarding.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect, useRef } from 'react' import { useNavigate } from 'react-router-dom' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { motion, AnimatePresence } from 'framer-motion' @@ -23,7 +23,7 @@ import { KeyRound, Route, } from 'lucide-react' -import { api } from '@/lib/api' +import { api, type ProviderField } from '@/lib/api' import { usePreferences, useSettings } from '@/lib/useSharedQueries' import { QK } from '@/lib/queryKeys' import { Logo } from '@/components/Logo' @@ -550,8 +550,17 @@ function DataSourceStep({ onNext, onBack }: { onNext: () => void; onBack: () => // ===== Step 3: 能力路由检测结果 ===== // 以能力路由矩阵呈现: 每个标准化数据集一行, 展示当前生效源与可用性, // 不再以 TickFlow 档位为中心 —— 多源下各数据集独立路由, 矩阵即真相。 +// 进入本步时按默认优先级自动设置路由: 前者可用的能力归前者, 没有则顺延下一个可用源。 + +// 自动路由的默认优先级 (前者可用的能力优先归前者) +const ROUTE_PRIORITY = [ + { name: 'tickflow', display: 'TickFlow' }, + { name: 'fuyao', display: 'fuyao' }, + { name: 'stocksdk', display: 'stock-sdk' }, +] function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void }) { + const qc = useQueryClient() const settings = useSettings() const prefs = usePreferences() const matrix = useQuery({ @@ -560,6 +569,40 @@ function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void staleTime: 30_000, }) + // 自动路由结果: null=尚未执行/执行中; []=已符合优先级无需调整; 有值=本次改写的路由 + const appliedRef = useRef(false) + const [appliedChanges, setAppliedChanges] = useState<{ label: string; to: string }[] | null>(null) + const [applyError, setApplyError] = useState(null) + + useEffect(() => { + if (!matrix.data || appliedRef.current) return + appliedRef.current = true + const desired: Partial> = {} + const changes: { label: string; to: string }[] = [] + for (const cap of matrix.data.capabilities) { + if (!cap.field) continue // 不可路由能力 (仅 TickFlow 提供) 跳过 + const pick = ROUTE_PRIORITY.find(p => ( + p.name === 'tickflow' + ? cap.tf_available + : cap.candidates.some(c => c.name === p.name && c.available) + )) + if (!pick || pick.name === cap.current) continue // 全都不可用或无需变化: 保持现状 + desired[cap.field] = pick.name + changes.push({ label: cap.label, to: pick.display }) + } + if (changes.length === 0) { + setAppliedChanges([]) + return + } + api.updateDataProviders(desired) + .then(() => { + setAppliedChanges(changes) + return qc.invalidateQueries({ queryKey: QK.preferences }) + }) + .then(() => qc.invalidateQueries({ queryKey: QK.capabilityMatrix })) + .catch(e => setApplyError(`自动设置能力路由失败: ${(e as Error).message}`)) + }, [matrix.data]) // eslint-disable-line react-hooks/exhaustive-deps + const activeName = prefs.data?.daily_data_provider || 'tickflow' const isTickflow = activeName === 'tickflow' const routes = matrix.data?.capabilities ?? [] @@ -576,11 +619,32 @@ function ResultStep({ onNext, onBack }: { onNext: () => void; onBack: () => void

能力路由检测

- 每个标准化数据集独立路由:下方是当前生效源与实际可用性,随时可在 - 设置 → 数据源 - 按数据集改选候选源。 + 进入本步时已按默认优先级 + TickFlow → fuyao → stock-sdk + 自动设置各数据集的路由:前者可用的能力归前者,没有则顺延下一个可用源。后续可随时在 + 设置 → 数据源 按数据集改选。

+ {/* 自动路由执行结果 */} + {applyError && ( +
+ + {applyError} +
+ )} + {!applyError && appliedChanges !== null && ( +
+ + {appliedChanges.length > 0 ? ( + + 已按优先级自动路由:{appliedChanges.map(c => `${c.label} → ${c.to}`).join('、')} + + ) : ( + 当前能力路由已符合默认优先级,无需调整。 + )} +
+ )} + {matrix.isLoading ? (