feat: 将 IndustryProvider (176行) 完整迁移到 Redux
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import communityDataReducer from './slices/communityDataSlice';
|
||||
import posthogReducer from './slices/posthogSlice';
|
||||
import industryReducer from './slices/industrySlice';
|
||||
import posthogMiddleware from './middleware/posthogMiddleware';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
communityData: communityDataReducer,
|
||||
posthog: posthogReducer, // ✅ PostHog Redux 状态管理
|
||||
industry: industryReducer, // ✅ 行业分类数据管理
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware({
|
||||
|
||||
178
src/store/slices/industrySlice.js
Normal file
178
src/store/slices/industrySlice.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// src/store/slices/industrySlice.js
|
||||
// 行业分类数据 Redux Slice - 从 IndustryContext 迁移
|
||||
|
||||
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { industryData as staticIndustryData } from '../../data/industryData';
|
||||
import { industryService } from '../../services/industryService';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
// 缓存配置
|
||||
const CACHE_KEY = 'industry_classifications_cache';
|
||||
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 1天(24小时)
|
||||
|
||||
/**
|
||||
* 从 localStorage 读取缓存
|
||||
*/
|
||||
const loadFromCache = () => {
|
||||
try {
|
||||
const cached = localStorage.getItem(CACHE_KEY);
|
||||
if (!cached) return null;
|
||||
|
||||
const { data, timestamp } = JSON.parse(cached);
|
||||
const now = Date.now();
|
||||
|
||||
// 检查缓存是否过期(1天)
|
||||
if (now - timestamp > CACHE_DURATION) {
|
||||
localStorage.removeItem(CACHE_KEY);
|
||||
logger.debug('industrySlice', '缓存已过期,清除缓存');
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug('industrySlice', '从缓存加载行业数据', {
|
||||
count: data?.length || 0,
|
||||
age: Math.round((now - timestamp) / 1000 / 60) + ' 分钟前'
|
||||
});
|
||||
return data;
|
||||
} catch (error) {
|
||||
logger.error('industrySlice', 'loadFromCache', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存到 localStorage
|
||||
*/
|
||||
const saveToCache = (data) => {
|
||||
try {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify({
|
||||
data,
|
||||
timestamp: Date.now()
|
||||
}));
|
||||
logger.debug('industrySlice', '行业数据已缓存', {
|
||||
count: data?.length || 0
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('industrySlice', 'saveToCache', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 异步 Thunk: 加载行业数据
|
||||
* 策略:缓存 -> API -> 静态数据
|
||||
*/
|
||||
export const fetchIndustryData = createAsyncThunk(
|
||||
'industry/fetchData',
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
logger.debug('industrySlice', '开始加载行业数据');
|
||||
|
||||
// 1. 先尝试从缓存加载
|
||||
const cachedData = loadFromCache();
|
||||
if (cachedData && cachedData.length > 0) {
|
||||
logger.debug('industrySlice', '使用缓存数据', { count: cachedData.length });
|
||||
return { data: cachedData, source: 'cache' };
|
||||
}
|
||||
|
||||
// 2. 缓存不存在或过期,调用 API
|
||||
logger.debug('industrySlice', '缓存无效,调用API获取数据');
|
||||
const response = await industryService.getClassifications();
|
||||
|
||||
if (response.success && response.data && response.data.length > 0) {
|
||||
saveToCache(response.data);
|
||||
logger.debug('industrySlice', 'API数据加载成功', {
|
||||
count: response.data.length
|
||||
});
|
||||
return { data: response.data, source: 'api' };
|
||||
} else {
|
||||
throw new Error('API返回数据为空');
|
||||
}
|
||||
} catch (error) {
|
||||
// 3. API 失败,回退到静态数据
|
||||
logger.warn('industrySlice', 'API加载失败,使用静态数据', {
|
||||
error: error.message
|
||||
});
|
||||
return { data: staticIndustryData, source: 'static', error: error.message };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 异步 Thunk: 刷新行业数据(清除缓存并重新加载)
|
||||
*/
|
||||
export const refreshIndustryData = createAsyncThunk(
|
||||
'industry/refresh',
|
||||
async (_, { dispatch }) => {
|
||||
logger.debug('industrySlice', '刷新行业数据,清除缓存');
|
||||
localStorage.removeItem(CACHE_KEY);
|
||||
return dispatch(fetchIndustryData());
|
||||
}
|
||||
);
|
||||
|
||||
// Industry Slice
|
||||
const industrySlice = createSlice({
|
||||
name: 'industry',
|
||||
initialState: {
|
||||
data: null, // 行业数据数组
|
||||
loading: false, // 加载状态
|
||||
error: null, // 错误信息
|
||||
source: null, // 数据来源: 'cache' | 'api' | 'static'
|
||||
lastFetchTime: null, // 最后加载时间
|
||||
},
|
||||
reducers: {
|
||||
// 清除缓存
|
||||
clearCache: (state) => {
|
||||
localStorage.removeItem(CACHE_KEY);
|
||||
logger.debug('industrySlice', '手动清除缓存');
|
||||
},
|
||||
// 重置状态
|
||||
resetState: (state) => {
|
||||
state.data = null;
|
||||
state.loading = false;
|
||||
state.error = null;
|
||||
state.source = null;
|
||||
state.lastFetchTime = null;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
// fetchIndustryData
|
||||
.addCase(fetchIndustryData.pending, (state) => {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchIndustryData.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
state.data = action.payload.data;
|
||||
state.source = action.payload.source;
|
||||
state.lastFetchTime = Date.now();
|
||||
if (action.payload.error) {
|
||||
state.error = action.payload.error;
|
||||
}
|
||||
})
|
||||
.addCase(fetchIndustryData.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error = action.error.message;
|
||||
// 确保总有数据可用
|
||||
if (!state.data) {
|
||||
state.data = staticIndustryData;
|
||||
state.source = 'static';
|
||||
}
|
||||
})
|
||||
// refreshIndustryData
|
||||
.addCase(refreshIndustryData.pending, (state) => {
|
||||
state.loading = true;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 导出 actions
|
||||
export const { clearCache, resetState } = industrySlice.actions;
|
||||
|
||||
// 导出 selectors
|
||||
export const selectIndustryData = (state) => state.industry.data;
|
||||
export const selectIndustryLoading = (state) => state.industry.loading;
|
||||
export const selectIndustryError = (state) => state.industry.error;
|
||||
export const selectIndustrySource = (state) => state.industry.source;
|
||||
|
||||
// 导出 reducer
|
||||
export default industrySlice.reducer;
|
||||
Reference in New Issue
Block a user