refactor: 优化分页存储架构和缓存逻辑...
This commit is contained in:
@@ -222,10 +222,18 @@ export const fetchDynamicNews = createAsyncThunk(
|
||||
total: response.data.pagination?.total || 0,
|
||||
per_page: finalPerPage
|
||||
});
|
||||
// 【兜底处理】支持多种 pagination 字段名:pages (后端) / total_pages (旧Mock) / totalPages
|
||||
const paginationData = response.data.pagination || {};
|
||||
const calculatedTotalPages = paginationData.pages // ← 后端格式 (优先)
|
||||
|| paginationData.total_pages // ← Mock 旧格式 (兼容)
|
||||
|| paginationData.totalPages // ← 其他可能格式
|
||||
|| Math.ceil((paginationData.total || 0) / finalPerPage); // ← 兜底计算
|
||||
|
||||
return {
|
||||
mode,
|
||||
events: response.data.events,
|
||||
total: response.data.pagination?.total || 0,
|
||||
total: paginationData.total || 0,
|
||||
totalPages: calculatedTotalPages,
|
||||
page,
|
||||
per_page: finalPerPage,
|
||||
clearCache,
|
||||
@@ -234,10 +242,15 @@ export const fetchDynamicNews = createAsyncThunk(
|
||||
}
|
||||
|
||||
logger.warn('CommunityData', '动态新闻返回数据为空', response);
|
||||
// 【兜底处理】空数据情况也尝试读取 pagination
|
||||
const emptyPaginationData = response.data?.pagination || {};
|
||||
const emptyTotalPages = emptyPaginationData.pages || emptyPaginationData.total_pages || 0;
|
||||
|
||||
return {
|
||||
mode,
|
||||
events: [],
|
||||
total: 0,
|
||||
totalPages: emptyTotalPages,
|
||||
page,
|
||||
per_page: finalPerPage,
|
||||
clearCache,
|
||||
@@ -327,12 +340,22 @@ const communityDataSlice = createSlice({
|
||||
hotEvents: [],
|
||||
|
||||
// 【纵向模式】独立存储(传统分页 + 每页10条)
|
||||
verticalEvents: [], // 完整缓存列表(累积所有已加载数据)
|
||||
verticalTotal: 0, // 服务端总数量(用于计算总页数)
|
||||
verticalEventsByPage: {}, // 页码映射存储 { 1: [10条], 2: [8条], 3: [10条] }
|
||||
verticalPagination: { // 分页元数据
|
||||
total: 0, // 总记录数
|
||||
total_pages: 0, // 总页数
|
||||
current_page: 1, // 当前页码
|
||||
per_page: 10 // 每页大小
|
||||
},
|
||||
|
||||
// 【平铺模式】独立存储(虚拟滚动 + 每页30条)
|
||||
fourRowEvents: [], // 完整缓存列表(虚拟滚动的数据源)
|
||||
fourRowTotal: 0, // 服务端总数量(用于判断 hasMore)
|
||||
fourRowPagination: { // 分页元数据
|
||||
total: 0, // 总记录数
|
||||
total_pages: 0, // 总页数
|
||||
current_page: 1, // 当前页码
|
||||
per_page: 30 // 每页大小
|
||||
},
|
||||
|
||||
eventFollowStatus: {}, // 事件关注状态(全局共享){ [eventId]: { isFollowing: boolean, followerCount: number } }
|
||||
|
||||
@@ -367,10 +390,10 @@ const communityDataSlice = createSlice({
|
||||
state.hotEvents = [];
|
||||
|
||||
// 清除动态新闻数据(两个模式)
|
||||
state.verticalEvents = [];
|
||||
state.verticalEventsByPage = {};
|
||||
state.fourRowEvents = [];
|
||||
state.verticalTotal = 0;
|
||||
state.fourRowTotal = 0;
|
||||
state.verticalPagination = { total: 0, total_pages: 0, current_page: 1, per_page: 10 };
|
||||
state.fourRowPagination = { total: 0, total_pages: 0, current_page: 1, per_page: 30 };
|
||||
|
||||
logger.info('CommunityData', '所有缓存已清除');
|
||||
},
|
||||
@@ -392,13 +415,13 @@ const communityDataSlice = createSlice({
|
||||
logger.info('CommunityData', '热点事件缓存已清除');
|
||||
} else if (type === 'verticalEvents') {
|
||||
// verticalEvents 不使用 localStorage,只清除 Redux state
|
||||
state.verticalEvents = [];
|
||||
state.verticalTotal = 0;
|
||||
state.verticalEventsByPage = {};
|
||||
state.verticalPagination = { total: 0, total_pages: 0, current_page: 1, per_page: 10 };
|
||||
logger.info('CommunityData', '纵向模式事件数据已清除');
|
||||
} else if (type === 'fourRowEvents') {
|
||||
// fourRowEvents 不使用 localStorage,只清除 Redux state
|
||||
state.fourRowEvents = [];
|
||||
state.fourRowTotal = 0;
|
||||
state.fourRowPagination = { total: 0, total_pages: 0, current_page: 1, per_page: 30 };
|
||||
logger.info('CommunityData', '平铺模式事件数据已清除');
|
||||
}
|
||||
},
|
||||
@@ -438,7 +461,7 @@ const communityDataSlice = createSlice({
|
||||
state.error[stateKey] = null;
|
||||
})
|
||||
.addCase(fetchDynamicNews.fulfilled, (state, action) => {
|
||||
const { mode, events, total, page, clearCache, prependMode, isEmpty } = action.payload;
|
||||
const { mode, events, total, page, per_page, clearCache, prependMode, isEmpty } = action.payload;
|
||||
const stateKey = mode === 'four-row' ? 'fourRowEvents' : 'verticalEvents';
|
||||
const totalKey = mode === 'four-row' ? 'fourRowTotal' : 'verticalTotal';
|
||||
|
||||
@@ -459,63 +482,103 @@ const communityDataSlice = createSlice({
|
||||
page,
|
||||
clearCache,
|
||||
prependMode,
|
||||
'state[stateKey] 之前': state[stateKey].length,
|
||||
'state[stateKey] 类型': Array.isArray(state[stateKey]) ? 'Array' : 'Object',
|
||||
'state[stateKey] 之前': Array.isArray(state[stateKey])
|
||||
? `数组长度: ${state[stateKey].length}`
|
||||
: `对象页数: ${Object.keys(state[stateKey] || {}).length}`,
|
||||
});
|
||||
|
||||
/**
|
||||
* 【数据去重和追加逻辑】
|
||||
* 【数据存储逻辑】根据模式选择不同的存储策略
|
||||
*
|
||||
* 三种模式:
|
||||
* 1. clearCache 模式:直接替换(用于刷新或模式切换)
|
||||
* 2. prependMode 模式:去重后插入头部(用于定时刷新,获取最新事件)
|
||||
* 3. append 模式(默认):去重后追加到末尾(用于无限滚动加载下一页)
|
||||
* 纵向模式(vertical):页码映射存储
|
||||
* - clearCache=true: 清空所有页,存储新页(第1页专用)
|
||||
* - clearCache=false: 存储到对应页码(第2、3、4...页)
|
||||
* - 优点:每页独立,不受去重影响,支持缓存
|
||||
*
|
||||
* 去重逻辑(append 和 prepend 模式):
|
||||
* - 使用 Set 提取已存在的事件 ID
|
||||
* - 过滤掉新数据中与现有数据重复的事件
|
||||
* - 只保留真正的新事件
|
||||
*
|
||||
* 为什么需要去重:
|
||||
* 1. 网络请求乱序:例如第3页比第2页先返回
|
||||
* 2. 定时刷新冲突:用户正在浏览时后台刷新了第一页
|
||||
* 3. 后端分页漂移:新事件插入导致页码边界变化
|
||||
* 平铺模式(four-row):去重追加存储
|
||||
* - clearCache=true: 直接替换(用于刷新)
|
||||
* - prependMode=true: 去重后插入头部(定时刷新)
|
||||
* - 默认:去重后追加到末尾(无限滚动)
|
||||
* - 优点:累积显示,支持虚拟滚动
|
||||
*/
|
||||
if (clearCache) {
|
||||
// 清空缓存模式:直接替换
|
||||
state[stateKey] = events;
|
||||
logger.debug('CommunityData', `清空缓存并加载新数据 (${mode})`, {
|
||||
count: events.length
|
||||
});
|
||||
if (mode === 'vertical') {
|
||||
// 【纵向模式】页码映射存储
|
||||
if (clearCache) {
|
||||
// 第1页:清空所有页,只保留新页
|
||||
state.verticalEventsByPage = { [page]: events };
|
||||
logger.debug('CommunityData', `清空缓存并加载第${page}页 (纵向模式)`, {
|
||||
count: events.length
|
||||
});
|
||||
console.log('%c[Redux] 纵向模式 clearCache,清空所有页', 'color: #10B981; font-weight: bold;', {
|
||||
page,
|
||||
eventsCount: events.length
|
||||
});
|
||||
} else {
|
||||
// 其他页:存储到对应页码
|
||||
state.verticalEventsByPage = state.verticalEventsByPage || {};
|
||||
state.verticalEventsByPage[page] = events;
|
||||
logger.debug('CommunityData', `存储第${page}页数据 (纵向模式)`, {
|
||||
page,
|
||||
count: events.length,
|
||||
totalPages: Object.keys(state.verticalEventsByPage || {}).length
|
||||
});
|
||||
console.log('%c[Redux] 纵向模式追加页面', 'color: #10B981; font-weight: bold;', {
|
||||
page,
|
||||
eventsCount: events.length,
|
||||
cachedPages: Object.keys(state.verticalEventsByPage || {})
|
||||
});
|
||||
}
|
||||
} else if (mode === 'four-row') {
|
||||
// 【平铺模式】去重追加存储
|
||||
if (clearCache) {
|
||||
// 清空缓存模式:直接替换
|
||||
state.fourRowEvents = events;
|
||||
logger.debug('CommunityData', `清空缓存并加载新数据 (平铺模式)`, {
|
||||
count: events.length
|
||||
});
|
||||
console.log('%c[Redux] 平铺模式 clearCache,直接替换数据', 'color: #10B981; font-weight: bold;', {
|
||||
eventsCount: events.length
|
||||
});
|
||||
} else if (prependMode) {
|
||||
// 追加到头部模式(用于定时刷新):去重后插入头部
|
||||
const existingIds = new Set((state.fourRowEvents || []).map(e => e.id));
|
||||
const newEvents = events.filter(e => !existingIds.has(e.id));
|
||||
state.fourRowEvents = [...newEvents, ...(state.fourRowEvents || [])];
|
||||
logger.debug('CommunityData', `追加新数据到头部 (平铺模式)`, {
|
||||
newCount: newEvents.length,
|
||||
totalCount: state.fourRowEvents.length
|
||||
});
|
||||
} else {
|
||||
// 默认追加模式:去重后追加到末尾(用于虚拟滚动加载下一页)
|
||||
const existingIds = new Set((state.fourRowEvents || []).map(e => e.id));
|
||||
const newEvents = events.filter(e => !existingIds.has(e.id));
|
||||
state.fourRowEvents = [...(state.fourRowEvents || []), ...newEvents];
|
||||
|
||||
// 🔍 调试:清空缓存后的状态
|
||||
console.log('%c[Redux] clearCache 模式,直接替换数据', 'color: #10B981; font-weight: bold;', {
|
||||
'state[stateKey] 之后': state[stateKey].length
|
||||
});
|
||||
} else if (prependMode) {
|
||||
// 追加到头部模式(用于定时刷新):去重后插入头部
|
||||
const existingIds = new Set(state[stateKey].map(e => e.id));
|
||||
const newEvents = events.filter(e => !existingIds.has(e.id));
|
||||
state[stateKey] = [...newEvents, ...state[stateKey]];
|
||||
logger.debug('CommunityData', `追加新数据到头部 (${mode})`, {
|
||||
newCount: newEvents.length,
|
||||
totalCount: state[stateKey].length
|
||||
});
|
||||
} else {
|
||||
// 默认追加模式:去重后追加到末尾(用于虚拟滚动加载下一页)
|
||||
const existingIds = new Set(state[stateKey].map(e => e.id));
|
||||
const newEvents = events.filter(e => !existingIds.has(e.id));
|
||||
state[stateKey] = [...state[stateKey], ...newEvents];
|
||||
|
||||
logger.debug('CommunityData', `追加新数据(去重,${mode})`, {
|
||||
page,
|
||||
originalEventsCount: events.length,
|
||||
newEventsCount: newEvents.length,
|
||||
filteredCount: events.length - newEvents.length,
|
||||
totalCount: state[stateKey].length
|
||||
});
|
||||
logger.debug('CommunityData', `追加新数据(去重,平铺模式)`, {
|
||||
page,
|
||||
originalEventsCount: events.length,
|
||||
newEventsCount: newEvents.length,
|
||||
filteredCount: events.length - newEvents.length,
|
||||
totalCount: state.fourRowEvents.length
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
state[totalKey] = total;
|
||||
// 【元数据存储】存储完整的 pagination 对象
|
||||
const paginationKey = mode === 'four-row' ? 'fourRowPagination' : 'verticalPagination';
|
||||
const finalPerPage = per_page || (mode === 'four-row' ? 30 : 10); // 兜底默认值
|
||||
state[paginationKey] = {
|
||||
total: total,
|
||||
total_pages: action.payload.totalPages || Math.ceil(total / finalPerPage),
|
||||
current_page: page,
|
||||
per_page: finalPerPage
|
||||
};
|
||||
|
||||
console.log('%c[Redux] 更新分页元数据', 'color: #8B5CF6; font-weight: bold;', {
|
||||
mode,
|
||||
pagination: state[paginationKey]
|
||||
});
|
||||
|
||||
state.loading[stateKey] = false;
|
||||
})
|
||||
@@ -550,14 +613,18 @@ export const selectLoading = (state) => state.communityData.loading;
|
||||
export const selectError = (state) => state.communityData.error;
|
||||
|
||||
// 纵向模式数据选择器
|
||||
export const selectVerticalEvents = (state) => state.communityData.verticalEvents;
|
||||
export const selectVerticalTotal = (state) => state.communityData.verticalTotal;
|
||||
export const selectVerticalCachedCount = (state) => state.communityData.verticalEvents.length;
|
||||
export const selectVerticalEventsByPage = (state) => state.communityData.verticalEventsByPage;
|
||||
export const selectVerticalPagination = (state) => state.communityData.verticalPagination;
|
||||
export const selectVerticalCachedPageCount = (state) => Object.keys(state.communityData.verticalEventsByPage || {}).length;
|
||||
|
||||
// 平铺模式数据选择器
|
||||
export const selectFourRowEvents = (state) => state.communityData.fourRowEvents;
|
||||
export const selectFourRowTotal = (state) => state.communityData.fourRowTotal;
|
||||
export const selectFourRowCachedCount = (state) => state.communityData.fourRowEvents.length;
|
||||
export const selectFourRowPagination = (state) => state.communityData.fourRowPagination;
|
||||
export const selectFourRowCachedCount = (state) => (state.communityData.fourRowEvents || []).length;
|
||||
|
||||
// 向后兼容的选择器(已废弃,建议使用 selectVerticalPagination.total)
|
||||
export const selectVerticalTotal = (state) => state.communityData.verticalPagination?.total || 0;
|
||||
export const selectFourRowTotal = (state) => state.communityData.fourRowPagination?.total || 0;
|
||||
|
||||
// 组合选择器
|
||||
export const selectPopularKeywordsWithLoading = (state) => ({
|
||||
@@ -574,11 +641,12 @@ export const selectHotEventsWithLoading = (state) => ({
|
||||
|
||||
// 纵向模式数据 + 加载状态选择器
|
||||
export const selectVerticalEventsWithLoading = (state) => ({
|
||||
data: state.communityData.verticalEvents, // 完整缓存列表
|
||||
data: state.communityData.verticalEventsByPage, // 页码映射 { 1: [...], 2: [...] }
|
||||
loading: state.communityData.loading.verticalEvents,
|
||||
error: state.communityData.error.verticalEvents,
|
||||
total: state.communityData.verticalTotal, // 服务端总数量
|
||||
cachedCount: state.communityData.verticalEvents.length // 已缓存有效数量
|
||||
pagination: state.communityData.verticalPagination, // 完整分页元数据 { total, total_pages, current_page, per_page }
|
||||
total: state.communityData.verticalPagination?.total || 0, // 向后兼容:服务端总数量
|
||||
cachedPageCount: Object.keys(state.communityData.verticalEventsByPage || {}).length // 已缓存页数
|
||||
});
|
||||
|
||||
// 平铺模式数据 + 加载状态选择器
|
||||
@@ -586,8 +654,9 @@ export const selectFourRowEventsWithLoading = (state) => ({
|
||||
data: state.communityData.fourRowEvents, // 完整缓存列表
|
||||
loading: state.communityData.loading.fourRowEvents,
|
||||
error: state.communityData.error.fourRowEvents,
|
||||
total: state.communityData.fourRowTotal, // 服务端总数量
|
||||
cachedCount: state.communityData.fourRowEvents.length // 已缓存有效数量
|
||||
pagination: state.communityData.fourRowPagination, // 完整分页元数据 { total, total_pages, current_page, per_page }
|
||||
total: state.communityData.fourRowPagination?.total || 0, // 向后兼容:服务端总数量
|
||||
cachedCount: (state.communityData.fourRowEvents || []).length // 已缓存有效数量
|
||||
});
|
||||
|
||||
export default communityDataSlice.reducer;
|
||||
|
||||
Reference in New Issue
Block a user