crush-level-web/docs/MessageLikeFeature.md

245 lines
7.5 KiB
Markdown
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.

# 消息点赞功能实现
## 概述
本功能基于网易云信 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 (
<div>
<button
onClick={handleLike}
className={currentStatus === MessageLikeStatus.Liked ? 'active' : ''}
>
👍
</button>
<button
onClick={handleDislike}
className={currentStatus === MessageLikeStatus.Disliked ? 'active' : ''}
>
👎
</button>
</div>
);
};
```
### 高级用法
```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实现