863 lines
21 KiB
TypeScript
863 lines
21 KiB
TypeScript
|
|
/**
|
|||
|
|
* ProcessPostUseCase - 处理帖子用例
|
|||
|
|
* 处理帖子的业务逻辑,协调 Repository 和状态管理
|
|||
|
|
* 纯业务逻辑,无UI依赖
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { postRepository } from '../../data/repositories/PostRepository';
|
|||
|
|
import type { Post } from '../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';
|
|||
|
|
|
|||
|
|
// ==================== 事件类型定义 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 帖子用例事件类型
|
|||
|
|
*/
|
|||
|
|
export type PostUseCaseEventType =
|
|||
|
|
| 'posts_loaded'
|
|||
|
|
| 'post_created'
|
|||
|
|
| 'post_updated'
|
|||
|
|
| 'post_deleted'
|
|||
|
|
| 'post_liked'
|
|||
|
|
| 'post_unliked'
|
|||
|
|
| 'post_favorited'
|
|||
|
|
| 'post_unfavorited'
|
|||
|
|
| 'loading_changed'
|
|||
|
|
| 'error_occurred'
|
|||
|
|
| 'state_changed';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 帖子用例事件
|
|||
|
|
*/
|
|||
|
|
export interface PostUseCaseEvent {
|
|||
|
|
type: PostUseCaseEventType;
|
|||
|
|
payload: any;
|
|||
|
|
timestamp: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 事件订阅者类型
|
|||
|
|
*/
|
|||
|
|
export type PostUseCaseSubscriber = (event: PostUseCaseEvent) => void;
|
|||
|
|
|
|||
|
|
// ==================== 状态类型定义 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 帖子列表状态
|
|||
|
|
*/
|
|||
|
|
export interface PostsState {
|
|||
|
|
/** 帖子列表 */
|
|||
|
|
posts: Post[];
|
|||
|
|
/** 是否正在加载 */
|
|||
|
|
isLoading: boolean;
|
|||
|
|
/** 是否正在刷新 */
|
|||
|
|
isRefreshing: boolean;
|
|||
|
|
/** 是否有更多数据 */
|
|||
|
|
hasMore: boolean;
|
|||
|
|
/** 错误信息 */
|
|||
|
|
error: string | null;
|
|||
|
|
/** 当前游标 */
|
|||
|
|
cursor: string | null;
|
|||
|
|
/** 最后加载时间 */
|
|||
|
|
lastLoadTime: number | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 帖子详情状态
|
|||
|
|
*/
|
|||
|
|
export interface PostDetailState {
|
|||
|
|
/** 帖子详情 */
|
|||
|
|
post: Post | null;
|
|||
|
|
/** 是否正在加载 */
|
|||
|
|
isLoading: boolean;
|
|||
|
|
/** 错误信息 */
|
|||
|
|
error: string | null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 默认配置 ====================
|
|||
|
|
|
|||
|
|
const DEFAULT_PAGE_SIZE = 20;
|
|||
|
|
|
|||
|
|
// ==================== ProcessPostUseCase 类 ====================
|
|||
|
|
|
|||
|
|
class ProcessPostUseCase {
|
|||
|
|
// 单例实例
|
|||
|
|
private static instance: ProcessPostUseCase;
|
|||
|
|
|
|||
|
|
// 订阅者管理
|
|||
|
|
private subscribers: Set<PostUseCaseSubscriber> = new Set();
|
|||
|
|
|
|||
|
|
// 状态管理
|
|||
|
|
private postsState: Map<string, PostsState> = new Map();
|
|||
|
|
private postDetailState: Map<string, PostDetailState> = new Map();
|
|||
|
|
|
|||
|
|
// 分页状态管理器
|
|||
|
|
private paginationManager: PaginationStateManager;
|
|||
|
|
|
|||
|
|
// 当前用户ID
|
|||
|
|
private currentUserId: string | null = null;
|
|||
|
|
|
|||
|
|
// 私有构造函数
|
|||
|
|
private constructor() {
|
|||
|
|
this.paginationManager = new PaginationStateManager();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取单例实例
|
|||
|
|
*/
|
|||
|
|
static getInstance(): ProcessPostUseCase {
|
|||
|
|
if (!ProcessPostUseCase.instance) {
|
|||
|
|
ProcessPostUseCase.instance = new ProcessPostUseCase();
|
|||
|
|
}
|
|||
|
|
return ProcessPostUseCase.instance;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 初始化和销毁 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化用例
|
|||
|
|
* @param currentUserId 当前用户ID
|
|||
|
|
*/
|
|||
|
|
initialize(currentUserId: string | null = null): void {
|
|||
|
|
this.currentUserId = currentUserId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 销毁用例
|
|||
|
|
*/
|
|||
|
|
destroy(): void {
|
|||
|
|
this.subscribers.clear();
|
|||
|
|
this.postsState.clear();
|
|||
|
|
this.postDetailState.clear();
|
|||
|
|
this.paginationManager.clear();
|
|||
|
|
this.currentUserId = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 订阅机制 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 订阅事件
|
|||
|
|
* @param subscriber 订阅者函数
|
|||
|
|
* @returns 取消订阅函数
|
|||
|
|
*/
|
|||
|
|
subscribe(subscriber: PostUseCaseSubscriber): () => void {
|
|||
|
|
this.subscribers.add(subscriber);
|
|||
|
|
return () => {
|
|||
|
|
this.subscribers.delete(subscriber);
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 通知所有订阅者
|
|||
|
|
* @param event 事件对象
|
|||
|
|
*/
|
|||
|
|
private notifySubscribers(event: PostUseCaseEvent): void {
|
|||
|
|
this.subscribers.forEach((subscriber) => {
|
|||
|
|
try {
|
|||
|
|
subscriber(event);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[ProcessPostUseCase] 订阅者执行失败:', error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 通知状态变化
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
private notifyStateChanged(key: string): void {
|
|||
|
|
const state = this.getPostsState(key);
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'state_changed',
|
|||
|
|
payload: { key, state },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 状态管理 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取帖子列表状态
|
|||
|
|
* @param key 列表键(默认为 'default')
|
|||
|
|
*/
|
|||
|
|
getPostsState(key: string = 'default'): PostsState {
|
|||
|
|
let state = this.postsState.get(key);
|
|||
|
|
if (!state) {
|
|||
|
|
state = {
|
|||
|
|
posts: [],
|
|||
|
|
isLoading: false,
|
|||
|
|
isRefreshing: false,
|
|||
|
|
hasMore: true,
|
|||
|
|
error: null,
|
|||
|
|
cursor: null,
|
|||
|
|
lastLoadTime: null,
|
|||
|
|
};
|
|||
|
|
this.postsState.set(key, state);
|
|||
|
|
}
|
|||
|
|
return state;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取帖子详情状态
|
|||
|
|
* @param postId 帖子ID
|
|||
|
|
*/
|
|||
|
|
getPostDetailState(postId: string): PostDetailState {
|
|||
|
|
let state = this.postDetailState.get(postId);
|
|||
|
|
if (!state) {
|
|||
|
|
state = {
|
|||
|
|
post: null,
|
|||
|
|
isLoading: false,
|
|||
|
|
error: null,
|
|||
|
|
};
|
|||
|
|
this.postDetailState.set(postId, state);
|
|||
|
|
}
|
|||
|
|
return state;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新帖子列表状态
|
|||
|
|
* @param key 列表键
|
|||
|
|
* @param partialState 部分状态
|
|||
|
|
*/
|
|||
|
|
private updatePostsState(key: string, partialState: Partial<PostsState>): void {
|
|||
|
|
const currentState = this.getPostsState(key);
|
|||
|
|
this.postsState.set(key, {
|
|||
|
|
...currentState,
|
|||
|
|
...partialState,
|
|||
|
|
});
|
|||
|
|
this.notifyStateChanged(key);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新帖子详情状态
|
|||
|
|
* @param postId 帖子ID
|
|||
|
|
* @param partialState 部分状态
|
|||
|
|
*/
|
|||
|
|
private updatePostDetailState(postId: string, partialState: Partial<PostDetailState>): void {
|
|||
|
|
const currentState = this.getPostDetailState(postId);
|
|||
|
|
this.postDetailState.set(postId, {
|
|||
|
|
...currentState,
|
|||
|
|
...partialState,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 帖子列表操作 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取帖子列表
|
|||
|
|
* @param params 查询参数
|
|||
|
|
* @param key 列表键(用于区分不同的列表)
|
|||
|
|
*/
|
|||
|
|
async fetchPosts(params?: GetPostsParams, key: string = 'default'): Promise<PostsResult> {
|
|||
|
|
const state = this.getPostsState(key);
|
|||
|
|
|
|||
|
|
// 更新加载状态
|
|||
|
|
this.updatePostsState(key, { isLoading: true, error: null });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 合并分页参数
|
|||
|
|
const queryParams: GetPostsParams = {
|
|||
|
|
page: params?.page || 1,
|
|||
|
|
pageSize: params?.pageSize || DEFAULT_PAGE_SIZE,
|
|||
|
|
cursor: params?.cursor || state.cursor || undefined,
|
|||
|
|
...params,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const result = await postRepository.getPosts(queryParams);
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
posts: result.posts,
|
|||
|
|
hasMore: result.hasMore,
|
|||
|
|
cursor: result.nextCursor || null,
|
|||
|
|
isLoading: false,
|
|||
|
|
lastLoadTime: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'posts_loaded',
|
|||
|
|
payload: { key, posts: result.posts, hasMore: result.hasMore },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '获取帖子列表失败';
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
isLoading: false,
|
|||
|
|
error: errorMessage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { key, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 刷新帖子列表
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
async refreshPosts(key: string = 'default'): Promise<PostsResult> {
|
|||
|
|
// 更新刷新状态
|
|||
|
|
this.updatePostsState(key, { isRefreshing: true, error: null });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await postRepository.getPosts({
|
|||
|
|
page: 1,
|
|||
|
|
pageSize: DEFAULT_PAGE_SIZE,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
posts: result.posts,
|
|||
|
|
hasMore: result.hasMore,
|
|||
|
|
cursor: result.nextCursor || null,
|
|||
|
|
isRefreshing: false,
|
|||
|
|
lastLoadTime: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 重置分页状态
|
|||
|
|
this.paginationManager.reset(key);
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '刷新帖子列表失败';
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
isRefreshing: false,
|
|||
|
|
error: errorMessage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 加载更多帖子
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
async loadMorePosts(key: string = 'default'): Promise<PostsResult> {
|
|||
|
|
const state = this.getPostsState(key);
|
|||
|
|
|
|||
|
|
// 检查是否还有更多数据
|
|||
|
|
if (!state.hasMore) {
|
|||
|
|
return {
|
|||
|
|
posts: [],
|
|||
|
|
hasMore: false,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否正在加载
|
|||
|
|
if (state.isLoading) {
|
|||
|
|
return {
|
|||
|
|
posts: [],
|
|||
|
|
hasMore: state.hasMore,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新加载状态
|
|||
|
|
this.updatePostsState(key, { isLoading: true, error: null });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const queryParams: GetPostsParams = {
|
|||
|
|
pageSize: DEFAULT_PAGE_SIZE,
|
|||
|
|
cursor: state.cursor || undefined,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const result = await postRepository.getPosts(queryParams);
|
|||
|
|
|
|||
|
|
// 合并新数据
|
|||
|
|
const newPosts = [...state.posts, ...result.posts];
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
posts: newPosts,
|
|||
|
|
hasMore: result.hasMore,
|
|||
|
|
cursor: result.nextCursor || null,
|
|||
|
|
isLoading: false,
|
|||
|
|
lastLoadTime: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
posts: result.posts,
|
|||
|
|
hasMore: result.hasMore,
|
|||
|
|
nextCursor: result.nextCursor,
|
|||
|
|
};
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '加载更多帖子失败';
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
isLoading: false,
|
|||
|
|
error: errorMessage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 帖子详情操作 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取单个帖子详情
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
*/
|
|||
|
|
async fetchPostById(id: string): Promise<Post | null> {
|
|||
|
|
// 更新加载状态
|
|||
|
|
this.updatePostDetailState(id, { isLoading: true, error: null });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.getPostById(id);
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
this.updatePostDetailState(id, {
|
|||
|
|
post,
|
|||
|
|
isLoading: false,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '获取帖子详情失败';
|
|||
|
|
this.updatePostDetailState(id, {
|
|||
|
|
isLoading: false,
|
|||
|
|
error: errorMessage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 帖子CRUD操作 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建帖子
|
|||
|
|
* @param data 创建数据
|
|||
|
|
*/
|
|||
|
|
async createPost(data: CreatePostData): Promise<Post> {
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.createPost(data);
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_created',
|
|||
|
|
payload: { post },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 更新所有列表(新帖子应该出现在列表顶部)
|
|||
|
|
this.postsState.forEach((state, key) => {
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
posts: [post, ...state.posts],
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '创建帖子失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'create', error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新帖子
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
* @param data 更新数据
|
|||
|
|
*/
|
|||
|
|
async updatePost(id: string, data: UpdatePostData): Promise<Post> {
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.updatePost(id, data);
|
|||
|
|
|
|||
|
|
// 更新详情状态
|
|||
|
|
this.updatePostDetailState(id, { post });
|
|||
|
|
|
|||
|
|
// 更新列表中的帖子
|
|||
|
|
this.updatePostInLists(id, post);
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_updated',
|
|||
|
|
payload: { postId: id, post },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '更新帖子失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'update', postId: id, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 删除帖子
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
*/
|
|||
|
|
async deletePost(id: string): Promise<void> {
|
|||
|
|
try {
|
|||
|
|
await postRepository.deletePost(id);
|
|||
|
|
|
|||
|
|
// 从详情状态中移除
|
|||
|
|
this.postDetailState.delete(id);
|
|||
|
|
|
|||
|
|
// 从所有列表中移除
|
|||
|
|
this.postsState.forEach((state, key) => {
|
|||
|
|
const filteredPosts = state.posts.filter((post) => post.id !== id);
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
posts: filteredPosts,
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_deleted',
|
|||
|
|
payload: { postId: id },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '删除帖子失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'delete', postId: id, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 互动操作 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 点赞帖子
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
*/
|
|||
|
|
async likePost(id: string): Promise<Post> {
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.likePost(id);
|
|||
|
|
|
|||
|
|
// 更新详情状态
|
|||
|
|
this.updatePostDetailState(id, { post });
|
|||
|
|
|
|||
|
|
// 更新列表中的帖子
|
|||
|
|
this.updatePostInLists(id, post);
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_liked',
|
|||
|
|
payload: { postId: id, post },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '点赞失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'like', postId: id, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 取消点赞
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
*/
|
|||
|
|
async unlikePost(id: string): Promise<Post> {
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.unlikePost(id);
|
|||
|
|
|
|||
|
|
// 更新详情状态
|
|||
|
|
this.updatePostDetailState(id, { post });
|
|||
|
|
|
|||
|
|
// 更新列表中的帖子
|
|||
|
|
this.updatePostInLists(id, post);
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_unliked',
|
|||
|
|
payload: { postId: id, post },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '取消点赞失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'unlike', postId: id, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 收藏帖子
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
*/
|
|||
|
|
async favoritePost(id: string): Promise<Post> {
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.favoritePost(id);
|
|||
|
|
|
|||
|
|
// 更新详情状态
|
|||
|
|
this.updatePostDetailState(id, { post });
|
|||
|
|
|
|||
|
|
// 更新列表中的帖子
|
|||
|
|
this.updatePostInLists(id, post);
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_favorited',
|
|||
|
|
payload: { postId: id, post },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '收藏失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'favorite', postId: id, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 取消收藏
|
|||
|
|
* @param id 帖子ID
|
|||
|
|
*/
|
|||
|
|
async unfavoritePost(id: string): Promise<Post> {
|
|||
|
|
try {
|
|||
|
|
const post = await postRepository.unfavoritePost(id);
|
|||
|
|
|
|||
|
|
// 更新详情状态
|
|||
|
|
this.updatePostDetailState(id, { post });
|
|||
|
|
|
|||
|
|
// 更新列表中的帖子
|
|||
|
|
this.updatePostInLists(id, post);
|
|||
|
|
|
|||
|
|
// 通知订阅者
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'post_unfavorited',
|
|||
|
|
payload: { postId: id, post },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return post;
|
|||
|
|
} catch (error) {
|
|||
|
|
const errorMessage = error instanceof Error ? error.message : '取消收藏失败';
|
|||
|
|
this.notifySubscribers({
|
|||
|
|
type: 'error_occurred',
|
|||
|
|
payload: { operation: 'unfavorite', postId: id, error: errorMessage },
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
});
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 搜索操作 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 搜索帖子
|
|||
|
|
* @param params 搜索参数
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
async searchPosts(params: SearchPostsParams, key: string = 'search'): Promise<PostsResult> {
|
|||
|
|
// 更新加载状态
|
|||
|
|
this.updatePostsState(key, { isLoading: true, error: null });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await postRepository.searchPosts(params);
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
this.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 : '搜索帖子失败';
|
|||
|
|
this.updatePostsState(key, {
|
|||
|
|
isLoading: false,
|
|||
|
|
error: errorMessage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 用户帖子操作 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取用户发布的帖子
|
|||
|
|
* @param userId 用户ID
|
|||
|
|
* @param params 查询参数
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
async fetchPostsByUser(
|
|||
|
|
userId: string,
|
|||
|
|
params?: GetPostsParams,
|
|||
|
|
key?: string
|
|||
|
|
): Promise<PostsResult> {
|
|||
|
|
const listKey = key || `user_${userId}`;
|
|||
|
|
|
|||
|
|
// 更新加载状态
|
|||
|
|
this.updatePostsState(listKey, { isLoading: true, error: null });
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await postRepository.getPostsByUser(userId, params);
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
this.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 : '获取用户帖子失败';
|
|||
|
|
this.updatePostsState(listKey, {
|
|||
|
|
isLoading: false,
|
|||
|
|
error: errorMessage,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 辅助方法 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新所有列表中的帖子
|
|||
|
|
* @param postId 帖子ID
|
|||
|
|
* @param updatedPost 更新后的帖子
|
|||
|
|
*/
|
|||
|
|
private updatePostInLists(postId: string, updatedPost: Post): void {
|
|||
|
|
this.postsState.forEach((state, key) => {
|
|||
|
|
const index = state.posts.findIndex((post) => post.id === postId);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
const newPosts = [...state.posts];
|
|||
|
|
newPosts[index] = updatedPost;
|
|||
|
|
this.updatePostsState(key, { posts: newPosts });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 清除指定列表的状态
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
clearPostsState(key: string = 'default'): void {
|
|||
|
|
this.postsState.delete(key);
|
|||
|
|
this.paginationManager.reset(key);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 清除帖子详情状态
|
|||
|
|
* @param postId 帖子ID
|
|||
|
|
*/
|
|||
|
|
clearPostDetailState(postId: string): void {
|
|||
|
|
this.postDetailState.delete(postId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 清除所有状态
|
|||
|
|
*/
|
|||
|
|
clearAllStates(): void {
|
|||
|
|
this.postsState.clear();
|
|||
|
|
this.postDetailState.clear();
|
|||
|
|
this.paginationManager.clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取当前用户ID
|
|||
|
|
*/
|
|||
|
|
getCurrentUserId(): string | null {
|
|||
|
|
return this.currentUserId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 设置当前用户ID
|
|||
|
|
*/
|
|||
|
|
setCurrentUserId(userId: string | null): void {
|
|||
|
|
this.currentUserId = userId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 分页状态管理集成 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取分页状态
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
getPaginationState(key: string): PaginationState {
|
|||
|
|
return this.paginationManager.getState(key);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重置分页状态
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
resetPagination(key: string): void {
|
|||
|
|
this.paginationManager.reset(key);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查是否正在加载
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
isLoading(key: string = 'default'): boolean {
|
|||
|
|
return this.getPostsState(key).isLoading;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查是否有错误
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
hasError(key: string = 'default'): boolean {
|
|||
|
|
return this.getPostsState(key).error !== null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取错误信息
|
|||
|
|
* @param key 列表键
|
|||
|
|
*/
|
|||
|
|
getError(key: string = 'default'): string | null {
|
|||
|
|
return this.getPostsState(key).error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 导出 ====================
|
|||
|
|
|
|||
|
|
// 导出单例实例
|
|||
|
|
export const processPostUseCase = ProcessPostUseCase.getInstance();
|
|||
|
|
export default processPostUseCase;
|