bujinyuan
|
ea28549a5f
|
feat(scripts): start.sh 启动完成时自动展示前端 / 管理端局域网访问地址
Vite dev server 已绑定 host: '0.0.0.0' 支持局域网访问,但原来的 start.sh
只在 summary 里打印 localhost 地址,Vite 输出的多条 Network URL
被埋在日志里用户看不到。这里扩展 print_summary:
- 新增 wait_and_collect_network_urls(log, timeout):等待日志出现
"Network:" 行(最多 N 秒),然后从中抽取所有 http://LAN_IP:PORT URL
并去重(排除 127.0.0.1 回环)
- summary 输出最后追加「局域网访问地址」分块,分别展示前台 / 管理端
在当前机器所有网络接口上的可达地址
- 附注提示:后端 API(8085) / 媒体 (3300) 的跨设备访问需要在
前端 .env 改 baseURL
这样在同 WiFi 的手机 / 其它设备上可以直接从启动日志里拿到可用入口,
无需再去 .run/logs/frontend.log 里手动 grep。
Made-with: Cursor
|
2026-04-22 16:50:43 +08:00 |
|
bujinyuan
|
d218b84843
|
docs(phase2e-2): 同步 Task 10-12 + UI 打磨进度至 CURRENT_STATUS / 实施计划 / project-context
- CURRENT_STATUS.md:
* 头部"最后更新"升级到 2026-04-22
* 新增完整段落记录 Task 10-12 + 一轮深度 UI 打磨
(uni-button ::after 全视口遮罩 / toolbar z-index 提升 /
面板 toggle / 全局 H5 容器铺满 / 静默 stale 清理 /
视频 HD / 会议聊天真实昵称)
* 附 Playwright 验证结论与相关 commit 列表
- 2026-04-21-phase2e-2-implementation.plan.md:
* 顶部状态改为"代码开发中(Task 0-12 ✅ / Task 13-16 待执行)"
* 新增 16 行进度看板表格 + 2026-04-22 UI 打磨专项要点
* Task 10/11/12 header 标注 ✅ 完成日期
- .cursor/rules/project-context.mdc:
* 当前进度条目更新为"Task 0-12 ✅"
* 新增 Task 10-12 + UI 打磨的独立条目
完整收录三轮攻坚(uni-button 遮罩两次 + z-index +
全局容器 + stale 静默 + HD + toggle)的技术细节
Made-with: Cursor
|
2026-04-22 16:45:56 +08:00 |
|
bujinyuan
|
cacf4ae074
|
fix(meeting): 提升 toolbar z-index 确保成员/邀请面板开启时仍可点击
MemberPanel 和 InviteDialog 的 .panel-root 使用了 position:fixed; inset:0;
z-index:200 的全屏遮罩,当面板打开时会覆盖整个视口,包括底部 toolbar。
这导致:
- 用户再次点击 toolbar 的"成员"/"邀请"按钮时,命中的是 mask 而非按钮
- mask 的 @click.self 虽然也会关闭面板(通过点击空白区域),
但与 toolbar 按钮 toggle 的用户预期行为不一致
修复:把 MeetingToolbar .toolbar 的 z-index 从 15 提升到 210,
确保其高于 .panel-root (z:200) 而低于 .leave-mask (z:220)。
验证:Playwright 实测点击成员按钮 hitSelf=true,打开后再次点击正常关闭。
Made-with: Cursor
|
2026-04-22 16:39:41 +08:00 |
|
bujinyuan
|
a311c03a21
|
fix(meeting): 聊天面板点击失效 & 成员/邀请按钮支持 toggle
ChatPanel 的 .btn-send 使用了 all:unset 导致 position 变为 static,
其 uni-app 自动注入的 ::after 伪元素(inset: 0 -1200px -80px 0)
absolute 定位会回溯到 body 作为 containing block,
形成一个覆盖几乎整个视口的隐形遮罩层,导致聊天面板的关闭按钮 (.btn-close)、
输入框 (textarea) 甚至发送按钮本身周围区域都无法点击/输入。
修复:
1. ChatPanel 的 .btn-send::after 从仅 border:none 升级为
content:none + display:none,彻底抹除遮罩
2. room.vue 中 openMembers / openInvite 改为 toggle 切换,
解决"再次点击成员按钮面板不会关闭"的问题
验证:Playwright 真实 DOM 命中测试 close/textarea/send 全部 hitSelf=true,
成员按钮二次点击面板正常关闭。
Made-with: Cursor
|
2026-04-22 16:34:30 +08:00 |
|
bujinyuan
|
3aa8174bc0
|
fix(meeting): 抹除 uni-button ::after 遮罩修复工具栏点击失效
uni-app H5 会给每个 <button> 编译后的 <uni-button> 自动注入一个
::after 伪元素作为点击态反馈遮罩,其 inset 为 0 -1200px -80px 0
(即 2400px × 160px),向按钮右侧扩展 1200px,下方扩展 80px。
这导致会议工具栏(MeetingToolbar)里每个按钮的 ::after 都横跨整个
toolbar,最后一个"离开"按钮 DOM 顺序最上,其 ::after 覆盖了所有
兄弟按钮的命中区域:点击"静音/视频/邀请/成员/聊天"时实际都命中
"离开"按钮的 ::after,触发离开弹窗;且弹窗中的"取消"按钮同样被
右侧"结束会议"按钮的 ::after 遮挡,点击取消反而会触发结束会议。
修复:在 .btn 和 .leave-btn 上显式设置 ::after { content: none;
display: none },并把按钮设为 position: relative 固化堆叠上下文。
验证:Playwright 真实点击各按钮 hitSelf=true,取消按钮不再
错误触发结束会议逻辑。
Made-with: Cursor
|
2026-04-22 16:27:16 +08:00 |
|
bujinyuan
|
ff46dc5f76
|
fix(phase2e-2): 视频 HD 分辨率 + toolbar 防溢出 + 聊天按钮 toggle
问题
1. 进入会议后视频比预览页更"近、更糊"
· 预览 / startLocalVideo 均固定 640x480,mediasoup 视频轨清晰度偏低
· 进房后 videoTile cover 裁切在大画布更明显 → 观感"放大"
2. 会议内聊天 / 邀请 / 成员按钮"点击无反应"
· 核心:.body flex 子内容过高时 toolbar 未固定收缩,被挤出视口
下方 (y > vh),用户点击位置在屏幕外
· 次要:openChat 仅 set true,已打开状态下再次点击无视觉反馈
3. 聊天按钮无法关闭面板:只有面板内 × 按钮能关
修复
- store/meeting.js startLocalVideo + preview.vue getUserMedia 统一
改为 ideal 1280x720 + frameRate ideal 24 / max 30,浏览器在设备不
支持时自动降级;进房与预览视觉表现一致
- components/meeting/MeetingToolbar.vue .toolbar 加 flex-shrink:0
+ position:relative + z-index:15,保证 toolbar 永远贴底不被 body
压缩,且点击层级高于 ChatPanel 背景
- pages/meeting/room.vue .top-bar 加 flex-shrink:0,避免 body 溢出
时顶栏压缩引发整体错位
- pages/meeting/room.vue openChat 改 toggle:已打开则关闭,未打开则
展开并 markChatsRead,交互更符合直觉
验证(Playwright 1024x576)
- 预览 getVideoTracks()[0].getSettings() → 1280x720 / 24fps ✓
- 进房后 localVideo settings 同样 1280x720 ✓
- 聊天按钮 boundingRect y=505 bottom=567 ≤ vh 576,inViewport=true ✓
- 点击聊天 → chat-col.open=true 宽度 320.5px;再点击 → open=false ✓
- 截图 room-hd-chat-open.png:视频清晰、聊天面板展开、toolbar 贴底
Made-with: Cursor
|
2026-04-22 16:13:25 +08:00 |
|
bujinyuan
|
ce09315fa6
|
feat(phase2e-2): 会议首页落地 "创建 / 加入" 双入口
问题
- tabBar "会议" 指向 pages/meeting/index,但该页仍是占位的
"视频会议功能开发中…",用户从底部 tab 进入无入口按钮,
必须手改 URL 到 /pages/meeting/create 或 /join 才能进会议
修复
- 重写 pages/meeting/index.vue 为真实的 Meeting Hub:
· 顶部 hero 标题 + 描述
· "创建即时会议" 主按钮(蓝色渐变卡片,跳 /pages/meeting/create)
· "加入会议" 次按钮(卡片,跳 /pages/meeting/join)
· 提示文案说明入会流程与主持人能力
- 保留 CustomTabBar 底栏 current=2
验证
- Playwright 截图确认布局、两个入口按钮视觉与点击区域均正常
Made-with: Cursor
|
2026-04-22 16:04:06 +08:00 |
|
bujinyuan
|
c529ff4689
|
fix(phase2e-2): 全局 H5 容器铺满视口 + 静默自动清理 stale 会议
问题
1. 桌面浏览器大窗口下所有页面内容偏左上、右下大片空白:
uni-app H5 的 html/body/#app/uni-page-* 默认不显式 100% 高度,
且 .room 仅靠 position:fixed inset:0 在某些模式下可能被祖先
容器尺寸限制,导致 toolbar / tabBar 不贴底、内容挤在左上角
2. 刷新会议后再次加入会触发 toast "你不在当前会议中":
cleanupStaleMeetings 对列表里已过期的会议调 leave 必然返回 400,
被全局 request 拦截器误报给用户
3. 会议界面部分按钮点击无反应:
根因同 #1,布局错位后按钮视觉位置与 hit box 不一致
修复
- App.vue: H5 条件编译注入全局基础样式,强制 html/body/#app/uni-app/
uni-page/uni-page-wrapper/uni-page-body 宽高铺满,min-height 100%
- room.vue: .room 追加 width/height/min-width/min-height: 100vw/100vh 冗余
+ z-index:10,彻底兜底 position:fixed 失效场景
- request.js: 新增 options.silent 选项,业务错误 / 401 / 403 / 500 /
网络异常均根据 silent 决定是否弹 toast;401 仍会跳登录
- api/meeting.js: createRoom / joinRoom / leaveRoom 均接受 options 参数
- store/meeting.js:
· cleanupStaleMeetings 内 leave 传 silent:true 忽略幂等错误
· joinAndEnter / createAndEnter 首次尝试(未重试时)传 silent:true,
若命中 staleMeetingHint 自动清理重试,对用户完全无感;重试仍失败
的真实错误走正常 toast
验证
- Playwright 1024x576:room 页 / profile 页 / chat 列表 / 刷新后重新加入
流程截图均显示全屏铺满、tabBar/toolbar 贴底、无误报 toast
- 刷新 → join → 预览 → 加入 日志:首次 400 静默 → 自动清理 → 重试加入
成功,MediaEngine 正常建立
Made-with: Cursor
|
2026-04-22 16:00:12 +08:00 |
|
bujinyuan
|
0c9f0483e5
|
fix(phase2e-2): 修复刷新后重入会失败 + 聊天输入区布局防御
问题
- 刷新会议页面被踢回 join 页,再次加入报"已在会议中"
- 极窄聊天面板下,发送按钮在 uni-app H5 渲染为 uni-button 时可能被默认
width:100% 样式撑满导致"竖排变形"
修复
1. meeting store: joinAndEnter / createAndEnter 捕获 staleMeetingHint 时自动
调用 cleanupStaleMeetings 清理后端残留的僵尸活跃成员记录,并重试一次;
日志链路:加入失败 → 检测僵尸 → 自动清理 → 重试加入
2. ChatPanel: input-area 显式声明 flex-direction:row + flex-wrap:nowrap;
.btn-send 追加 width:auto / line-height:1 / box-sizing,并通过 ::v-deep
复位 uni-app 内部 <uni-button> 默认 width,彻底避免按钮独占一行变形
验证
- Playwright 刷新 room 页 → 回 join → 预览 → 加入会议:日志显示自动清理僵尸
"581-702-753"、重试加入 "925-409-354" 成功,MediaEngine 正常建立
- 1024x576 视口下 toolbar 完整可见,聊天面板并排无右侧留白,发送按钮在
输入框右侧
Made-with: Cursor
|
2026-04-22 15:45:56 +08:00 |
|
bujinyuan
|
16225c413d
|
fix(phase2e-2): 会议内聊天面板 UI 布局优化
问题修复:
- 原遮罩式抽屉覆盖底部 toolbar,导致按钮不可点击
- 发送按钮过小不显眼
- 聊天面板打开时视频区被遮挡出现大片空白
改进:
- room.vue 主区改为 flex-row 布局:视频列 + 聊天列并排,聊天打开时视频自动收窄
- ChatPanel 去除全屏遮罩,作为侧边栏嵌入 chat-col,保留过渡动画
- 发送按钮增加纸飞机图标并采用渐变背景,hover / active / disabled 状态清晰
- msg-list 使用 flex:1 + min-height:0 + height:0 修复 scroll-view 高度撑破问题,保证输入区始终固定在底部
- 小屏(<750px)自动回退为全屏浮层,避免挤压视频
Made-with: Cursor
|
2026-04-22 15:32:10 +08:00 |
|
bujinyuan
|
1222bf551d
|
feat(phase2e-2): 会议内聊天面板落地(Task 12)
- 新增 ChatPanel 组件:右侧抽屉式聊天 UI,支持发送/加载更早消息/未读清零
- store/meeting.js:扩展 chatHasMore/chatUnreadCount/chatHistoryLoaded 等状态与 loadMoreChats/markChatsRead 动作;WS 消息到达时按 id 去重
- room.vue:替换临时 toast 占位为 ChatPanel,绑定未读徽标、成员展示名映射;watch unread 自动在面板打开时清零
- 后端:SendChat WS 广播与 REST 响应补充 user_name/user_avatar,聊天面板无需额外查询即可展示昵称
Made-with: Cursor
|
2026-04-22 15:16:17 +08:00 |
|
bujinyuan
|
59f824ad27
|
feat(phase2e-2): 会议室主页与核心 UI 组件落地(Task 11)
新增页面:
- pages/meeting/room.vue:会议主容器,组装 VideoGrid/Toolbar/MemberPanel/InviteDialog
* 顶部条:标题/会议号/复制、连接状态 pill、网络徽标、计时器
* 主区:按成员数自适应网格渲染 VideoTile
* 底部:麦/摄/邀请/成员/聊天/挂断工具条
* 离会确认弹窗:主持人可选"结束会议 / 仅自己离开"
* onUnload 兜底调用 leave 避免脏状态残留
新增组件(components/meeting/):
- VideoTile.vue:原生 video/audio 挂载(H5)+ 名字/静音/主持人/说话者边框
- VideoGrid.vue:1/2/3-4/5-9/10+ 响应式 CSS Grid 布局
- MeetingToolbar.vue:6 按钮工具条,loading 态 + 徽标
- MemberPanel.vue:右侧滑入抽屉,主持人可转让/踢出
- InviteDialog.vue:居中弹窗,一键复制会议号/链接/邀请文案
- NetworkBadge.vue:4 格信号柱,level 0-4 分色
配套改动:
- preview.vue:入会成功后跳 /pages/meeting/room?code=xxx(原先指向 debug)
- pages.json:注册 room 路由,navigationStyle=custom 隐藏默认导航
- store/meeting.js:remoteConsumers 外层 slot 解除 markRaw,使
slot.audio/slot.video 赋值能驱动 VideoTile 响应式重渲染
验证:Playwright E2E 完成 create → preview → room 全链路
- 视频块、会议号、连接状态、计时器、工具条渲染正确
- 邀请弹窗、成员抽屉、静音切换、离会确认均工作正常
Made-with: Cursor
|
2026-04-22 14:41:27 +08:00 |
|
bujinyuan
|
eb3857a37a
|
feat(phase2e-2): 前端创建/加入/设备预览 3 页落地(Task 10)
页面
- pages/meeting/create.vue:创建表单,标题(默认"{昵称}的会议"/40 字限制) + 密码(4-16 位)
+ 入会静音开关 + 允许聊天开关。提交时表单暂存到 store.draftCreatePayload,跳 preview 页
- pages/meeting/join.vue:会议号输入(9 位纯数字/带连字符均可,自动格式化 XXX-XXX-XXX)
+ 可选密码。支持 URL query code/password 回填(邀请链接一键入会入口)
- pages/meeting/preview.vue:设备预览页
- navigator.mediaDevices.enumerateDevices() 列出摄像头/麦克风/扬声器
- getUserMedia({video,audio}) 本地画面预览(用原生 <video> 避开 uni-input 对 srcObject 的限制)
- AudioContext.AnalyserNode 以 rAF 采样 RMS,渲染渐变音量条
- 显示名输入 + 入会默认开麦/开摄像头开关
- 权限被拒/浏览器不支持 WebRTC 时给清晰错误提示
- 离开页时释放 preview stream 与 AudioContext,避免占用摄像头
Store 扩展(meeting.js)
- 新增 state:draftCreatePayload(create→preview 暂存) + devicePreview(设备选择结果)
- createAndEnter(payload, mediaPrefs?) / joinAndEnter(code, password, mediaPrefs?) 新增
可选 mediaPrefs 参数:{ startAudio, startVideo, audioDeviceId, videoDeviceId }
- _afterJoined 内按 mediaPrefs 入会后自动 startLocalAudio/Video(失败仅告警不阻断)
- startLocalAudio/Video 支持可选 deviceId,映射到 getUserMedia 的 deviceId.exact 约束
路由
- pages.json 注册 create/join/preview 3 条路由(均非 TabBar 页,从 URL 或后续入口跳入)
临时占位
- preview 页加入成功后暂时 redirectTo 到 debug.vue(Task 11 room 页待落地时改为 /pages/meeting/room)
Playwright 验证(真实 macOS 设备)
- create → preview:表单 "新昵称的会议" 正确携带到 preview 页
- preview 页:FaceTime 摄像头 + MacBook Pro 麦克风枚举成功,video readyState=4,音量条工作正常
- 点"加入会议":createAndEnter 成功,debug 页显示 localState=connected,主持人=是
mediaPrefs 生效——麦克风/摄像头按钮显示"关闭麦克风/关闭摄像头"说明自动推流成功
- join 页:URL ?code=123-456-789&password=secret 正确预填,"下一步" 按钮启用
Made-with: Cursor
|
2026-04-22 14:20:57 +08:00 |
|
bujinyuan
|
467135e0d5
|
feat(phase2e-2): WS 稳定性改进——重连后自动重绑定房间 + 多端登录抖动检测
背景
- Task 9 联调发现同账号多 tab 登录会触发后端单设备踢下线机制,WS 每秒被关闭一次,
所有 meeting.* 的 sendWithAck 都因 "连接已断开" 而失败,表现为"开启音视频提示失败"
- 同时 WS 断线重连后后端对该连接的 roomCode 绑定丢失,会议广播不再送达本端
改动
- store/meeting.js:_registerListeners 增加 WS 生命周期监听
- _disconnected:从 CONNECTED/CONNECTING 切到 RECONNECTING,UI 可展示"重连中…"
- _connected:在 RECONNECTING 状态自动重发 meeting.room.join 重绑定房间,并刷新参与者列表
- services/websocket.js:_recordCloseForFlapping 做 10s 滑窗 flapping 检测
- 窗口内累计 >=3 次非主动断连 → 触发 _flapping 事件(含 count/windowMs)
- 15s 冷却避免抖动期间重复告警
- debug.vue:新增 WS 状态指示灯(已连接/重连中/已断)+ flapping 警告横幅
- 订阅 _connected / _disconnected / _flapping 三个 WS 内置事件,onBeforeUnmount 注销
验证(Playwright)
- 单次断连:testuser1 创建会议后手动 close WS → 指示灯 reconnecting,1s 后重连
- 日志链 "WS 已断开 → 进入 reconnecting → 连接成功 → 重绑定房间 307-857-909 → 恢复 connected" 全通
- 4 次连续断连:第 3 次触发 flapping,UI 横幅 "⚠ 检测到异常重连(3 次/10s),可能同账号在其他端登录"
- 每次断连后房号、主持人状态、localState 均正确恢复为 connected
Made-with: Cursor
|
2026-04-22 14:08:07 +08:00 |
|
bujinyuan
|
807e111cb9
|
chore(scripts): start/stop/status 接入 media-server(port 3300)
- start.sh:新增 start_media(),端口 3300,缺 .env 时自动从 .env.example 复制;
缺 node_modules 时给出提示不跑命令;加入 all/独立子命令
- stop.sh:新增 stop_media(),通过 PID 文件 + 端口兜底停止媒体服务器
- status.sh:状态面板增加 "媒体服务器 SFU (:3300)" 行
- 完整用法:./scripts/start.sh [all|backend|frontend|admin|media|docker|--no-docker]
Made-with: Cursor
|
2026-04-22 11:27:21 +08:00 |
|
bujinyuan
|
f97fec24a8
|
feat(phase2e-2): 前端 mediasoup-client 集成 + Pinia meeting Store(Task 9)
- frontend/src/api/meeting.js:12 个 REST 接口封装,统一 unwrap envelope.data
- frontend/src/services/websocket.js:新增 sendWithAck(Promise 化 + 超时 + 序列号)
- frontend/src/utils/mediasoup-client.js:MediaEngine 包装 Device/Transport/Producer/Consumer
- frontend/src/store/meeting.js:Pinia 会议状态机,桥接 14 个 WS 事件 + cleanupStaleMeetings
- frontend/src/constants/meeting.js:状态枚举 + 事件名集中管理
- frontend/src/pages/meeting/debug.vue:临时调试页(H5 原生 video/audio DOM 绕过 uni 组件限制)
- backend:meeting.consume.resume WS 事件 + create/join 响应透传 router_id + rtp_capabilities
- 文档:frontend/meeting.md、websocket.md、CURRENT_STATUS、plan 全部同步 Task 9 落地
Made-with: Cursor
|
2026-04-22 11:22:49 +08:00 |
|
bujinyuan
|
e7f6a32dfe
|
docs(phase2e-2): 归档 Task 8 实施 plan 文件至 docs/plans
将 Cursor Plan 模式下的临时产物 task_8_会议生命周期状态机_3a0bc37b.plan.md
归档至 docs/plans/ 目录,统一命名风格为日期前缀 + 阶段/任务描述:
2026-04-21-phase2e-2-task8-lifecycle.plan.md
用途:
- 与 docs/plans/2026-04-21-phase2e-2-{design,implementation.plan}.md 共同构成
Phase 2e-2 Task 8 的完整设计 → 实施 → 归档三件套
- 保留 15 个 todo 的详细拆分 + 参考附录,作为后续复杂 Task 的实施样本
- 头部补充 archived_at / archived_note,指向实际交付结果与 commit
不改动文件主体内容,仅追加归档元数据。
Made-with: Cursor
|
2026-04-21 18:24:49 +08:00 |
|
bujinyuan
|
3b83c79036
|
feat(phase2e-2): 落地会议生命周期状态机 + Router 幂等双层防御(Task 8)
核心交付:
- 新建 MeetingLifecycleService(6 钩子 + sync.Map 本地 timer + Redis key 双保险 + RescheduleFromRedis)
- 新建 MeetingCleanupTask(启动重建 timer + 每 N 秒扫 host_grace/empty_ttl 兜底 + 4h stale active 回收)
- MediaOrchestrator 新增 ResolveRouterID;HTTPMediaOrchestrator.CreateRouter 入口 sync.Map 幂等防御
- 业务层 JoinRoom 移除 CreateRouter 调用改走 CancelEmptyTTL + ResolveRouterID;LeaveRoom 空房分支改调 OnAllMembersLeft 不再立即销毁
- MeetingSignalService 新增 OnWSDisconnect 实现 ws.MeetingDisconnectHook;OnRoomJoin 追加 host 重连钩子
- ws.handler 定义 MeetingDisconnectHook 接口 + SetMeetingDisconnectHook,解耦 ws→meeting 反向依赖
- config 新增 MeetingConfig{HostGrace=120, EmptyRoomTTL=300, CleanupInterval=30, StaleRoomHours=4}
关键设计决策:
- Redis key TTL = 业务时长 + max(CleanupIntervalSeconds*2, 30s) buffer:避免本地 timer 与
Redis 自动过期同步到期导致 DEL 返回 0 被误判为"已被其他路径处理"而跳过业务逻辑
- Router 幂等双层防御(决策 q2_router_dedup=a2_both):业务层不重复调 + HTTP 层 sync.Map 命中直接返回
- 普通成员 WS 断开仅清 media 资源不动 participant 表(决策 q1_nonhost_disconnect=a1_keep_current)
E2E 验证:docs/verify/meeting_t8_verify.mjs PASS=20 FAIL=0,覆盖 5 场景:
- S1 host 宽限期过期自动转让(meeting.host.changed + DB host_id 更新)
- S2 宽限期内重连保留身份
- S3 empty_ttl 期内新成员加入复活房间
- S4 empty_ttl 过期 → 房间 Ended + 新 join 被拒
- S5 CreateRoom +1 Router / JoinRoom 不再创建新 Router(通过 media-server /internal/info stats.routers 断言)
media-server:/internal/info 响应追加 stats.routers + routers[] 供 E2E 断言 Router 幂等
文档同步:
- docs/progress/CURRENT_STATUS.md 头部 + 新增 Task 8 交付条目
- docs/plans/2026-04-21-phase2e-2-implementation.plan.md Task 8 标记完成 + 实际产出/决策/验证
- docs/api/frontend/meeting.md 补充 host.changed.auto_reason / room.ended.reason=system_error / 空房 TTL 复活语义 + Task 8 验证记录
- docs/architecture/system-architecture.md meeting 模块职责补充"会议生命周期状态机"
- .cursor/rules/project-context.mdc 追加 Task 8 条目并更新 Phase 2e-2 进度(Task 0-8 ✅)
Made-with: Cursor
|
2026-04-21 18:21:04 +08:00 |
|
bujinyuan
|
55d6352c6a
|
feat(phase2e-2): 落地 HTTPMediaOrchestrator 打通 Go↔Node 媒体链路(Task 7)
- 新增 MediaServerConfig + config.{dev,docker}.yaml 的 media_server 段
(base_url / internal_token / timeout_ms / close_timeout_ms / close_retry)
- 新建 HTTPMediaOrchestrator(8 方法 + sync.Map 缓存 roomCode→routerID
+ 关闭类指数退避重试 200/500ms + ErrMediaResourceNotFound/ErrMediaServerError
两类错误)实现 MediaOrchestrator 接口
- wire 绑定由 NoopMediaOrchestrator 切换到 HTTPMediaOrchestrator;
MeetingService / MeetingSignalService 调用侧零改动
- E2E 脚本 docs/verify/meeting_t7_verify.mjs 证明 16/16 PASS:
健康检查/错token 401/REST 创房加入/WS room.join/真实 mediasoup
transport.create(ICE/DTLS 指纹均由 Node 返回非占位)/404 幂等关 producer
/host 结束会议触发 CloseRouter
- 同步更新 13 份文档:CURRENT_STATUS / implementation.plan / design
变更记录 / project-context / api 导览 / api websocket / api frontend meeting
/ system-architecture / media-server README / 顶层 README 等
- go build ./... 全绿
Made-with: Cursor
|
2026-04-21 17:47:11 +08:00 |
|
bujinyuan
|
dfd9b6011c
|
docs(phase2e-2): 同步 Task 5/6 落地到总 API 索引 + 总 WS 协议 + 设计文档变更记录
- docs/api/README.md:frontend/meeting.md 状态从"📋 设计阶段"升为"✅ Task 5/6",
补全 12 REST + 13 WS 事件描述;目录结构表 meeting.md/notify.md 同步标记完成
- docs/api/websocket.md:会议信令章节从设计阶段草稿重写为事件总览表 + SSOT 引用
(移除与 Task 6 实际实现不一致的旧事件名 meeting.member.mute/video/produce.stop
/room.info/consume.resume;明确 C→S 白名单与 ACK/资源追踪约定)
- docs/plans/2026-04-21-phase2e-2-design.md:§十六 变更记录追加 Task 5 + Task 6
两条偏离说明,完整记录事件合并/重命名/白名单/Redis 资源追踪等实施级决策
Made-with: Cursor
|
2026-04-21 17:14:14 +08:00 |
|
bujinyuan
|
096563d3ea
|
feat(phase2e-2): WebSocket 信令协议 13 事件全量落地(Task 6)
将 meeting.* 事件族从 Task 5 的 PublishToUser 循环升级为完整 WS 信令协议:
抽离统一广播层、扩容 MediaOrchestrator 接口、实现 8 个 C→S 事件业务逻辑
+ Redis 资源追踪 + host 权限校验,端到端 18/18 PASS。
核心产出:
- 新建 MeetingBroadcaster(统一广播层,REST/WS 共用)
- 新建 MeetingSignalService 8 C→S 事件 + cleanupUserResources
- 新建 MeetingWSHandler 薄层 controller
- MediaOrchestrator 扩容 9 方法 + NoopMediaOrchestrator 占位(Task 7 替换)
- C→S 白名单机制防恶意伪造广播事件
- Redis Set 资源追踪防 mediasoup 端资源泄漏
文档同步:
- docs/api/frontend/meeting.md 追加 §WebSocket 信令协议(Task 6)200 行
- docs/progress/CURRENT_STATUS.md + project-context.mdc + 实施计划 Task 6 ✅
Made-with: Cursor
|
2026-04-21 17:10:32 +08:00 |
|
bujinyuan
|
e235e001b3
|
feat(phase2e-2): Go meeting 模块 12 个 REST 接口业务逻辑全量落地(Task 5)
主要产出:
- DTO 层:app/dto/meeting_dto.go 完整定义 13 个 DTO(请求/响应/基础共用)
- Service 层:MeetingService 12 业务方法 + 11 个 sentinel 错误 + 4 辅助
- CreateRoom/JoinRoom/LeaveRoom/EndRoom 核心生命周期
- KickMember/TransferHost/GetRoom/ListMyMeetings 会议管理
- InviteUsers(+ NotifyPusher)/ RedeemInviteToken 邀请链路
- SendChat/ListChats 会议内聊天
- Controller 层:12 handler + handleError 领域错误 → HTTP 映射
- 工具层:pkg/utils/meeting_code.go(XXX-XXX-XXX + invite token)
- Stub 接口:MediaOrchestrator(Task 7 替换)+ NoopMediaOrchestrator
- 路径修正:/rooms → /rooms/mine、/invites → /invite-tokens 对齐设计
- DAO 契约修复:GetByID/GetByCode/GetByRoomAndUser/FindActiveByUser
将 gorm.ErrRecordNotFound 转为 (nil, nil),service 统一 nil 判定
- 安全强化:密码 bcrypt + 5 次错误锁 10 分钟;邀请 token 仅通过
NotifyPusher.Extra 定向下发,响应不回传
- host 自动转让:host 离会时将最早加入者提升为 host
- 单点参会:用户同一时间仅能在一个活跃会议
- API 文档:docs/api/frontend/meeting.md 重写为 12 接口完整规范
- Wire 依赖注入:MediaOrchestrator + NoopMediaOrchestrator provider
验证:
- go build / go vet / wire 零告警
- 端到端 3 用户场景:12 happy path + 5 错误路径 PASS=19 / FAIL=0
覆盖密码错/房间不存在/单点冲突/越权/邀请失效
文档同步:
- docs/progress/CURRENT_STATUS.md 增补 Task 5 章节
- .cursor/rules/project-context.mdc 更新阶段状态
- docs/plans/2026-04-21-phase2e-2-implementation.plan.md 标记 T5 ✅
下一步:Task 6(WS 信令协议 + BroadcastToMeeting 替换 PublishToUser 循环)
Made-with: Cursor
|
2026-04-21 16:39:59 +08:00 |
|
bujinyuan
|
d22f61fb9b
|
feat(phase2e-2): Go meeting 模块 DDL + Model + DAO + service/controller/router 骨架(Task 3+4)
Task 3 - 数据库与持久化层:
- init.sql 追加 3 张表(meeting_rooms / meeting_participants / meeting_chats)+ 9 索引 + 全量 COMMENT
- 新增 phase2e2_migration.sql 增量升级脚本(IF NOT EXISTS 幂等)
- app/constants/meeting.go 统一会议常量(类型/状态/角色/结束原因/离会原因/默认配置/WS 事件 8 组)
- app/meeting/model 3 个 model + GORM tag(TIMESTAMP(0) 对齐现有表风格)
- app/meeting/dao 3 个 DAO 共 24 个方法:
* meeting_room_dao:Create/Get/Exists/MarkStarted/MarkEnded(乐观锁)/UpdateHost/UpdateSettings/ListByHost/ListExpiredForCleanup
* meeting_participant_dao:JoinRoom(事务内复用旧记录)/LeaveRoom(EXTRACT(EPOCH) DB 时间)/LeaveAllActive/TransferHost(事务)/FindActiveByUser(JOIN 单点校验)/列表计数更新
* meeting_chat_dao:Create/ListByRoom(游标)/DeleteByRoomIDs/CountByRoom
- psql 真库跑通 CRUD + UNIQUE + CASCADE + 主持人转让事务 + duration=10s 精确匹配 8 场景
Task 4 - Go 侧 service/controller/router/wire 骨架:
- app/meeting/service/interfaces.go:NotifyPusher / UserInfoResolver / OnlineChecker 三接口(解耦 notify/contact/ws)
- app/meeting/service/meeting_service.go:MeetingService + 8 sentinel error + 17 个空方法(返回 ErrNotImplemented)
- app/meeting/controller/meeting_controller.go:12 个 Gin handler + responseNotImplemented(501)
- app/meeting/router.go:12 条路由挂 /api/v1/meeting/* 并套 jwtAuth(扁平化 router.go,与 group/notify 一致)
- app/meeting/provider.go:MeetingSet = wire.NewSet(DAO×3, Service, Controller)
- 全局 wire.go 挂入 MeetingSet + 3 条 wire.Bind(Notify/UserInfo/Online);App struct/NewApp 加字段;router/router.go 挂载
- 存量修复:admin/provider.go 补齐 MessageManage{DAO,Service,Controller} 解决旧版 wire 重生成报 no provider found
- 验证:go build ./... / go vet ./... / wire ./app/provider 零错误;GIN_MODE=debug 启动打印全部 12 条路由;无 token curl 3 条代表性路由均返回 401(JWT 中间件生效)
关键风格修正(偏离草案):
- 常量归入 app/constants/meeting.go 单文件(与 group.go / notify.go 同构),非草案 app/meeting/constants/
- TIMESTAMP(0) 取代草案 TIMESTAMPTZ 对齐所有现有表
- 冗余 idx_meeting_rooms_code 移除(UNIQUE 已自动建索引)
- router/provider 目录扁平化(app/meeting/router.go / app/meeting/provider.go)
- 延续项目 Go 侧"零 _test.go"风格,用代码审查 + psql 真库验证 + Playwright E2E 三层守护
文档同步:
- docs/progress/CURRENT_STATUS.md 新增 Task 3 / Task 4 交付段落
- .cursor/rules/project-context.mdc Phase 2e-2 进度推进至 Task 0-4 ✅
- docs/plans/2026-04-21-phase2e-2-implementation.plan.md Task 3 / Task 4 标记完成 + 实际产出 vs 计划差异
Made-with: Cursor
|
2026-04-21 16:04:57 +08:00 |
|
bujinyuan
|
6e2e792db0
|
feat(phase2e-2): media-server Task 0/1/2 落地 + 代码审查修复
本次提交一次性落盘 Phase 2e-2 的 Task 0(PoC Spike)、Task 1(骨架 + Fastify 5 升级)
和 Task 2(9 个内部 REST API + code-reviewer 审查修复),覆盖 media-server 子项目
从零到可用的全部工作。
【Task 0 - PoC Spike】
- media-server/poc/:Node + mediasoup + fastify-websocket + 前端 mediasoup-client
- Playwright 双 tab 自动化验证 2 人会议:4 transports / 4 producers / 4 consumers / RSS 61MB
- media-server/docs/poc-notes.md 归档 7 项关键坑 + 启动步骤 + Task 1/2/9 复用映射
- 锁定技术栈:mediasoup + fastify + mediasoup-client,不改用 livekit-server
【Task 1 - 骨架 + Fastify 5 升级】
- 依赖版本:mediasoup@3.19.0 + fastify@5.8.5 + fastify-plugin@5.1.0
+ @fastify/sensible@6.0.4 + @fastify/websocket@11.2.0
+ pino@9.3.2 + zod@3.23.8
- src 五件套:app.ts / config.ts / utils/logger.ts / mediasoup/worker.ts
/ middlewares/internal-auth.ts
- /healthz + /readyz + /internal/info 三端点实测通过
- X-Internal-Token 鉴权用 timingSafeEqual 防侧信道
- mediasoup Worker died 指数退避自愈通过 kill -9 验证
- 多阶段 Dockerfile:非 root + curl HEALTHCHECK + 暴露 40000-40199 UDP/TCP
- Fastify 4 → 5 升级:loggerInstance: logger 消灭两个 pino 实例
【Task 2 - 9 个内部 REST API + 审查修复】
接口全部挂 /internal/v1/* 前缀,覆盖 Router/Transport/Producer/Consumer 完整生命周期:
- POST /routers, DELETE /routers/:id
- POST /transports, POST /transports/:id/connect
- POST /producers, DELETE /producers/:id
- POST /consumers, POST /consumers/:id/resume, DELETE /consumers/:id
工程特性:
- zod 手动 parse + 全局 errorHandler(不引入 fastify-type-provider-zod 避免
zod v4 依赖冲突)
- AppError 统一错误码(NOT_FOUND/CONFLICT/CAN_NOT_CONSUME/ROUTER_LIMIT_EXCEEDED
/MEDIASOUP_ERROR/VALIDATION_ERROR/UNAUTHORIZED/INTERNAL_ERROR)
- 所有资源用 Map + observer.once('close') 自清理,Consumer 监听 producerclose 级联关闭
- Consumer 强制 paused:true 创建,/resume 独立接口
- Transport direction 强约束:recv 拒 produce、send 拒 consume
code-reviewer 子代理审查"有条件通过",同步修复:
- M1 _clearXxxMap 新增 src/utils/test-guard.ts#assertTestOnly 守卫(生产误调用抛错)
- M2 新增 src/schemas/rtp.ts 对 rtpParameters / rtpCapabilities 做 codecs 浅层校验
(mimeType/clockRate/payloadType 必填、codecs ≥1),消除 as unknown as 双跳断言
- m1 connectTransport 改乐观锁:先置位再 await,失败回退
- m2 改读 consumer.producerPaused(更符合 mediasoup 语义)
- m3 producerclose 改为 once(风格一致)
- m5 internal-auth 改为反向白名单 PRIVATE_PATH_PREFIXES = ['/internal/']
验证:
- typecheck / lint 0 错误
- vitest:65 passed / 8 spec 文件
- 覆盖率 stmts 82.87% / branches 75.83% / funcs 91.3% / lines 82.87%
- 9 接口 happy path + 6 类错误路径 curl 手测全部按预期返回
【文档同步】
- CURRENT_STATUS.md +232 行:新增 Task 0/1/2 完整记录 + 代码审查修复章节 + 延后清单
- project-context.mdc:Task 0/1/2 状态同步
- phase2e-2-design.md +20 行:Fastify 5 升级相关决策记录
- phase2e-2-implementation.plan.md +117 行:Task 0/1/2 实际产出 + 修复记录
余下 Minor/Nits(m4/m6~m10/n1~n10)登记至 Task 16 收尾清单一次性清扫。
Made-with: Cursor
|
2026-04-21 15:31:01 +08:00 |
|
bujinyuan
|
df538e8df6
|
feat:更新---2e-2阶段相关设计文档和实施步骤文档产出
|
2026-04-21 13:53:29 +08:00 |
|
bujinyuan
|
f1853f125d
|
feat: Phase 2e-1 统一通知中心 + 我的 TabBar 聚合未读红点
后端(notify 模块)
- 新增 notify 模块:DAO/Service/Pusher 接口/Controller/Router/CleanupTask
- 数据库 DDL:notify_notifications 表 + 3 索引(user+created/user+is_read/user+category)
- 11 种 type 枚举(好友/群聊 9 种 + meeting_* 2 种预留)+ 4 种 category
- 跨模块集成:contact 3 处 Pusher(friend_request/accepted/rejected)
- 跨模块集成:group 6 处 Pusher(invite/join_request/approved/rejected/kicked/role_changed)
- WS handler 断线补偿:连接建立即推送 notify.unread.total
- 5 REST API(4 用户 + 1 管理员广播)+ 2 WS 事件(notify.new / notify.unread.total)
- 30 天已读通知定时清理(未读永久保留)
- Provider/Wire 依赖注入(NotifyPusher、NotifyConnectHook、UserInfoResolver 接口)
前端
- 新增 notify 模块:API/Pinia Store(5 分类分页缓存 + 未读数 + WS 事件)/NotifyItem/通知中心主页
- profile 入口:铃铛 badge + 菜单项 badge + 数字显示
- App.vue/login 初始化 notifyStore WS 监听;logout 调用 notifyStore.reset() 清缓存
- 清理 contact.js/group.js 中散落 toast 与冗余 notify.friend.request/group.join.request 处理
- CustomTabBar 新增 hasDot() 聚合指示器:我的 Tab 显示纯红点(无数字),
当前聚合 notifyStore.unreadTotal,未来可扩展「资料待完善/安全提醒/新版本」等
文档
- 新增 Phase 2e 整体路线图 docs/plans/2026-04-20-phase2e-design.md
- 新增 Phase 2e-1 专用设计 docs/plans/2026-04-20-phase2e-1-design.md(§6.4 TabBar 聚合红点)
- 新增 Phase 2e-1 实施计划 docs/plans/2026-04-20-phase2e-1-implementation.plan.md
- 新增 E2E 验证报告 test-report-phase2e-1-notification.md(含 Playwright MCP 2 个现场 Bug 修复记录)
- 更新 docs/progress/CURRENT_STATUS.md、docs/api/README.md、docs/api/frontend/notify.md
- 更新 .cursor/rules/project-context.mdc、docs/plans/2026-02-27-echochat-system-design.md
其他
- .gitignore 排除 .playwright-mcp/ MCP 临时快照
架构决策
- 单端 WS 连接:沿用现有 ws.Hub,多端已读同步推迟到 Phase 2f/二期
- 跨模块依赖:contact/group → notify 严格单向(接口注入模式)
- 降级策略:Pusher 先入库后推送;WS 失败不回滚入库;入库失败仅 Warn 不影响业务
Playwright MCP 回归(4 类场景全通)
- 实时推送(admin 广播 → 1s 内前端自动插入 + 角标 +1)
- Deep-link 跳转(好友申请通知 → contact/request 页)
- 批量清零(全部已读按钮)
- TabBar 聚合红点(有未读亮/全部已读灭)与 notifyStore.unreadTotal 三层同步
Made-with: Cursor
|
2026-04-21 10:21:01 +08:00 |
|
bujinyuan
|
ccfceefdaf
|
feat:聊天页面相关功能优化,提供用户体验感
|
2026-04-20 15:40:16 +08:00 |
|
bujinyuan
|
0c1540bdee
|
feat:聊天信息支持 文件、图片、语音发送,增加一键启停脚本
|
2026-04-20 11:53:27 +08:00 |
|
bujinyuan
|
1a5aac9998
|
feat:修改地址配置
|
2026-03-13 16:34:28 +08:00 |
|
bujinyuan
|
8ee870636c
|
feat:群聊问题修复+相关项目文档更新
|
2026-03-05 09:24:49 +08:00 |
|
bujinyuan
|
4f55ef6de0
|
feat:代码推送测试
|
2026-03-04 15:07:31 +08:00 |
|
bujinyuan
|
19979da59e
|
feat(phase2c): 群聊与已读回执功能完整实现
Phase 2c 全部 14 个 Task 完成,包含:
后端(Go):
- MinIO 文件存储服务集成(Docker + Go SDK + 通用上传 API)
- Group 模块完整实现(DAO + Service + Controller + Router + Wire)
- 18 个群管理 REST API + 11 个 WS 群事件推送
- 群创建/解散/邀请/踢人/退出/转让群主/设管理员/禁言/全体禁言/群公告/群昵称/免打扰/搜索
- IM Service 扩展(群消息发送/撤回 + @提醒 + 管理员无时限撤回)
- 已读回执后端(单聊会话级 last_read_msg_id + 群聊消息级 im_message_reads)
- 管理端群组管理(列表/详情/解散)
- 数据库迁移(3 张新表 + 2 张表字段扩展)
前端(uni-app):
- 群聊 Store + API 封装 + 11 个 WS 事件监听
- 已读回执 UI(单聊已读/未读标记 + 群聊 X人已读 + 已读详情页)
- 7 个群聊页面(对话/创建/设置/成员/邀请/审批/搜索)
- 会话列表改造(全部/单聊/群聊 Tab + @标记 + 免打扰标识)
管理端(Vue 3 + Element Plus):
- 群组列表页(搜索/分页/详情弹窗/解散群聊)
- 侧边栏群组管理入口
文档同步:进度/架构/设计/API/规范文档全部更新
Made-with: Cursor
|
2026-03-04 14:58:23 +08:00 |
|
bujinyuan
|
ad76d9a613
|
docs: 修正分支状态标记(待创建→已创建)
Made-with: Cursor
|
2026-03-04 10:53:48 +08:00 |
|
bujinyuan
|
1d9f7a7432
|
docs: 新增后端架构规范文档 + 拆分集成规范
- 新增 backend-module-architecture.md(10 章:模块分层/接口注入/Wire/日志/错误处理/批量查询/系统消息/WS 推送/Store 封装)
- 从 frontend-backend-integration.md 拆分(621→360 行),符合 ≤500 行规范
- project-context.mdc 新增规则 #10 引用后端架构规范
Made-with: Cursor
|
2026-03-04 10:51:38 +08:00 |
|
bujinyuan
|
6568565c37
|
docs: 项目规则新增文档同步流程规范
- 新增「每阶段开始前的文档准备」清单(8 项检查)
- 新增「每个 Task 完成后的文档同步」清单(6 项检查)
- 补充「每阶段结束时的额外检查」新增集成规范时间戳更新
Made-with: Cursor
|
2026-03-04 10:46:08 +08:00 |
|
bujinyuan
|
13d26ca43a
|
docs: Phase 2c 规划同步到全局文档
- system-architecture.md: 新增 group/file 模块,admin 模块扩展群聊管理
- echochat-system-design.md: Phase 2c 开发分期详情 + 19 个新 API + 4 个管理端 API
- api/README.md: 新增 group.md 导航(前台+管理端)
- frontend-backend-integration.md: 更新最后修改时间
Made-with: Cursor
|
2026-03-04 10:43:33 +08:00 |
|
bujinyuan
|
cc09b192a4
|
docs: Phase 2c 设计文档补充 — @计数存储/系统消息格式/离线推送/错误常量
- 新增 at_me_count 字段设计(im_conversation_members)
- 明确系统消息 content 格式为纯文本 + 12 种消息模板
- 群聊离线推送策略(沿用 Phase 2b OfflineMessagePusher)
- 文件上传策略(两步解耦,无需接口注入)
- 群聊错误常量规划(15 个业务错误)
Made-with: Cursor
|
2026-03-04 10:37:48 +08:00 |
|
bujinyuan
|
86f371cc5c
|
docs: Phase 2c 设计文档 + 实施计划 + 项目进度更新
- 新增 Phase 2c 设计文档(群聊与已读回执)
- 新增 Phase 2c 实施计划(14 个 Task)
- 更新 CURRENT_STATUS.md 反映 Phase 2c 规划
- 更新 project-context.mdc 新增 Phase 2c 进度和模块信息
Made-with: Cursor
|
2026-03-04 10:34:35 +08:00 |
|
bujinyuan
|
9399f86a19
|
feat:单聊页面bug修复+后台管理端好友页面bug修复+项目进度、记忆文件更新
|
2026-03-04 10:03:16 +08:00 |
|
bujinyuan
|
7b1d061955
|
feat:修复聊天界面消息位置展示错误问题
|
2026-03-03 13:46:03 +08:00 |
|
bujinyuan
|
35788f6d2f
|
style(frontend): ui-ux-pro-max 规范改造 — chat 页面 + CustomTabBar
改造内容:
- 安装 @dcloudio/uni-ui + sass,配置 easycom 自动引入 uni-icons
- 4 个 chat 页面:emoji/HTML 实体 → uni-icons 组件(跨平台兼容)
- CustomTabBar:emoji → uni-icons(chatbubble/contact/videocam/person)
- 选中态使用 filled 变体,未选中态使用轮廓图标
- 所有可交互元素添加 :active 按压反馈 + transition (150-200ms)
- 触控目标保证 ≥ 88rpx (44px)
- input-bar/tab-bar 适配 safe-area-inset-bottom
设计规范文件:
- MASTER.md 修正为项目蓝色色板 (#2563EB) + 移动聊天应用规范
- 新增 4 个页面覆盖规范:chat-index/conversation/settings/search.md
- Pre-Delivery Checklist 全部通过
- npm run build:h5 编译验证通过
Made-with: Cursor
|
2026-03-03 11:23:11 +08:00 |
|
bujinyuan
|
143cb8aa5f
|
docs: Task 9 - Phase 2b 文档更新
- CURRENT_STATUS.md: 更新 Phase 2b 完成状态(10 个 Task)
- 新增功能描述(IM/会话/WS事件/REST API/前端页面/数据库)
- 更新目录结构、技术决策、测试指南
- 下一阶段规划更新为 Phase 2c
- project-context.mdc: 更新当前进度和模块列表
Made-with: Cursor
|
2026-03-03 11:04:19 +08:00 |
|
bujinyuan
|
0ea8c0d331
|
feat(frontend): Task 8 - 聊天设置页 + 消息搜索页 + 联系人改造
pages/chat/settings.vue(聊天设置页):
- 对方信息展示(头像+昵称)
- 置顶/取消置顶切换
- 清空聊天记录(确认弹窗)
- 删除会话(确认弹窗 + 双层返回)
pages/chat/search.vue(消息搜索页):
- 全局消息搜索(关键词匹配)
- 搜索结果列表(发送者头像+昵称+内容+时间)
- 点击跳转到对应会话
联系人模块改造:
- contact/detail.vue "发消息"按钮改为跳转聊天页
Made-with: Cursor
|
2026-03-03 11:02:30 +08:00 |
|
bujinyuan
|
565afb3149
|
feat(frontend): Task 7 - 会话列表页 + 聊天对话页
pages/chat/index.vue(会话列表页):
- 排序展示会话列表(置顶优先 → 时间降序)
- 未读 badge + 时间格式化(今天/昨天/日期)
- 长按操作菜单(置顶/删除)
- 正在输入状态提示
- 空状态 UI
pages/chat/conversation.vue(聊天页):
- 消息气泡(自己蓝色/对方白色)
- 游标分页加载历史消息
- 发送消息 + 三态确认(sending/sent/failed)
- 长按撤回 + 重发失败消息
- 正在输入通知(发送 + 接收)
- 自定义导航栏 + 设置入口
pages.json:
- 新增 chat/conversation、chat/settings、chat/search 路由
Made-with: Cursor
|
2026-03-03 11:01:15 +08:00 |
|
bujinyuan
|
780dea3c31
|
feat(frontend): Task 6 - Chat Store + API + WS 事件 + TabBar badge
API (api/im.js):
- getConversations / getHistoryMessages / pinConversation
- deleteConversation / clearHistory / searchMessages / getTotalUnread
Chat Store (store/chat.js):
- 会话列表管理 + 排序(置顶优先)
- 消息缓存 + 三态确认(sending/sent/failed)
- 全局未读数管理(Redis 同步)
- WS 事件监听:im.message.new/recalled/send.ack/offline.sync/typing
- 工具方法:游标分页加载、typing 超时清除
CustomTabBar:
- 消息 Tab 显示全局未读 badge(>99 显示 99+)
Made-with: Cursor
|
2026-03-03 10:58:35 +08:00 |
|
bujinyuan
|
2c7be494f2
|
feat(im): Task 5 - IM REST Controller + Router + Wire 集成
IMController (REST API):
- GET /api/v1/im/conversations: 会话列表
- GET /api/v1/im/messages: 历史消息(游标分页)
- PUT /api/v1/im/conversations/:id/pin: 置顶/取消
- DELETE /api/v1/im/conversations/🆔 删除会话
- DELETE /api/v1/im/conversations/:id/messages: 清空记录
- GET /api/v1/im/messages/search: 全局搜索
- GET /api/v1/im/unread: 全局未读数
Wire 集成:
- IMSet Provider Set + wire.Bind 接口注入
- App struct 新增 IM 字段
- wire_gen.go 手动更新
- OfflinePusher 通过 SetOfflinePusher 注入 WS Handler
Made-with: Cursor
|
2026-03-03 10:51:56 +08:00 |
|
bujinyuan
|
1e0c061e9d
|
feat(im): Task 4 - WS 事件处理器 + 离线消息推送
EventHandler:
- im.message.send: 发送消息(解析→业务→ACK+推送)
- im.message.recall: 撤回消息
- im.conversation.read: 标记已读
- im.typing: 正在输入通知(转发对方)
- 所有事件通过 Hub.RegisterEvent 注册到路由表
OfflinePusher:
- PushOfflineMessages: 推送未读会话摘要 + 全局未读总数
- 通过 OfflineMessagePusher 接口注入 WS Handler
- WebSocket 连接建立后自动触发离线推送
Made-with: Cursor
|
2026-03-03 10:48:36 +08:00 |
|
bujinyuan
|
334d3d860b
|
feat(im): Task 3 - IM Service 核心业务 + DTO + 接口定义
IMService 核心方法:
- SendMessage: 发送消息(好友校验→查找/创建会话→幂等去重→写入→推送)
- RecallMessage: 2分钟撤回
- GetConversationList: 会话列表(含对方用户信息)
- GetHistoryMessages: 游标分页历史消息
- MarkRead / PinConversation / DeleteConversation / ClearHistory
- SearchMessages: 全局消息搜索
- GetTotalUnread: Redis 全局未读总数
DTO:
- SendMessageRequest / RecallMessageRequest
- ConversationDTO / HistoryMessageResponse
- SearchMessageRequest / MarkReadRequest / TypingRequest
接口:
- FriendChecker: 好友校验(contact 模块注入)
- UserInfoGetter: 用户信息批量查询(contact 模块注入)
Made-with: Cursor
|
2026-03-03 10:46:57 +08:00 |
|
bujinyuan
|
e091fbccb3
|
feat(im): Task 2 - IM DAO 层(会话 + 消息)
ConversationDAO:
- FindPrivateConversation: 单聊会话去重查询
- CreateWithMembers: 事务创建会话+成员
- GetUserConversations: 会话列表(含未读/置顶)
- UpdateLastMessage / IncrementUnread / ClearUnread
- SoftDeleteMember / RestoreMember / UpdateMemberPinned
- GetUnreadConversations: 离线消息推送用
MessageDAO:
- Create / GetByID / GetByConversation(游标分页)
- UpdateStatus(撤回) / DeleteByConversation(清空)
- SearchMessages(全局搜索,JOIN 权限校验)
- FindByClientMsgID(幂等去重)
Made-with: Cursor
|
2026-03-03 10:44:34 +08:00 |
|
bujinyuan
|
c23b9087b8
|
feat(ws): Task 1 - Hub 事件路由表机制
- Hub 新增 eventHandlers map + RegisterEvent / DispatchEvent 方法
- handler.go onMessage 优先查路由表,未命中走内置 fallback
- default 分支改为返回"未知事件"错误响应 + 警告日志
Made-with: Cursor
|
2026-03-03 10:43:01 +08:00 |
|