refactor(post): consolidate post sync to PostSyncService and remove ProcessPostUseCase
- Replace ProcessPostUseCase with new PostSyncService in src/services/post/ - Add postListStore for state management in src/stores/post/ - Remove deprecated ProcessPostUseCase and ProcessMessageUseCase files - Delete architecture documentation files (ARCHITECTURE_REFACTOR_PLAN.md, architecture-comparison-report.md) - Update all consumers (HomeScreen, PostDetailScreen, SearchScreen, useUserProfile, useDifferentialPosts) - Simplify postService to only contain API layer methods - Remove unused type exports from message stores BREAKING CHANGE: ProcessPostUseCase and ProcessMessageUseCase have been removed. Use postSyncService for post operations instead.
This commit is contained in:
346
src/services/post/PostSyncService.ts
Normal file
346
src/services/post/PostSyncService.ts
Normal file
@@ -0,0 +1,346 @@
|
||||
import { postRepository } from '../../data/repositories/PostRepository';
|
||||
import type { Post } from '../../core/entities/Post';
|
||||
import type {
|
||||
GetPostsParams,
|
||||
CreatePostData,
|
||||
UpdatePostData,
|
||||
PostsResult,
|
||||
SearchPostsParams,
|
||||
} from '../../data/repositories/interfaces/IPostRepository';
|
||||
import { PaginationStateManager } from '../../infrastructure/pagination/PaginationStateManager';
|
||||
import type { PaginationState } from '../../infrastructure/pagination/types';
|
||||
import { usePostListStore, type PostsState } from '../../stores/post/postListStore';
|
||||
import { mergePostListWithFastPath, mergeRefreshWindow } from './postMerge';
|
||||
|
||||
export type { PostsState } from '../../stores/post/postListStore';
|
||||
|
||||
export interface PostDetailState {
|
||||
post: Post | null;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
const MERGE_PERF_WARN_THRESHOLD_MS = 12;
|
||||
|
||||
class PostSyncService {
|
||||
private static instance: PostSyncService;
|
||||
private paginationManager: PaginationStateManager;
|
||||
private currentUserId: string | null = null;
|
||||
|
||||
private constructor() {
|
||||
this.paginationManager = new PaginationStateManager();
|
||||
}
|
||||
|
||||
static getInstance(): PostSyncService {
|
||||
if (!PostSyncService.instance) {
|
||||
PostSyncService.instance = new PostSyncService();
|
||||
}
|
||||
return PostSyncService.instance;
|
||||
}
|
||||
|
||||
initialize(currentUserId: string | null = null): void {
|
||||
this.currentUserId = currentUserId;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
usePostListStore.getState().clearAllStates();
|
||||
this.paginationManager.clear();
|
||||
this.currentUserId = null;
|
||||
}
|
||||
|
||||
getPostsState(key: string = 'default'): PostsState {
|
||||
return usePostListStore.getState().getPostsState(key);
|
||||
}
|
||||
|
||||
getPostDetailState(postId: string): PostDetailState {
|
||||
return usePostListStore.getState().getPostDetailState(postId);
|
||||
}
|
||||
|
||||
applyShareCountUpdate(postId: string, sharesCount: number): void {
|
||||
usePostListStore.getState().applyShareCountUpdate(postId, sharesCount);
|
||||
}
|
||||
|
||||
async fetchPosts(params?: GetPostsParams, key: string = 'default'): Promise<PostsResult> {
|
||||
const store = usePostListStore.getState();
|
||||
store.updatePostsState(key, { isLoading: true, error: null });
|
||||
|
||||
try {
|
||||
const queryParams: GetPostsParams = {
|
||||
pageSize: params?.pageSize || DEFAULT_PAGE_SIZE,
|
||||
...params,
|
||||
cursor: params?.cursor !== undefined ? params.cursor : '',
|
||||
};
|
||||
|
||||
const result = await postRepository.getPosts(queryParams);
|
||||
const isCursorMode = result.nextCursor !== undefined && result.nextCursor !== null;
|
||||
|
||||
const currentState = usePostListStore.getState().getPostsState(key);
|
||||
const incomingPosts = Array.isArray(result.posts) ? result.posts : [];
|
||||
const mergeStart = Date.now();
|
||||
const mergedPosts = mergeRefreshWindow(currentState.posts, incomingPosts);
|
||||
const mergeCost = Date.now() - mergeStart;
|
||||
if (mergeCost >= MERGE_PERF_WARN_THRESHOLD_MS) {
|
||||
console.log('[PostPerf] fetchPosts merge cost', { key, costMs: mergeCost, prev: currentState.posts.length, next: incomingPosts.length });
|
||||
}
|
||||
|
||||
usePostListStore.getState().updatePostsState(key, {
|
||||
posts: mergedPosts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: isCursorMode ? result.nextCursor : null,
|
||||
currentPage: isCursorMode ? undefined : 1,
|
||||
isLoading: false,
|
||||
lastLoadTime: Date.now(),
|
||||
lastParams: params,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : '获取帖子列表失败';
|
||||
usePostListStore.getState().updatePostsState(key, { isLoading: false, error: errorMessage });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async refreshPosts(key: string = 'default'): Promise<PostsResult> {
|
||||
const state = usePostListStore.getState().getPostsState(key);
|
||||
usePostListStore.getState().updatePostsState(key, { isRefreshing: true, error: null });
|
||||
|
||||
try {
|
||||
const result = await postRepository.getPosts({
|
||||
pageSize: DEFAULT_PAGE_SIZE,
|
||||
...state.lastParams,
|
||||
cursor: state.lastParams?.cursor !== undefined ? state.lastParams.cursor : '',
|
||||
});
|
||||
|
||||
const isCursorMode = result.nextCursor !== undefined && result.nextCursor !== null;
|
||||
const incomingPosts = Array.isArray(result.posts) ? result.posts : [];
|
||||
const mergeStart = Date.now();
|
||||
const mergedPosts = mergeRefreshWindow(state.posts, incomingPosts);
|
||||
const mergeCost = Date.now() - mergeStart;
|
||||
if (mergeCost >= MERGE_PERF_WARN_THRESHOLD_MS) {
|
||||
console.log('[PostPerf] refreshPosts merge cost', { key, costMs: mergeCost, prev: state.posts.length, next: incomingPosts.length });
|
||||
}
|
||||
|
||||
usePostListStore.getState().updatePostsState(key, {
|
||||
posts: mergedPosts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: isCursorMode ? result.nextCursor : null,
|
||||
currentPage: isCursorMode ? undefined : 1,
|
||||
isRefreshing: false,
|
||||
lastLoadTime: Date.now(),
|
||||
});
|
||||
|
||||
this.paginationManager.reset(key);
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : '刷新帖子列表失败';
|
||||
usePostListStore.getState().updatePostsState(key, { isRefreshing: false, error: errorMessage });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async loadMorePosts(key: string = 'default'): Promise<PostsResult> {
|
||||
const state = usePostListStore.getState().getPostsState(key);
|
||||
|
||||
if (!state.hasMore) return { posts: [], hasMore: false };
|
||||
if (state.isLoading) return { posts: [], hasMore: state.hasMore };
|
||||
|
||||
usePostListStore.getState().updatePostsState(key, { isLoading: true, error: null });
|
||||
|
||||
try {
|
||||
const useCursorMode = state.cursor !== null && state.cursor !== undefined;
|
||||
const base = { ...(state.lastParams ?? {}) } as GetPostsParams;
|
||||
let queryParams: GetPostsParams;
|
||||
|
||||
if (useCursorMode) {
|
||||
delete base.page;
|
||||
queryParams = { ...base, pageSize: DEFAULT_PAGE_SIZE, cursor: state.cursor! };
|
||||
} else {
|
||||
const nextPage = (state.currentPage || 1) + 1;
|
||||
delete base.cursor;
|
||||
queryParams = { ...base, pageSize: DEFAULT_PAGE_SIZE, page: nextPage };
|
||||
}
|
||||
|
||||
const result = await postRepository.getPosts(queryParams);
|
||||
const incomingPosts = Array.isArray(result.posts) ? result.posts : [];
|
||||
const mergeStart = Date.now();
|
||||
const newPosts = mergePostListWithFastPath(state.posts, incomingPosts);
|
||||
const mergeCost = Date.now() - mergeStart;
|
||||
if (mergeCost >= MERGE_PERF_WARN_THRESHOLD_MS) {
|
||||
console.log('[PostPerf] loadMore merge cost', { key, costMs: mergeCost, prev: state.posts.length, incoming: incomingPosts.length, merged: newPosts.length });
|
||||
}
|
||||
|
||||
const responseIsCursorMode = result.nextCursor !== undefined && result.nextCursor !== null;
|
||||
|
||||
usePostListStore.getState().updatePostsState(key, {
|
||||
posts: newPosts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: responseIsCursorMode ? result.nextCursor : null,
|
||||
currentPage: useCursorMode ? state.currentPage : ((state.currentPage || 1) + 1),
|
||||
isLoading: false,
|
||||
lastLoadTime: Date.now(),
|
||||
});
|
||||
|
||||
return { posts: incomingPosts, hasMore: result.hasMore, nextCursor: result.nextCursor };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : '加载更多帖子失败';
|
||||
console.error('[PostSyncService] Load more error:', errorMessage);
|
||||
usePostListStore.getState().updatePostsState(key, { isLoading: false, error: errorMessage });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchPostById(id: string): Promise<Post | null> {
|
||||
usePostListStore.getState().updatePostDetailState(id, { isLoading: true, error: null });
|
||||
|
||||
try {
|
||||
const post = await postRepository.getPostById(id);
|
||||
usePostListStore.getState().updatePostDetailState(id, { post, isLoading: false });
|
||||
return post;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : '获取帖子详情失败';
|
||||
usePostListStore.getState().updatePostDetailState(id, { isLoading: false, error: errorMessage });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async createPost(data: CreatePostData): Promise<Post> {
|
||||
const post = await postRepository.createPost(data);
|
||||
const store = usePostListStore.getState();
|
||||
const newMap = new Map(store.postsStateMap);
|
||||
for (const [key, postsState] of newMap) {
|
||||
newMap.set(key, { ...postsState, posts: [post, ...postsState.posts] });
|
||||
}
|
||||
usePostListStore.setState({ postsStateMap: newMap });
|
||||
return post;
|
||||
}
|
||||
|
||||
async updatePost(id: string, data: UpdatePostData): Promise<Post> {
|
||||
const post = await postRepository.updatePost(id, data);
|
||||
usePostListStore.getState().updatePostDetailState(id, { post });
|
||||
usePostListStore.getState().updatePostInLists(id, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
async deletePost(id: string): Promise<void> {
|
||||
await postRepository.deletePost(id);
|
||||
usePostListStore.getState().clearPostDetailState(id);
|
||||
usePostListStore.getState().removePostFromLists(id);
|
||||
}
|
||||
|
||||
async likePost(id: string): Promise<Post> {
|
||||
const post = await postRepository.likePost(id);
|
||||
usePostListStore.getState().updatePostDetailState(id, { post });
|
||||
usePostListStore.getState().updatePostInLists(id, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
async unlikePost(id: string): Promise<Post> {
|
||||
const post = await postRepository.unlikePost(id);
|
||||
usePostListStore.getState().updatePostDetailState(id, { post });
|
||||
usePostListStore.getState().updatePostInLists(id, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
async favoritePost(id: string): Promise<Post> {
|
||||
const post = await postRepository.favoritePost(id);
|
||||
usePostListStore.getState().updatePostDetailState(id, { post });
|
||||
usePostListStore.getState().updatePostInLists(id, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
async unfavoritePost(id: string): Promise<Post> {
|
||||
const post = await postRepository.unfavoritePost(id);
|
||||
usePostListStore.getState().updatePostDetailState(id, { post });
|
||||
usePostListStore.getState().updatePostInLists(id, post);
|
||||
return post;
|
||||
}
|
||||
|
||||
async searchPosts(params: SearchPostsParams, key: string = 'search'): Promise<PostsResult> {
|
||||
usePostListStore.getState().updatePostsState(key, { isLoading: true, error: null });
|
||||
|
||||
try {
|
||||
const result = await postRepository.searchPosts(params);
|
||||
usePostListStore.getState().updatePostsState(key, {
|
||||
posts: result.posts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: result.nextCursor || null,
|
||||
isLoading: false,
|
||||
lastLoadTime: Date.now(),
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : '搜索帖子失败';
|
||||
usePostListStore.getState().updatePostsState(key, { isLoading: false, error: errorMessage });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchPostsByUser(userId: string, params?: GetPostsParams, key?: string): Promise<PostsResult> {
|
||||
const listKey = key || `user_${userId}`;
|
||||
usePostListStore.getState().updatePostsState(listKey, { isLoading: true, error: null });
|
||||
|
||||
try {
|
||||
const result = await postRepository.getPostsByUser(userId, params);
|
||||
usePostListStore.getState().updatePostsState(listKey, {
|
||||
posts: result.posts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: result.nextCursor || null,
|
||||
isLoading: false,
|
||||
lastLoadTime: Date.now(),
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : '获取用户帖子失败';
|
||||
usePostListStore.getState().updatePostsState(listKey, { isLoading: false, error: errorMessage });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
clearPostsState(key: string = 'default'): void {
|
||||
usePostListStore.getState().clearPostsState(key);
|
||||
this.paginationManager.reset(key);
|
||||
}
|
||||
|
||||
clearPostDetailState(postId: string): void {
|
||||
usePostListStore.getState().clearPostDetailState(postId);
|
||||
}
|
||||
|
||||
clearAllStates(): void {
|
||||
usePostListStore.getState().clearAllStates();
|
||||
this.paginationManager.clear();
|
||||
}
|
||||
|
||||
getCurrentUserId(): string | null {
|
||||
return this.currentUserId;
|
||||
}
|
||||
|
||||
setCurrentUserId(userId: string | null): void {
|
||||
this.currentUserId = userId;
|
||||
}
|
||||
|
||||
getPaginationState(key: string): PaginationState {
|
||||
return this.paginationManager.getState(key);
|
||||
}
|
||||
|
||||
resetPagination(key: string): void {
|
||||
this.paginationManager.reset(key);
|
||||
}
|
||||
|
||||
isLoading(key: string = 'default'): boolean {
|
||||
return usePostListStore.getState().getPostsState(key).isLoading;
|
||||
}
|
||||
|
||||
hasError(key: string = 'default'): boolean {
|
||||
return usePostListStore.getState().getPostsState(key).error !== null;
|
||||
}
|
||||
|
||||
getError(key: string = 'default'): string | null {
|
||||
return usePostListStore.getState().getPostsState(key).error;
|
||||
}
|
||||
}
|
||||
|
||||
export const postSyncService = PostSyncService.getInstance();
|
||||
export { PostSyncService };
|
||||
export default postSyncService;
|
||||
Reference in New Issue
Block a user