# 消息点赞功能实现 ## 概述 本功能基于网易云信 NIM Web SDK V2 实现了聊天消息的点赞和踩功能,通过更新消息的 `serverExtension` 字段来持久化存储用户的点赞状态,使用 NIM SDK 的 `modifyMessage` API 实现服务端同步。 ## 功能特性 - ✅ 支持对AI回复消息进行点赞/踩 - ✅ 简洁的状态标记:只记录用户的点赞/踩状态,不统计数量 - ✅ 视觉反馈:点赞后按钮高亮显示 - ✅ 防重复点赞:再次点击取消点赞 - ✅ 状态持久化:通过 NIM SDK 的 `serverExtension` 字段保存到云端 - ✅ 多用户支持:支持多个用户对同一消息进行独立的点赞 - ✅ 自动同步:点赞状态自动同步到所有客户端 - ✅ 轻量级:简化数据结构,减少存储空间 ## 技术实现 ### 1. 数据结构 #### MessageLikeStatus 枚举 ```typescript export enum MessageLikeStatus { None = 'none', // 未点赞/踩 Liked = 'liked', // 已点赞 Disliked = 'disliked' // 已踩 } ``` #### MessageServerExtension 接口(简化版) ```typescript export interface MessageServerExtension { [userId: string]: MessageLikeStatus; // 用户ID -> 点赞状态的直接映射 } ``` #### 工具函数 ```typescript // 解析消息的serverExtension字段 export const parseMessageServerExtension = (serverExtension?: string): MessageServerExtension // 序列化MessageServerExtension对象 export const stringifyMessageServerExtension = (extension: MessageServerExtension): string // 获取用户对消息的点赞状态 export const getUserLikeStatus = (message: ExtendedMessage, userId: string): MessageLikeStatus ``` ### 2. 核心功能 #### NimMsgContext 扩展 在 `NimMsgContext` 中添加了 `updateMessageLikeStatus` 方法,使用 NIM SDK 的 `modifyMessage` API: ```typescript const updateMessageLikeStatus = useCallback(async ( conversationId: string, messageClientId: string, likeStatus: MessageLikeStatus ) => { // 1. 获取当前登录用户ID const currentUserId = nim.V2NIMLoginService.getLoginUser(); // 2. 解析当前消息的serverExtension const currentServerExt = parseMessageServerExtension(targetMessage.serverExtension); // 3. 更新用户的点赞状态(简化版) const newServerExt = { ...currentServerExt }; if (likeStatus === MessageLikeStatus.None) { delete newServerExt[currentUserId]; // 移除点赞状态 } else { newServerExt[currentUserId] = likeStatus; // 设置新状态 } // 4. 调用NIM SDK更新消息 const modifyResult = await nim.V2NIMMessageService.modifyMessage(targetMessage, { serverExtension: stringifyMessageServerExtension(newServerExt) }); // 5. 更新本地状态 addMsg(conversationId, [modifyResult.message], false); }, [addMsg]); ``` #### useMessageLike Hook 提供便捷的点赞操作方法: ```typescript const { likeMessage, // 点赞消息 dislikeMessage, // 踩消息 cancelLikeMessage, // 取消点赞/踩 toggleLike, // 切换点赞状态 toggleDislike, // 切换踩状态 } = useMessageLike(); ``` ### 3. UI组件 #### ChatOtherTextContainer AI消息容器组件已集成点赞功能: - 鼠标悬停显示操作按钮 - 点赞后按钮高亮(红色) - 踩后按钮高亮(灰色) - 显示点赞/踩数量 ## 使用方法 ### 基本用法 ```typescript import { useMessageLike } from '@/hooks/useMessageLike'; import { getUserLikeStatus, MessageLikeStatus } from '@/atoms/im'; import { useNimChat } from '@/context/NimChat/useNimChat'; const MyComponent = ({ message }: { message: ExtendedMessage }) => { const { toggleLike, toggleDislike } = useMessageLike(); const { nim } = useNimChat(); // 获取当前用户的点赞状态 const currentUserId = nim.V2NIMLoginService.getLoginUser(); const currentStatus = getUserLikeStatus(message, currentUserId || ''); const handleLike = async () => { await toggleLike(message.conversationId, message.messageClientId, currentStatus); }; const handleDislike = async () => { await toggleDislike(message.conversationId, message.messageClientId, currentStatus); }; return (
); }; ``` ### 高级用法 ```typescript // 直接设置点赞状态 await likeMessage(conversationId, messageClientId); // 直接设置踩状态 await dislikeMessage(conversationId, messageClientId); // 取消所有状态 await cancelLikeMessage(conversationId, messageClientId); ``` ## 状态管理 点赞状态通过以下方式管理: 1. **服务端状态**: 存储在 `message.serverExtension` 字段中,通过 NIM SDK 同步到云端 2. **多用户支持**: 每个用户的点赞状态独立存储,使用用户ID作为键 3. **简化存储**: 仅存储用户的点赞状态,不计算总数,节省存储空间 4. **状态同步**: 通过 `msgListAtom` 全局状态管理,并通过 NIM SDK 自动同步到所有客户端 5. **持久化**: 点赞状态持久化存储在 NIM 服务器,不会丢失 ## 扩展建议 ### 1. 消息更新监听 由于使用了 NIM SDK 的 `modifyMessage` API,建议监听消息更新事件: ```typescript // 监听消息修改事件 nim.V2NIMMessageService.on('onMessageUpdated', (messages: V2NIMMessage[]) => { messages.forEach(message => { // 处理点赞状态更新 const serverExt = parseMessageServerExtension(message.serverExtension); if (serverExt.likes) { console.log('消息点赞状态已更新:', message.messageClientId, serverExt); } }); }); ``` ### 2. 错误处理 为点赞操作添加错误处理和重试机制: ```typescript const updateMessageLikeStatusWithRetry = async ( conversationId: string, messageClientId: string, likeStatus: MessageLikeStatus, retryCount = 3 ) => { try { await updateMessageLikeStatus(conversationId, messageClientId, likeStatus); } catch (error) { if (retryCount > 0) { console.log(`点赞失败,剩余重试次数: ${retryCount}`, error); await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒 return updateMessageLikeStatusWithRetry(conversationId, messageClientId, likeStatus, retryCount - 1); } else { throw error; } } }; ``` ### 3. 批量操作 对于大量消息的点赞状态批量更新: ```typescript const batchUpdateLikes = (updates: Array<{ conversationId: string; messageClientId: string; likeStatus: MessageLikeStatus; }>) => { // 批量更新逻辑 }; ``` ## 注意事项 1. **性能考虑**: 点赞状态更新会触发组件重渲染,建议使用 React.memo 优化 2. **网络请求**: 每次点赞都会调用 NIM SDK 的 `modifyMessage` API,请考虑网络状况 3. **并发控制**: 快速连续点击可能导致并发请求,建议添加防抖或节流 4. **权限验证**: NIM SDK 会自动验证用户权限,无需额外处理 5. **消息类型限制**: `modifyMessage` API 仅支持特定类型的消息,请参考 NIM 文档 6. **扩展字段大小**: `serverExtension` 字段有大小限制,请合理设计数据结构 ## 相关文件 - `src/atoms/im.ts` - 数据类型定义 - `src/context/NimChat/NimMsgContext.tsx` - 核心逻辑 - `src/hooks/useMessageLike.ts` - 便捷Hook - `src/app/(main)/chat/[aiId]/components/ChatMessageItems/ChatOtherTextContainer.tsx` - UI实现