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」?」后调用归档接口 |
| 「添加成员」 | 抽屉成员管理节 | 输入用户 ID(注册用户)并选角色(editor/viewer/owner)后添加;用户 ID 必填 |
| 「移除」 | 成员表操作列 | confirm 确认「确认移除成员 {user_id}?」后移除 |
| 「保存视图」 | 抽屉共享视图节 | 视图名称必填;选择类型(cross_filter/graph/map/timeline/report),State JSON 可空(非法 JSON 自动包成 {raw: 原文}) |
| 「恢复」 | 共享视图表操作列 | 解析 State 并提示「已恢复视图「name」:{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 项目展示「归档」入口。
- 共享视图:
state存 cross_filter/视图参数 JSON,前端「恢复」仅解析展示并提示可跳转对应分析页面,不直接跳转。 - 实时协作:后端 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}}。
问题四:点「恢复」只弹提示,没有跳转到分析页面
现象:「恢复」仅出现提示框。原因:共享视图「恢复」仅解析展示 State 文本,不负责路由跳转。处理:根据提示中的 State JSON 手动切换到对应分析页面(如多视图联动/地图)还原上下文。
问题五:保存视图时 State 输入了非法 JSON 但保存成功
现象:非法 JSON 也能保存。原因:前端把非法输入自动包装为 {raw: 原文} 保存,属设计行为。处理:按规范填写 JSON(如 {"filters":[{"field":"region","op":"eq","value":"北京"}]}),非法内容以 raw 形式原样保存,恢复时以原文展示。
8. 已知缺陷与边界
| 边界 | 说明 |
|---|---|
| 项目删除 | DELETE 为软删除(置 archived),无回收站概念;页面无显式「删除」按钮,仅归档 |
| 共享视图恢复 | 「恢复」不自动跳转路由,仅提示 State 内容,需用户手动进入对应分析页 |
| 成员即用户 | 成员按用户 ID(注册用户)添加;后端无用户枚举/搜索接口,无法提供搜索下拉,已补输入校验并给出明确提示(格式不符或用户不存在时报错) |
| 视图类型 | view_type 选项固定 cross_filter/graph/map/timeline/report,不校验与实际页面一致 |
| 角色变更 | owner 成员可在成员表行内角色下拉直接改他人角色(PUT 即改);不能修改自己的角色(owner 本人行显示只读徽标) |
| WebSocket | 本页不主动建立 WS 连接,实时通知依赖其他页面/协同动态页启动连接 |
| 归档即删 | DELETE 与归档共用 ArchiveProject 语义,均为软删除置 archived,无显式「删除」按钮 |
| 描述截断 | 项目描述列 max-width 220px 超出省略号截断,完整内容需打开详情确认 |