refactor: 架构重构 - 解耦过度耦合模块

主要改动:
1. 创建乐观更新工具函数 (optimisticUpdate.ts)
   - 消除 userStore.ts 中的重复代码

2. 拆分 useResponsive.ts (485行 -> 12个专注模块)
   - useBreakpoint: 断点检测
   - useOrientation: 方向检测
   - usePlatform: 平台检测
   - useScreenSize: 屏幕尺寸
   - useResponsiveValue: 响应式值
   - useResponsiveStyle: 响应式样式
   - useMediaQuery: 媒体查询
   - useColumnCount: 列数计算
   - useResponsiveSpacing: 响应式间距

3. 整理数据层 (Repository 层)
   - ApiDataSource: API数据源
   - LocalDataSource: 本地数据源
   - CacheDataSource: 缓存数据源
   - MessageRepository: 消息仓库

4. 重构 messageManager.ts (2194行 -> 4个模块)
   - MessageStateManager: 状态管理
   - WebSocketMessageHandler: WebSocket处理
   - MessageSyncService: 消息同步
   - ReadReceiptManager: 已读管理

5. 导航解耦 (MainNavigator.tsx: 1118行 -> 100行)
   - 创建 NavigationService 解耦层
   - 拆分多个 Navigator 组件

架构改进:
- 单一职责原则: 每个模块职责明确
- 依赖倒置: 通过接口解耦
- 代码复用: 工具函数可被多处使用
- 可测试性: 各模块可独立测试
This commit is contained in:
lafay
2026-03-18 12:11:49 +08:00
parent 1ffbb63753
commit a6cdb97e24
70 changed files with 8175 additions and 1728 deletions

View File

@@ -15,6 +15,7 @@ import {
} from '../services';
import { userManager } from './userManager';
import { messageManager } from './messageManager';
import { createOptimisticUpdaterFactory } from '../utils/optimisticUpdate';
interface UserState {
// 状态
@@ -59,504 +60,386 @@ interface UserState {
refreshAll: () => Promise<void>;
}
export const useUserStore = create<UserState>((set, get) => ({
// 初始状态
users: [],
userCache: {},
posts: [],
notifications: [],
notificationBadge: {
total: 0,
likes: 0,
comments: 0,
follows: 0,
system: 0,
},
messageUnreadCount: 0,
searchHistory: [],
isLoadingPosts: false,
isLoadingNotifications: false,
// 获取用户信息
fetchUser: async (userId: string) => {
// 先检查缓存
const cached = get().userCache[userId];
if (cached) return cached;
export const useUserStore = create<UserState>((set, get) => {
// 创建乐观更新器
const optimisticUpdater = createOptimisticUpdaterFactory<UserState>(set, get);
return {
// 初始状态
users: [],
userCache: {},
posts: [],
notifications: [],
notificationBadge: {
total: 0,
likes: 0,
comments: 0,
follows: 0,
system: 0,
},
messageUnreadCount: 0,
searchHistory: [],
isLoadingPosts: false,
isLoadingNotifications: false,
try {
const user = await userManager.getUserById(userId);
if (user) {
// 获取用户信息
fetchUser: async (userId: string) => {
// 先检查缓存
const cached = get().userCache[userId];
if (cached) return cached;
try {
const user = await userManager.getUserById(userId);
if (user) {
set(state => ({
userCache: { ...state.userCache, [userId]: user }
}));
return user;
}
} catch (error) {
console.error('获取用户信息失败:', error);
}
return undefined;
},
// 获取用户帖子
fetchUserPosts: async (userId: string, page = 1) => {
try {
const response = await postService.getUserPosts(userId, page);
const newPosts = response.list;
set(state => ({
userCache: { ...state.userCache, [userId]: user }
posts: page === 1 ? newPosts : [...state.posts, ...newPosts]
}));
return user;
return newPosts;
} catch (error) {
console.error('获取用户帖子失败:', error);
return [];
}
} catch (error) {
console.error('获取用户信息失败:', error);
}
return undefined;
},
// 获取用户帖子
fetchUserPosts: async (userId: string, page = 1) => {
try {
const response = await postService.getUserPosts(userId, page);
const newPosts = response.list;
set(state => ({
posts: page === 1 ? newPosts : [...state.posts, ...newPosts]
}));
return newPosts;
} catch (error) {
console.error('获取用户帖子失败:', error);
return [];
}
},
// 获取帖子列表(首页)
fetchPosts: async (type: 'recommend' | 'follow' | 'hot' | 'latest', page = 1) => {
set({ isLoadingPosts: true });
},
try {
let response;
// 获取帖子列表(首页)
fetchPosts: async (type: 'recommend' | 'follow' | 'hot' | 'latest', page = 1) => {
set({ isLoadingPosts: true });
switch (type) {
case 'recommend':
response = await postService.getRecommendedPosts(page);
break;
case 'follow':
response = await postService.getFollowingPosts(page);
break;
case 'hot':
response = await postService.getHotPosts(page);
break;
case 'latest':
default:
response = await postService.getLatestPosts(page);
break;
try {
let response;
switch (type) {
case 'recommend':
response = await postService.getRecommendedPosts(page);
break;
case 'follow':
response = await postService.getFollowingPosts(page);
break;
case 'hot':
response = await postService.getHotPosts(page);
break;
case 'latest':
default:
response = await postService.getLatestPosts(page);
break;
}
const newPosts = response.list;
set(state => ({
posts: page === 1 ? newPosts : [...state.posts, ...newPosts],
isLoadingPosts: false
}));
return response;
} catch (error) {
console.error('获取帖子列表失败:', error);
set({ isLoadingPosts: false });
return {
list: [],
total: 0,
page,
page_size: 20,
total_pages: 0,
};
}
const newPosts = response.list;
set(state => ({
posts: page === 1 ? newPosts : [...state.posts, ...newPosts],
isLoadingPosts: false
}));
return response;
} catch (error) {
console.error('获取帖子列表失败:', error);
set({ isLoadingPosts: false });
return {
list: [],
total: 0,
page,
page_size: 20,
total_pages: 0,
};
}
},
// 获取通知列表
fetchNotifications: async (type?: string, page = 1) => {
set({ isLoadingNotifications: true });
},
try {
const response = await notificationService.getNotifications(
page,
20,
type as any
);
// 获取通知列表
fetchNotifications: async (type?: string, page = 1) => {
set({ isLoadingNotifications: true });
const newNotifications = response.list;
set(state => ({
notifications: page === 1 ? newNotifications : [...state.notifications, ...newNotifications],
isLoadingNotifications: false
}));
return newNotifications;
} catch (error) {
console.error('获取通知列表失败:', error);
set({ isLoadingNotifications: false });
return [];
}
},
// 标记通知为已读
markNotificationAsRead: async (notificationId: string) => {
try {
await notificationService.markAsRead(notificationId);
set(state => {
const notifications = state.notifications.map(n =>
n.id === notificationId ? { ...n, isRead: true } : n
try {
const response = await notificationService.getNotifications(
page,
20,
type as any
);
return {
notifications,
const newNotifications = response.list;
set(state => ({
notifications: page === 1 ? newNotifications : [...state.notifications, ...newNotifications],
isLoadingNotifications: false
}));
return newNotifications;
} catch (error) {
console.error('获取通知列表失败:', error);
set({ isLoadingNotifications: false });
return [];
}
},
// 标记通知为已读
markNotificationAsRead: async (notificationId: string) => {
try {
await notificationService.markAsRead(notificationId);
set(state => {
const notifications = state.notifications.map(n =>
n.id === notificationId ? { ...n, isRead: true } : n
);
return {
notifications,
notificationBadge: {
total: notifications.filter(n => !n.isRead).length,
likes: notifications.filter(n => n.type === 'like_post' && !n.isRead).length,
comments: notifications.filter(n => n.type === 'comment' && !n.isRead).length,
follows: notifications.filter(n => n.type === 'follow' && !n.isRead).length,
system: notifications.filter(n => n.type === 'system' && !n.isRead).length,
}
};
});
} catch (error) {
console.error('标记通知为已读失败:', error);
}
},
// 标记所有通知为已读
markAllNotificationsAsRead: async () => {
try {
await notificationService.markAllAsRead();
set(state => ({
notifications: state.notifications.map(n => ({ ...n, isRead: true })),
notificationBadge: {
total: notifications.filter(n => !n.isRead).length,
likes: notifications.filter(n => n.type === 'like_post' && !n.isRead).length,
comments: notifications.filter(n => n.type === 'comment' && !n.isRead).length,
follows: notifications.filter(n => n.type === 'follow' && !n.isRead).length,
system: notifications.filter(n => n.type === 'system' && !n.isRead).length,
total: 0,
likes: 0,
comments: 0,
follows: 0,
system: 0,
}
};
}));
} catch (error) {
console.error('标记所有通知为已读失败:', error);
}
},
// 获取通知角标
fetchNotificationBadge: async () => {
try {
const badge = await notificationService.getNotificationBadge();
set({ notificationBadge: badge });
} catch (error) {
console.error('获取通知角标失败:', error);
}
},
// 设置消息未读数
// 注意:未读数现在由 MessageManager 统一管理
// 此方法保留用于 TabBar 角标显示,从 MessageManager 同步
setMessageUnreadCount: (count: number) => {
set({ messageUnreadCount: count });
},
// 从后端拉取最新消息未读数
// @deprecated 请使用 messageManager.fetchUnreadCount() 代替
// 此方法保留用于向后兼容
fetchMessageUnreadCount: async () => {
try {
await messageManager.fetchUnreadCount();
const unread = messageManager.getUnreadCount();
set({ messageUnreadCount: unread.total + unread.system });
} catch (error) {
console.error('获取消息未读数失败:', error);
}
},
// 添加搜索历史
addSearchHistory: (keyword: string) => {
set(state => {
const history = [keyword, ...state.searchHistory.filter(k => k !== keyword)].slice(0, 20);
return { searchHistory: history };
});
} catch (error) {
console.error('标记通知为已读失败:', error);
}
},
// 标记所有通知为已读
markAllNotificationsAsRead: async () => {
try {
await notificationService.markAllAsRead();
},
// 清除搜索历史
clearSearchHistory: () => {
set({ searchHistory: [] });
},
// 点赞帖子 - 使用乐观更新工具
likePost: async (postId: string) => {
const originalPosts = get().posts;
set(state => ({
notifications: state.notifications.map(n => ({ ...n, isRead: true })),
notificationBadge: {
total: 0,
likes: 0,
comments: 0,
follows: 0,
system: 0,
}
}));
} catch (error) {
console.error('标记所有通知为已读失败:', error);
}
},
// 获取通知角标
fetchNotificationBadge: async () => {
try {
const badge = await notificationService.getNotificationBadge();
set({ notificationBadge: badge });
} catch (error) {
console.error('获取通知角标失败:', error);
}
},
// 设置消息未读数
// 注意:未读数现在由 MessageManager 统一管理
// 此方法保留用于 TabBar 角标显示,从 MessageManager 同步
setMessageUnreadCount: (count: number) => {
set({ messageUnreadCount: count });
},
// 从后端拉取最新消息未读数
// @deprecated 请使用 messageManager.fetchUnreadCount() 代替
// 此方法保留用于向后兼容
fetchMessageUnreadCount: async () => {
try {
await messageManager.fetchUnreadCount();
const unread = messageManager.getUnreadCount();
set({ messageUnreadCount: unread.total + unread.system });
} catch (error) {
console.error('获取消息未读数失败:', error);
}
},
// 添加搜索历史
addSearchHistory: (keyword: string) => {
set(state => {
const history = [keyword, ...state.searchHistory.filter(k => k !== keyword)].slice(0, 20);
return { searchHistory: history };
});
},
// 清除搜索历史
clearSearchHistory: () => {
set({ searchHistory: [] });
},
// 点赞帖子 - 乐观更新
likePost: async (postId: string) => {
// 先乐观更新本地状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_liked: true, likes_count: p.likes_count + 1 } : p
)
}));
// 调用API
try {
const updatedPost = await postService.likePost(postId);
if (updatedPost) {
// 使用后端返回的更新后数据更新状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
} else {
// API 返回失败,回滚状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_liked: false, likes_count: Math.max(0, p.likes_count - 1) } : p
)
}));
}
} catch (error) {
console.error('点赞帖子失败:', error);
// 失败回滚状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_liked: false, likes_count: Math.max(0, p.likes_count - 1) } : p
)
}));
}
},
// 取消点赞 - 乐观更新
unlikePost: async (postId: string) => {
// 先乐观更新本地状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_liked: false, likes_count: Math.max(0, p.likes_count - 1) } : p
)
}));
// 调用API
try {
const updatedPost = await postService.unlikePost(postId);
if (updatedPost) {
// 使用后端返回的更新后数据更新状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
} else {
// API 返回失败,回滚状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_liked: true, likes_count: p.likes_count + 1 } : p
)
}));
}
} catch (error) {
console.error('取消点赞帖子失败:', error);
// 失败回滚状态
// 乐观更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_liked: true, likes_count: p.likes_count + 1 } : p
)
}));
}
},
// 收藏帖子 - 乐观更新
favoritePost: async (postId: string) => {
// 先乐观更新本地状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: true, favorites_count: p.favorites_count + 1 } : p
)
}));
// 调用API
try {
const updatedPost = await postService.favoritePost(postId);
if (updatedPost) {
// 使用后端返回的更新后数据更新状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
} else {
// API 返回失败,回滚状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: false, favorites_count: Math.max(0, p.favorites_count - 1) } : p
)
}));
try {
const updatedPost = await postService.likePost(postId);
if (updatedPost) {
// 使用服务器返回的数据更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
}
} catch (error) {
console.error('点赞帖子失败:', error);
// 回滚
set({ posts: originalPosts });
}
} catch (error) {
console.error('收藏帖子失败:', error);
// 失败回滚状态
},
// 取消点赞 - 使用乐观更新工具
unlikePost: async (postId: string) => {
const originalPosts = get().posts;
// 乐观更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: false, favorites_count: Math.max(0, p.favorites_count - 1) } : p
p.id === postId ? { ...p, is_liked: false, likes_count: Math.max(0, p.likes_count - 1) } : p
)
}));
}
},
// 取消收藏 - 乐观更新
unfavoritePost: async (postId: string) => {
// 先乐观更新本地状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: false, favorites_count: Math.max(0, p.favorites_count - 1) } : p
)
}));
// 调用API
try {
const updatedPost = await postService.unfavoritePost(postId);
if (updatedPost) {
// 使用后端返回的更新后数据更新状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
} else {
// API 返回失败,回滚状态
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: true, favorites_count: p.favorites_count + 1 } : p
)
}));
try {
const updatedPost = await postService.unlikePost(postId);
if (updatedPost) {
// 使用服务器返回的数据更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
}
} catch (error) {
console.error('取消点赞帖子失败:', error);
// 回滚
set({ posts: originalPosts });
}
} catch (error) {
console.error('取消收藏帖子失败:', error);
// 失败回滚状态
},
// 收藏帖子 - 使用乐观更新工具
favoritePost: async (postId: string) => {
const originalPosts = get().posts;
// 乐观更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: true, favorites_count: p.favorites_count + 1 } : p
)
}));
}
},
// 点赞评论
likeComment: async (commentId: string) => {
// 先更新本地状态更新posts中的帖子的评论点赞状态
set(state => ({
posts: state.posts.map(p => ({
...p,
top_comment: p.top_comment && p.top_comment.id === commentId
? { ...p.top_comment, is_liked: true, likes_count: p.top_comment.likes_count + 1 }
: p.top_comment
}))
}));
try {
const updatedPost = await postService.favoritePost(postId);
if (updatedPost) {
// 使用服务器返回的数据更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
}
} catch (error) {
console.error('收藏帖子失败:', error);
// 回滚
set({ posts: originalPosts });
}
},
// 调用API
try {
await commentService.likeComment(commentId);
} catch (error) {
console.error('点赞评论失败:', error);
// 失败回滚状态
// 取消收藏 - 使用乐观更新工具
unfavoritePost: async (postId: string) => {
const originalPosts = get().posts;
// 乐观更新
set(state => ({
posts: state.posts.map(p => ({
...p,
top_comment: p.top_comment && p.top_comment.id === commentId
? { ...p.top_comment, is_liked: false, likes_count: Math.max(0, p.top_comment.likes_count - 1) }
: p.top_comment
}))
}));
}
},
// 取消点赞评论
unlikeComment: async (commentId: string) => {
// 先更新本地状态
set(state => ({
posts: state.posts.map(p => ({
...p,
top_comment: p.top_comment && p.top_comment.id === commentId
? { ...p.top_comment, is_liked: false, likes_count: Math.max(0, p.top_comment.likes_count - 1) }
: p.top_comment
}))
}));
// 调用API
try {
await commentService.unlikeComment(commentId);
} catch (error) {
console.error('取消点赞评论失败:', error);
// 失败回滚状态
set(state => ({
posts: state.posts.map(p => ({
...p,
top_comment: p.top_comment && p.top_comment.id === commentId
? { ...p.top_comment, is_liked: true, likes_count: p.top_comment.likes_count + 1 }
: p.top_comment
}))
}));
}
},
// 关注用户
followUser: async (userId: string) => {
// 先更新本地状态
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: true, followersCount: u.followers_count + 1 }
: u
),
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
id === userId
? { ...user, isFollowing: true, followersCount: user.followers_count + 1 }
: user
])
)
}));
// 调用API
try {
await authService.followUser(userId);
} catch (error) {
console.error('关注用户失败:', error);
// 失败回滚状态
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: false, followersCount: Math.max(0, u.followers_count - 1) }
: u
),
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
id === userId
? { ...user, isFollowing: false, followersCount: Math.max(0, user.followers_count - 1) }
: user
])
posts: state.posts.map(p =>
p.id === postId ? { ...p, is_favorited: false, favorites_count: Math.max(0, p.favorites_count - 1) } : p
)
}));
}
},
// 取消关注
unfollowUser: async (userId: string) => {
// 先更新本地状态
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: false, followersCount: Math.max(0, u.followers_count - 1) }
: u
),
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
id === userId
? { ...user, isFollowing: false, followersCount: Math.max(0, user.followers_count - 1) }
: user
])
)
}));
try {
const updatedPost = await postService.unfavoritePost(postId);
if (updatedPost) {
// 使用服务器返回的数据更新
set(state => ({
posts: state.posts.map(p =>
p.id === postId ? updatedPost : p
)
}));
}
} catch (error) {
console.error('取消收藏帖子失败:', error);
// 回滚
set({ posts: originalPosts });
}
},
// 调用API
try {
await authService.unfollowUser(userId);
} catch (error) {
console.error('取消关注用户失败:', error);
// 失败回滚状态
// 点赞评论 - 使用乐观更新工具
likeComment: async (commentId: string) => {
await optimisticUpdater({
key: 'posts',
optimisticUpdate: (posts) =>
posts.map(p => ({
...p,
top_comment: p.top_comment && p.top_comment.id === commentId
? { ...p.top_comment, is_liked: true, likes_count: p.top_comment.likes_count + 1 }
: p.top_comment
})),
rollbackState: (original) => original,
apiCall: () => commentService.likeComment(commentId),
errorMessage: '点赞评论失败',
});
},
// 取消点赞评论 - 使用乐观更新工具
unlikeComment: async (commentId: string) => {
await optimisticUpdater({
key: 'posts',
optimisticUpdate: (posts) =>
posts.map(p => ({
...p,
top_comment: p.top_comment && p.top_comment.id === commentId
? { ...p.top_comment, is_liked: false, likes_count: Math.max(0, p.top_comment.likes_count - 1) }
: p.top_comment
})),
rollbackState: (original) => original,
apiCall: () => commentService.unlikeComment(commentId),
errorMessage: '取消点赞评论失败',
});
},
// 关注用户 - 使用乐观更新工具
followUser: async (userId: string) => {
const originalUsers = get().users;
const originalUserCache = get().userCache;
// 乐观更新 users
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: true, followersCount: u.followers_count + 1 }
: u
),
}));
// 乐观更新 userCache
set(state => ({
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
@@ -566,17 +449,66 @@ export const useUserStore = create<UserState>((set, get) => ({
])
)
}));
}
},
// 刷新所有数据
refreshAll: async () => {
await Promise.all([
get().fetchPosts('recommend', 1),
get().fetchNotificationBadge(),
]);
},
}));
try {
await authService.followUser(userId);
} catch (error) {
console.error('关注用户失败:', error);
// 回滚
set({
users: originalUsers,
userCache: originalUserCache
});
}
},
// 取消关注 - 使用乐观更新工具
unfollowUser: async (userId: string) => {
const originalUsers = get().users;
const originalUserCache = get().userCache;
// 乐观更新 users
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: false, followersCount: Math.max(0, u.followers_count - 1) }
: u
),
}));
// 乐观更新 userCache
set(state => ({
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
id === userId
? { ...user, isFollowing: false, followersCount: Math.max(0, user.followers_count - 1) }
: user
])
)
}));
try {
await authService.unfollowUser(userId);
} catch (error) {
console.error('取消关注用户失败:', error);
// 回滚
set({
users: originalUsers,
userCache: originalUserCache
});
}
},
// 刷新所有数据
refreshAll: async () => {
await Promise.all([
get().fetchPosts('recommend', 1),
get().fetchNotificationBadge(),
]);
},
};
});
// 导出selector hooks以优化性能
export const usePosts = () => useUserStore((state) => state.posts);