feat(meeting): Task 15 UI 打磨 + 主持人四件套 + 媒体层稳定性回归修复
- Task 15 UI:6 项原创特色(说话者流光 / 柔性网格 / 自视频浮窗 / 静音氛围色 / 入会滑入 / NetworkBadge 3 条波浪)+ 说话者双源探测(RTP audioLevel + WebAudio RMS)+ 主持人四件套(静音/开麦/转让/踢出) - 新增 SelfVideoFloat 浮窗组件 + 4 份 design-system 页面文档(home / preview / room / invite) - 媒体回归补丁(手工联调触发): * 后端 signal_service 在 OnRoomJoin 追加 pushExistingRoomState → 向新加入者补发历史 producers + 历史成员 audio/video 状态 * 新增 Redis Hash memberStateKey 持久化成员 audio/video enabled,OnMemberStateChanged 落盘、cleanupUserResources 清理 * 前端 _broadcastSelfState 在本地音视频开关末尾同步 state.changed;_afterJoined 先 getRoom 再 room.join 修复后入者成员状态时序;_onMemberStateChanged 占位兜底 * _cleanupRemoteProducer 改为按 producerId 精准清理(修复 "关音频误关视频") * _onRoomEnded + createAndEnter/joinAndEnter 强化 reset(修复重入会看不到自己画面) * mediasoup-client ensureSend/RecvTransport 引入 in-flight Promise 锁防并发重复建连 - 文档同步:CURRENT_STATUS.md / Task 15 plan / phase2e-2 implementation plan Made-with: Cursor
This commit is contained in:
@@ -105,7 +105,8 @@ func roomToDTO(r *model.MeetingRoom, onlineCount int) *dto.MeetingRoomDTO {
|
||||
}
|
||||
|
||||
// participantToDTO 将 model.MeetingParticipant 转 DTO
|
||||
func participantToDTO(p *model.MeetingParticipant) *dto.MeetingParticipantDTO {
|
||||
// userMap 可选:若提供则附带 user_name / user_avatar,便于前端 MemberPanel/VideoTile 直接渲染昵称头像
|
||||
func participantToDTO(p *model.MeetingParticipant, userMap map[int64]service.UserDisplayInfo) *dto.MeetingParticipantDTO {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -123,6 +124,10 @@ func participantToDTO(p *model.MeetingParticipant) *dto.MeetingParticipantDTO {
|
||||
if p.LeftAt != nil {
|
||||
out.LeftAt = p.LeftAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if info, ok := userMap[p.UserID]; ok {
|
||||
out.UserName = info.Name
|
||||
out.UserAvatar = info.Avatar
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -190,9 +195,15 @@ func (ctl *MeetingController) GetRoom(c *gin.Context) {
|
||||
ctl.handleError(c, err, "获取会议详情失败")
|
||||
return
|
||||
}
|
||||
userIDs := make([]int64, 0, len(participants))
|
||||
for i := range participants {
|
||||
userIDs = append(userIDs, participants[i].UserID)
|
||||
}
|
||||
userMap := ctl.meetingService.ResolveUsersDisplay(c.Request.Context(), userIDs)
|
||||
|
||||
parts := make([]dto.MeetingParticipantDTO, 0, len(participants))
|
||||
for i := range participants {
|
||||
parts = append(parts, *participantToDTO(&participants[i]))
|
||||
parts = append(parts, *participantToDTO(&participants[i], userMap))
|
||||
}
|
||||
resp := dto.GetMeetingRoomResponse{
|
||||
Room: *roomToDTO(room, int(onlineCount)),
|
||||
@@ -225,9 +236,10 @@ func (ctl *MeetingController) JoinRoom(c *gin.Context) {
|
||||
}
|
||||
// Task 9:JoinRoom 响应携带 rtpCapabilities,供前端 mediasoup-client Device.load 直接使用
|
||||
_, rtpCaps, _ := ctl.meetingService.ResolveRouterInfo(code)
|
||||
userMap := ctl.meetingService.ResolveUsersDisplay(c.Request.Context(), []int64{participant.UserID})
|
||||
resp := dto.JoinMeetingRoomResponse{
|
||||
Room: *roomToDTO(room, 0),
|
||||
Participant: *participantToDTO(participant),
|
||||
Participant: *participantToDTO(participant, userMap),
|
||||
RouterID: routerID,
|
||||
RtpCapabilities: rtpCaps,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user