1. 页面概览
协同动态页(GothamCollaborationPage.vue)是 LightGotham 项目协作域(TAD-11)的实时协同中心:选中一个协作项目后,集中展示三块内容——成员对情报对象的评论列表、项目活动时间线、以及个人通知中心。页面最独特的是 WebSocket 实时通道:登录即自动连接 /gotham-api/v1/ws,订阅当前项目 room 后,其他人的评论、任务状态变化、系统告警会以 notification / activity 消息实时推送到页面,通知列表自动刷新并弹 toast 提示,无需手动刷新。
一句话总结:协同动态页把"谁在项目里说了什么、做了什么、通知了我什么"通过 WebSocket 变为实时可见。
2. 访问入口
- 路由 path
/gotham/collab、nameGothamCollaboration、meta.title「Gotham 协同动态」、requiresAuth 为真,挂在 GothamLayout 子路由;侧边栏入口见 GothamLayout.vue 菜单「协同动态」(位于「任务看板」之后)。前端源码action/web/src/views/GothamCollaborationPage.vue(483 行),API 客户端action/web/src/api/gothamClient.js。 - 认证:JWT(localStorage 键
aip_token)+ 刷新令牌gotham_refresh_token;401 时 gothamClient 自动用 refresh token 换新 token 并重放原请求,刷新也 401 则跳登录页。WebSocket 连接 token 走 query 参数?token=。 - 端口 18083,API 前缀
/gotham-api/v1(Vite/网关代理到 Gotham 后端并重写为/api/v1)。
3. 界面布局
+--------------------------------------------------+
| Gotham 协同动态 [项目下拉] [刷新] |
+--------------------------------------------------+
| [alert 操作结果提示条(可关闭)] |
+--------------------------------------------------+
| [ws-bar 实时推送状态条] ● 实时通知已连接/未连接 |
+--------------------------------------------------+
| [评论(N)] | [活动时间线(N)] |
| [评论输入(@ 补全下拉)][类型][对象ID][发表] |
| 评论项:作者/对象徽标/时间/内容/提及N人/删除 |
| | [分页:上一页/下一页] |
+--------------------------------------------------+
| [通知中心(N)] [刷新] [全部已读] [仅看未读] |
| 类型 | 标题 | 内容 | 时间 | 状态 | 标记已读 |
+--------------------------------------------------+
- 页头:标题 + 项目下拉(选项来自项目列表,切换即重新订阅项目 room)+「刷新」。
- 实时推送状态条:绿点「实时通知已连接」/ 灰点「实时通知未连接(自动重连中)」,收到推送时右侧显示 6 秒 toast。
- 评论卡片:发表新评论(含 @ 成员自动补全下拉)+ 评论列表(含 @ 提及人数展开)。
- 活动时间线卡片:项目内全部成员的操作动态流,底部为渲染层分页条(后端仅 limit 无 offset)。
- 通知中心卡片:当前用户的全部/未读通知表格,支持标记已读。
4. 交互元素
| 控件 | 位置 | 含义与作用 |
|---|---|---|
| 项目下拉 | 页头 | 选择协作项目(#id 名称);切换时先退订旧项目 room 再订阅新项目 room,并刷新评论/活动/通知 |
| 「刷新」 | 页头 | 并行重新加载项目、评论、活动、通知 |
| 评论内容输入框 | 评论卡片顶部 | placeholder「输入评论,可 @用户名 提及成员,如:请 @bob 核实法人信息」,回车即发表 |
| @ 自动补全下拉 | 评论输入框下方 | 输入 @ 前缀(字符集同后端正则 @([\w\p{Han}]+))时浮出候选:当前项目成员(GET /collab/projects/:id/members,最多 8 条,按 username(登录名)子串过滤);点选以 @username 回填(后端按 username 解析提及;成员 user_id 为 UUID 不可命中) |
| 评论编辑(缺) | 评论项 | 后端无评论更新端点(仅 DELETE /collab/comments/:id),评论不支持编辑;卡片内常驻提示「如需修改请删除后重发」 |
| 对象类型下拉 | 评论输入行 | target_type 五选一:实体 entity / 关系 relationship / 告警 alert / 报告 report / 任务 task |
| 对象 ID 输入框 | 评论输入行 | target_id,placeholder「对象 ID,如 graph:org:xuanwu」 |
| 「发表」 | 评论输入行 | 提交评论(需先选项目、内容非空);成功后清空内容并刷新评论与活动 |
| 「提及 N 人」 | 评论项 | 展开显示该评论被 @ 的用户 ID 列表(N 为 mentions 数组长度) |
| 活动分页「上一页/下一页」 | 活动时间线卡片底部 | 渲染层分页:一次取最近 200 条,每页 20 条;后端无 offset 无法服务端翻页,页脚常驻显示「已取最近 N 条(后端仅 limit 无 offset)」 |
| 「刷新」/「全部已读」 | 通知中心标题栏 | 重新加载通知 / 调 POST /collab/notifications/read-all |
| 「仅看未读」勾选 | 通知中心 | 勾选后仅加载未读通知(unread_only=1) |
| 「标记已读」 | 通知表格操作列 | 仅未读行显示,调 POST /collab/notifications/:id/read |
5. 后端关联
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /collab/projects | 列出当前用户参与的项目 |
| GET | /collab/projects/:id/members | 项目成员列表(本页用于构建 @ 自动补全候选:username + role;ListMembers 批量回填登录名) |
| GET | /collab/projects/:id/comments | 项目评论列表 |
| POST | /collab/projects/:id/comments | 发表评论 {target_type, target_id, content},@ 用户自动提取 |
| DELETE | /collab/comments/:id | 删除评论(作者本人);无 PUT/PATCH 更新端点 |
| GET | /collab/projects/:id/activities?limit=200 | 项目活动时间线(后端 service.go:913-932 仅支持 limit,取值 1~500,无 offset/page) |
| GET | /collab/notifications?unread_only=1 | 当前用户通知(可仅未读) |
| POST | /collab/notifications/:id/read | 标记单条已读 |
| POST | /collab/notifications/read-all | 全部标记已读 |
| WS | /ws?token= | WebSocket 实时通道 |
关键机制
WebSocket 协议:客户端发 {"type":"subscribe","project_id":"1"} 订阅项目 room、{"type":"unsubscribe","project_id":"1"} 退订;服务端每 30s Ping、读超时 60s;连接断开后前端 5s 自动重连。收到消息经 window 自定义事件 gotham:ws 广播,页面按消息 type 处理:connected(标记在线并重新订阅当前项目)、notification(toast「新通知:标题」+ 刷新通知)、activity(刷新活动时间线)、alert(toast「系统告警:规则名」+ 刷新通知)。
推送链路:collab 服务的 Notifier(ws.Hub 实现)完成推送——SendToUser 推个人通知({"type":"notification","data":{...}},如 @ 提及、任务状态变化)、SendToProject 广播活动到项目 room({"type":"activity","data":{...}})。
权限模型:collab 服务内校验——读操作需为项目成员,写操作(评论/任务/成员管理)需 owner/editor,成员增删仅 owner。
6. 权限与安全
- 认证:JWT Bearer + refresh token 轮换(TAD-12);WebSocket 用 query token 鉴权(浏览器 WS 无法自定义 header),非法 token 返回 401
{"code":"AUTH_ERROR","error":"invalid token"}。 - 数据级安全:评论/活动/通知均按"当前登录用户"口径返回(通知只查本人),评论 target_type/target_id 关联情报对象。
- 写操作防护:发表评论需先选项目(前端校验)+ 项目成员校验(后端);@ 提及由后端正则
@([\w\p{Han}]+)提取并映射为真实用户,仅给被提及人发通知。
7. 常见问题与排错
7.1 状态条长期显示「实时通知未连接(自动重连中)」
- 现象:ws-bar 灰点,5s 重连但一直连不上。
- 原因:
aip_token过期/缺失(本地无 token 时 connectWebSocket 直接跳过),或后端 18083 未启动、Vite 未代理/gotham-api。 - 处理:重新登录获取新 token;确认 Gotham 后端已启动且代理配置正确;打开 DevTools Network 查看 WS 握手返回码。
7.2 通知一直收不到
- 现象:别人评论后自己页面无新通知 toast。
- 原因:未订阅该项目 room(切换项目时未触发 subscribe),或本人不是该项目成员(推送按用户/room 维度过滤)。
- 处理:切换一下项目下拉触发重新订阅;确认自己是项目成员;看 Network 里 WS 帧是否发出 subscribe 指令。
7.3 发表评论报错
- 现象:点「发表」提示「发表评论失败:...」。
- 原因:未选择项目、评论内容为空(前端拦截),或后端返回项目成员校验失败。
- 处理:先选项目并填内容;查看错误文案区分校验错误与服务错误,确认 target_type 取值合法。
7.4 通知列表与实时推送不同步
- 现象:「全部已读」成功后勾选「仅看未读」列表未变化。
- 原因:标记已读是异步落库,列表加载与推送刷新存在时序差异。
- 处理:再点一次通知中心「刷新」;若仍异常查看后端通知表记录。
8. 已知缺陷与边界
| 缺陷/边界 | 说明 |
|---|---|
| 评论无编辑(后端缺口) | 后端只有 GET/POST /collab/projects/:id/comments 与 DELETE /collab/comments/:id(server.go:687-689),无 PUT/PATCH 更新端点,纯前端无法代偿;卡片内已常驻如实标注「暂不支持编辑,如需修改请删除后重发」 |
| @ 自动补全候选范围 | 候选来自当前项目成员列表(GET /collab/projects/:id/members),非全局用户枚举(后端无 /users 类端点);非项目成员或成员列表拉取失败时无候选,仍可手输 @username(登录名),后端按正则 @([\w\p{Han}]+) 提取并按 username 查 users 表映射,提及不到真实用户则不发通知 |
| 活动时间线为渲染层分页 | 后端 ListActivities(collab/service.go:913-932)仅支持 limit(默认 100,上限 500),无 offset/page,无法服务端翻页;页面一次取最近 200 条后渲染层分页(每页 20),更早活动不在返回集内(分页条常驻此提示) |
| WS 依赖全局事件 | 组件通过 window 事件 gotham:ws 订阅,推送消息会广播到所有监听者 |
| token 走 query | WS 地址带 token 可能出现在代理日志,安全性由短生命周期 token 兜底 |
注:评论删除入口已于 2026-09-06 修复(删除按钮作者本人可见,调用后端 DELETE)。