docs: 更新项目文档
- 更新 CLAUDE.md 中 HTTP 客户端说明(apiFetch -> http) - 更新 docs/web/DESIGN.md 技术栈说明 - 添加任务工作流文档 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
28
CLAUDE.md
28
CLAUDE.md
@@ -6,6 +6,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
Seclusion 是一个基于 Next.js + NestJS 的 Monorepo 项目模板,使用 pnpm workspace + Turborepo 管理。
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Node.js >= 20.0.0
|
||||
- pnpm >= 9.0.0
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
@@ -39,12 +44,33 @@ cd apps/api && pnpm db:studio # 打开 Prisma Studio
|
||||
|
||||
### Monorepo 结构
|
||||
|
||||
- **apps/web** - Next.js 16 前端 (端口 3000),使用 React 19
|
||||
- **apps/web** - Next.js 16 前端 (端口 3000),使用 React 19 + Tailwind CSS v4
|
||||
- **apps/api** - NestJS 10 后端 (端口 4000,API 文档: /api/docs)
|
||||
- **packages/shared** - 共享类型定义、工具函数和加密模块
|
||||
- **packages/eslint-config** - 共享 ESLint 9 flat config 配置
|
||||
- **packages/typescript-config** - 共享 TypeScript 配置
|
||||
|
||||
### 前端架构 (apps/web)
|
||||
|
||||
**状态管理分类**
|
||||
|
||||
| 状态类型 | 管理方式 | 示例 |
|
||||
|----------|----------|------|
|
||||
| 服务端状态 | TanStack Query | 用户列表、详情数据 |
|
||||
| 客户端全局状态 | Zustand | 认证信息、主题设置 |
|
||||
| 组件本地状态 | useState | 表单输入、弹窗开关 |
|
||||
| URL 状态 | Next.js Router | 分页参数、筛选条件 |
|
||||
|
||||
**数据请求分层**
|
||||
|
||||
```
|
||||
Component → Hook (TanStack Query) → Service → http
|
||||
```
|
||||
|
||||
- **Service 层** (`services/*.service.ts`): 封装 API 调用
|
||||
- **Hook 层** (`hooks/use*.ts`): 封装 TanStack Query,提供 queryKey 管理
|
||||
- **http** (`lib/http.ts`): Axios HTTP 客户端封装,含加密处理、Token 自动刷新
|
||||
|
||||
### 后端模块 (apps/api)
|
||||
|
||||
NestJS 采用模块化架构:
|
||||
|
||||
@@ -4,11 +4,18 @@
|
||||
|
||||
## 文档列表
|
||||
|
||||
| 文档 | 说明 | 适用场景 |
|
||||
| ---------------------------------------------------- | ----------------- | -------------------------------- |
|
||||
| [design.md](./design.md) | 项目设计文档 | 了解整体架构、技术选型、模块设计 |
|
||||
| [backend/soft-delete.md](./backend/soft-delete.md) | 软删除设计文档 | 了解软删除机制、扩展新模型 |
|
||||
| [backend/crud-service.md](./backend/crud-service.md) | CRUD Service 模板 | 快速创建标准 CRUD 服务 |
|
||||
| 文档 | 说明 | 适用场景 |
|
||||
| ---------------------------------------------------- | -------------------- | -------------------------------- |
|
||||
| [design.md](./design.md) | 项目设计文档 | 了解整体架构、技术选型、模块设计 |
|
||||
| [backend/soft-delete.md](./backend/soft-delete.md) | 软删除设计文档 | 了解软删除机制、扩展新模型 |
|
||||
| [backend/crud-service.md](./backend/crud-service.md) | CRUD Service 模板 | 快速创建标准 CRUD 服务 |
|
||||
| [../plop/README.md](../plop/README.md) | CRUD 代码生成器 | 一键生成全栈 CRUD 模块 |
|
||||
|
||||
## 任务文档
|
||||
|
||||
| 文档 | 说明 | 日期 |
|
||||
| -------------------------------------------------------------- | ------------------ | ---------- |
|
||||
| [workflow/250117-权限管理体系实施.md](./workflow/250117-权限管理体系实施.md) | 菜单及路由权限管理体系 | 2025-01-17 |
|
||||
|
||||
## 快速链接
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| **表单处理** | React Hook Form | ^7.x | 高性能表单库 |
|
||||
| **表单验证** | Zod | ^3.x | TypeScript 优先的 Schema 验证 |
|
||||
| **图标** | Lucide Icons | latest | shadcn/ui 默认搭配 |
|
||||
| **HTTP 客户端** | Native Fetch | - | 已封装 apiFetch |
|
||||
| **HTTP 客户端** | Axios | ^1.x | 封装为 http 模块 |
|
||||
|
||||
## 二、目录结构
|
||||
|
||||
@@ -106,12 +106,12 @@ apps/web/src/
|
||||
│ └── captcha.service.ts # 验证码 API
|
||||
│
|
||||
├── lib/ # 工具库
|
||||
│ ├── api.ts # API 请求封装 (已有)
|
||||
│ ├── http.ts # Axios HTTP 客户端封装
|
||||
│ ├── crypto.ts # 加密模块 (已有)
|
||||
│ ├── auth.ts # 认证相关工具函数
|
||||
│ ├── query-client.ts # TanStack Query 配置
|
||||
│ ├── validations.ts # Zod Schema 定义
|
||||
│ ├── utils.ts # 通用工具函数
|
||||
│ └── cn.ts # className 合并工具
|
||||
│ └── utils.ts # 通用工具函数
|
||||
│
|
||||
├── types/ # 类型定义
|
||||
│ └── index.ts # 类型导出 (已有)
|
||||
@@ -257,7 +257,7 @@ export const useUIStore = create<UIState>()(
|
||||
└──────┬──────┘
|
||||
│ 调用
|
||||
┌──────▼──────┐
|
||||
│ apiFetch │ HTTP 请求 + 加密处理
|
||||
│ http │ Axios HTTP 客户端 + 加密处理
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
@@ -265,12 +265,13 @@ export const useUIStore = create<UIState>()(
|
||||
|
||||
```typescript
|
||||
// services/user.service.ts
|
||||
import { apiFetch } from '@/lib/api';
|
||||
import { http } from '@/lib/http';
|
||||
import type {
|
||||
UserResponse,
|
||||
PaginatedResponse,
|
||||
UpdateUserDto
|
||||
} from '@seclusion/shared';
|
||||
import { API_ENDPOINTS } from '@/config/constants';
|
||||
|
||||
export interface GetUsersParams {
|
||||
page?: number;
|
||||
@@ -280,47 +281,28 @@ export interface GetUsersParams {
|
||||
|
||||
export const userService = {
|
||||
// 获取用户列表
|
||||
getUsers: async (params: GetUsersParams, token: string) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params.page) searchParams.set('page', String(params.page));
|
||||
if (params.pageSize) searchParams.set('pageSize', String(params.pageSize));
|
||||
if (params.search) searchParams.set('search', params.search);
|
||||
|
||||
const query = searchParams.toString();
|
||||
return apiFetch<PaginatedResponse<UserResponse>>(
|
||||
`/users${query ? `?${query}` : ''}`,
|
||||
{ token }
|
||||
);
|
||||
getUsers: (params: GetUsersParams = {}): Promise<PaginatedResponse<UserResponse>> => {
|
||||
return http.get<PaginatedResponse<UserResponse>>(API_ENDPOINTS.USERS, { params });
|
||||
},
|
||||
|
||||
// 获取单个用户
|
||||
getUser: async (id: string, token: string) => {
|
||||
return apiFetch<UserResponse>(`/users/${id}`, { token });
|
||||
getUser: (id: string): Promise<UserResponse> => {
|
||||
return http.get<UserResponse>(`${API_ENDPOINTS.USERS}/${id}`);
|
||||
},
|
||||
|
||||
// 更新用户
|
||||
updateUser: async (id: string, data: UpdateUserDto, token: string) => {
|
||||
return apiFetch<UserResponse>(`/users/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(data),
|
||||
token,
|
||||
});
|
||||
updateUser: (id: string, data: UpdateUserDto): Promise<UserResponse> => {
|
||||
return http.patch<UserResponse>(`${API_ENDPOINTS.USERS}/${id}`, data);
|
||||
},
|
||||
|
||||
// 删除用户
|
||||
deleteUser: async (id: string, token: string) => {
|
||||
return apiFetch<void>(`/users/${id}`, {
|
||||
method: 'DELETE',
|
||||
token,
|
||||
});
|
||||
deleteUser: (id: string): Promise<void> => {
|
||||
return http.delete<void>(`${API_ENDPOINTS.USERS}/${id}`);
|
||||
},
|
||||
|
||||
// 恢复用户
|
||||
restoreUser: async (id: string, token: string) => {
|
||||
return apiFetch<UserResponse>(`/users/${id}/restore`, {
|
||||
method: 'PATCH',
|
||||
token,
|
||||
});
|
||||
restoreUser: (id: string): Promise<UserResponse> => {
|
||||
return http.patch<UserResponse>(`${API_ENDPOINTS.USERS}/${id}/restore`);
|
||||
},
|
||||
};
|
||||
```
|
||||
@@ -331,7 +313,7 @@ export const userService = {
|
||||
// hooks/useUsers.ts
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { userService, type GetUsersParams } from '@/services/user.service';
|
||||
import { useAuthStore } from '@/stores/authStore';
|
||||
import { useIsAuthenticated } from '@/stores';
|
||||
|
||||
// Query Keys
|
||||
export const userKeys = {
|
||||
@@ -344,34 +326,33 @@ export const userKeys = {
|
||||
|
||||
// 获取用户列表
|
||||
export function useUsers(params: GetUsersParams = {}) {
|
||||
const token = useAuthStore((state) => state.token);
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
|
||||
return useQuery({
|
||||
queryKey: userKeys.list(params),
|
||||
queryFn: () => userService.getUsers(params, token!),
|
||||
enabled: !!token,
|
||||
queryFn: () => userService.getUsers(params),
|
||||
enabled: isAuthenticated,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取单个用户
|
||||
export function useUser(id: string) {
|
||||
const token = useAuthStore((state) => state.token);
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
|
||||
return useQuery({
|
||||
queryKey: userKeys.detail(id),
|
||||
queryFn: () => userService.getUser(id, token!),
|
||||
enabled: !!token && !!id,
|
||||
queryFn: () => userService.getUser(id),
|
||||
enabled: isAuthenticated && !!id,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新用户
|
||||
export function useUpdateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
const token = useAuthStore((state) => state.token);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateUserDto }) =>
|
||||
userService.updateUser(id, data, token!),
|
||||
userService.updateUser(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: userKeys.detail(id) });
|
||||
@@ -382,10 +363,9 @@ export function useUpdateUser() {
|
||||
// 删除用户
|
||||
export function useDeleteUser() {
|
||||
const queryClient = useQueryClient();
|
||||
const token = useAuthStore((state) => state.token);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => userService.deleteUser(id, token!),
|
||||
mutationFn: (id: string) => userService.deleteUser(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
|
||||
},
|
||||
|
||||
544
docs/workflow/250117-权限管理体系实施.md
Normal file
544
docs/workflow/250117-权限管理体系实施.md
Normal file
@@ -0,0 +1,544 @@
|
||||
# 菜单及路由管理体系实施文档
|
||||
|
||||
## 需求确认
|
||||
|
||||
| 项目 | 选择 |
|
||||
|------|------|
|
||||
| 菜单来源 | 混合模式(核心菜单静态 + 扩展菜单数据库)|
|
||||
| 权限粒度 | 按钮级别 |
|
||||
| 权限模型 | RBAC(角色-权限模型)|
|
||||
| 超级管理员 | 需要内置 |
|
||||
| 权限编码 | `资源:操作` 格式(如 `user:create`)|
|
||||
| 前端权限 | Hook + 组件 + 指令 |
|
||||
| 路由守卫 | 自动拦截(跳转 403)|
|
||||
|
||||
---
|
||||
|
||||
## 一、数据库模型设计
|
||||
|
||||
### 新增模型
|
||||
|
||||
```prisma
|
||||
// 角色表
|
||||
model Role {
|
||||
id String @id @default(cuid(2))
|
||||
code String @unique // 角色编码: admin, user
|
||||
name String // 角色名称
|
||||
description String?
|
||||
isSystem Boolean @default(false) // 系统内置角色不可删
|
||||
isEnabled Boolean @default(true)
|
||||
sort Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
users UserRole[]
|
||||
permissions RolePermission[]
|
||||
menus RoleMenu[]
|
||||
}
|
||||
|
||||
// 权限表
|
||||
model Permission {
|
||||
id String @id @default(cuid(2))
|
||||
code String @unique // 权限编码: user:create
|
||||
name String
|
||||
description String?
|
||||
resource String // 资源: user
|
||||
action String // 操作: create
|
||||
isEnabled Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
roles RolePermission[]
|
||||
}
|
||||
|
||||
// 菜单表
|
||||
model Menu {
|
||||
id String @id @default(cuid(2))
|
||||
parentId String?
|
||||
code String @unique
|
||||
name String
|
||||
type String @default("menu") // dir / menu / button
|
||||
path String?
|
||||
icon String? // Lucide 图标名
|
||||
component String?
|
||||
permission String? // 关联权限编码
|
||||
isExternal Boolean @default(false)
|
||||
isHidden Boolean @default(false)
|
||||
isEnabled Boolean @default(true)
|
||||
isStatic Boolean @default(true) // 静态/动态菜单
|
||||
sort Int @default(0)
|
||||
meta Json?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
parent Menu? @relation("MenuTree", fields: [parentId], references: [id])
|
||||
children Menu[] @relation("MenuTree")
|
||||
roles RoleMenu[]
|
||||
}
|
||||
|
||||
// 关联表
|
||||
model UserRole { ... }
|
||||
model RolePermission { ... }
|
||||
model RoleMenu { ... }
|
||||
|
||||
// User 模型扩展
|
||||
model User {
|
||||
// ... 现有字段
|
||||
isSuperAdmin Boolean @default(false) // 超级管理员标记
|
||||
roles UserRole[]
|
||||
}
|
||||
```
|
||||
|
||||
### 超级管理员设计
|
||||
|
||||
- `User.isSuperAdmin` 字段标记
|
||||
- 超管拥有所有权限,跳过权限检查
|
||||
- 超管标记仅允许数据库直接修改
|
||||
|
||||
---
|
||||
|
||||
## 二、共享类型定义
|
||||
|
||||
**文件**: `packages/shared/src/types/permission.ts`
|
||||
|
||||
### 枚举常量
|
||||
|
||||
```typescript
|
||||
export const MenuType = { DIR: 'dir', MENU: 'menu', BUTTON: 'button' } as const;
|
||||
export const SystemRoleCode = { SUPER_ADMIN: 'super_admin', ADMIN: 'admin', USER: 'user' } as const;
|
||||
export const PermissionAction = { CREATE: 'create', READ: 'read', UPDATE: 'update', DELETE: 'delete' } as const;
|
||||
```
|
||||
|
||||
### 接口类型
|
||||
|
||||
- `RoleResponse` / `RoleDetailResponse` - 角色响应
|
||||
- `PermissionResponse` - 权限响应
|
||||
- `MenuResponse` / `MenuTreeNode` - 菜单响应
|
||||
- `AuthUserWithPermissions` - 带权限的认证用户
|
||||
- `UserMenusAndPermissionsResponse` - 用户菜单和权限响应
|
||||
|
||||
---
|
||||
|
||||
## 三、后端模块设计
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
apps/api/src/permission/
|
||||
├── decorators/
|
||||
│ ├── index.ts
|
||||
│ └── require-permission.decorator.ts
|
||||
├── guards/
|
||||
│ ├── index.ts
|
||||
│ └── permission.guard.ts
|
||||
├── dto/
|
||||
│ ├── index.ts
|
||||
│ ├── permission.dto.ts
|
||||
│ ├── role.dto.ts
|
||||
│ └── menu.dto.ts
|
||||
├── services/
|
||||
│ ├── index.ts
|
||||
│ ├── permission.service.ts
|
||||
│ ├── role.service.ts
|
||||
│ └── menu.service.ts
|
||||
├── controllers/
|
||||
│ ├── index.ts
|
||||
│ ├── permission.controller.ts
|
||||
│ ├── role.controller.ts
|
||||
│ └── menu.controller.ts
|
||||
├── index.ts
|
||||
└── permission.module.ts
|
||||
```
|
||||
|
||||
### 核心实现
|
||||
|
||||
**权限装饰器** `@RequirePermission(...permissions)`
|
||||
|
||||
```typescript
|
||||
export const RequirePermission = (...permissions: string[]) =>
|
||||
SetMetadata(PERMISSION_KEY, permissions);
|
||||
```
|
||||
|
||||
**权限守卫** `PermissionGuard`
|
||||
|
||||
```typescript
|
||||
canActivate(context) {
|
||||
const permissions = reflector.get(PERMISSION_KEY, handler);
|
||||
if (!permissions) return true;
|
||||
|
||||
const user = request.user;
|
||||
if (user.isSuperAdmin) return true; // 超管直接通过
|
||||
|
||||
return permissions.some(p => user.permissions.includes(p));
|
||||
}
|
||||
```
|
||||
|
||||
**JWT 策略更新** - 返回用户权限信息
|
||||
|
||||
```typescript
|
||||
validate(payload) {
|
||||
const user = await prisma.user.findUnique({
|
||||
include: { roles: { include: { role: { include: { permissions } } } } }
|
||||
});
|
||||
return { ...user, roles: [...], roleIds: [...], permissions: [...] };
|
||||
}
|
||||
```
|
||||
|
||||
### API 端点
|
||||
|
||||
| 模块 | 端点 | 权限 |
|
||||
|------|------|------|
|
||||
| 角色 | `GET/POST /roles` | `role:read/create` |
|
||||
| 角色 | `GET/PATCH/DELETE /roles/:id` | `role:read/update/delete` |
|
||||
| 权限 | `GET /permissions` | `permission:read` |
|
||||
| 菜单 | `GET/POST /menus` | `menu:read/create` |
|
||||
| 菜单 | `PATCH/DELETE /menus/:id` | `menu:update/delete` |
|
||||
| 当前用户 | `GET /auth/menus` | 登录即可 |
|
||||
|
||||
---
|
||||
|
||||
## 四、前端权限系统设计
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
apps/web/src/
|
||||
├── stores/permissionStore.ts # 权限状态
|
||||
├── hooks/usePermission.ts # 权限检查 Hook
|
||||
├── components/permission/
|
||||
│ ├── index.ts
|
||||
│ ├── PermissionGuard.tsx # 权限守卫组件
|
||||
│ └── WithPermission.tsx # 权限 HOC
|
||||
├── services/permission.service.ts # 权限服务
|
||||
├── config/route-permissions.ts # 路由权限映射
|
||||
├── lib/icons.ts # 图标映射
|
||||
└── app/403/page.tsx # 无权限页面
|
||||
```
|
||||
|
||||
### 权限 Store
|
||||
|
||||
```typescript
|
||||
interface PermissionState {
|
||||
menus: MenuTreeNode[];
|
||||
permissions: string[];
|
||||
isSuperAdmin: boolean;
|
||||
isLoaded: boolean;
|
||||
}
|
||||
|
||||
interface PermissionActions {
|
||||
setPermissionData: (data) => void;
|
||||
clearPermissionData: () => void;
|
||||
hasPermission: (p: string | string[]) => boolean;
|
||||
hasAllPermissions: (permissions: string[]) => boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### usePermission Hook
|
||||
|
||||
```typescript
|
||||
function usePermission() {
|
||||
const { permissions, isSuperAdmin } = usePermissionStore();
|
||||
|
||||
const hasPermission = (permission: string | string[]) => {
|
||||
if (isSuperAdmin) return true;
|
||||
if (Array.isArray(permission)) return permission.some(p => permissions.includes(p));
|
||||
return permissions.includes(permission);
|
||||
};
|
||||
|
||||
return { hasPermission, hasAnyPermission, hasAllPermissions };
|
||||
}
|
||||
```
|
||||
|
||||
### PermissionGuard 组件
|
||||
|
||||
```tsx
|
||||
<PermissionGuard permission="user:create">
|
||||
<CreateUserButton />
|
||||
</PermissionGuard>
|
||||
|
||||
<PermissionGuard permission={['user:create', 'user:update']} fallback={<span>无权限</span>}>
|
||||
<UserForm />
|
||||
</PermissionGuard>
|
||||
|
||||
<PermissionGuard permission={['user:create', 'user:update']} mode="all">
|
||||
<UserForm />
|
||||
</PermissionGuard>
|
||||
```
|
||||
|
||||
### 路由守卫
|
||||
|
||||
在 Dashboard Layout 中实现:
|
||||
|
||||
1. 登录后获取用户菜单和权限
|
||||
2. 存入 permissionStore
|
||||
3. 路由变化时检查权限
|
||||
4. 无权限跳转 403 页面
|
||||
|
||||
---
|
||||
|
||||
## 五、文件清单
|
||||
|
||||
### 后端文件
|
||||
|
||||
| 优先级 | 文件路径 | 说明 |
|
||||
|--------|----------|------|
|
||||
| P0 | `apps/api/prisma/schema.prisma` | 数据库模型 |
|
||||
| P0 | `apps/api/prisma/seed.ts` | 种子数据 |
|
||||
| P0 | `apps/api/src/permission/guards/permission.guard.ts` | 权限守卫 |
|
||||
| P0 | `apps/api/src/permission/decorators/require-permission.decorator.ts` | 权限装饰器 |
|
||||
| P0 | `apps/api/src/auth/strategies/jwt.strategy.ts` | JWT 策略(已更新) |
|
||||
| P0 | `apps/api/src/auth/auth.controller.ts` | 认证控制器(已更新) |
|
||||
| P0 | `apps/api/src/auth/auth.module.ts` | 认证模块(已更新) |
|
||||
| P1 | `apps/api/src/permission/services/*.ts` | 服务层 |
|
||||
| P1 | `apps/api/src/permission/controllers/*.ts` | 控制器层 |
|
||||
| P1 | `apps/api/src/permission/dto/*.ts` | DTO 定义 |
|
||||
| P1 | `apps/api/src/permission/permission.module.ts` | 权限模块 |
|
||||
| P1 | `apps/api/src/app.module.ts` | 应用模块(已更新) |
|
||||
| P1 | `apps/api/src/prisma/prisma.service.ts` | Prisma 服务(已更新) |
|
||||
|
||||
### 共享包文件
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|----------|------|
|
||||
| `packages/shared/src/types/permission.ts` | 权限相关类型定义 |
|
||||
| `packages/shared/src/types/index.ts` | 类型导出(已更新) |
|
||||
|
||||
### 前端文件
|
||||
|
||||
| 优先级 | 文件路径 | 说明 |
|
||||
|--------|----------|------|
|
||||
| P0 | `apps/web/src/stores/permissionStore.ts` | 权限状态管理 |
|
||||
| P0 | `apps/web/src/app/(dashboard)/layout.tsx` | Dashboard 布局(已更新) |
|
||||
| P1 | `apps/web/src/hooks/usePermission.ts` | 权限检查 Hook |
|
||||
| P1 | `apps/web/src/components/permission/PermissionGuard.tsx` | 权限守卫组件 |
|
||||
| P1 | `apps/web/src/components/permission/WithPermission.tsx` | 权限 HOC |
|
||||
| P1 | `apps/web/src/components/layout/Sidebar.tsx` | 侧边栏(已更新) |
|
||||
| P1 | `apps/web/src/lib/icons.ts` | 图标映射工具 |
|
||||
| P1 | `apps/web/src/config/route-permissions.ts` | 路由权限配置 |
|
||||
| P1 | `apps/web/src/config/constants.ts` | 常量配置(已更新) |
|
||||
| P1 | `apps/web/src/services/permission.service.ts` | 权限服务 |
|
||||
| P1 | `apps/web/src/app/403/page.tsx` | 403 页面 |
|
||||
| P2 | `apps/web/src/components/ui/collapsible.tsx` | 折叠组件 |
|
||||
|
||||
---
|
||||
|
||||
## 六、使用指南
|
||||
|
||||
### 后端使用
|
||||
|
||||
#### 1. 保护接口
|
||||
|
||||
```typescript
|
||||
@Controller('users')
|
||||
@UseGuards(JwtAuthGuard, PermissionGuard)
|
||||
@ApiBearerAuth()
|
||||
export class UserController {
|
||||
@Get()
|
||||
@RequirePermission('user:read')
|
||||
findAll() { ... }
|
||||
|
||||
@Post()
|
||||
@RequirePermission('user:create')
|
||||
create() { ... }
|
||||
|
||||
@Delete(':id')
|
||||
@RequirePermission('user:delete')
|
||||
delete() { ... }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 获取当前用户权限
|
||||
|
||||
```typescript
|
||||
@Get('profile')
|
||||
getProfile(@CurrentUser() user: AuthUserWithPermissions) {
|
||||
// user.isSuperAdmin - 是否超管
|
||||
// user.roles - 角色编码列表
|
||||
// user.permissions - 权限编码列表
|
||||
}
|
||||
```
|
||||
|
||||
### 前端使用
|
||||
|
||||
#### 1. 权限守卫组件
|
||||
|
||||
```tsx
|
||||
import { PermissionGuard } from '@/components/permission';
|
||||
|
||||
// 单个权限
|
||||
<PermissionGuard permission="user:create">
|
||||
<Button>创建用户</Button>
|
||||
</PermissionGuard>
|
||||
|
||||
// 多个权限(OR 关系)
|
||||
<PermissionGuard permission={['user:create', 'user:update']}>
|
||||
<UserForm />
|
||||
</PermissionGuard>
|
||||
|
||||
// 多个权限(AND 关系)
|
||||
<PermissionGuard permission={['user:create', 'user:update']} mode="all">
|
||||
<UserForm />
|
||||
</PermissionGuard>
|
||||
|
||||
// 带 fallback
|
||||
<PermissionGuard permission="user:delete" fallback={<span>无权限</span>}>
|
||||
<DeleteButton />
|
||||
</PermissionGuard>
|
||||
```
|
||||
|
||||
#### 2. usePermission Hook
|
||||
|
||||
```tsx
|
||||
import { usePermission } from '@/hooks';
|
||||
|
||||
function MyComponent() {
|
||||
const { hasPermission, isSuperAdmin } = usePermission();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{hasPermission('user:create') && <CreateButton />}
|
||||
{hasPermission(['user:update', 'user:delete']) && <ActionMenu />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 路由权限配置
|
||||
|
||||
```typescript
|
||||
// config/route-permissions.ts
|
||||
export const routePermissions: Record<string, string | string[]> = {
|
||||
'/users': 'user:read',
|
||||
'/users/create': 'user:create',
|
||||
'/roles': 'role:read',
|
||||
};
|
||||
|
||||
export const publicRoutes = [
|
||||
'/dashboard',
|
||||
'/profile',
|
||||
'/settings',
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、部署步骤
|
||||
|
||||
### 1. 数据库迁移
|
||||
|
||||
```bash
|
||||
# 启动数据库
|
||||
docker compose -f deploy/docker-compose.yml up -d
|
||||
|
||||
# 执行数据库迁移
|
||||
cd apps/api && pnpm db:migrate
|
||||
|
||||
# 初始化种子数据
|
||||
cd apps/api && pnpm db:seed
|
||||
```
|
||||
|
||||
### 2. 验证
|
||||
|
||||
1. 使用超级管理员账号登录
|
||||
- 邮箱: `admin@seclusion.dev`
|
||||
- 密码: `admin123`
|
||||
|
||||
2. 访问 Swagger 文档查看新增的权限管理 API
|
||||
- `http://localhost:4000/api/docs`
|
||||
|
||||
3. 验证菜单根据权限动态显示
|
||||
|
||||
4. 验证无权限路由跳转到 403 页面
|
||||
|
||||
---
|
||||
|
||||
## 八、预置数据
|
||||
|
||||
### 角色
|
||||
|
||||
| 编码 | 名称 | 说明 |
|
||||
|------|------|------|
|
||||
| `super_admin` | 超级管理员 | 系统内置,拥有所有权限 |
|
||||
| `admin` | 管理员 | 系统内置,拥有大部分权限 |
|
||||
| `user` | 普通用户 | 系统内置,基础权限 |
|
||||
|
||||
### 权限
|
||||
|
||||
| 编码 | 名称 | 资源 | 操作 |
|
||||
|------|------|------|------|
|
||||
| `user:create` | 创建用户 | user | create |
|
||||
| `user:read` | 查看用户 | user | read |
|
||||
| `user:update` | 更新用户 | user | update |
|
||||
| `user:delete` | 删除用户 | user | delete |
|
||||
| `role:create` | 创建角色 | role | create |
|
||||
| `role:read` | 查看角色 | role | read |
|
||||
| `role:update` | 更新角色 | role | update |
|
||||
| `role:delete` | 删除角色 | role | delete |
|
||||
| `permission:read` | 查看权限 | permission | read |
|
||||
| `menu:create` | 创建菜单 | menu | create |
|
||||
| `menu:read` | 查看菜单 | menu | read |
|
||||
| `menu:update` | 更新菜单 | menu | update |
|
||||
| `menu:delete` | 删除菜单 | menu | delete |
|
||||
|
||||
### 菜单
|
||||
|
||||
| 编码 | 名称 | 类型 | 路径 | 图标 | 权限 |
|
||||
|------|------|------|------|------|------|
|
||||
| `dashboard` | 仪表盘 | menu | /dashboard | LayoutDashboard | - |
|
||||
| `user-management` | 用户管理 | menu | /users | Users | user:read |
|
||||
| `system` | 系统管理 | dir | - | Settings | - |
|
||||
| `role-management` | 角色管理 | menu | /roles | Shield | role:read |
|
||||
| `menu-management` | 菜单管理 | menu | /menus | Menu | menu:read |
|
||||
| `profile` | 个人中心 | menu | /profile | User | - |
|
||||
| `settings` | 系统设置 | menu | /settings | Settings | - |
|
||||
|
||||
---
|
||||
|
||||
## 九、阶段四:管理界面(已完成)
|
||||
|
||||
### 角色管理
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|----------|------|
|
||||
| `apps/web/src/app/(dashboard)/roles/page.tsx` | 角色管理页面 |
|
||||
| `apps/web/src/components/roles/RolesTable.tsx` | 角色列表表格组件 |
|
||||
| `apps/web/src/components/roles/RoleEditDialog.tsx` | 角色编辑弹窗组件 |
|
||||
| `apps/web/src/hooks/useRoles.ts` | 角色相关 Hooks |
|
||||
| `apps/web/src/services/role.service.ts` | 角色服务层 |
|
||||
|
||||
### 菜单管理
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|----------|------|
|
||||
| `apps/web/src/app/(dashboard)/menus/page.tsx` | 菜单管理页面 |
|
||||
| `apps/web/src/components/menus/MenusTable.tsx` | 菜单列表表格组件 |
|
||||
| `apps/web/src/components/menus/MenuEditDialog.tsx` | 菜单编辑弹窗组件 |
|
||||
| `apps/web/src/hooks/useMenus.ts` | 菜单相关 Hooks |
|
||||
| `apps/web/src/services/menu.service.ts` | 菜单服务层 |
|
||||
|
||||
### 功能说明
|
||||
|
||||
1. **角色管理**:
|
||||
- 角色列表展示(支持分页、搜索、排序)
|
||||
- 新建/编辑角色
|
||||
- 为角色分配权限
|
||||
- 删除角色(系统内置角色不可删除)
|
||||
|
||||
2. **菜单管理**:
|
||||
- 菜单列表展示(支持分页、搜索、排序)
|
||||
- 新建/编辑菜单
|
||||
- 支持目录、菜单、按钮三种类型
|
||||
- 删除菜单(静态菜单不可删除)
|
||||
|
||||
---
|
||||
|
||||
## 十、后续扩展建议
|
||||
|
||||
1. **数据权限** - 支持数据行级权限控制
|
||||
2. **权限缓存** - Redis 缓存用户权限减少数据库查询
|
||||
3. **权限继承** - 支持角色继承
|
||||
4. **操作日志** - 记录权限变更日志
|
||||
5. **批量导入导出** - 支持权限配置的批量操作
|
||||
Reference in New Issue
Block a user