Files
ftb-project-management/docs/superpowers/specs/2026-06-16-role-permission-design.md
Script Generator c2f2fed82c feat: 加班/版本/项目调整 + 角色权限设计
加班记录:
- 加班人默认回显当前用户且不可改
- 列表加创建日期列
- 移除编辑功能(创建即提交,仅可删除)
- 提交时校验结束>开始

版本列表:
- 移除顶部 9 个状态 tab + 表格状态列
- 仅保留搜索、项目、优先级筛选

项目列表:
- 行内显示进度未达 100% 的版本(与版本页同源算法)
- 新增 lib/version-progress.ts 抽出公共进度计算

角色权限:
- RoleItem 加 permissions 字段
- 新建 lib/permissions.ts: 14 组 37 个权限点 + 默认 5 角色映射 + hasPermission
- 角色表单内嵌权限矩阵(主模块 4 件套 + Tab 二档 + Bug Tab 4 档)+ 全选/反选/仅查看快捷
- 新建 components/auth/Guard.tsx: RouteGuard + PermissionGuard + useHasPermission + AccessDenied
- Sidebar 菜单按 view 权限过滤
- 7 个主路由(products/projects/versions/requirements/overtime/admin/members/roles)包 RouteGuard
- 版本详情 Tab 按 view 权限过滤,自动跳转到第一个有权限的 Tab
- 默认未登录/无角色按只读最严格
- 超管 role-admin: ['*'] 通配符,permissions 不可改

通用:
- 新建 components/FieldError.tsx 统一表单错误文案
- PlanTab 提交时校验结束>开始

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-16 19:15:28 +08:00

255 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 角色权限设计
## Context
当前系统有 5 个默认角色(超管/产品经理/开发/测试/设计师),但 `RoleItem` 没有 `permissions` 字段,新增角色表单也只能填名字+描述。所有页面/按钮都没做权限拦截,任何登录用户都能进所有模块、做所有操作。
本期目标:给角色加权限字段、为默认 5 角色按职能分配权限、新增角色 UI 支持勾选权限、并且把权限拦截一并接入到 7 个主模块 + 版本详情 7 个 Tab + 各按钮。
## 决策摘要
| 项 | 决策 |
|---|---|
| 权限粒度 | 细粒度view/create/edit/delete + 加班特殊操作 export |
| 模块清单 | 7 主模块 + 7 个版本详情 Tab与我相关不纳入 |
| 数据结构 | `RoleItem.permissions: string[]``module:action` 形式(推荐方案 A |
| 超管标记 | `permissions: ['*']` 通配符 |
| Bug Tab 粒度 | 单独拆 4 档view/create/edit/delete其他 Tab 二档 |
| 默认 5 角色映射 | 按职能划分(详见下方) |
| 新增角色默认值 | 全部不勾选(最小授权) |
| 新增角色 UI | 现有表单内嵌权限矩阵 + 顶部"全选/反选/仅查看"快捷 |
| 拦截接入范围 | 本期一并接入:路由级 + 按钮级 + Sidebar 菜单 + Tab 级 |
| 未登录/无角色 | 默认按只读(最严格) |
| 权限点总数 | 37 个 |
| localStorage 迁移 | fetchMembers 启动检测旧 RoleItem 无 permissions 字段则补默认值 |
## 1. 权限点字典37 个)
**主模块4 件套,加班加 export**
| 模块 | 权限点 |
|---|---|
| 产品 product | view / create / edit / delete |
| 项目 project | view / create / edit / delete |
| 版本 version | view / create / edit / delete |
| 需求池 requirement | view / create / edit / delete |
| 加班记录 overtime | view / create / delete / export |
| 成员 member | view / create / edit / delete |
| 角色 role | view / create / edit / delete |
**版本详情 Tab其他二档Bug 单独 4 档)**
| Tab | 权限点 |
|---|---|
| 需求 Tab `version.req` | view / manage |
| 调研 Tab `version.research` | view / manage |
| 产品方案 Tab `version.product_plan` | view / manage |
| UI 设计 Tab `version.ui_plan` | view / manage |
| 开发任务 Tab `version.devtask` | view / manage |
| 测试用例 Tab `version.testcase` | view / manage |
| Bug Tab `version.bug` | view / create / edit / delete |
**说明**Tab 二档因为 Tab 内部操作高度耦合manage = 该 Tab 内全部增删改。Bug 单独拆 4 档因为开发与测试在 Bug 流程上职能不同:测试创建/关闭,开发改状态/修复,测试验证。
## 2. 默认 5 角色权限映射
**(1) 超级管理员 `role-admin`** — `permissions: ['*']`
**(2) 产品经理 `role-pm`**
- 全权product / project / version / requirement / version.req / version.product_plan
- 仅查看overtimeview/ memberview/ roleview/ version.research / version.ui_plan / version.devtask / version.testcase / version.bug
**(3) 开发工程师 `role-dev`**
- 全权version.devtask
- Bug Tabview + edit
- 加班view + create
- 仅查看product / project / version / requirement / version.req / version.research / version.product_plan / version.ui_plan / version.testcase
- 不可member / role / overtime export+delete / 主模块的 create/edit/delete
**(4) 测试工程师 `role-test`**
- 全权version.testcase / version.bug4 档全权)
- 加班view + create
- 仅查看product / project / version / requirement / version.req / version.research / version.product_plan / version.ui_plan / version.devtask
- 不可:同开发
**(5) 设计师 `role-design`**
- 全权version.ui_plan
- 加班view + create
- 仅查看product / project / version / requirement / version.req / version.research / version.product_plan / version.devtask / version.testcase / version.bug
- 不可:同开发
## 3. 数据结构 + Store
**`apps/web/lib/members.ts`**`RoleItem``permissions: string[]`(必填,默认 `[]`
**`apps/web/lib/permissions.ts`** 新增:
```ts
export type Permission = string;
export interface ActionDef {
action: 'view' | 'create' | 'edit' | 'delete' | 'manage' | 'export';
label: string;
permission: string;
}
export interface PermissionGroup {
module: string;
moduleLabel: string;
category: 'main' | 'version_tab';
actions: ActionDef[];
}
export const PERMISSION_GROUPS: PermissionGroup[] = [/* 14 组 */];
export const ALL_PERMISSIONS: string[] = /* 37 个展开 */;
export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
'role-admin': ['*'],
'role-pm': [...],
'role-dev': [...],
'role-test': [...],
'role-design': [...],
};
export function hasPermission(role: RoleItem | undefined, permission: string): boolean {
if (!role) return false;
if (role.permissions.includes('*')) return true;
return role.permissions.includes(permission);
}
```
**`apps/web/stores/useMemberStore.ts`**
- `PRESET_ROLES` 5 条加 `permissions`
- `fetchMembers` 加迁移:旧 RoleItem 无 permissions 字段时补默认值
- `createRole/updateRole` 接收 `permissions: string[]`
- 超管 `role-admin` 不可改 permissions`updateRole` 拦截)
## 4. UI 改动
**(A) 角色表单**`apps/web/app/admin/roles/page.tsx`
`RoleModal` 加权限区块(在 description 下方。Modal 宽度 `max-w-xs``max-w-2xl`
布局:
```
[全选] [反选] [仅查看] ← 顶部快捷按钮
主模块
─────────────────────────
产品 ☐查看 ☐创建 ☐编辑 ☐删除
项目 ☐查看 ☐创建 ☐编辑 ☐删除
版本 ☐查看 ☐创建 ☐编辑 ☐删除
需求池 ☐查看 ☐创建 ☐编辑 ☐删除
加班记录 ☐查看 ☐创建 ☐删除 ☐导出
成员 ☐查看 ☐创建 ☐编辑 ☐删除
角色 ☐查看 ☐创建 ☐编辑 ☐删除
版本详情 Tab
─────────────────────────
需求 Tab ☐查看 ☐管理
调研 Tab ☐查看 ☐管理
产品方案 Tab ☐查看 ☐管理
UI 设计 Tab ☐查看 ☐管理
开发任务 Tab ☐查看 ☐管理
测试用例 Tab ☐查看 ☐管理
Bug Tab ☐查看 ☐创建 ☐编辑 ☐删除
```
- 系统角色(`isSystem`)表单顶部黄色提示"超管权限不可修改",所有 checkbox 设 `disabled`
- 编辑态:`initial.permissions` 默认勾选
**(B) 全局拦截层**`apps/web/components/auth/Guard.tsx` 新增)
```tsx
// 路由级
<RouteGuard permission="product:view">
<ProductsPage />
</RouteGuard>
// 按钮级
<PermissionGuard permission="product:create" fallback={null}>
<button>新建产品</button>
</PermissionGuard>
// hook
const canEdit = useHasPermission('product:edit');
```
`useHasPermission` 实现:从 `useAuthStore` 取 user.roleId`useMemberStore` 查 role.permissions`hasPermission`
**(C) 接入清单**
**路由级**
- `/products` `product:view`
- `/projects` `project:view`
- `/versions` `version:view`
- `/requirements` `requirement:view`
- `/overtime` `overtime:view`
- `/admin/members` `member:view`
- `/admin/roles` `role:view`
- 无权限:`<AccessDenied />` 占位组件(不跳转,避免循环)
- `/workspace` 不拦截
**按钮级**
- 各页面"新建/编辑/删除/导出"按钮包 `PermissionGuard`
- `OvertimeModal` 内的"删除原因"按钮要 `overtime:delete`
**Sidebar 菜单过滤**
- 7 个主菜单按 `<module>:view` 过滤——无权限的菜单不显示(不灰显)
- 工作区组永远显示(与我相关)
**版本详情 Tab 过滤**`apps/web/app/versions/[id]/page.tsx`
- Tab 列表渲染时按 `version.<tab>:view` 过滤——无权限的 Tab 不渲染
- Tab 内"新建/管理"按钮按对应 manage / 4 档权限拦截
**(D) 默认状态处理**
- mock 用户已登录但 role 找不到 / 用户未登录:当前组件 `useHasPermission` 返回 `false`,所有页面/菜单/按钮都显示无权限——最严格只读
- 现有 mock 数据 `m-8 陈十``role-admin`,登录该用户即超管全权
- 切换其他 mock 用户PM/开发/测试/设计)测试不同视图
## 数据流
```
useAuthStore.user
└─ user.roleId
└─ useMemberStore.roles.find(r => r.id === user.roleId)
└─ role.permissions: string[]
├─ useHasPermission(perm) ─→ 按钮/Guard
├─ Sidebar 菜单过滤 ─→ 顶级菜单
└─ Tab 过滤 ─→ 版本详情
```
## 实施清单
新增:
- `apps/web/lib/permissions.ts` — 权限字典 + DEFAULT_ROLE_PERMISSIONS + hasPermission
- `apps/web/components/auth/Guard.tsx` — RouteGuard + PermissionGuard + useHasPermission + AccessDenied
修改:
- `apps/web/lib/members.ts` — RoleItem 加 permissions
- `apps/web/stores/useMemberStore.ts` — PRESET_ROLES 补 permissions + 迁移逻辑 + 超管保护
- `apps/web/app/admin/roles/page.tsx` — RoleModal 加权限矩阵 + 顶部快捷
- `apps/web/components/layout/Sidebar.tsx` — 菜单按 view 过滤
- `apps/web/app/products/page.tsx` — RouteGuard + 按钮拦截
- `apps/web/app/projects/page.tsx` — 同上
- `apps/web/app/versions/page.tsx` — 同上
- `apps/web/app/requirements/page.tsx` — 同上
- `apps/web/app/overtime/page.tsx` — 同上 + export 按钮拦截
- `apps/web/app/admin/members/page.tsx` — 同上
- `apps/web/app/versions/[id]/page.tsx` — Tab 过滤 + Tab 内按钮拦截
## 验证
1. `pnpm type-check` 0 错误
2. `pnpm build` 14 个页面成功
3. 角色页:超管显示"系统不可修改"+ checkbox disabled其他 4 个角色显示已勾选权限
4. 新建角色:勾选"产品-查看 + 加班-查看"保存,应能在新角色下看到对应菜单
5. 切换 mock 用户为开发工程师(如李四 m-2Sidebar 看不到成员/角色菜单;进版本详情看不到测试用例/Bug 的 manage 按钮(但能看 view
6. 切换设计师(孙七 m-5UI 设计 Tab 全权,开发任务 Tab 只读
7. localStorage 旧 RoleItem 无 permissions 字段启动后自动补默认值UI 正常渲染