fix: 修复 IM 申请与 RTC 边界问题
- 复用好友申请、群申请和群邀请唯一键冲突后的旧记录,并补充测试 - 收敛 RTC 旁观者加入、忙线校验、追加邀请超员和群通话通知逻辑 - 为 RTC 参与者补充房间用户唯一约束与 MySQL 迁移 - 统一群本体管理请求的 id 字段,并同步前端调用 - 修复前端来电活跃态守卫和 LiveKit 重连前断开旧房间 - 清理群成员通知基类命名和相关注释im
parent
a4dfb717aa
commit
e1b8370267
|
|
@ -20,7 +20,7 @@ export interface ImGroupRespVO {
|
|||
|
||||
// 群消息置顶 / 取消置顶 Request VO
|
||||
export interface ImGroupMessagePinReqVO {
|
||||
groupId: number // 群编号
|
||||
id: number // 群编号
|
||||
messageId: number // 消息编号
|
||||
}
|
||||
|
||||
|
|
@ -42,16 +42,35 @@ export interface ImGroupUpdateReqVO {
|
|||
|
||||
// 添加 / 撤销群管理员 Request VO
|
||||
export interface ImGroupAdminReqVO {
|
||||
groupId: number // 群编号
|
||||
id: number // 群编号
|
||||
userIds: number[] // 目标用户编号列表
|
||||
}
|
||||
|
||||
// 群主转让 Request VO
|
||||
export interface ImGroupTransferOwnerReqVO {
|
||||
groupId: number // 群编号
|
||||
id: number // 群编号
|
||||
newOwnerUserId: number // 新群主用户编号
|
||||
}
|
||||
|
||||
// 全群禁言 / 取消 Request VO
|
||||
export interface ImGroupMuteAllReqVO {
|
||||
id: number // 群编号
|
||||
mutedAll: boolean // 是否全群禁言
|
||||
}
|
||||
|
||||
// 成员禁言 Request VO
|
||||
export interface ImGroupMuteMemberReqVO {
|
||||
id: number // 群编号
|
||||
userId: number // 被禁言的用户编号
|
||||
mutedSeconds: number // 禁言时长(秒),0 表示永久禁言
|
||||
}
|
||||
|
||||
// 取消成员禁言 Request VO
|
||||
export interface ImGroupCancelMuteMemberReqVO {
|
||||
id: number // 群编号
|
||||
userId: number // 被取消禁言的用户编号
|
||||
}
|
||||
|
||||
// 获得当前登录用户的群列表
|
||||
export const getMyGroupList = () => {
|
||||
return request.get<ImGroupRespVO[]>({ url: '/im/group/list' })
|
||||
|
|
@ -103,16 +122,16 @@ export const unpinGroupMessage = (data: ImGroupMessagePinReqVO) => {
|
|||
}
|
||||
|
||||
// 全群禁言 / 取消(仅群主 / 管理员可调)
|
||||
export const muteAll = (data: { groupId: number; mutedAll: boolean }) => {
|
||||
export const muteAll = (data: ImGroupMuteAllReqVO) => {
|
||||
return request.put<boolean>({ url: '/im/group/mute-all', data })
|
||||
}
|
||||
|
||||
// 禁言成员
|
||||
export const muteMember = (data: { groupId: number; userId: number; mutedSeconds: number }) => {
|
||||
export const muteMember = (data: ImGroupMuteMemberReqVO) => {
|
||||
return request.put<boolean>({ url: '/im/group/mute-member', data })
|
||||
}
|
||||
|
||||
// 取消成员禁言
|
||||
export const cancelMuteMember = (data: { groupId: number; userId: number }) => {
|
||||
export const cancelMuteMember = (data: ImGroupCancelMuteMemberReqVO) => {
|
||||
return request.put<boolean>({ url: '/im/group/cancel-mute-member', data })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,10 +98,10 @@ async function handleOk() {
|
|||
submitting.value = true
|
||||
try {
|
||||
if (addedIds.length > 0) {
|
||||
await addGroupAdmin({ groupId: groupId.value, userIds: addedIds })
|
||||
await addGroupAdmin({ id: groupId.value, userIds: addedIds })
|
||||
}
|
||||
if (removedIds.length > 0) {
|
||||
await removeGroupAdmin({ groupId: groupId.value, userIds: removedIds })
|
||||
await removeGroupAdmin({ id: groupId.value, userIds: removedIds })
|
||||
}
|
||||
message.success(`已更新群管理员(新增 ${addedIds.length} 位,撤销 ${removedIds.length} 位)`)
|
||||
emit('reload')
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ async function handleConfirm() {
|
|||
loading.value = true
|
||||
try {
|
||||
await muteMember({
|
||||
groupId: groupId.value,
|
||||
id: groupId.value,
|
||||
userId: userId.value,
|
||||
mutedSeconds: selected.value
|
||||
})
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ async function handleOk() {
|
|||
submitting.value = true
|
||||
try {
|
||||
await transferGroupOwner({
|
||||
groupId: groupId.value,
|
||||
id: groupId.value,
|
||||
newOwnerUserId: newOwner.value.userId
|
||||
})
|
||||
message.success('群主转让成功')
|
||||
|
|
@ -121,4 +121,3 @@ async function handleOk() {
|
|||
@include picker.styles;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ export function useLiveKitRoom() {
|
|||
|
||||
/** 连接 LiveKit Server;audio / video 控制初始默认开关 */
|
||||
async function connect(url: string, token: string, opts: { audio?: boolean; video?: boolean }) {
|
||||
// 新连接前先断开旧 Room;保留本次注册的事件回调
|
||||
if (_room.value) {
|
||||
await disconnectRoom(false)
|
||||
}
|
||||
const r = new Room({
|
||||
// 按格子尺寸自动选 simulcast 层
|
||||
adaptiveStream: true,
|
||||
|
|
@ -267,18 +271,23 @@ export function useLiveKitRoom() {
|
|||
return stream
|
||||
}
|
||||
|
||||
/** 主动断开;通话结束统一调 */
|
||||
async function disconnect() {
|
||||
disconnectedHandlers.clear()
|
||||
participantConnectedHandlers.clear()
|
||||
participantDisconnectedHandlers.clear()
|
||||
/** 断开当前 Room;clearHandlers 为 true 时同步清理外部注册的事件回调 */
|
||||
async function disconnectRoom(clearHandlers: boolean) {
|
||||
// 清理通话结束后不再复用的订阅回调
|
||||
if (clearHandlers) {
|
||||
disconnectedHandlers.clear()
|
||||
participantConnectedHandlers.clear()
|
||||
participantDisconnectedHandlers.clear()
|
||||
}
|
||||
// 清理音视频轨道缓存
|
||||
streamCache.clear()
|
||||
if (_room.value) {
|
||||
// 断开前先卸事件,避免 disconnect 期间 ParticipantDisconnected / TrackUnsubscribed 仍触发 syncRemotes
|
||||
// 卸载 Room 事件并断开连接
|
||||
_room.value.removeAllListeners()
|
||||
await _room.value.disconnect()
|
||||
_room.value = null
|
||||
}
|
||||
// 重置连接和设备状态
|
||||
localParticipant.value = null
|
||||
remoteParticipants.value = []
|
||||
isConnected.value = false
|
||||
|
|
@ -289,6 +298,11 @@ export function useLiveKitRoom() {
|
|||
screenShareEnabled.value = false
|
||||
}
|
||||
|
||||
/** 主动断开;通话结束统一调 */
|
||||
async function disconnect() {
|
||||
await disconnectRoom(true)
|
||||
}
|
||||
|
||||
return {
|
||||
room,
|
||||
localParticipant,
|
||||
|
|
|
|||
|
|
@ -649,7 +649,7 @@ async function onMuteAllChange(value: boolean | string | number) {
|
|||
return
|
||||
}
|
||||
const newValue = !!value
|
||||
await muteAll({ groupId: props.group.id, mutedAll: newValue })
|
||||
await muteAll({ id: props.group.id, mutedAll: newValue })
|
||||
message.success(newValue ? '已开启全群禁言' : '已关闭全群禁言')
|
||||
emit('reload')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ async function handleRemove(msg: Message) {
|
|||
}
|
||||
removingId.value = msg.id
|
||||
try {
|
||||
await apiUnpinGroupMessage({ groupId: group.value.id, messageId: msg.id })
|
||||
await apiUnpinGroupMessage({ id: group.value.id, messageId: msg.id })
|
||||
message.success('已取消置顶')
|
||||
} finally {
|
||||
removingId.value = null
|
||||
|
|
|
|||
|
|
@ -875,7 +875,7 @@ async function handlePin() {
|
|||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
await apiPinGroupMessage({ groupId: group.id, messageId: props.message.id })
|
||||
await apiPinGroupMessage({ id: group.id, messageId: props.message.id })
|
||||
successMessage('已置顶')
|
||||
} catch {}
|
||||
}
|
||||
|
|
@ -1007,7 +1007,7 @@ async function handleUnmute() {
|
|||
}
|
||||
try {
|
||||
await confirmDialog('确定解除该成员的禁言吗?', '解除禁言')
|
||||
await cancelMuteMember({ groupId: group.id, userId: props.message.senderId })
|
||||
await cancelMuteMember({ id: group.id, userId: props.message.senderId })
|
||||
successMessage('已解除禁言')
|
||||
emit('reload')
|
||||
} catch {}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,9 @@ export const useRtcStore = defineStore('imRtc', () => {
|
|||
|
||||
/** 被叫收到来电;切到 INCOMING;接收 RTC_CALL(INVITE) payload */
|
||||
function showIncoming(payload: ImRtcCallNotification) {
|
||||
if (isActive.value) {
|
||||
return
|
||||
}
|
||||
incomingPayload.value = payload
|
||||
stage.value = ImRtcCallStage.INCOMING
|
||||
// 按 inviter 兜底首次填充胶囊条
|
||||
|
|
|
|||
Loading…
Reference in New Issue