feat(im): 增加频道消息的已读状态

im
YunaiV 2026-05-20 01:00:46 +08:00
parent 30b963149a
commit fc812aef26
5 changed files with 54 additions and 11 deletions

View File

@ -6,6 +6,7 @@ export interface ImManagerChannelMessageVO {
channelName?: string
materialId: number
materialTitle?: string
materialCoverUrl?: string
type: number
content?: string
receiverUserIds?: number[]

View File

@ -6,6 +6,8 @@ export interface ImChannelMessageRespVO {
materialId: number
type: number
content: string
/** 当前用户已读态pull 时按 Redis 游标计算填充,多端同步使用 */
status?: number
sendTime: string
}
@ -16,3 +18,11 @@ export const pullChannelMessages = (params: { minId: number; size?: number }) =>
params
})
}
// 上报频道消息已读位置;切到频道会话或拉到新消息后调
export const readChannelMessages = (channelId: number, messageId: number) => {
return request.put({
url: '/im/channel/message/read',
params: { channelId, messageId }
})
}

View File

@ -19,6 +19,7 @@ import {
} from '@/api/im/message/channel'
import {
ImConversationType,
ImMessageStatus,
ImMessageType,
isFriendChatTip,
isFriendNotification
@ -96,7 +97,7 @@ export const useMessagePuller = () => {
clientMessageId: '',
type: message.type,
content: message.content,
status: 0, // 频道消息无状态机;占位 UNREAD
status: message.status ?? ImMessageStatus.UNREAD,
sendTime: new Date(message.sendTime).getTime(),
senderId: 0, // 系统下发,无发送人
targetId: message.channelId, // 会话归属到频道编号

View File

@ -10,6 +10,7 @@ import {
readGroupMessages as apiReadGroupMessages,
recallGroupMessage as apiRecallGroupMessage
} from '@/api/im/message/group'
import { readChannelMessages as apiReadChannelMessages } from '@/api/im/message/channel'
import {
generateClientMessageId,
serializeMessage,
@ -233,19 +234,24 @@ export const useMessageSender = () => {
// 接口调用:按会话类型分发,并按对应已读开关控制;失败仅记录日志,不回退本地已读状态
const isPrivate = conversation.type === ImConversationType.PRIVATE
const isGroup = conversation.type === ImConversationType.GROUP
// 频道目前不上报已读
// TODO @AI频道已读应该还是要上报的同步到别的端。但是不用记录 status 字段。
if (!isPrivate && !isGroup) {
const isChannel = conversation.type === ImConversationType.CHANNEL
if (!isPrivate && !isGroup && !isChannel) {
return
}
const readEnabled = isPrivate ? MESSAGE_PRIVATE_READ_ENABLED : MESSAGE_GROUP_READ_ENABLED
if (!readEnabled) {
if (isPrivate && !MESSAGE_PRIVATE_READ_ENABLED) {
return
}
if (isGroup && !MESSAGE_GROUP_READ_ENABLED) {
return
}
try {
await (isPrivate
? apiReadPrivateMessages(conversation.targetId, maxMessageId)
: apiReadGroupMessages(conversation.targetId, maxMessageId))
if (isPrivate) {
await apiReadPrivateMessages(conversation.targetId, maxMessageId)
} else if (isGroup) {
await apiReadGroupMessages(conversation.targetId, maxMessageId)
} else {
await apiReadChannelMessages(conversation.targetId, maxMessageId)
}
} catch (e) {
console.error(
'[IM] 标记已读失败',

View File

@ -5,6 +5,7 @@ import { useUserStore } from '@/store/modules/user'
import {
ImWebSocketMessageType,
ImMessageStatus,
ImMessageType,
ImConversationType,
ImRtcParticipantStatus,
@ -213,13 +214,37 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
this.dispatchGroupFrame(content as ImGroupMessageDTO)
break
case ImWebSocketMessageType.CHANNEL_MESSAGE:
this.handleChannelMessage(content as ImChannelMessageRespVO)
this.dispatchChannelFrame(content as ImChannelMessageRespVO)
break
default:
console.debug('[IM WS] 未识别事件', frame)
}
},
/**
* payload.type READ
*/
dispatchChannelFrame(websocketMessage: ImChannelMessageRespVO) {
if (websocketMessage.type === ImMessageType.READ) {
this.handleChannelRead(websocketMessage)
return
}
this.handleChannelMessage(websocketMessage)
},
/** 频道 READ自己其它终端在某频道里标为已读本端同步清零该频道未读 */
handleChannelRead(websocketMessage: ImChannelMessageRespVO) {
const conversationStore = useConversationStore()
const conversation = conversationStore.getConversation(
ImConversationType.CHANNEL,
websocketMessage.channelId
)
if (conversation) {
conversation.unreadCount = 0
}
conversationStore.saveConversations()
},
/**
* + insertMessage
* pull WS id conversationStore.insertMessage id
@ -245,7 +270,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
clientMessageId: '',
type: websocketMessage.type,
content: websocketMessage.content,
status: 0,
status: ImMessageStatus.UNREAD,
sendTime: sendTimeMs,
senderId: 0,
targetId: websocketMessage.channelId,