# 前端游标分页适配方案设计文档 ## 一、背景说明 后端已完成游标分页功能实现,共改造了 11 个接口。前端需要对接这些新的游标分页接口,替换现有的基于页码的分页方式。 ### 后端游标分页接口列表 | 模块 | 接口路径 | 排序方式 | |------|----------|----------| | 帖子 | GET /posts/cursor | created_at DESC | | 帖子搜索 | GET /posts/search/cursor | created_at DESC | | 用户帖子 | GET /users/:id/posts/cursor | created_at DESC | | 会话列表 | GET /conversations/cursor | updated_at DESC | | 消息列表 | GET /conversations/:id/messages/cursor | seq DESC | | 评论列表 | GET /comments/post/:id/cursor | created_at DESC | | 评论回复 | GET /comments/:id/replies/cursor | created_at ASC | | 通知列表 | GET /notifications/cursor | created_at DESC | | 群组列表 | GET /groups/cursor | created_at DESC | | 群成员 | GET /groups/:id/members/cursor | join_time DESC | | 群公告 | GET /groups/:id/announcements/cursor | created_at DESC | ### 后端 API 规范 **请求参数:** - `cursor` - 游标字符串(首次请求不传) - `direction` - 分页方向:`forward` 或 `backward`(默认 forward) - `page_size` - 每页数量(默认 20,最大 100) **响应格式:** ```json { "items": [...], "next_cursor": "xxx", "prev_cursor": "xxx", "has_more": true } ``` --- ## 二、现有前端分页机制分析 ### 2.1 分页类型定义 (`src/infrastructure/pagination/types.ts`) ```typescript // 现有分页状态 - 基于页码 interface PaginationState { currentPage: number; pageSize: number; totalLoaded: number; hasMore: boolean; isLoading: boolean; lastLoadTime: number; cursor?: string | number | null; // 已支持但未充分使用 hasError: boolean; error?: string; } // 现有 FetchPageFunction type FetchPageFunction = ( page: number, pageSize: number, cursor?: string | number | null ) => Promise<{ data: T[]; hasMore: boolean; cursor?: string | number | null; }>; ``` ### 2.2 分页状态管理器 (`PaginationStateManager.ts`) - 使用 `currentPage` 作为分页基准 - 基于页码的缓存机制(`pageCaches: Map>`) - `loadMore()` 方法自动递增页码 ### 2.3 usePagination Hook (`usePagination.ts`) - 暴露 `currentPage`、`loadMore`、`refresh` 等方法 - 内部调用 `PaginationStateManager` 进行状态管理 - 已有 `cursor` 支持,但 UI 层仍使用页码 ### 2.4 现有 API 服务层 **postService.ts:** ```typescript // 现有分页方式 async getPosts(page = 1, pageSize = 20, tab?: string): Promise> ``` **messageService.ts:** ```typescript // 现有分页方式 - 基于 seq async getMessages(conversationId, afterSeq?, beforeSeq?, limit?): Promise ``` ### 2.5 现有页面组件分页方式 各页面使用**手动管理状态**的方式: ```typescript const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const loadMore = useCallback(() => { if (!loading && hasMore) { const nextPage = page + 1; setPage(nextPage); loadData(nextPage); } }, [loading, hasMore, page, loadData]); ``` **涉及页面:** - `HomeScreen.tsx` - 帖子列表 - `SearchScreen.tsx` - 帖子搜索 - `PostDetailScreen.tsx` - 帖子详情(评论列表) - `MessageListScreen.tsx` - 会话列表 - `ChatScreen.tsx` - 消息列表 - `NotificationsScreen.tsx` - 通知列表 - `GroupMembersScreen.tsx` - 群组成员 - `JoinGroupScreen.tsx` - 群组列表 --- ## 三、游标分页类型定义设计 ### 3.1 新增游标分页相关类型 (`src/infrastructure/pagination/types.ts`) ```typescript // ==================== 游标分页类型 ==================== /** * 游标分页方向 */ export type CursorDirection = 'forward' | 'backward'; /** * 游标分页请求参数 */ export interface CursorPageRequest { cursor?: string | null; direction?: CursorDirection; page_size?: number; } /** * 游标分页响应 */ export interface CursorPageResponse { items: T[]; next_cursor: string | null; prev_cursor: string | null; has_more: boolean; } /** * 游标分页状态 - 扩展现有 PaginationState */ export interface CursorPaginationState { // 游标相关 nextCursor: string | null; prevCursor: string | null; currentCursor: string | null; // 方向 direction: CursorDirection; // 通用 hasMore: boolean; isLoading: boolean; hasError: boolean; error?: string; } /** * 游标分页配置 */ export interface CursorPaginationConfig { pageSize: number; prefetchThreshold: number; enablePrefetch: boolean; maxRetries: number; retryDelay: number; } /** * 游标分页加载函数 */ export type CursorFetchFunction = ( request: CursorPageRequest ) => Promise>; /** * 游标分页加载结果 */ export interface CursorLoadMoreResult { success: boolean; data: T[]; hasMore: boolean; nextCursor: string | null; prevCursor: string | null; fromCache: boolean; error?: string; } /** * 游标分页刷新结果 */ export interface CursorRefreshResult { success: boolean; data: T[]; hasMore: boolean; nextCursor: string | null; error?: string; } ``` ### 3.2 新增游标分页 DTO 类型 (`src/types/dto.ts`) ```typescript // ==================== 游标分页 DTO ==================== /** * 游标分页响应基础结构 */ export interface CursorPaginatedResponse { items: T[]; next_cursor: string | null; prev_cursor: string | null; has_more: boolean; } /** * 帖子列表游标分页响应 */ export interface PostCursorResponse extends CursorPaginatedResponse {} /** * 会话列表游标分页响应 */ export interface ConversationCursorResponse extends CursorPaginatedResponse {} /** * 消息列表游标分页响应 */ export interface MessageCursorResponse extends CursorPaginatedResponse {} /** * 评论列表游标分页响应 */ export interface CommentCursorResponse extends CursorPaginatedResponse {} /** * 通知列表游标分页响应 */ export interface NotificationCursorResponse extends CursorPaginatedResponse {} /** * 群组列表游标分页响应 */ export interface GroupCursorResponse extends CursorPaginatedResponse {} /** * 群成员列表游标分页响应 */ export interface GroupMemberCursorResponse extends CursorPaginatedResponse {} /** * 群公告列表游标分页响应 */ export interface GroupAnnouncementCursorResponse extends CursorPaginatedResponse {} ``` --- ## 四、API 服务层改造设计 ### 4.1 新增游标分页 API 方法 #### postService.ts 改造 ```typescript class PostService { // ==================== 游标分页方法 ==================== /** * 获取帖子列表(游标分页) * GET /api/v1/posts/cursor */ async getPostsCursor( request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get('/posts/cursor', params); return { items: response.data.items, next_cursor: response.data.next_cursor, prev_cursor: response.data.prev_cursor, has_more: response.data.has_more, }; } /** * 搜索帖子(游标分页) * GET /api/v1/posts/search/cursor */ async searchPostsCursor( keyword: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { keyword, page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get('/posts/search/cursor', params); return response.data; } /** * 获取用户帖子列表(游标分页) * GET /api/v1/users/:id/posts/cursor */ async getUserPostsCursor( userId: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get( `/users/${userId}/posts/cursor`, params ); return response.data; } } ``` #### messageService.ts 改造 ```typescript class MessageService { // ==================== 会话列表游标分页 ==================== /** * 获取会话列表(游标分页) * GET /api/v1/conversations/cursor */ async getConversationsCursor( request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get('/conversations/cursor', params); return response.data; } // ==================== 消息列表游标分页 ==================== /** * 获取消息列表(游标分页) * GET /api/v1/conversations/:id/messages/cursor */ async getMessagesCursor( conversationId: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get( `/conversations/${encodeURIComponent(conversationId)}/messages/cursor`, params ); return response.data; } } ``` #### 新增 commentService.ts ```typescript // 新建文件: src/services/commentService.ts import { api } from './api'; import { CommentCursorResponse, CursorPageRequest, CursorPageResponse } from '../types/dto'; import { CommentDTO } from '../types/dto'; class CommentService { /** * 获取帖子评论列表(游标分页) * GET /api/v1/comments/post/:id/cursor */ async getPostCommentsCursor( postId: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get( `/comments/post/${postId}/cursor`, params ); return response.data; } /** * 获取评论回复列表(游标分页) * GET /api/v1/comments/:id/replies/cursor */ async getCommentRepliesCursor( commentId: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get( `/comments/${commentId}/replies/cursor`, params ); return response.data; } } export const commentService = new CommentService(); ``` #### 新增 notificationService.ts ```typescript // 新建文件: src/services/notificationService.ts import { api } from './api'; import { NotificationCursorResponse, CursorPageRequest, CursorPageResponse } from '../types/dto'; import { NotificationDTO } from '../types/dto'; class NotificationService { /** * 获取通知列表(游标分页) * GET /api/v1/notifications/cursor */ async getNotificationsCursor( request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get('/notifications/cursor', params); return response.data; } } export const notificationService = new NotificationService(); ``` #### 新增 groupService.ts (扩展现有群组服务) ```typescript // 在现有 groupService.ts 中添加游标分页方法 class GroupService { /** * 获取群组列表(游标分页) * GET /api/v1/groups/cursor */ async getGroupsCursor( request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get('/groups/cursor', params); return response.data; } /** * 获取群成员列表(游标分页) * GET /api/v1/groups/:id/members/cursor */ async getGroupMembersCursor( groupId: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get( `/groups/${groupId}/members/cursor`, params ); return response.data; } /** * 获取群公告列表(游标分页) * GET /api/v1/groups/:id/announcements/cursor */ async getGroupAnnouncementsCursor( groupId: string, request: CursorPageRequest = {} ): Promise> { const params: Record = { page_size: request.page_size || 20, }; if (request.cursor) params.cursor = request.cursor; if (request.direction) params.direction = request.direction; const response = await api.get( `/groups/${groupId}/announcements/cursor`, params ); return response.data; } } ``` --- ## 五、分页 Hook 改造设计 ### 5.1 新增 useCursorPagination Hook (`src/hooks/useCursorPagination.ts`) ```typescript /** * 游标分页 Hook * * 提供基于游标的分页功能,包括: * - loadMore: 加载下一页 * - loadPrevious: 加载上一页(双向游标支持) * - refresh: 刷新数据 * - 状态管理 */ import { useState, useCallback, useEffect, useRef, useMemo } from 'react'; import { CursorPaginationState, CursorPaginationConfig } from '../infrastructure/pagination/types'; export interface UseCursorPaginationOptions { /** 分页键 */ key: string | null; /** 数据获取函数 */ fetchFunction: CursorFetchFunction; /** 分页配置 */ config?: Partial; /** 初始数据 */ initialData?: T[]; /** 是否自动加载第一页 */ autoLoad?: boolean; /** 是否启用自动预加载 */ enableAutoPrefetch?: boolean; } export interface UseCursorPaginationReturn { /** 当前数据列表 */ data: T[]; /** 是否正在加载 */ isLoading: boolean; /** 是否正在刷新 */ isRefreshing: boolean; /** 是否有更多数据 */ hasMore: boolean; /** 是否有上一页 */ hasPrevious: boolean; /** 是否有错误 */ hasError: boolean; /** 错误信息 */ error?: string; /** 当前游标 */ currentCursor: string | null; /** 加载下一页 */ loadMore: () => Promise>; /** 加载上一页 */ loadPrevious: () => Promise>; /** 刷新数据 */ refresh: () => Promise>; /** 重置分页状态 */ reset: () => void; } export function useCursorPagination( options: UseCursorPaginationOptions ): UseCursorPaginationReturn { const { key, fetchFunction, config: userConfig, initialData = [], autoLoad = false, enableAutoPrefetch = true, } = options; // 合并配置 const config = useMemo(() => ({ ...DEFAULT_CURSOR_PAGINATION_CONFIG, ...userConfig, }), [userConfig]); // 数据状态 const [data, setData] = useState(initialData); const [state, setState] = useState({ nextCursor: null, prevCursor: null, currentCursor: null, direction: 'forward', hasMore: true, isLoading: false, hasError: false, error: undefined, }); // Refs const autoLoadRef = useRef(false); const isMountedRef = useRef(true); // 派生的 UI 状态 const isRefreshing = state.isLoading && data.length > 0; const hasPrevious = state.prevCursor !== null; /** * 加载下一页 */ const loadMore = useCallback(async (): Promise> => { if (!key) { return { success: false, data: [], hasMore: false, nextCursor: null, prevCursor: null, fromCache: false, error: 'No pagination key' }; } try { setState(prev => ({ ...prev, isLoading: true, hasError: false })); const response = await fetchFunction({ cursor: state.nextCursor, direction: 'forward', page_size: config.pageSize, }); if (isMountedRef.current) { setData(prev => [...prev, ...response.items]); setState({ nextCursor: response.next_cursor, prevCursor: response.prev_cursor, currentCursor: response.next_cursor, direction: 'forward', hasMore: response.has_more, isLoading: false, hasError: false, }); } return { success: true, data: response.items, hasMore: response.has_more, nextCursor: response.next_cursor, prevCursor: response.prev_cursor, fromCache: false, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (isMountedRef.current) { setState(prev => ({ ...prev, isLoading: false, hasError: true, error: errorMessage })); } return { success: false, data: [], hasMore: false, nextCursor: null, prevCursor: null, fromCache: false, error: errorMessage }; } }, [key, fetchFunction, state.nextCursor, config.pageSize]); /** * 加载上一页 */ const loadPrevious = useCallback(async (): Promise> => { if (!key || !state.prevCursor) { return { success: false, data: [], hasMore: false, nextCursor: null, prevCursor: null, fromCache: false, error: 'No previous page' }; } try { setState(prev => ({ ...prev, isLoading: true, hasError: false })); const response = await fetchFunction({ cursor: state.prevCursor, direction: 'backward', page_size: config.pageSize, }); if (isMountedRef.current) { setData(prev => [...response.items, ...prev]); setState({ nextCursor: response.next_cursor, prevCursor: response.prev_cursor, currentCursor: response.prev_cursor, direction: 'backward', hasMore: response.has_more, isLoading: false, hasError: false, }); } return { success: true, data: response.items, hasMore: response.has_more, nextCursor: response.next_cursor, prevCursor: response.prev_cursor, fromCache: false, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (isMountedRef.current) { setState(prev => ({ ...prev, isLoading: false, hasError: true, error: errorMessage })); } return { success: false, data: [], hasMore: false, nextCursor: null, prevCursor: null, fromCache: false, error: errorMessage }; } }, [key, fetchFunction, state.prevCursor, config.pageSize]); /** * 刷新数据 */ const refresh = useCallback(async (): Promise> => { if (!key) { return { success: false, data: [], hasMore: false, nextCursor: null, error: 'No pagination key' }; } try { setState(prev => ({ ...prev, isLoading: true, hasError: false })); const response = await fetchFunction({ cursor: null, direction: 'forward', page_size: config.pageSize, }); if (isMountedRef.current) { setData(response.items); setState({ nextCursor: response.next_cursor, prevCursor: null, currentCursor: null, direction: 'forward', hasMore: response.has_more, isLoading: false, hasError: false, }); } return { success: true, data: response.items, hasMore: response.has_more, nextCursor: response.next_cursor, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (isMountedRef.current) { setState(prev => ({ ...prev, isLoading: false, hasError: true, error: errorMessage })); } return { success: false, data: [], hasMore: false, nextCursor: null, error: errorMessage }; } }, [key, fetchFunction, config.pageSize]); /** * 重置分页状态 */ const reset = useCallback(() => { if (isMountedRef.current) { setData([]); setState({ nextCursor: null, prevCursor: null, currentCursor: null, direction: 'forward', hasMore: true, isLoading: false, hasError: false, }); autoLoadRef.current = false; } }, []); // 自动加载第一页 useEffect(() => { if (!key || !autoLoad || autoLoadRef.current) return; if (data.length > 0) return; autoLoadRef.current = true; refresh(); }, [key, autoLoad, data.length, refresh]); // 组件卸载时清理 useEffect(() => { return () => { isMountedRef.current = false; }; }, []); return { data, isLoading: state.isLoading, isRefreshing, hasMore: state.hasMore, hasPrevious, hasError: state.hasError, error: state.error, currentCursor: state.currentCursor, loadMore, loadPrevious, refresh, reset, }; } ``` --- ## 六、各列表页面改造计划 ### 6.1 帖子列表 (HomeScreen.tsx) ```typescript // 改造后的分页方式 const { data: posts, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: 'home-posts', fetchFunction: async (request) => { return postService.getPostsCursor(request); }, config: { pageSize: 20 }, autoLoad: true, }); // UI 层使用 FlatList/FlashList 的 onEndReached 0} onRefresh={refresh} /> ``` ### 6.2 会话列表 (MessageListScreen.tsx) ```typescript const { data: conversations, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: 'conversations', fetchFunction: async (request) => { return messageService.getConversationsCursor(request); }, autoLoad: true, }); ``` ### 6.3 消息列表 (ChatScreen.tsx) ```typescript // 聊天消息需要支持双向加载 const { data: messages, isLoading, hasMore, hasPrevious, loadMore, // 加载更多历史 loadPrevious, // 加载更新的消息 refresh, } = useCursorPagination({ key: `chat-${conversationId}`, fetchFunction: async (request) => { return messageService.getMessagesCursor(conversationId, request); }, }); ``` ### 6.4 帖子搜索 (SearchScreen.tsx) ```typescript const { data: searchResults, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: `search-${keyword}`, fetchFunction: async (request) => { return postService.searchPostsCursor(keyword, request); }, autoLoad: !!keyword, }); ``` ### 6.5 评论列表 (PostDetailScreen.tsx) ```typescript const { data: comments, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: `comments-${postId}`, fetchFunction: async (request) => { return commentService.getPostCommentsCursor(postId, request); }, }); ``` ### 6.6 通知列表 (NotificationsScreen.tsx) ```typescript const { data: notifications, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: 'notifications', fetchFunction: async (request) => { return notificationService.getNotificationsCursor(request); }, }); ``` ### 6.7 群组列表 (JoinGroupScreen.tsx) ```typescript const { data: groups, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: 'groups', fetchFunction: async (request) => { return groupService.getGroupsCursor(request); }, }); ``` ### 6.8 群组成员 (GroupMembersScreen.tsx) ```typescript const { data: members, isLoading, hasMore, loadMore, refresh, } = useCursorPagination({ key: `group-members-${groupId}`, fetchFunction: async (request) => { return groupService.getGroupMembersCursor(groupId, request); }, }); ``` --- ## 七、需要修改的文件清单 ### 7.1 新增文件 | 文件路径 | 说明 | |----------|------| | `src/hooks/useCursorPagination.ts` | 新增游标分页 Hook | | `src/services/commentService.ts` | 新增评论服务(含游标分页) | | `src/services/notificationService.ts` | 新增通知服务(含游标分页) | | `src/types/cursor.ts` | 新增游标分页相关类型定义 | ### 7.2 需要修改的文件 | 文件路径 | 修改内容 | |----------|----------| | `src/types/dto.ts` | 新增游标分页 DTO 类型 | | `src/services/postService.ts` | 新增游标分页方法(保留旧方法兼容) | | `src/services/messageService.ts` | 新增会话/消息游标分页方法 | | `src/services/groupService.ts` | 新增群组相关游标分页方法 | | `src/infrastructure/pagination/types.ts` | 新增游标分页类型定义 | | `src/infrastructure/pagination/CursorPaginationStateManager.ts` | 新增游标分页状态管理器(可选) | | `src/screens/home/HomeScreen.tsx` | 改造为使用 useCursorPagination | | `src/screens/home/SearchScreen.tsx` | 改造为使用 useCursorPagination | | `src/screens/home/PostDetailScreen.tsx` | 改造评论列表使用 useCursorPagination | | `src/screens/message/MessageListScreen.tsx` | 改造为使用 useCursorPagination | | `src/screens/message/ChatScreen.tsx` | 改造消息列表使用 useCursorPagination | | `src/screens/message/NotificationsScreen.tsx` | 改造为使用 useCursorPagination | | `src/screens/message/JoinGroupScreen.tsx` | 改造为使用 useCursorPagination | | `src/screens/message/GroupMembersScreen.tsx` | 改造为使用 useCursorPagination | --- ## 八、实现顺序建议 ### 阶段一:基础设施(优先级高) 1. 在 `src/types/dto.ts` 新增游标分页 DTO 类型 2. 在 `src/infrastructure/pagination/types.ts` 新增游标分页类型 3. 创建 `src/hooks/useCursorPagination.ts` ### 阶段二:API 服务层 4. 改造 `postService.ts` - 新增游标分页方法 5. 改造 `messageService.ts` - 新增游标分页方法 6. 创建 `commentService.ts` 7. 创建 `notificationService.ts` 8. 改造 `groupService.ts` - 新增游标分页方法 ### 阶段三:UI 组件改造(按依赖顺序) 9. `HomeScreen.tsx` - 帖子列表 10. `SearchScreen.tsx` - 帖子搜索 11. `PostDetailScreen.tsx` - 评论列表 12. `MessageListScreen.tsx` - 会话列表 13. `ChatScreen.tsx` - 消息列表 14. `NotificationsScreen.tsx` - 通知列表 15. `JoinGroupScreen.tsx` - 群组列表 16. `GroupMembersScreen.tsx` - 群组成员 --- ## 九、架构图 ```mermaid graph TB subgraph "UI Layer" HomeScreen[HomeScreen.tsx] SearchScreen[SearchScreen.tsx] ChatScreen[ChatScreen.tsx] MessageListScreen[MessageListScreen.tsx] NotificationsScreen[NotificationsScreen.tsx] GroupMembersScreen[GroupMembersScreen.tsx] end subgraph "Hooks Layer" useCursorPagination[useCursorPagination.ts] usePagination[usePagination.ts - 保留兼容] end subgraph "Service Layer" postService[postService.ts] messageService[messageService.ts] commentService[commentService.ts] notificationService[notificationService.ts] groupService[groupService.ts] end subgraph "API Layer" API[API Client] end subgraph "Backend" CursorAPI[游标分页 API] end HomeScreen --> useCursorPagination SearchScreen --> useCursorPagination ChatScreen --> useCursorPagination MessageListScreen --> useCursorPagination NotificationsScreen --> useCursorPagination GroupMembersScreen --> useCursorPagination useCursorPagination --> postService useCursorPagination --> messageService useCursorPagination --> commentService useCursorPagination --> notificationService useCursorPagination --> groupService postService --> API messageService --> API commentService --> API notificationService --> API groupService --> API API --> CursorAPI ``` --- ## 十、注意事项 ### 10.1 兼容性考虑 - 保留现有的页码分页方法一段时间,避免一次性全量替换带来的风险 - 通过功能开关或配置控制是否启用游标分页 ### 10.2 消息列表特殊处理 - 聊天消息需要支持双向加载(加载历史 + 加载最新) - 需要处理好新消息的插入位置(头部 vs 尾部) ### 10.3 缓存策略 - 游标分页的缓存策略需要调整,不再基于页码 - 可以考虑基于 cursor 字符串进行缓存 ### 10.4 错误处理 - 网络错误时需要显示重试选项 - 游标失效时需要提示用户刷新列表