- Renamed communityId to channelId across Post entity, PostMapper, and PostRepository for consistency and clarity. - Updated CreatePostScreen to include channel selection functionality, enhancing user experience when creating posts. - Adjusted HomeScreen to support filtering posts by channel, improving content organization. - Refactored related interfaces and services to align with the new channelId terminology, ensuring a cohesive codebase.
476 lines
13 KiB
TypeScript
476 lines
13 KiB
TypeScript
/**
|
||
* 帖子服务
|
||
* 处理帖子的增删改查、点赞、收藏等功能
|
||
*
|
||
* @deprecated 此服务已弃用,请使用 ProcessPostUseCase 代替
|
||
* 互动操作(点赞、收藏)已委托给 processPostUseCase
|
||
*/
|
||
|
||
import { api, PaginatedData } from './api';
|
||
import { Post, CreatePostInput } from '../types';
|
||
import { CursorPaginationRequest, CursorPaginationResponse } from '../types/dto';
|
||
import { processPostUseCase } from '../core/usecases/ProcessPostUseCase';
|
||
import type { Post as PostEntity } from '../core/entities/Post';
|
||
|
||
// 帖子列表响应
|
||
interface PostListResponse {
|
||
list: Post[];
|
||
total: number;
|
||
page: number;
|
||
page_size: number;
|
||
total_pages: number;
|
||
}
|
||
|
||
// 帖子响应 - API直接返回Post对象
|
||
type PostResponse = Post;
|
||
|
||
// 创建帖子请求
|
||
interface CreatePostRequest {
|
||
title: string;
|
||
content: string;
|
||
images?: string[];
|
||
channel_id?: string;
|
||
}
|
||
|
||
// 更新帖子请求
|
||
interface UpdatePostRequest {
|
||
title?: string;
|
||
content?: string;
|
||
images?: string[];
|
||
}
|
||
|
||
// 帖子服务类
|
||
class PostService {
|
||
// 获取帖子列表
|
||
async getPosts(
|
||
page = 1,
|
||
pageSize = 20,
|
||
tab?: string,
|
||
channelId?: string
|
||
): Promise<PaginatedData<Post>> {
|
||
const params: Record<string, any> = {
|
||
page,
|
||
page_size: pageSize,
|
||
};
|
||
|
||
if (tab) {
|
||
params.tab = tab;
|
||
}
|
||
if (channelId) {
|
||
params.channel_id = channelId;
|
||
}
|
||
|
||
const response = await api.get<PostListResponse>('/posts', params);
|
||
return response.data;
|
||
}
|
||
|
||
// 获取关注帖子
|
||
async getFollowingPosts(page = 1, pageSize = 20): Promise<PaginatedData<Post>> {
|
||
return this.getPosts(page, pageSize, 'follow');
|
||
}
|
||
|
||
// 获取热门帖子
|
||
async getHotPosts(page = 1, pageSize = 20): Promise<PaginatedData<Post>> {
|
||
return this.getPosts(page, pageSize, 'hot');
|
||
}
|
||
|
||
// 获取最新帖子
|
||
async getLatestPosts(page = 1, pageSize = 20): Promise<PaginatedData<Post>> {
|
||
return this.getPosts(page, pageSize, 'latest');
|
||
}
|
||
|
||
// 获取帖子详情(不增加浏览量)
|
||
async getPost(postId: string): Promise<Post | null> {
|
||
try {
|
||
const response = await api.get<PostResponse>(`/posts/${postId}`);
|
||
return response.data;
|
||
} catch (error) {
|
||
console.error('获取帖子详情失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 记录帖子浏览(增加浏览量)
|
||
async recordView(postId: string): Promise<boolean> {
|
||
try {
|
||
const response = await api.post(`/posts/${postId}/view`);
|
||
return response.code === 0;
|
||
} catch (error) {
|
||
console.error('记录浏览失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 创建帖子
|
||
async createPost(data: CreatePostRequest): Promise<Post | null> {
|
||
const response = await api.post<PostResponse>('/posts', data);
|
||
return response.data;
|
||
}
|
||
|
||
// 更新帖子
|
||
async updatePost(postId: string, data: UpdatePostRequest): Promise<Post | null> {
|
||
try {
|
||
const response = await api.put<PostResponse>(`/posts/${postId}`, data);
|
||
return response.data;
|
||
} catch (error) {
|
||
console.error('更新帖子失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 删除帖子
|
||
async deletePost(postId: string): Promise<boolean> {
|
||
try {
|
||
const response = await api.delete(`/posts/${postId}`);
|
||
return response.code === 0;
|
||
} catch (error) {
|
||
console.error('删除帖子失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 点赞帖子
|
||
// @deprecated 请使用 processPostUseCase.likePost() 代替
|
||
async likePost(postId: string): Promise<Post | null> {
|
||
try {
|
||
const post = await processPostUseCase.likePost(postId);
|
||
// 转换为旧版 Post 类型以保持向后兼容
|
||
return post as unknown as Post;
|
||
} catch (error) {
|
||
console.error('[postService] likePost error:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 取消点赞帖子
|
||
// @deprecated 请使用 processPostUseCase.unlikePost() 代替
|
||
async unlikePost(postId: string): Promise<Post | null> {
|
||
try {
|
||
const post = await processPostUseCase.unlikePost(postId);
|
||
return post as unknown as Post;
|
||
} catch (error) {
|
||
console.error('[postService] unlikePost error:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 收藏帖子
|
||
// @deprecated 请使用 processPostUseCase.favoritePost() 代替
|
||
async favoritePost(postId: string): Promise<Post | null> {
|
||
try {
|
||
const post = await processPostUseCase.favoritePost(postId);
|
||
return post as unknown as Post;
|
||
} catch (error) {
|
||
console.error('[postService] favoritePost error:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 取消收藏帖子
|
||
// @deprecated 请使用 processPostUseCase.unfavoritePost() 代替
|
||
async unfavoritePost(postId: string): Promise<Post | null> {
|
||
try {
|
||
const post = await processPostUseCase.unfavoritePost(postId);
|
||
return post as unknown as Post;
|
||
} catch (error) {
|
||
console.error('[postService] unfavoritePost error:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 获取用户帖子列表
|
||
async getUserPosts(
|
||
userId: string,
|
||
page = 1,
|
||
pageSize = 20
|
||
): Promise<PaginatedData<Post>> {
|
||
try {
|
||
const response = await api.get<PostListResponse>(`/users/${userId}/posts`, {
|
||
page,
|
||
page_size: pageSize,
|
||
});
|
||
return response.data;
|
||
} catch (error) {
|
||
console.error('获取用户帖子列表失败:', error);
|
||
return {
|
||
list: [],
|
||
total: 0,
|
||
page: 1,
|
||
page_size: pageSize,
|
||
total_pages: 0,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 获取用户收藏列表
|
||
async getUserFavorites(
|
||
userId: string,
|
||
page = 1,
|
||
pageSize = 20
|
||
): Promise<PaginatedData<Post>> {
|
||
try {
|
||
const response = await api.get<PostListResponse>(`/users/${userId}/favorites`, {
|
||
page,
|
||
page_size: pageSize,
|
||
});
|
||
return response.data;
|
||
} catch (error) {
|
||
console.error('获取用户收藏列表失败:', error);
|
||
return {
|
||
list: [],
|
||
total: 0,
|
||
page: 1,
|
||
page_size: pageSize,
|
||
total_pages: 0,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 搜索帖子
|
||
async searchPosts(
|
||
keyword: string,
|
||
page = 1,
|
||
pageSize = 20
|
||
): Promise<PaginatedData<Post>> {
|
||
try {
|
||
const response = await api.get<PostListResponse>('/posts/search', {
|
||
keyword,
|
||
page,
|
||
page_size: pageSize,
|
||
});
|
||
return response.data;
|
||
} catch (error) {
|
||
console.error('搜索帖子失败:', error);
|
||
return {
|
||
list: [],
|
||
total: 0,
|
||
page: 1,
|
||
page_size: pageSize,
|
||
total_pages: 0,
|
||
};
|
||
}
|
||
}
|
||
|
||
/** 上报分享成功;返回服务端最新 shares_count 供界面同步 */
|
||
async sharePost(
|
||
postId: string,
|
||
body?: { channel?: string }
|
||
): Promise<{ ok: boolean; shares_count?: number }> {
|
||
try {
|
||
const response = await api.post<{ success: boolean; shares_count: number }>(
|
||
`/posts/${postId}/share`,
|
||
body && body.channel ? { channel: body.channel } : undefined
|
||
);
|
||
if (response.code !== 0) {
|
||
return { ok: false };
|
||
}
|
||
const n = response.data?.shares_count;
|
||
return { ok: true, shares_count: typeof n === 'number' ? n : undefined };
|
||
} catch (error) {
|
||
console.error('分享帖子失败:', error);
|
||
return { ok: false };
|
||
}
|
||
}
|
||
|
||
// 举报帖子
|
||
async reportPost(postId: string, reason: string): Promise<boolean> {
|
||
try {
|
||
const response = await api.post(`/posts/${postId}/report`, { reason });
|
||
return response.code === 0;
|
||
} catch (error) {
|
||
console.error('举报帖子失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// ==================== 游标分页方法 ====================
|
||
|
||
/**
|
||
* 获取帖子列表(游标分页)
|
||
* GET /api/v1/posts
|
||
* @param params 游标分页请求参数(包含 post_type 可选:follow, hot, latest)
|
||
*/
|
||
async getPostsCursor(
|
||
params: CursorPaginationRequest = {}
|
||
): Promise<CursorPaginationResponse<Post>> {
|
||
try {
|
||
// 构建请求参数
|
||
// 后端使用 `tab` 参数区分帖子类型,使用 `cursor` 参数启用游标分页
|
||
// 始终传递 cursor 参数(空字符串表示首次请求),确保使用游标分页
|
||
const requestParams: Record<string, any> = {
|
||
cursor: params.cursor || '', // 空字符串表示首次请求
|
||
page_size: params.page_size,
|
||
};
|
||
|
||
// 将 post_type 映射为后端的 tab 参数
|
||
if (params.post_type) {
|
||
requestParams.tab = params.post_type;
|
||
}
|
||
|
||
const response = await api.get<any>('/posts', requestParams);
|
||
|
||
const data = response.data;
|
||
|
||
if (data && typeof data === 'object' && Array.isArray(data.list)) {
|
||
const isPageShape =
|
||
typeof data.page === 'number' &&
|
||
typeof data.total_pages === 'number' &&
|
||
data.next_cursor === undefined &&
|
||
data.prev_cursor === undefined;
|
||
if (isPageShape) {
|
||
const currentPage = data.page || 1;
|
||
const totalPages = data.total_pages || 1;
|
||
return {
|
||
list: data.list,
|
||
next_cursor: currentPage < totalPages ? String(currentPage + 1) : null,
|
||
prev_cursor: currentPage > 1 ? String(currentPage - 1) : null,
|
||
has_more: currentPage < totalPages,
|
||
};
|
||
}
|
||
return {
|
||
list: data.list,
|
||
next_cursor: data.next_cursor ?? null,
|
||
prev_cursor: data.prev_cursor ?? null,
|
||
has_more: data.has_more ?? false,
|
||
};
|
||
}
|
||
|
||
return {
|
||
list: [],
|
||
next_cursor: null,
|
||
prev_cursor: null,
|
||
has_more: false,
|
||
};
|
||
} catch (error) {
|
||
console.error('获取帖子列表失败:', error);
|
||
return {
|
||
list: [],
|
||
next_cursor: null,
|
||
prev_cursor: null,
|
||
has_more: false,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 搜索帖子(游标分页)
|
||
* GET /api/v1/posts/search
|
||
* @param query 搜索关键词
|
||
* @param params 游标分页请求参数
|
||
*/
|
||
async searchPostsCursor(
|
||
query: string,
|
||
params: CursorPaginationRequest = {}
|
||
): Promise<CursorPaginationResponse<Post>> {
|
||
try {
|
||
const response = await api.get<any>('/posts/search', {
|
||
...params,
|
||
keyword: query,
|
||
});
|
||
|
||
const data = response.data;
|
||
|
||
if (data && typeof data === 'object' && Array.isArray(data.list)) {
|
||
const isPageShape =
|
||
typeof data.page === 'number' &&
|
||
typeof data.total_pages === 'number' &&
|
||
data.next_cursor === undefined &&
|
||
data.prev_cursor === undefined;
|
||
if (isPageShape) {
|
||
const currentPage = data.page || 1;
|
||
const totalPages = data.total_pages || 1;
|
||
return {
|
||
list: data.list,
|
||
next_cursor: currentPage < totalPages ? String(currentPage + 1) : null,
|
||
prev_cursor: currentPage > 1 ? String(currentPage - 1) : null,
|
||
has_more: currentPage < totalPages,
|
||
};
|
||
}
|
||
return {
|
||
list: data.list,
|
||
next_cursor: data.next_cursor ?? null,
|
||
prev_cursor: data.prev_cursor ?? null,
|
||
has_more: data.has_more ?? false,
|
||
};
|
||
}
|
||
|
||
return {
|
||
list: [],
|
||
next_cursor: null,
|
||
prev_cursor: null,
|
||
has_more: false,
|
||
};
|
||
} catch (error) {
|
||
console.error('搜索帖子失败:', error);
|
||
return {
|
||
list: [],
|
||
next_cursor: null,
|
||
prev_cursor: null,
|
||
has_more: false,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户帖子列表(游标分页)
|
||
* GET /api/v1/users/:id/posts
|
||
* @param userId 用户ID
|
||
* @param params 游标分页请求参数
|
||
*/
|
||
async getUserPostsCursor(
|
||
userId: string,
|
||
params: CursorPaginationRequest = {}
|
||
): Promise<CursorPaginationResponse<Post>> {
|
||
try {
|
||
const response = await api.get<any>(
|
||
`/users/${userId}/posts`,
|
||
params
|
||
);
|
||
|
||
const data = response.data;
|
||
|
||
if (data && typeof data === 'object' && Array.isArray(data.list)) {
|
||
const isPageShape =
|
||
typeof data.page === 'number' &&
|
||
typeof data.total_pages === 'number' &&
|
||
data.next_cursor === undefined &&
|
||
data.prev_cursor === undefined;
|
||
if (isPageShape) {
|
||
const currentPage = data.page || 1;
|
||
const totalPages = data.total_pages || 1;
|
||
return {
|
||
list: data.list,
|
||
next_cursor: currentPage < totalPages ? String(currentPage + 1) : null,
|
||
prev_cursor: currentPage > 1 ? String(currentPage - 1) : null,
|
||
has_more: currentPage < totalPages,
|
||
};
|
||
}
|
||
return {
|
||
list: data.list,
|
||
next_cursor: data.next_cursor ?? null,
|
||
prev_cursor: data.prev_cursor ?? null,
|
||
has_more: data.has_more ?? false,
|
||
};
|
||
}
|
||
|
||
return {
|
||
list: [],
|
||
next_cursor: null,
|
||
prev_cursor: null,
|
||
has_more: false,
|
||
};
|
||
} catch (error) {
|
||
console.error('获取用户帖子列表失败:', error);
|
||
return {
|
||
list: [],
|
||
next_cursor: null,
|
||
prev_cursor: null,
|
||
has_more: false,
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
// 导出帖子服务实例
|
||
export const postService = new PostService();
|