feat: 添加 RTK Query 集成用于事件数据获取(实验性)...
This commit is contained in:
144
src/store/api/eventsApi.js
Normal file
144
src/store/api/eventsApi.js
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// src/store/api/eventsApi.js
|
||||||
|
// RTK Query API for Events - 事件数据获取 API
|
||||||
|
|
||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Events API Slice - 使用 RTK Query 管理事件数据
|
||||||
|
*
|
||||||
|
* 特性:
|
||||||
|
* - ✅ 自动缓存管理(按 queryKey 缓存)
|
||||||
|
* - ✅ 自动去重请求
|
||||||
|
* - ✅ 返回第一页刷新数据(invalidateTags)
|
||||||
|
* - ✅ 预加载支持(prefetch)
|
||||||
|
* - ✅ 统一在 Redux DevTools 中调试
|
||||||
|
* - ✅ 无需额外 Provider
|
||||||
|
*/
|
||||||
|
export const eventsApi = createApi({
|
||||||
|
reducerPath: 'eventsApi',
|
||||||
|
|
||||||
|
// 基础查询配置
|
||||||
|
baseQuery: fetchBaseQuery({
|
||||||
|
baseUrl: '/api',
|
||||||
|
prepareHeaders: (headers) => {
|
||||||
|
// 可以在这里添加认证 token
|
||||||
|
// const token = localStorage.getItem('token');
|
||||||
|
// if (token) {
|
||||||
|
// headers.set('Authorization', `Bearer ${token}`);
|
||||||
|
// }
|
||||||
|
return headers;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 标签类型定义(用于缓存失效)
|
||||||
|
tagTypes: ['Events'],
|
||||||
|
|
||||||
|
// API 端点定义
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
/**
|
||||||
|
* 获取分页事件列表
|
||||||
|
*
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {number} params.page - 页码
|
||||||
|
* @param {number} params.per_page - 每页数量
|
||||||
|
* @param {string} params.mode - 显示模式(vertical / four-row)
|
||||||
|
* @param {string} params.sort - 排序方式
|
||||||
|
* @param {string} params.importance - 重要性筛选
|
||||||
|
* @param {string} params.q - 搜索关键词
|
||||||
|
* @param {string} params.date_range - 日期范围
|
||||||
|
* @param {string} params.industry_code - 行业代码
|
||||||
|
*
|
||||||
|
* @returns {Object} { events: Array, pagination: Object }
|
||||||
|
*/
|
||||||
|
getEvents: builder.query({
|
||||||
|
query: ({ page = 1, per_page = 5, mode = 'vertical', ...filters }) => {
|
||||||
|
logger.debug('eventsApi', 'getEvents 请求', {
|
||||||
|
page,
|
||||||
|
per_page,
|
||||||
|
mode,
|
||||||
|
filters,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: '/events',
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
per_page,
|
||||||
|
...filters,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// 🔥 缓存标签:用于缓存失效
|
||||||
|
providesTags: (result, error, { page, mode }) => {
|
||||||
|
if (error) return [];
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ type: 'Events', id: `${mode}-${page}` }, // 具体页面的标签
|
||||||
|
{ type: 'Events', id: `${mode}-LIST` }, // 模式的总标签
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
// 转换响应数据
|
||||||
|
transformResponse: (response) => {
|
||||||
|
logger.debug('eventsApi', 'getEvents 响应', {
|
||||||
|
eventsCount: response.data?.events?.length,
|
||||||
|
total: response.data?.pagination?.total,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success || !response.data?.events) {
|
||||||
|
throw new Error('数据格式错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: response.data.events,
|
||||||
|
pagination: response.data.pagination || {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// 错误处理
|
||||||
|
transformErrorResponse: (response) => {
|
||||||
|
logger.error('eventsApi', 'getEvents 失败', new Error(response.status));
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
message: response.data?.message || '获取事件数据失败',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// 🔥 keepUnusedDataFor: 缓存保留时间(秒)
|
||||||
|
keepUnusedDataFor: 600, // 10分钟
|
||||||
|
|
||||||
|
// 🔥 合并查询结果(用于无限滚动)
|
||||||
|
// serializeQueryArgs: ({ endpointName, queryArgs }) => {
|
||||||
|
// const { mode, ...filters } = queryArgs;
|
||||||
|
// return `${endpointName}(${mode})`;
|
||||||
|
// },
|
||||||
|
// merge: (currentCache, newItems) => {
|
||||||
|
// currentCache.events.push(...newItems.events);
|
||||||
|
// },
|
||||||
|
// forceRefetch: ({ currentArg, previousArg }) => {
|
||||||
|
// return currentArg.page !== previousArg?.page;
|
||||||
|
// },
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预加载下一页(性能优化)
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* dispatch(eventsApi.util.prefetch('getEvents', { page: 2, ... }))
|
||||||
|
*/
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出自动生成的 Hooks
|
||||||
|
export const {
|
||||||
|
useGetEventsQuery,
|
||||||
|
useLazyGetEventsQuery, // 手动触发的版本
|
||||||
|
usePrefetch, // 预加载 Hook
|
||||||
|
} = eventsApi;
|
||||||
|
|
||||||
|
// 导出工具方法
|
||||||
|
export const {
|
||||||
|
util: { invalidateTags, prefetch },
|
||||||
|
} = eventsApi;
|
||||||
@@ -7,6 +7,7 @@ import stockReducer from './slices/stockSlice';
|
|||||||
import authModalReducer from './slices/authModalSlice';
|
import authModalReducer from './slices/authModalSlice';
|
||||||
import subscriptionReducer from './slices/subscriptionSlice';
|
import subscriptionReducer from './slices/subscriptionSlice';
|
||||||
import posthogMiddleware from './middleware/posthogMiddleware';
|
import posthogMiddleware from './middleware/posthogMiddleware';
|
||||||
|
import { eventsApi } from './api/eventsApi'; // ✅ RTK Query API
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@@ -16,6 +17,7 @@ export const store = configureStore({
|
|||||||
stock: stockReducer, // ✅ 股票和事件数据管理
|
stock: stockReducer, // ✅ 股票和事件数据管理
|
||||||
authModal: authModalReducer, // ✅ 认证弹窗状态管理
|
authModal: authModalReducer, // ✅ 认证弹窗状态管理
|
||||||
subscription: subscriptionReducer, // ✅ 订阅信息状态管理
|
subscription: subscriptionReducer, // ✅ 订阅信息状态管理
|
||||||
|
[eventsApi.reducerPath]: eventsApi.reducer, // ✅ RTK Query 事件 API
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: (getDefaultMiddleware) =>
|
||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
@@ -29,7 +31,9 @@ export const store = configureStore({
|
|||||||
'stock/fetchStockQuotes/fulfilled',
|
'stock/fetchStockQuotes/fulfilled',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}).concat(posthogMiddleware), // ✅ PostHog 自动追踪中间件
|
})
|
||||||
|
.concat(posthogMiddleware) // ✅ PostHog 自动追踪中间件
|
||||||
|
.concat(eventsApi.middleware), // ✅ RTK Query 中间件(自动缓存、去重、重试)
|
||||||
});
|
});
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
|||||||
Reference in New Issue
Block a user