1. 页面概览
1.1 是什么
「项目协作」页面(页面内标题为 Gotham 项目协作,TAD-11)是 LightGotham 的团队分析项目管理页:创建分析项目(如「反洗钱专项」)、管理项目成员角色,以及保存/恢复共享视图(分析上下文 State JSON)。它把「一群人围绕一个分析主题协作」的最小闭环——建项目、拉成员、存视图——集中在一个页面完成。
1.2 核心价值
| 维度 | 说明 |
|---|---|
| 项目列表 | 「我参与的项目」按成员关系过滤,仅展示当前用户参与的项目 |
| 成员角色 | owner/editor/viewer 三级角色;创建者自动成为 owner,写操作需 owner/editor |
| 成员管理 | 添加成员时选角色、移除成员,owner 专属操作 |
| 共享视图 | 保存分析上下文(view_type + State JSON),成员可一键「恢复」 |
| 归档 | active 项目可归档(archived),归档后从列表操作列移除归档入口 |
1.3 一句话总结
2. 访问入口
2.1 路由与菜单
- 路由:
/gotham/projects;路由名称:GothamProjects - 路由 meta:
title: Gotham 项目协作,requiresAuth: true,挂在父路由/gotham(GothamLayout)下 - 菜单位置:Gotham 左侧边栏「协作工作流」分组下的「项目协作」
- 前端源码:
action/web/src/views/GothamProjectsPage.vue - API 客户端:
action/web/src/api/gothamClient.js
2.2 认证与权限
- 路由挂
requiresAuth: true,未登录访问被全局守卫重定向到/login。 - 请求走 gothamClient:请求拦截器自动附带
Authorization: Bearer <aip_token>;响应拦截器遇 401 时用gotham_refresh_token换发新 token 并重放原请求。 - 后端 collab 服务按成员角色鉴权:读操作需项目成员,写操作需 owner/editor,成员管理仅 owner。
2.3 端口与 API 前缀
- Gotham 后端端口:18083(Vite 将
/gotham-api前缀代理到该端口并重写为/api/v1)。 - API 前缀:
/gotham-api/v1(gothamClient 的 baseURL)。
3. 界面布局
页面为「主列表 + 右侧抽屉」布局:
┌──────────────────────────────────────────────────────────────┐
│ Gotham 项目协作 [刷新] │
│ [alert 操作结果提示条(可关闭)] │
│ ① 新建项目:项目名称(如 反洗钱专项)/ 项目描述 [创建项目] │
│ ② 我参与的项目(N) │
│ ID/名称/描述/状态/创建者/操作(详情/成员/共享视图|归档|删除)│
└──────────────────────────────────────────────────────────────┘
┌ 抽屉:项目 #id:name [关闭] ─────────────────────┐
│ 成员管理:用户 ID(注册用户)/ 角色 [添加成员] │
│ [搜索已加入成员(渲染层过滤)] │
│ 用户 ID/角色/加入时间/移除 │
│ 共享视图(恢复分析上下文):视图名称/类型 [保存视图] │
│ State JSON(可选) │
│ 名称/类型/创建人/State/恢复(跳转)/删除 │
└───────────────────────────────────────────────────┘
- 新建项目卡片:名称与描述两个输入框 + 「创建项目」按钮。
- 项目列表卡片:展示我参与的项目;active 项目提供「归档」操作,所有项目提供「删除」操作(软删除,语义同归档);列表下方常驻说明软删除与无级联的范围。
- 详情抽屉:点击「详情 / 成员 / 共享视图」从右侧滑出,包含成员管理与共享视图两节。
4. 交互元素
| 控件 | 位置 | 含义与作用 |
|---|---|---|
| 「刷新」按钮 | 页头右上 | 重载项目列表 |
| 项目名称/描述输入框 + 「创建项目」 | 新建项目卡片 | 名称必填;成功后提示「项目创建成功(id=...,你已自动成为 owner)」并刷新列表 |
| 行内「详情 / 成员 / 共享视图」 | 项目列表操作列 | 打开详情抽屉,并行加载成员与共享视图 |
| 行内「归档」 | 项目列表操作列 | 仅 active 项目显示;confirm 确认「确认归档项目「name」?」后调用 POST /collab/projects/:id/archive |
| 行内「删除」 | 项目列表操作列 | 所有项目均显示;confirm 中明确说明「软删除(等同归档)、不级联删除任何关联数据、项目仍留在列表」,确认后调用 DELETE /collab/projects/:id |
| 成员表「搜索」输入框 | 抽屉成员管理节 | 渲染层过滤已加载成员(按 user_id 子串,大小写不敏感);后端无用户枚举接口,不能搜索尚未加入的候选用户 |
| 「添加成员」 | 抽屉成员管理节 | 输入用户 ID(注册用户)并选角色(editor/viewer/owner)后添加;用户 ID 必填 |
| 「移除」 | 成员表操作列 | confirm 确认「确认移除成员 {user_id}?」后移除 |
| 「保存视图」 | 抽屉共享视图节 | 视图名称必填;选择类型(cross_filter/graph/map/timeline/report),State JSON 可空(非法 JSON 自动包成 {raw: 原文}) |
| 「恢复」 | 共享视图表操作列 | 解析 State 并提示,同时按 view_type 自动跳转对应分析路由(graph→/gotham、map→/gotham/map、timeline→/gotham/timeline、cross_filter→/gotham/views、report→/gotham/reports),携带 view_id/view_type 查询参数;未知类型仅展示 State 不跳转 |
| 「删除」 | 共享视图表操作列 | confirm 确认「确认删除共享视图「name」?」后删除 |
5. 后端关联
5.1 API 客户端
- 文件:
action/web/src/api/gothamClient.js;baseURL/gotham-api/v1,超时 30000ms。 - 本页为纯 CRUD,无轮询;统一响应体
{code: 0, data: ...},前端取res.data.data。
5.2 端点表
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /collab/projects | 列出当前用户参与的项目 |
| POST | /collab/projects | 创建项目(创建者自动成为 owner 成员) |
| GET | /collab/projects/:id | 项目详情 |
| PUT | /collab/projects/:id | 更新项目信息 |
| DELETE | /collab/projects/:id | 删除项目(软删除:状态置 archived,对齐 TAD-11 §4.1 DELETE 语义) |
| POST | /collab/projects/:id/archive | 归档项目(状态置 archived) |
| GET | /collab/projects/:id/members | 列出项目成员 |
| POST | /collab/projects/:id/members | 添加成员(body {user_id, role},仅 owner) |
| DELETE | /collab/projects/:id/members/:user_id | 移除成员(仅 owner) |
| GET | /collab/projects/:id/shared-views | 列出项目共享视图 |
| POST | /collab/projects/:id/shared-views | 创建共享视图(body {name, view_type, state}) |
| GET | /collab/shared-views/:id | 获取共享视图(恢复分析上下文) |
| DELETE | /collab/shared-views/:id | 删除共享视图 |
5.3 响应结构
统一响应体 {code: 0, data: ...}。项目与共享视图示例:
{ "code": 0, "data": [ { "id": 1, "name": "反洗钱专项", "description": "资金链路分析",
"status": "active", "owner_id": "alice", "created_at": "2026-08-30T..." } ] }
{ "code": 0, "data": { "id": 1, "project_id": 1, "name": "华北情报视图",
"view_type": "cross_filter", "state": {"filters":[{"field":"region","op":"eq","value":"北京"}]},
"created_by": "alice" } }
5.4 关键机制
- 成员角色鉴权:读操作要求项目成员(否则 403「非项目成员,无权访问该项目」);写操作要求 owner/editor(否则「仅 owner/editor 可执行该写操作」);成员添加/移除仅 owner(「仅项目 owner 可执行该操作」)。
- 项目状态:active / archived / closed;删除与归档都落到 archived(见下条),页面仅对 active 项目展示「归档」入口。
- 删除 = 归档(软删除):
DELETE /collab/projects/:id(handlers_collab.go:79 handleDeleteCollabProject)与POST /:id/archive(:93 handleArchiveCollabProject)都调用collabService.ArchiveProject(collab/service.go:458),仅把status置为archived并写一条活动记录,不级联删除成员/任务/评论/共享视图,也不清理项目记录;ListProjects(collab/service.go:395)不按状态过滤,故删除后项目仍在列表中。页面确认弹窗与列表下方说明均如实标注。 - 共享视图:
state存 cross_filter/视图参数 JSON,前端「恢复」按view_type跳转对应分析路由并携带view_id/view_type查询参数(未知类型仅展示 State)。 - 实时协作:后端 collab 服务经 ws.Hub(Notifier)在项目成员变更、任务变动时向项目 room 推送 WebSocket 通知;gothamClient 提供
connectWebSocket/wsSubscribeProject供订阅,本页未直接调用。
6. 权限与安全
- 认证:全部
/collab/*端点位于 protected 组,JWT 无效一律 401,前端自动刷新/跳登录。 - 项目级数据隔离:项目列表按成员关系过滤(
ListProjects(currentUserID)),非成员无法读取/写入他人项目,服务层 requireMember 兜底。 - 写操作防护:归档、移除成员、删除共享视图均经
window.confirm二次确认;成员管理操作限 owner,避免越权。 - 输入校验:项目名称必填、成员用户 ID 必填、共享视图名称必填,非法 State JSON 前端自动降级为
{raw: 原文}不阻断保存。
7. 常见问题与排错
问题一:项目列表为空,但项目确实存在
现象:列表页看不到已有项目。原因:列表接口按成员关系过滤,只返回当前用户参与的项目。处理:确认当前登录用户是否是该项目的成员;查看 GET /collab/projects 返回;创建者需先在项目里把自己设为成员(创建时已自动成为 owner)。
问题二:添加成员报「仅项目 owner 可执行该操作」
现象:添加/移除成员被拒绝。原因:当前用户角色为 editor/viewer,成员管理仅限 owner。处理:让 owner 成员执行添加/移除;或在项目中把自己提升为 owner 角色。
问题三:创建项目成功但提示框里没有 id
现象:成功提示未显示项目 id。原因:接口返回体解析异常或网络抖动。处理:刷新项目列表确认是否已出现新项目;用 curl POST /api/v1/collab/projects 复现并检查响应体 {code:0, data:{id}}。
问题四:点「恢复」只弹提示,没有跳转到分析页面
现象:点「恢复」后跳转到了分析页面,但上下文未自动还原。原因:「恢复」按 view_type 跳转到对应路由并携带 view_id/view_type 查询参数,目标页面仍以自身查询逻辑为准(State 内容仅完整展示在提示条中,不做跨页状态注入)。处理:查看提示条中的 State JSON,在目标页面按其筛选条件复原上下文;若 view_type 为未知值则不会跳转,仅展示 State。
问题五:保存视图时 State 输入了非法 JSON 但保存成功
现象:非法 JSON 也能保存。原因:前端把非法输入自动包装为 {raw: 原文} 保存,属设计行为。处理:按规范填写 JSON(如 {"filters":[{"field":"region","op":"eq","value":"北京"}]}),非法内容以 raw 形式原样保存,恢复时以原文展示。
8. 已知缺陷与边界
| 边界 | 说明 |
|---|---|
| 项目删除为软删除 | DELETE /collab/projects/:id 与归档同义(共用 ArchiveProject,collab/service.go:458):仅置 status=archived,无回收站、无级联删除(成员/任务/评论/共享视图全部保留),项目仍出现在列表中(列表不按状态过滤)。页面已补「删除」入口,确认弹窗与列表说明如实标注该范围 |
| 共享视图恢复 | 「恢复」已按 view_type 自动跳转(graph/map/timeline/cross_filter/report → 对应路由,携带 view_id/view_type);未知 view_type 仅展示 State 不跳转 |
| 成员即用户(后端缺口) | 成员按用户 ID(注册用户)添加;后端无用户/成员枚举接口(server.go 无 users 路由),无法提供待添加用户的候选搜索下拉。已补:输入格式校验与提示 + 成员表渲染层搜索(仅过滤已加载成员)。候选枚举需后端新增 /users 类端点 |
| 视图类型 | view_type 选项固定 cross_filter/graph/map/timeline/report,跳转映射以 router/index.js Gotham 子路由为准,不校验 State 内容与页面参数一致 |
| 角色变更 | owner 成员可在成员表行内角色下拉直接改他人角色(PUT 即改);不能修改自己的角色(owner 本人行显示只读徽标) |
| WebSocket | 本页不主动建立 WS 连接,实时通知依赖其他页面/协同动态页启动连接 |
| 描述截断 | 项目描述列 max-width 220px 超出省略号截断,完整内容需打开详情确认 |