Files
frontend/src/services/postService.ts
lafay 126e204592
Some checks failed
Frontend CI / build-and-push-web (push) Successful in 2m48s
Frontend CI / ota-android (push) Successful in 11m3s
Frontend CI / build-android-apk (push) Has been cancelled
refactor(Post, PostMapper, PostRepository, CreatePostScreen, HomeScreen): update communityId to channelId for improved clarity
- 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.
2026-03-24 22:27:33 +08:00

476 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 帖子服务
* 处理帖子的增删改查、点赞、收藏等功能
*
* @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();