Files
vf_react/src/store/slices/industrySlice.js

179 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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;