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