🐛 fix(im): 私聊已读消费端卡 maxReadId + 上报 messageId 与后端对齐
handlePrivateReceipt 收到对方 RECEIPT 时丢弃了后端编码在 DTO id 字段 的 maxReadId,applyReadReceipt 把会话里所有 selfSend 未撤回消息一刀切 标 READ;回执在路上时刚发的消息会被误标已读。 - applyReadReceipt 的 markPrivateRead 改为 privateReadMaxId,按 id <= maxReadId 卡边界,超过 maxReadId 的自发消息保留原状态; - handlePrivateReceipt 透传 websocketMessage.id 作为 privateReadMaxId; - apiReadPrivateMessages 增加 messageId 形参,与后端新接口对齐; - websocketStore 私聊自动已读用刚到的消息 id;useMessageSender.readActive 把私聊 / 群聊的 maxMessageId 计算合并到调用前。im
parent
a35698fc07
commit
8c1f17f5a6
|
|
@ -38,13 +38,17 @@ export const pullPrivateMessages = (params: { minId: number | string; size: numb
|
|||
}
|
||||
|
||||
// 查询私聊历史消息
|
||||
// TODO @AI:历史消息,是不是通过这个接口?
|
||||
export const getPrivateMessageList = (params: ImPrivateMessageListReqVO) => {
|
||||
return request.get<ImPrivateMessageRespVO[]>({ url: '/im/message/private/list', params })
|
||||
}
|
||||
|
||||
// 标记私聊消息已读
|
||||
export const readPrivateMessages = (receiverId: number | string) => {
|
||||
return request.put<boolean>({ url: '/im/message/private/read', params: { receiverId } })
|
||||
export const readPrivateMessages = (receiverId: number | string, messageId: number | string) => {
|
||||
return request.put<boolean>({
|
||||
url: '/im/message/private/read',
|
||||
params: { receiverId, messageId }
|
||||
})
|
||||
}
|
||||
|
||||
// 撤回私聊消息
|
||||
|
|
|
|||
|
|
@ -512,8 +512,9 @@ export const useConversationStore = defineStore('imConversationStore', {
|
|||
applyReadReceipt(options: {
|
||||
conversationType: number
|
||||
targetId: number
|
||||
// 私聊:把和该好友的「自己发送的」消息标为已读
|
||||
markPrivateRead?: boolean
|
||||
// 私聊:把和该好友的「自己发送的、id <= privateReadMaxId 的」消息标为已读
|
||||
// 必须卡 maxId 边界:回执在路上时新发的消息不能被误标为已读
|
||||
privateReadMaxId?: number
|
||||
// 群聊:针对单条消息的回执刷新
|
||||
groupMessageId?: number
|
||||
readCount?: number
|
||||
|
|
@ -523,9 +524,15 @@ export const useConversationStore = defineStore('imConversationStore', {
|
|||
if (!conversation) {
|
||||
return
|
||||
}
|
||||
if (options.conversationType === ImConversationType.PRIVATE && options.markPrivateRead) {
|
||||
if (options.conversationType === ImConversationType.PRIVATE && options.privateReadMaxId) {
|
||||
const maxReadId = options.privateReadMaxId
|
||||
conversation.messages.forEach((message) => {
|
||||
if (message.selfSend && message.status !== ImMessageStatus.RECALL) {
|
||||
if (
|
||||
message.selfSend &&
|
||||
message.id &&
|
||||
message.id <= maxReadId &&
|
||||
message.status !== ImMessageStatus.RECALL
|
||||
) {
|
||||
message.status = ImMessageStatus.READ
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -318,8 +318,9 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
|
|||
conversationStore.activeConversation?.targetId === peerId
|
||||
if (isActive) {
|
||||
// 聊天窗口打开 = 实际看到了:本端清未读 + 上报后端,让对方 UI 立刻切到"已读"
|
||||
// 已读位置直接用刚到的消息 id(这条就是当前会话最大 id)
|
||||
conversationStore.markActiveAsRead()
|
||||
apiReadPrivateMessages(peerId).catch((e) => {
|
||||
apiReadPrivateMessages(peerId, websocketMessage.id).catch((e) => {
|
||||
console.warn('[IM WS] 自动已读上报失败', e)
|
||||
})
|
||||
} else if (!conversation?.muted) {
|
||||
|
|
@ -342,13 +343,20 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
|
|||
conversationStore.saveConversations()
|
||||
},
|
||||
|
||||
/** 私聊 RECEIPT 事件:对方读了我的消息,把和对方会话里自己发的消息标为已读 */
|
||||
/**
|
||||
* 私聊 RECEIPT 事件:对方读了我的消息,把和对方会话里自己发的消息标为已读
|
||||
* 后端将 maxReadId 编码在 DTO 的 id 字段(见 ImPrivateMessageDTO.ofReceipt),
|
||||
* 这里据此卡边界,避免把"回执在路上时刚发的消息"误标为已读。
|
||||
*/
|
||||
handlePrivateReceipt(websocketMessage: ImPrivateMessageDTO) {
|
||||
if (!websocketMessage.id) {
|
||||
return
|
||||
}
|
||||
const conversationStore = useConversationStore()
|
||||
conversationStore.applyReadReceipt({
|
||||
conversationType: ImConversationType.PRIVATE,
|
||||
targetId: websocketMessage.senderId,
|
||||
markPrivateRead: true
|
||||
privateReadMaxId: websocketMessage.id
|
||||
})
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue