mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-17 23:29:01 +02:00
110 lines
6.6 KiB
Markdown
110 lines
6.6 KiB
Markdown
# Workspace URL 化重构 — 项目汇报
|
||
|
||
**日期**:2026-04-15
|
||
**作者**:Naiyuan
|
||
**状态**:调研完成,待评审
|
||
|
||
---
|
||
|
||
## 一、为什么要做
|
||
|
||
当前 workspace 上下文完全靠 `X-Workspace-ID` HTTP header + Zustand store + localStorage 承载,URL 里**不含任何 workspace 信息**。所有路径都是 `/issues`、`/issues/:id` 这种 workspace-agnostic 的。
|
||
|
||
这个设计已经在产品里直接表现为 3 个已知问题:
|
||
|
||
1. **分享链接不可靠**(MUL-43):`/issues/abc` 发给另一个成员,会用他自己 localStorage 里的 workspace 去解析,导致 404 或看到错误 workspace 的数据
|
||
2. **手机端无法切 workspace**(MUL-509):切换只靠 sidebar UI,手机端不展开 sidebar 就没有切换入口
|
||
3. **多 tab 互相覆盖**:`multica_workspace_id` 是全局 localStorage key,两个 tab 打开不同 workspace 会互相污染
|
||
|
||
除了这 3 个显性 bug,架构上的"多份 workspace 状态拷贝互相同步"也带来一些隐性问题(创建 workspace 闪页、切换 workspace 时 cache 竞态等),积累时间越长后续改动越难。
|
||
|
||
行业惯例(Linear / Notion / Vercel / GitHub)都是 `/{workspace-slug}/...` 的 URL 形态,把 URL 当作 workspace 的唯一来源。这是我们应该对齐的最佳实践。
|
||
|
||
## 二、调研结论
|
||
|
||
### 好消息:基础设施已经就位
|
||
|
||
- 数据库 `workspace.slug` 字段已经存在(`TEXT UNIQUE NOT NULL`),用户创建时手动指定且不可修改
|
||
- 后端已有 `GetWorkspaceBySlug` 查询
|
||
- 前端 `Workspace` 类型已包含 `slug` 字段
|
||
- Web 端认证已经切换为 HttpOnly cookie 模式,Next.js middleware 可读到登录态
|
||
|
||
也就是说这次改造**不需要大量后端改动**,主要是前端路由和状态管理的重新组织。
|
||
|
||
### 坏消息:范围比最初估计大
|
||
|
||
初看以为只是"URL 前缀加个 slug",调研后发现必须一起做的事情有:
|
||
|
||
1. **URL 路由重组**:web 端所有 dashboard 路由迁到 `app/[workspaceSlug]/(dashboard)/*`;desktop 端所有 react-router 路由加 `/:workspaceSlug` 前缀
|
||
2. **状态管理清理**:删除 `useWorkspaceStore.workspace` 作为独立状态,改为从 URL 派生;删除 `hydrateWorkspace` / `switchWorkspace` actions(切 workspace 变成纯导航);删除 `localStorage["multica_workspace_id"]`
|
||
3. **所有路径引用替换**:`push("/issues")` 改为 path builder(`paths.issues()`),影响 ~25 个组件文件
|
||
4. **Mutation 副作用重构**:`useCreateWorkspace` / `useLeaveWorkspace` / `useDeleteWorkspace` 里的 `switchWorkspace` 调用全部移除(这些调用正是 MUL-727 闪页、MUL-728 删除后不跳转、MUL-820 接受邀请不切 workspace 等一系列 bug 的根因)
|
||
5. **桌面端 tab 系统适配**:tab 路径天然包含 workspace,切 workspace = 开新 tab 或导航,不再有全局切换动作
|
||
6. **Shareable URL 修复**:桌面端 `getShareableUrl` 当前生成 `https://www.multica.ai/issues/abc`(缺 slug),需要更新
|
||
7. **后端保留词校验**:slug 不能和前端顶级路由冲突(`login`、`onboarding`、`invite`、`api`、`settings` 等),后端创建时校验
|
||
8. **内部 markdown 链接兼容**:issue 评论里写的 `[foo](/issues/abc)` 触发的 `multica:navigate` 事件需要自动补当前 workspace slug
|
||
|
||
### 不需要改的(边界已确认)
|
||
|
||
- 邮件邀请链接 `/invite/{id}` — 接受邀请是 pre-workspace 流程,不需要 slug
|
||
- `mention://type/id` 协议 — 只存 UUID,workspace-agnostic
|
||
- CLI 登录 URL — `/login` 也是 pre-workspace,不需要 slug
|
||
- 后端 API 路径 — 保持 `/api/workspaces/{id}`,slug 仅用于前端 URL
|
||
- 桌面端 `multica://auth/callback` — 认证回调,不涉及 workspace
|
||
|
||
## 三、方案要点
|
||
|
||
**核心原则**:URL 是 workspace 上下文的唯一 source of truth,其他状态都是派生态。
|
||
|
||
**URL 形状**:`/{workspace-slug}/issues/{id}` (和 Linear / Notion 一致)
|
||
|
||
**切换 workspace = 导航**:sidebar 下拉改为 `<Link href="/{new-slug}/issues">`,不再有命令式的 `switchWorkspace` 函数。这样一次性消除前面列出的一大批 mutation 副作用 bug。
|
||
|
||
**预估影响面**:~30-35 个文件,其中约 20 个是机械替换(hardcoded 路径 → path builder),真正需要思考的核心逻辑改动集中在 5-6 个文件。
|
||
|
||
**一个 PR 合并**:中间状态不可运行(URL 结构是原子变化),不拆 PR。worktree 里充分开发和自测,一次 review 合并。
|
||
|
||
## 四、执行与测试计划
|
||
|
||
### 执行阶段
|
||
|
||
1. **本周内**:完成方案详细实施文档(精确到文件 / 行号 / 代码片段)
|
||
2. **下一步**:在独立 worktree 上开发,AI 辅助写代码,过程中人工 review
|
||
3. **开发完成后**:本地跑全套验证(`make check` — TypeScript + 单测 + Go 测试 + E2E)
|
||
|
||
### 测试阶段
|
||
|
||
1. **本地自测**:
|
||
- 已知功能路径(创建 / 浏览 / 搜索 issue,切换 workspace,接受邀请,分享链接)
|
||
- 已知 bug 场景(MUL-43 / MUL-509 / MUL-727 / MUL-820)逐一验证已修复
|
||
- 多 tab 场景(两个 tab 打开不同 workspace 互不影响)
|
||
2. **测试环境部署**:本地通过后发测试环境,全员试用几天,观察:
|
||
- 是否有回归(特别是导航流、创建/删除 workspace、邀请流程)
|
||
- URL 使用感受(分享、收藏、刷新)
|
||
3. **灰度 / 生产**:测试环境稳定后推生产
|
||
|
||
### 风险提示
|
||
|
||
- **唯一的硬中断点**:现有的 `/issues` 等 URL 在重构后会 404(产品还没正式 ship、用户量可忽略,所以不做兼容性重定向)
|
||
- **E2E 测试断言**:约 20-30 处 URL 断言需要更新
|
||
- **后端保留词清单**:如果现有 workspace 里有名字撞到保留词的(例如正好叫 `settings`),需要提前 migrate(可能性极低,因 slug 限制较严)
|
||
|
||
## 五、附注
|
||
|
||
这次重构会**顺带修掉**以下已登记 issue,不需要单独开 PR:
|
||
|
||
| Issue | 修复方式 |
|
||
|---|---|
|
||
| MUL-43(切换 workspace 报错 / 分享链接失效) | URL 带 slug,根本解决 |
|
||
| MUL-509(手机端无法切 workspace) | 切换变导航,手机能点链接就能切 |
|
||
| MUL-723(workspace 不在 URL) | 核心目标 |
|
||
| MUL-727(创建 workspace 闪 /issues) | 删除 mutation 里的 switchWorkspace 副作用 |
|
||
| MUL-728(删除 workspace 后留在 /settings) | 删除成功后 navigate 到下一个 workspace |
|
||
| MUL-820(sidebar Join 不切 workspace) | Join 改成跳转到 `/invite/{id}` 走统一路径 |
|
||
|
||
不在本次范围内的:Issue #951(WebSocket 半开导致 cache 陈旧)—— 这是 realtime 层独立问题,单独 PR 处理。
|
||
|
||
---
|
||
|
||
**当前状态**:准备进入详细实施方案撰写,预计完成后再同步一次。
|