fix(dashboard): preserve trader selection in URL and silence background requests (#1459)

* refactor: streamline trader selection logic and URL handling in App component

* refactor: update API request handling across components to use silent mode for improved error management

---------

Co-authored-by: Dean <afei.wuhao@gmail.com>
This commit is contained in:
deanokk
2026-04-04 17:16:47 +08:00
committed by GitHub
parent 8a0f3f5a13
commit 80272c0d5a
8 changed files with 76 additions and 41 deletions
+34 -20
View File
@@ -170,9 +170,7 @@ function App() {
) {
setCurrentPage('trader')
// 如果 URL 中有 trader 参数(slug 格式),更新选中的 trader
if (traderParam) {
setSelectedTraderSlug(traderParam)
}
setSelectedTraderSlug(traderParam || undefined)
} else if (
path === '/competition' ||
hash === 'competition' ||
@@ -200,7 +198,7 @@ function App() {
// 获取trader列表(仅在用户登录时)
const { data: traders, error: tradersError } = useSWR<TraderInfo[]>(
user && token ? 'traders' : null,
api.getTraders,
() => api.getTraders(currentPage === 'trader'),
{
refreshInterval: 10000,
shouldRetryOnError: false, // 避免在后端未运行时无限重试
@@ -219,19 +217,22 @@ function App() {
// 当获取到traders后,根据 URL 中的 trader slug 设置选中的 trader,或默认选中第一个
useEffect(() => {
if (traders && traders.length > 0 && !selectedTraderId) {
if (selectedTraderSlug) {
// 通过 slug 找到对应的 trader
const trader = findTraderBySlug(selectedTraderSlug, traders)
if (trader) {
setSelectedTraderId(trader.trader_id)
} else {
// 如果找不到,选中第一个
setSelectedTraderId(traders[0].trader_id)
}
} else {
setSelectedTraderId(traders[0].trader_id)
if (!traders || traders.length === 0) {
return
}
if (selectedTraderSlug) {
// 通过 slug 找到对应的 trader
const trader = findTraderBySlug(selectedTraderSlug, traders)
const nextTraderId = trader?.trader_id || traders[0].trader_id
if (nextTraderId !== selectedTraderId) {
setSelectedTraderId(nextTraderId)
}
return
}
if (!selectedTraderId) {
setSelectedTraderId(traders[0].trader_id)
}
}, [traders, selectedTraderId, selectedTraderSlug])
@@ -240,7 +241,7 @@ function App() {
currentPage === 'trader' && selectedTraderId
? `status-${selectedTraderId}`
: null,
() => api.getStatus(selectedTraderId),
() => api.getStatus(selectedTraderId, true),
{
refreshInterval: 15000, // 15秒刷新(配合后端15秒缓存)
revalidateOnFocus: false, // 禁用聚焦时重新验证,减少请求
@@ -303,7 +304,7 @@ function App() {
currentPage === 'trader' && selectedTraderId
? `statistics-${selectedTraderId}`
: null,
() => api.getStatistics(selectedTraderId),
() => api.getStatistics(selectedTraderId, true),
{
refreshInterval: 30000, // 30秒刷新(统计数据更新频率较低)
revalidateOnFocus: false,
@@ -520,7 +521,18 @@ function App() {
<AITradersPage
onTraderSelect={(traderId) => {
setSelectedTraderId(traderId)
window.history.pushState({}, '', '/dashboard')
const trader = traders?.find((item) => item.trader_id === traderId)
const url = new URL(window.location.href)
url.pathname = '/dashboard'
if (trader) {
const slug = getTraderSlug(trader)
url.searchParams.set('trader', slug)
setSelectedTraderSlug(slug)
} else {
url.searchParams.delete('trader')
setSelectedTraderSlug(undefined)
}
window.history.pushState({}, '', url.toString())
setRoute('/dashboard')
setCurrentPage('trader')
}}
@@ -550,8 +562,10 @@ function App() {
// 更新 URL 参数(使用 slug: name-id前4位)
const trader = traders?.find(t => t.trader_id === traderId)
if (trader) {
const slug = getTraderSlug(trader)
setSelectedTraderSlug(slug)
const url = new URL(window.location.href)
url.searchParams.set('trader', getTraderSlug(trader))
url.searchParams.set('trader', slug)
window.history.replaceState({}, '', url.toString())
}
}}
+9 -3
View File
@@ -153,7 +153,7 @@ export function AdvancedChart({
try {
const limit = 1500
const klineUrl = `/api/klines?symbol=${symbol}&interval=${interval}&limit=${limit}&exchange=${exchange}`
const result = await httpClient.get(klineUrl)
const result = await httpClient.request(klineUrl, { silent: true })
if (!result.success || !result.data) {
throw new Error('Failed to fetch kline data')
@@ -243,7 +243,10 @@ export function AdvancedChart({
try {
console.log('[AdvancedChart] Fetching orders for trader:', traderID, 'symbol:', symbol)
// Fetch filled orders, up to 200 for more history
const result = await httpClient.get(`/api/orders?trader_id=${traderID}&symbol=${symbol}&status=FILLED&limit=200`)
const result = await httpClient.request(
`/api/orders?trader_id=${traderID}&symbol=${symbol}&status=FILLED&limit=200`,
{ silent: true }
)
console.log('[AdvancedChart] Orders API response:', result)
@@ -326,7 +329,10 @@ export function AdvancedChart({
const fetchOpenOrders = async (traderID: string, symbol: string): Promise<OpenOrder[]> => {
try {
console.log('[AdvancedChart] Fetching open orders for trader:', traderID, 'symbol:', symbol)
const result = await httpClient.get(`/api/open-orders?trader_id=${traderID}&symbol=${symbol}`)
const result = await httpClient.request(
`/api/open-orders?trader_id=${traderID}&symbol=${symbol}`,
{ silent: true }
)
console.log('[AdvancedChart] Open orders API response:', result)
@@ -114,7 +114,7 @@ export function ChartWithOrders({
const limit = 2000 // Fetch recent 2000 candles (more historical data)
const klineUrl = `/api/klines?symbol=${symbol}&interval=${interval}&limit=${limit}&exchange=${exchange}`
const result = await httpClient.get(klineUrl)
const result = await httpClient.request(klineUrl, { silent: true })
if (!result.success || !result.data) {
throw new Error('Failed to fetch kline data from our service')
@@ -142,7 +142,10 @@ export function ChartWithOrders({
const fetchOrders = async (traderID: string, symbol: string): Promise<OrderMarker[]> => {
try {
// Fetch filled orders for this trader from backend API
const result = await httpClient.get(`/api/orders?trader_id=${traderID}&symbol=${symbol}&status=FILLED&limit=50`)
const result = await httpClient.request(
`/api/orders?trader_id=${traderID}&symbol=${symbol}&status=FILLED&limit=50`,
{ silent: true }
)
if (!result.success || !result.data) {
console.warn('Failed to fetch orders:', result.message)
@@ -31,7 +31,7 @@ export function ChartWithOrdersSimple({
const klineUrl = `/api/klines?symbol=${symbol}&interval=${interval}&limit=${limit}`
console.log('[ChartSimple] Fetching klines from our service:', klineUrl)
const klineResult = await httpClient.get(klineUrl)
const klineResult = await httpClient.request(klineUrl, { silent: true })
if (!klineResult.success || !klineResult.data) {
throw new Error('Failed to fetch klines from our service')
@@ -44,7 +44,7 @@ export function ChartWithOrdersSimple({
if (traderID) {
const tradesUrl = `/api/trades?trader_id=${traderID}&symbol=${symbol}&limit=100`
console.log('[ChartSimple] Fetching trades from:', tradesUrl)
const tradesResult = await httpClient.get(tradesUrl)
const tradesResult = await httpClient.request(tradesUrl, { silent: true })
if (tradesResult.success && tradesResult.data) {
console.log('[ChartSimple] Received trades:', tradesResult.data.length)
+2 -2
View File
@@ -43,7 +43,7 @@ export function EquityChart({ traderId, embedded = false }: EquityChartProps) {
const { data: history, error, isLoading } = useSWR<EquityPoint[]>(
user && token && traderId ? `equity-history-${traderId}` : null,
() => api.getEquityHistory(traderId),
() => api.getEquityHistory(traderId, true),
{
refreshInterval: 30000, // 30秒刷新(历史数据更新频率较低)
revalidateOnFocus: false,
@@ -53,7 +53,7 @@ export function EquityChart({ traderId, embedded = false }: EquityChartProps) {
const { data: account } = useSWR(
user && token && traderId ? `account-${traderId}` : null,
() => api.getAccount(traderId),
() => api.getAccount(traderId, true),
{
refreshInterval: 15000, // 15秒刷新(配合后端缓存)
revalidateOnFocus: false,
@@ -359,7 +359,11 @@ export function PositionHistory({ traderId }: PositionHistoryProps) {
setLoading(true)
setError(null)
// Fetch more data than needed to support filtering, but respect pageSize for initial load
const data = await api.getPositionHistory(traderId, Math.max(200, pageSize * 5))
const data = await api.getPositionHistory(
traderId,
Math.max(200, pageSize * 5),
true
)
setPositions(data.positions || [])
setStats(data.stats)
setSymbolStats(data.symbol_stats || [])
+14 -9
View File
@@ -10,11 +10,11 @@ import type {
import { API_BASE, httpClient } from './helpers'
export const dataApi = {
async getStatus(traderId?: string): Promise<SystemStatus> {
async getStatus(traderId?: string, silent?: boolean): Promise<SystemStatus> {
const url = traderId
? `${API_BASE}/status?trader_id=${traderId}`
: `${API_BASE}/status`
const result = await httpClient.get<SystemStatus>(url)
const result = await httpClient.request<SystemStatus>(url, { silent })
if (!result.success) throw new Error('Failed to fetch system status')
return result.data!
},
@@ -65,20 +65,20 @@ export const dataApi = {
return result.data!
},
async getStatistics(traderId?: string): Promise<Statistics> {
async getStatistics(traderId?: string, silent?: boolean): Promise<Statistics> {
const url = traderId
? `${API_BASE}/statistics?trader_id=${traderId}`
: `${API_BASE}/statistics`
const result = await httpClient.get<Statistics>(url)
const result = await httpClient.request<Statistics>(url, { silent })
if (!result.success) throw new Error('Failed to fetch statistics')
return result.data!
},
async getEquityHistory(traderId?: string): Promise<any[]> {
async getEquityHistory(traderId?: string, silent?: boolean): Promise<any[]> {
const url = traderId
? `${API_BASE}/equity-history?trader_id=${traderId}`
: `${API_BASE}/equity-history`
const result = await httpClient.get<any[]>(url)
const result = await httpClient.request<any[]>(url, { silent })
if (!result.success) throw new Error('Failed to fetch equity history')
return result.data!
},
@@ -114,9 +114,14 @@ export const dataApi = {
return result.data!
},
async getPositionHistory(traderId: string, limit: number = 100): Promise<PositionHistoryResponse> {
const result = await httpClient.get<PositionHistoryResponse>(
`${API_BASE}/positions/history?trader_id=${traderId}&limit=${limit}`
async getPositionHistory(
traderId: string,
limit: number = 100,
silent?: boolean
): Promise<PositionHistoryResponse> {
const result = await httpClient.request<PositionHistoryResponse>(
`${API_BASE}/positions/history?trader_id=${traderId}&limit=${limit}`,
{ silent }
)
if (!result.success) throw new Error('Failed to fetch position history')
return result.data!
+5 -2
View File
@@ -16,8 +16,11 @@ function throwApiError(
}
export const traderApi = {
async getTraders(): Promise<TraderInfo[]> {
const result = await httpClient.get<TraderInfo[]>(`${API_BASE}/my-traders`)
async getTraders(silent?: boolean): Promise<TraderInfo[]> {
const result = await httpClient.request<TraderInfo[]>(
`${API_BASE}/my-traders`,
{ silent }
)
if (!result.success) throw new Error('Failed to fetch trader list')
return Array.isArray(result.data) ? result.data : []
},