232 lines
6.9 KiB
TypeScript
232 lines
6.9 KiB
TypeScript
|
|
/**
|
||
|
|
* 帖子状态 Zustand Store
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { create } from 'zustand';
|
||
|
|
import type { Post } from '../../types';
|
||
|
|
import { CacheEntry, createCacheEntry, isCacheExpired, DEFAULT_TTL } from '../utils/cache';
|
||
|
|
|
||
|
|
// ==================== 状态接口 ====================
|
||
|
|
|
||
|
|
export interface PostManagerState {
|
||
|
|
// 帖子列表缓存 (key: list:type:page:pageSize)
|
||
|
|
listsMap: Map<string, CacheEntry<Post[]>>;
|
||
|
|
|
||
|
|
// 帖子详情缓存
|
||
|
|
detailsMap: Map<string, CacheEntry<Post | null>>;
|
||
|
|
|
||
|
|
// 加载状态
|
||
|
|
loadingListKeys: Set<string>;
|
||
|
|
loadingDetailIds: Set<string>;
|
||
|
|
|
||
|
|
// 待处理请求(去重)
|
||
|
|
pendingRequests: Map<string, Promise<any>>;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface PostManagerActions {
|
||
|
|
// 获取状态
|
||
|
|
getPostsList: (type: string, page: number, pageSize: number) => Post[] | null;
|
||
|
|
getPostDetail: (postId: string) => Post | null;
|
||
|
|
isListExpired: (type: string, page: number, pageSize: number) => boolean;
|
||
|
|
isDetailExpired: (postId: string) => boolean;
|
||
|
|
isLoadingList: (type: string, page: number, pageSize: number) => boolean;
|
||
|
|
isLoadingDetail: (postId: string) => boolean;
|
||
|
|
|
||
|
|
// 设置状态
|
||
|
|
setPostsList: (type: string, page: number, pageSize: number, posts: Post[]) => void;
|
||
|
|
setPostDetail: (postId: string, post: Post | null) => void;
|
||
|
|
setLoadingList: (type: string, page: number, pageSize: number, loading: boolean) => void;
|
||
|
|
setLoadingDetail: (postId: string, loading: boolean) => void;
|
||
|
|
|
||
|
|
// 缓存管理
|
||
|
|
invalidateList: (type?: string, page?: number, pageSize?: number) => void;
|
||
|
|
invalidateDetail: (postId: string) => void;
|
||
|
|
invalidateAll: () => void;
|
||
|
|
|
||
|
|
// 请求去重
|
||
|
|
getPendingRequest: <T>(key: string) => Promise<T> | undefined;
|
||
|
|
setPendingRequest: <T>(key: string, request: Promise<T>) => void;
|
||
|
|
deletePendingRequest: (key: string) => void;
|
||
|
|
clearPendingRequests: () => void;
|
||
|
|
|
||
|
|
// 重置
|
||
|
|
reset: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==================== 辅助函数 ====================
|
||
|
|
|
||
|
|
function buildListKey(type: string, page: number, pageSize: number): string {
|
||
|
|
return `list:${type}:${page}:${pageSize}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==================== 初始状态 ====================
|
||
|
|
|
||
|
|
const initialState: PostManagerState = {
|
||
|
|
listsMap: new Map(),
|
||
|
|
detailsMap: new Map(),
|
||
|
|
loadingListKeys: new Set(),
|
||
|
|
loadingDetailIds: new Set(),
|
||
|
|
pendingRequests: new Map(),
|
||
|
|
};
|
||
|
|
|
||
|
|
// ==================== Store 创建 ====================
|
||
|
|
|
||
|
|
export type PostManagerStore = PostManagerState & PostManagerActions;
|
||
|
|
|
||
|
|
export const usePostManagerStore = create<PostManagerStore>((set, get) => ({
|
||
|
|
// ==================== 初始状态 ====================
|
||
|
|
...initialState,
|
||
|
|
|
||
|
|
// ==================== 获取状态 ====================
|
||
|
|
|
||
|
|
getPostsList: (type: string, page: number, pageSize: number) => {
|
||
|
|
const key = buildListKey(type, page, pageSize);
|
||
|
|
const entry = get().listsMap.get(key);
|
||
|
|
if (!entry) return null;
|
||
|
|
if (isCacheExpired(entry)) return null;
|
||
|
|
return entry.data;
|
||
|
|
},
|
||
|
|
|
||
|
|
getPostDetail: (postId: string) => {
|
||
|
|
const entry = get().detailsMap.get(postId);
|
||
|
|
if (!entry) return null;
|
||
|
|
if (isCacheExpired(entry)) return null;
|
||
|
|
return entry.data;
|
||
|
|
},
|
||
|
|
|
||
|
|
isListExpired: (type: string, page: number, pageSize: number) => {
|
||
|
|
const key = buildListKey(type, page, pageSize);
|
||
|
|
const entry = get().listsMap.get(key);
|
||
|
|
if (!entry) return true;
|
||
|
|
return isCacheExpired(entry);
|
||
|
|
},
|
||
|
|
|
||
|
|
isDetailExpired: (postId: string) => {
|
||
|
|
const entry = get().detailsMap.get(postId);
|
||
|
|
if (!entry) return true;
|
||
|
|
return isCacheExpired(entry);
|
||
|
|
},
|
||
|
|
|
||
|
|
isLoadingList: (type: string, page: number, pageSize: number) => {
|
||
|
|
const key = buildListKey(type, page, pageSize);
|
||
|
|
return get().loadingListKeys.has(key);
|
||
|
|
},
|
||
|
|
|
||
|
|
isLoadingDetail: (postId: string) => {
|
||
|
|
return get().loadingDetailIds.has(postId);
|
||
|
|
},
|
||
|
|
|
||
|
|
// ==================== 设置状态 ====================
|
||
|
|
|
||
|
|
setPostsList: (type: string, page: number, pageSize: number, posts: Post[]) => {
|
||
|
|
const key = buildListKey(type, page, pageSize);
|
||
|
|
set(state => {
|
||
|
|
const newListsMap = new Map(state.listsMap);
|
||
|
|
newListsMap.set(key, createCacheEntry(posts, DEFAULT_TTL.POST_LIST));
|
||
|
|
return { listsMap: newListsMap };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
setPostDetail: (postId: string, post: Post | null) => {
|
||
|
|
set(state => {
|
||
|
|
const newDetailsMap = new Map(state.detailsMap);
|
||
|
|
newDetailsMap.set(postId, createCacheEntry(post, DEFAULT_TTL.POST_DETAIL));
|
||
|
|
return { detailsMap: newDetailsMap };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
setLoadingList: (type: string, page: number, pageSize: number, loading: boolean) => {
|
||
|
|
const key = buildListKey(type, page, pageSize);
|
||
|
|
set(state => {
|
||
|
|
const newLoadingListKeys = new Set(state.loadingListKeys);
|
||
|
|
if (loading) {
|
||
|
|
newLoadingListKeys.add(key);
|
||
|
|
} else {
|
||
|
|
newLoadingListKeys.delete(key);
|
||
|
|
}
|
||
|
|
return { loadingListKeys: newLoadingListKeys };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
setLoadingDetail: (postId: string, loading: boolean) => {
|
||
|
|
set(state => {
|
||
|
|
const newLoadingDetailIds = new Set(state.loadingDetailIds);
|
||
|
|
if (loading) {
|
||
|
|
newLoadingDetailIds.add(postId);
|
||
|
|
} else {
|
||
|
|
newLoadingDetailIds.delete(postId);
|
||
|
|
}
|
||
|
|
return { loadingDetailIds: newLoadingDetailIds };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// ==================== 缓存管理 ====================
|
||
|
|
|
||
|
|
invalidateList: (type?: string, page?: number, pageSize?: number) => {
|
||
|
|
if (type !== undefined && page !== undefined && pageSize !== undefined) {
|
||
|
|
const key = buildListKey(type, page, pageSize);
|
||
|
|
set(state => {
|
||
|
|
const newListsMap = new Map(state.listsMap);
|
||
|
|
newListsMap.delete(key);
|
||
|
|
return { listsMap: newListsMap };
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
set({ listsMap: new Map() });
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
invalidateDetail: (postId: string) => {
|
||
|
|
set(state => {
|
||
|
|
const newDetailsMap = new Map(state.detailsMap);
|
||
|
|
newDetailsMap.delete(postId);
|
||
|
|
return { detailsMap: newDetailsMap };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
invalidateAll: () => {
|
||
|
|
set({
|
||
|
|
listsMap: new Map(),
|
||
|
|
detailsMap: new Map(),
|
||
|
|
pendingRequests: new Map(),
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// ==================== 请求去重 ====================
|
||
|
|
|
||
|
|
getPendingRequest: <T>(key: string) => {
|
||
|
|
return get().pendingRequests.get(key) as Promise<T> | undefined;
|
||
|
|
},
|
||
|
|
|
||
|
|
setPendingRequest: <T>(key: string, request: Promise<T>) => {
|
||
|
|
set(state => {
|
||
|
|
const newPendingRequests = new Map(state.pendingRequests);
|
||
|
|
newPendingRequests.set(key, request);
|
||
|
|
return { pendingRequests: newPendingRequests };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
deletePendingRequest: (key: string) => {
|
||
|
|
set(state => {
|
||
|
|
const newPendingRequests = new Map(state.pendingRequests);
|
||
|
|
newPendingRequests.delete(key);
|
||
|
|
return { pendingRequests: newPendingRequests };
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
clearPendingRequests: () => {
|
||
|
|
set({ pendingRequests: new Map() });
|
||
|
|
},
|
||
|
|
|
||
|
|
// ==================== 重置 ====================
|
||
|
|
|
||
|
|
reset: () => {
|
||
|
|
set({
|
||
|
|
...initialState,
|
||
|
|
listsMap: new Map(),
|
||
|
|
detailsMap: new Map(),
|
||
|
|
loadingListKeys: new Set(),
|
||
|
|
loadingDetailIds: new Set(),
|
||
|
|
pendingRequests: new Map(),
|
||
|
|
});
|
||
|
|
},
|
||
|
|
}));
|