duoaohui
|
981c12c5bb
|
视频会议
|
2026-05-23 23:04:10 +08:00 |
|
duoaohui
|
ba7447ddb9
|
视频会议
|
2026-05-23 22:42:26 +08:00 |
|
duoaohui
|
52c074e372
|
视频会议
|
2026-05-23 22:34:31 +08:00 |
|
duoaohui
|
c17cd836b2
|
视频会议
|
2026-05-23 22:16:40 +08:00 |
|
duoaohui
|
724bbcd309
|
视频会议
|
2026-05-23 22:10:44 +08:00 |
|
duoaohui
|
1a14b7ce70
|
视频会议
|
2026-05-23 22:07:36 +08:00 |
|
duoaohui
|
91c3bce0af
|
视频会议
|
2026-05-23 22:00:29 +08:00 |
|
duoaohui
|
b6fe8b227a
|
视频会议
|
2026-05-23 21:59:05 +08:00 |
|
duoaohui
|
50234db7a9
|
视频会议
|
2026-05-23 21:49:08 +08:00 |
|
duoaohui
|
fb7b0a8dde
|
视频会议
|
2026-05-23 21:45:47 +08:00 |
|
duoaohui
|
391508f1c8
|
视频会议
|
2026-05-23 21:42:49 +08:00 |
|
duoaohui
|
4cfc16b6e1
|
视频会议
|
2026-05-23 21:41:50 +08:00 |
|
duoaohui
|
94b05ff181
|
视频会议
|
2026-05-23 21:36:29 +08:00 |
|
duoaohui
|
9df92977fd
|
视频会议
|
2026-05-23 21:35:46 +08:00 |
|
duoaohui
|
c118ac43f5
|
视频会议
|
2026-05-23 21:29:29 +08:00 |
|
duoaohui
|
a8db973411
|
视频会议
|
2026-05-23 21:26:52 +08:00 |
|
duoaohui
|
ea7cd1edbd
|
视频会议
|
2026-05-23 21:23:41 +08:00 |
|
duoaohui
|
55f678bd40
|
视频会议
|
2026-05-23 21:12:51 +08:00 |
|
duoaohui
|
69efb8e498
|
视频会议
|
2026-05-23 20:43:39 +08:00 |
|
duoaohui
|
ea1fbde7a2
|
视频会议
|
2026-05-23 20:36:15 +08:00 |
|
duoaohui
|
7402e72c08
|
视频会议
|
2026-05-23 19:54:21 +08:00 |
|
duoaohui
|
d45d6d9d4e
|
视频会议
|
2026-05-23 19:44:00 +08:00 |
|
duoaohui
|
2568f15bda
|
视频会议
|
2026-05-23 18:55:47 +08:00 |
|
duoaohui
|
39594388e3
|
视频会议
|
2026-05-23 17:57:26 +08:00 |
|
duoaohui
|
3e56a6c3db
|
视频会议
|
2026-05-23 17:51:35 +08:00 |
|
duoaohui
|
759ddb513d
|
视频会议
|
2026-05-23 17:15:19 +08:00 |
|
duoaohui
|
63c799c921
|
视频会议
|
2026-05-23 15:26:40 +08:00 |
|
duoaohui
|
3772b763c1
|
视频会议
|
2026-05-23 15:18:57 +08:00 |
|
duoaohui
|
2e66717932
|
视频会议
|
2026-05-23 14:15:19 +08:00 |
|
duoaohui
|
2689301721
|
视频会议
|
2026-05-23 12:53:30 +08:00 |
|
duoaohui
|
3e6ddb0e02
|
视频会议
|
2026-05-23 12:19:10 +08:00 |
|
duoaohui
|
6347fdd2db
|
视频会议
|
2026-05-23 09:35:03 +08:00 |
|
duoaohui
|
4c9f462ff1
|
视频会议
|
2026-05-22 11:16:10 +08:00 |
|
duoaohui
|
23a74f737e
|
视频保存
|
2026-05-19 17:21:40 +08:00 |
|
duoaohui
|
b8c2bf2dc4
|
视频会议
|
2026-05-18 23:13:59 +08:00 |
|
duoaohui
|
ac8ec4c903
|
视频会议保存
|
2026-05-18 19:42:55 +08:00 |
|
|
|
315884685f
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 21:15:39 +08:00 |
|
|
|
45c0852a36
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 21:12:21 +08:00 |
|
|
|
fce4a49971
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 19:59:28 +08:00 |
|
|
|
6c0491bf7d
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 19:38:19 +08:00 |
|
|
|
ec56cc4dd1
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 19:11:35 +08:00 |
|
|
|
8292bf86e4
|
更新 frontend/vite.config.js
|
2026-05-14 20:40:04 +08:00 |
|
|
|
3b8cb6c274
|
更新 frontend/vite.config.js
|
2026-05-14 19:07:22 +08:00 |
|
bujinyuan
|
c35097a0d8
|
fix(meeting): Task 16 修复 code-reviewer 审计 P2 七项 + Nit 七项
覆盖 Phase 2e-2 代码审查报告(docs/reviews/2026-04-23-phase2e-2-code-review.md)
P2/Nit 收尾批次,均在本仓库完成闭环;剩余 5 项登记推迟至 Phase 2f/3。
===== P2 七项 =====
- P2-1 cleanupUserResources 补 transport 清理
· media-server 新增 DELETE /internal/v1/transports/:id + transport.service.closeTransport
· Go MediaOrchestrator 接口新增 CloseTransport;HTTP 实现按 doCloseRequest 走 4xx 幂等 + 指数退避
· meeting_signal_service.cleanupUserResources 新增 "transport:<id>" 分支
- P2-2 preview.vue 快速切设备竞态
· previewSeq 序号 + nextTick 后 stale 判断,丢弃过期结果
· onVideoChange/onAudioChange 走 scheduleRestartPreview 200ms 防抖
· onBeforeUnmount 清理 changeDebounceTimer
- P2-3 room.vue onLoad redirectTo 后补 return
· 引入 redirectingToJoin 守卫,跳转页不再执行 onMounted 初始化
- P2-6 generateUniqueRoomCode 重试上限监控
· 重试后成功:Warn 日志(码空间健康度告警)
· 重试耗尽:Error 日志 + ErrRoomCodeConflict
· 修正 logs.Error 调用签名(去掉多余的 nil)
- P2-7 SendChatMessage 服务端长度 + 频率限制
· 新增 ErrChatContentEmpty / ErrChatContentTooLong / ErrChatRateLimited
· utf8.RuneCountInString 校验 500 字符上限
· Redis INCR + EXPIRE 滑动窗口(30 条/60s,首次写入 EXPIRE 兜底)
· controller.handleError 映射为 HTTP 400
- P2-8 MEETING_ENDED_REASON_LABEL 覆盖复核
· 新增前端专属常量 MEETING_ENDED_REASON_KICKED + 文案
· store/meeting.js _onMemberKicked 使用常量
· 同步修复后端 OnWSDisconnect 硬编码 "ws_disconnect" → constants.MeetingLeftReasonDisconnect
- P2 已修 P2-1/2/3/6/7/8;P2-4(WS token 迁出 URL query)与 P2-5(Chat 服务拆分)登记推迟
===== Nit 七项 =====
- Nit: kind:id 解析改用 strings.SplitN
· cleanupUserResources / pushExistingRoomState 两处同步
- Nit: resourceTTL 中央化
· 新增 constants.MeetingResourceTrackTTLSeconds(3600)
· meeting_signal_service.go resourceTTL 由 const 改 var 并引用常量
- Nit: ws/handler.go CheckOrigin 白名单
· NewHandler 新增 serverCfg 依赖;checkOrigin 支持同源放行 / dev 模式放行 / release 模式白名单严格匹配
· config.ServerConfig 新增 WSAllowedOrigins(逗号分隔)+ AllowedOrigins() / IsRelease() 辅助方法
· provider.go 新增 provideServerConfig,wire_gen.go 同步
- Nit: http_media_orchestrator.go 超时 + CreateRouter 重试
· 默认 TimeoutMS 5000→10000ms 兼容 Worker 冷启动
· 新增 CreateRouterRetry(默认 1 次,300ms 退避),仅对非 404 错误重试
· config.dev.yaml / config.docker.yaml 同步写入显式配置
- Nit: deploy-public.sh REDIS_PASSWORD × redis.conf 联动校验
· 检测 REDIS_PASSWORD 与 redis.conf 的 requirepass 配对一致性
· redis.conf 增加公网部署 requirepass 使用说明
- Nit: media-server internal-auth isPrivatePath 按 path 匹配
· 剔除 query/hash 后再与白名单 startsWith,避免 "?" 语义混淆
- Nit: mediasoup-client.js in-flight 锁走读确认
· finally 分支已覆盖 resolve/reject 两路,追加注释强化语义
- Nit 走读复核:_onMemberLeft 整槽关闭 vs _onProducerNew(closed=true) 精确匹配 producerId
· 粒度正确,无需改动(登记结论)
===== 构建验证 =====
- go vet ./... / go build ./... 通过
- frontend npm run build:h5 通过(仅 uni-app legacy warning,无 error)
- media-server npx tsc --noEmit 通过
===== 审查追踪小节 =====
docs/reviews/2026-04-23-phase2e-2-code-review.md 追加 "Task 16 修复追踪(2026-04-24 更新)":
- 已修复一览(本批次 14 处 + 历次 commit cdaa39d / ea2bf96 / f5ae095 / 5ed14c2)
- 推迟登记表(P2-4 / P2-5 / 端口收敛 / appData 校验 / RFC3339 时间格式,共 5 项)
Made-with: Cursor
|
2026-04-23 17:45:03 +08:00 |
|
bujinyuan
|
f5ae095033
|
fix(meeting): Task 16 会议销毁路径资源清理专项
前端修复:
- _onRoomEnded 保留结束页数据,新增 exitEndedRoom() 动作
room.vue onUnload 在 ENDED 状态调用,释放 currentRoom / participants / chatMessages 等 pinia state
- endMeeting() API 成功后立即触发 _onRoomEnded 本地兜底,不再依赖 room.ended 广播
避免 API 成功但 WS 抖动导致 engine / timer / AudioContext 泄漏
- 新增 _pendingBroadcastTimers 集合收纳 _broadcastSelfState 的 setTimeout 句柄
_cleanupMedia 统一 clearTimeout,避免会议结束后 stale 重试逻辑排队
后端修复:
- MeetingLifecycleService.OnRoomEnded 统一生命周期收尾
取消 graceTimers / emptyTTLTimers + Pipeline DEL host_grace / empty_ttl / 两个 handling 锁 key
- EndRoom 事务提交后遍历 activesBefore 调 cleanupRoomRedisResidual
批量 DEL 每个前任活跃成员的 resourceTrackKey + memberStateKey
补调 lifecycleSvc.OnRoomEnded 清理自身 timer + keys
- HandleEmptyRoomExpired MarkEnded 后补调 cleanupRoomRedisResidual + 清 host_grace 残留
验证:go vet + go build + npm run build:h5 全部通过
Made-with: Cursor
|
2026-04-23 17:12:38 +08:00 |
|
bujinyuan
|
ea2bf96c2f
|
fix(meeting): Task 16 修复 code-reviewer 审计 P1 八项
按 docs/reviews/2026-04-23-phase2e-2-code-review.md 清单落地 P1 批次:
- P1-1 主持人转让原子性:MeetingParticipantDAO.TransferHost 内部事务
合并 meeting_rooms.host_id 更新,service/lifecycle 移除冗余 UpdateHost
- P1-2 ListChatMessages / ListMyMeetings 回归 DAO:新增
MeetingChatDAO.ListByRoomBefore 反向游标,service 移除 s.db 直查
- P1-3 ListMyMeetings N+1 放大优化:新增
MeetingParticipantDAO.ListJoinedRoomsByUser JOIN + DISTINCT 单次 SQL
- P1-4 EndRoom 行锁 + 事务:s.db.Transaction 包裹
SELECT FOR UPDATE → 快照 → MarkEnded → LeaveAllActive;DAO 补
WithTx(*gorm.DB) 和 GetByIDForUpdate
- P1-5 后台 goroutine trace_id 保留:新增 logs.DetachContext(ctx);
替换 meeting_service / meeting_signal_service 共 10 处 context.Background();
meeting_ws_handler 每条 WS 消息分配独立 trace_id
- P1-6 _broadcastSelfState 重试:最多 2 次指数退避(700ms→2100ms),
检测 localAudioEnabled/localVideoEnabled 已被后续动作覆盖时放弃旧 patch
- P1-7 HandleHostGraceExpired / HandleEmptyRoomExpired 处理锁:
独立 host_grace_handling:<code> / empty_ttl_handling:<code> SETNX + 60s TTL,
消除 Redis 自然过期 + 本地 timer 到点 + DEL 返回 0 的盲区
- P1-8 pushExistingRoomState 同步化:OnRoomJoin 返回前完成补推,
消除与 REST member.joined 并行造成的前端状态闪烁
并附带修复 .gitignore 中 logs/ 规则误伤 pkg/logs/ 代码目录的历史遗留问题,
将 logger.go / trace.go 正式纳入版本控制。
验证:backend go build + go vet 通过;frontend npm run build:h5 通过。
Made-with: Cursor
|
2026-04-23 16:53:30 +08:00 |
|
bujinyuan
|
cdaa39d686
|
fix(meeting): Task 16 修复 code-reviewer 审计 P0 四项 + 落盘审查报告
code-reviewer 子代理(docs/reviews/2026-04-23-phase2e-2-code-review.md)对 Phase 2e-2
整体做全栈审计,暴露 P0 × 4 / P1 × 8 / P2 × 8 / Nit × 11。本次先修 P0:
- P0-1 / P0-2 媒体资源归属越权:
新增 assertOwnsResource(roomCode, userID, kind, id) helper,用 resourceTrackKey Redis Set
SIsMember 校验 transport/producer/consumer 归属;统一接入 OnTransportConnect /
OnProduceStart / OnConsumeStart / OnConsumeResume / OnProducerClose 五个信令入口,
一次封死同类横向越权漏洞(Redis 查询失败时 fail-closed,避免抖动开口子)。
新增 service.ErrResourceNotOwned 错误常量。
- P0-3 CreateRoom Router 失败吞错:
旧实现 mediaErr != nil 仅 logs.Warn 继续返回成功 → zombie 房间 + 入会必败 +
一人一会议名额被占。改为 fail-closed 补偿:LeaveRoom + MarkEnded(system_error) +
返回新 service.ErrMediaServiceUnavailable,controller.handleError 映射为 500。
- P0-4 会议密码明文走 URL:
join.vue onNext 旧逻辑把密码拼 ?password=xxx,浏览器历史 / DevTools 留痕。
新增 meetingStore.draftJoinPayload 内存态草稿,join.vue 只跳转
?mode=join&code=xxx,preview.vue onLoad 读取后立即 draftJoinPayload = null 清空,
邀请链接里的 password 参数一律忽略。
验证:
- cd backend/go-service && go build ./... 通过
- cd frontend && npm run build:h5 通过
- 仅剩 1 个预先存在 unusedparams warning,与本次改动无关
P1 / P2 / Nit 清单保留在审查报告,按顺序继续处理。
Made-with: Cursor
|
2026-04-23 16:34:13 +08:00 |
|
bujinyuan
|
790996feaf
|
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
|
2026-04-23 16:04:46 +08:00 |
|
bujinyuan
|
d6fb7ce291
|
feat(meeting): Task 13 完成 meeting_invite 通知卡片对接
## 后端
- MeetingService.InviteUsers 的 PushPayload.Extra 补齐设计 §10.1 要求字段:
inviter_id / inviter_name / inviter_avatar / expired_at(Unix 秒,
与 Redis TTL MeetingInviteTokenTTL=600s 对齐)
## 前端
- constants/notify.js:supportsInlineAction 扩展支持 meeting_invite;
新增 NOTIFY_INLINE_ACTION_LABEL 映射(meeting_invite → 立即加入/稍后)
+ NOTIFY_INLINE_ACTION_DEFAULT 兜底
- components/notify/NotifyItem.vue:
* 按 type 动态渲染按钮文案(actionLabel 计算属性)
* isExpired 计算属性:meeting_invite 比对 extra.expired_at * 1000
* 过期态合并为单个 disabled 的"邀请已过期"按钮
* 新增 .notify-btn--expired 灰显样式
- pages/notify/index.vue:
* handleAccept / handleReject / _navigateByNotify 增加 meeting_invite 分支
* 新增 _navigateToMeetingInvite 辅助:跳 /pages/meeting/preview?mode=join&code=xxx
* 过期时 toast "邀请已过期"不跳转,"稍后"仅 markRead 不发接口
## 文档
- docs/plans/2026-04-21-phase2e-2-implementation.plan.md:Task 13 标 ✅ + 详细产出
- docs/progress/CURRENT_STATUS.md:新增 Task 13 专节(关键技术点 / 验证记录 / 下一步)
- .cursor/rules/project-context.mdc:2e-2 进度刷 Task 0-13 ✅
## 验证
后端 curl 双角色端到端(testuser1 创会 → 邀请 testuser2 → 拉通知列表):
- type=meeting_invite,extra 8 字段齐全
- expired_at = invited_at + 600,与 Redis TTL 对齐
Made-with: Cursor
|
2026-04-22 17:26:31 +08:00 |
|
bujinyuan
|
310ea03ce1
|
fix(im): 修复发送方消息一直卡在"发送中"圆圈、下方不显示已读/未读的 Bug
现象
----
在单聊里发送新消息后,本人气泡左侧一直显示 loop 圆圈(_sending=true),
气泡下方看不到"已读 / 未读"标签;但对端能正常收到消息。刷新页面、
走 fetchHistory 之后才能恢复正常显示。
根因
----
1. services/websocket.js 的 _onMessage:一旦发现 event 以 ".ack" 结尾,
就走 _handleAck(seq→pendingAcks) 处理完后 **直接 return**,不再 _emit。
这样通过 on('im.message.send.ack') / on('im.message.read.ack')
订阅 ACK 的 listener(chat store 的 _onSendACK / _onReadACK)
**永远收不到回调**,导致 _sending 标记一直没被清、id 没被合入,
模板里 getReadLabel() 因为 !msg.id / _sending=true 返回空字符串。
2. store/chat.js 的 _appendMessage:按 client_msg_id 命中旧临时消息时,
只是 isDup=true 直接 return,把服务端权威字段(包括 id)丢弃了,
没有形成有效的兜底路径。
修复
----
- websocket.js: .ack 报文在 _handleAck 之后 **继续走 _emit**,让 Promise
通路(sendWithAck → pendingAcks)与订阅通路(on(...ack))并行分发,
互不冲突。这是真正解决"卡圈"的主修复。
- chat.js _appendMessage: 把 dedup 改成 "就地合并" 语义:
* 同 id 命中 → 真重复丢弃(不回退)
* 同 client_msg_id 命中本地 _sending 临时消息 → 用服务端字段
覆盖 id/created_at/..., 同时清 _sending/_failed
* 其余情况才 push 新条
作为广播帧先于 ACK 到达时的二级兜底,也保证后续再有 ACK/广播
重复到达不会产生重复气泡。
影响面
------
- 单聊/群聊 发送消息后"已读 / 未读 / N人已读"标签恢复实时显示
- im.message.read.ack(对方读到某条)也能正常驱动 readStatusMap 更新
- meeting 等模块的 sendWithAck() 不受影响(走的是 pendingAcks Promise
通路,本次只是在此之后多了一个 _emit 分发,对业务无副作用)
Made-with: Cursor
|
2026-04-22 17:05:31 +08:00 |
|