✨ feat(im): 增加频道消息的已读状态
parent
30b963149a
commit
fc812aef26
|
|
@ -6,6 +6,7 @@ export interface ImManagerChannelMessageVO {
|
||||||
channelName?: string
|
channelName?: string
|
||||||
materialId: number
|
materialId: number
|
||||||
materialTitle?: string
|
materialTitle?: string
|
||||||
|
materialCoverUrl?: string
|
||||||
type: number
|
type: number
|
||||||
content?: string
|
content?: string
|
||||||
receiverUserIds?: number[]
|
receiverUserIds?: number[]
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ export interface ImChannelMessageRespVO {
|
||||||
materialId: number
|
materialId: number
|
||||||
type: number
|
type: number
|
||||||
content: string
|
content: string
|
||||||
|
/** 当前用户已读态;pull 时按 Redis 游标计算填充,多端同步使用 */
|
||||||
|
status?: number
|
||||||
sendTime: string
|
sendTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,3 +18,11 @@ export const pullChannelMessages = (params: { minId: number; size?: number }) =>
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上报频道消息已读位置;切到频道会话或拉到新消息后调
|
||||||
|
export const readChannelMessages = (channelId: number, messageId: number) => {
|
||||||
|
return request.put({
|
||||||
|
url: '/im/channel/message/read',
|
||||||
|
params: { channelId, messageId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
} from '@/api/im/message/channel'
|
} from '@/api/im/message/channel'
|
||||||
import {
|
import {
|
||||||
ImConversationType,
|
ImConversationType,
|
||||||
|
ImMessageStatus,
|
||||||
ImMessageType,
|
ImMessageType,
|
||||||
isFriendChatTip,
|
isFriendChatTip,
|
||||||
isFriendNotification
|
isFriendNotification
|
||||||
|
|
@ -96,7 +97,7 @@ export const useMessagePuller = () => {
|
||||||
clientMessageId: '',
|
clientMessageId: '',
|
||||||
type: message.type,
|
type: message.type,
|
||||||
content: message.content,
|
content: message.content,
|
||||||
status: 0, // 频道消息无状态机;占位 UNREAD
|
status: message.status ?? ImMessageStatus.UNREAD,
|
||||||
sendTime: new Date(message.sendTime).getTime(),
|
sendTime: new Date(message.sendTime).getTime(),
|
||||||
senderId: 0, // 系统下发,无发送人
|
senderId: 0, // 系统下发,无发送人
|
||||||
targetId: message.channelId, // 会话归属到频道编号
|
targetId: message.channelId, // 会话归属到频道编号
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
readGroupMessages as apiReadGroupMessages,
|
readGroupMessages as apiReadGroupMessages,
|
||||||
recallGroupMessage as apiRecallGroupMessage
|
recallGroupMessage as apiRecallGroupMessage
|
||||||
} from '@/api/im/message/group'
|
} from '@/api/im/message/group'
|
||||||
|
import { readChannelMessages as apiReadChannelMessages } from '@/api/im/message/channel'
|
||||||
import {
|
import {
|
||||||
generateClientMessageId,
|
generateClientMessageId,
|
||||||
serializeMessage,
|
serializeMessage,
|
||||||
|
|
@ -233,19 +234,24 @@ export const useMessageSender = () => {
|
||||||
// 接口调用:按会话类型分发,并按对应已读开关控制;失败仅记录日志,不回退本地已读状态
|
// 接口调用:按会话类型分发,并按对应已读开关控制;失败仅记录日志,不回退本地已读状态
|
||||||
const isPrivate = conversation.type === ImConversationType.PRIVATE
|
const isPrivate = conversation.type === ImConversationType.PRIVATE
|
||||||
const isGroup = conversation.type === ImConversationType.GROUP
|
const isGroup = conversation.type === ImConversationType.GROUP
|
||||||
// 频道目前不上报已读
|
const isChannel = conversation.type === ImConversationType.CHANNEL
|
||||||
// TODO @AI:频道已读,应该还是要上报的,同步到别的端。但是不用记录 status 字段。
|
if (!isPrivate && !isGroup && !isChannel) {
|
||||||
if (!isPrivate && !isGroup) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const readEnabled = isPrivate ? MESSAGE_PRIVATE_READ_ENABLED : MESSAGE_GROUP_READ_ENABLED
|
if (isPrivate && !MESSAGE_PRIVATE_READ_ENABLED) {
|
||||||
if (!readEnabled) {
|
return
|
||||||
|
}
|
||||||
|
if (isGroup && !MESSAGE_GROUP_READ_ENABLED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await (isPrivate
|
if (isPrivate) {
|
||||||
? apiReadPrivateMessages(conversation.targetId, maxMessageId)
|
await apiReadPrivateMessages(conversation.targetId, maxMessageId)
|
||||||
: apiReadGroupMessages(conversation.targetId, maxMessageId))
|
} else if (isGroup) {
|
||||||
|
await apiReadGroupMessages(conversation.targetId, maxMessageId)
|
||||||
|
} else {
|
||||||
|
await apiReadChannelMessages(conversation.targetId, maxMessageId)
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(
|
||||||
'[IM] 标记已读失败',
|
'[IM] 标记已读失败',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ImWebSocketMessageType,
|
ImWebSocketMessageType,
|
||||||
|
ImMessageStatus,
|
||||||
ImMessageType,
|
ImMessageType,
|
||||||
ImConversationType,
|
ImConversationType,
|
||||||
ImRtcParticipantStatus,
|
ImRtcParticipantStatus,
|
||||||
|
|
@ -213,13 +214,37 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
|
||||||
this.dispatchGroupFrame(content as ImGroupMessageDTO)
|
this.dispatchGroupFrame(content as ImGroupMessageDTO)
|
||||||
break
|
break
|
||||||
case ImWebSocketMessageType.CHANNEL_MESSAGE:
|
case ImWebSocketMessageType.CHANNEL_MESSAGE:
|
||||||
this.handleChannelMessage(content as ImChannelMessageRespVO)
|
this.dispatchChannelFrame(content as ImChannelMessageRespVO)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.debug('[IM WS] 未识别事件', frame)
|
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 即可
|
* 频道消息实时入会话;频道消息单向 + 无状态机,直接 insertMessage 即可
|
||||||
* pull 与 WS 拿到同一条 id 时,conversationStore.insertMessage 内部按 id 去重,不会重复
|
* pull 与 WS 拿到同一条 id 时,conversationStore.insertMessage 内部按 id 去重,不会重复
|
||||||
|
|
@ -245,7 +270,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
|
||||||
clientMessageId: '',
|
clientMessageId: '',
|
||||||
type: websocketMessage.type,
|
type: websocketMessage.type,
|
||||||
content: websocketMessage.content,
|
content: websocketMessage.content,
|
||||||
status: 0,
|
status: ImMessageStatus.UNREAD,
|
||||||
sendTime: sendTimeMs,
|
sendTime: sendTimeMs,
|
||||||
senderId: 0,
|
senderId: 0,
|
||||||
targetId: websocketMessage.channelId,
|
targetId: websocketMessage.channelId,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue