新增内容: - pnpm generate 代码生成命令 - 前端权限控制(PermissionGuard 组件、usePermissionStore) - 后端权限控制(@RequirePermission 装饰器、PermissionModule) - 代码生成器生成内容说明 - DataTable 通用组件说明 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Seclusion 是一个基于 Next.js + NestJS 的 Monorepo 项目模板,使用 pnpm workspace + Turborepo 管理。
环境要求
- Node.js >= 20.0.0
- pnpm >= 9.0.0
Commands
# 启动数据库 (PostgreSQL + Redis)
docker compose -f deploy/docker-compose.yml up -d
# 开发(同时启动前后端)
pnpm dev
# 构建
pnpm build
# 代码检查
pnpm lint
pnpm format # 格式化
pnpm format:check # 检查格式
# 测试
pnpm test
cd apps/api && pnpm test:watch # 单个测试文件监听
cd apps/api && pnpm test:cov # 测试覆盖率报告
# 数据库
pnpm db:generate # 生成 Prisma Client
pnpm db:push # 推送 schema 到数据库
pnpm db:migrate # 运行迁移
cd apps/api && pnpm db:studio # 打开 Prisma Studio
# 代码生成
pnpm generate # CRUD 模块生成器(交互式,生成后端+前端+类型+种子脚本)
Architecture
Monorepo 结构
- 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 | 分页参数、筛选条件 |
前端权限控制:使用 <PermissionGuard> 组件包裹需要权限的 UI 元素:
<PermissionGuard permission="user:create">
<CreateButton />
</PermissionGuard>
// 多个权限(OR 关系)
<PermissionGuard permission={['user:update', 'user:delete']}>
<ActionMenu />
</PermissionGuard>
// 多个权限(AND 关系)
<PermissionGuard permission={['user:update', 'user:delete']} mode="all">
<AdminPanel />
</PermissionGuard>
权限数据通过 usePermissionStore 管理,登录后自动缓存,退出时清除。
数据请求分层
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 采用模块化架构:
- PrismaModule - 全局数据库服务,内置软删除扩展
- CryptoModule - 全局加密服务,AES-256-GCM 请求/响应加密
- AuthModule - JWT 认证(注册、登录、token 验证)
- UserModule - 用户 CRUD,继承 CrudService 基类
- CaptchaModule - 验证码服务,支持多场景验证
- PermissionModule - 权限管理(角色、权限、菜单)
认证流程:使用 @Public() 装饰器标记公开接口,其他接口需要 JWT Bearer Token。使用 @CurrentUser() 装饰器获取当前登录用户信息(类型为 AuthUser)。
权限控制:使用 @RequirePermission() 装饰器控制接口权限,配合 PermissionGuard 使用:
@UseGuards(JwtAuthGuard, PermissionGuard)
@RequirePermission('user:create')
@Post()
create(@Body() dto: CreateUserDto) { ... }
权限码格式:{resource}:{action}(如 user:create、user:read、user:update、user:delete)
后端导入规范:禁止使用 Barrel Imports
后端代码禁止使用 index.ts 统一导出(barrel exports),必须直接从具体文件导入:
// ❌ 错误 - 使用 barrel import
import { RequirePermission } from '@/permission';
import { PrismaService } from '@/prisma';
// ✅ 正确 - 直接导入具体文件
import { RequirePermission } from '@/permission/decorators/require-permission.decorator';
import { PrismaService } from '@/prisma/prisma.service';
// ✅ 正确 - 模块内部使用相对路径
import { RequirePermission } from '../decorators/require-permission.decorator';
原因:
- 避免循环依赖
- 更清晰的依赖关系
- 更好的 IDE 跳转体验
软删除机制
PrismaService 使用 $extends 实现底层自动软删除,自动检测 schema 中有 deletedAt 字段的模型:
- 查询自动过滤:
findMany、findFirst、findUnique、findFirstOrThrow、findUniqueOrThrow、count、aggregate、groupBy自动添加deletedAt: null条件 - 更新保护:
update、updateMany自动过滤已删除记录,防止误更新 - 删除自动转换:
delete、deleteMany自动转换为设置deletedAt时间戳 - 绕过过滤: 显式指定
deletedAt条件可查询已删除数据,如where: { deletedAt: { not: null } }
启用软删除只需在 schema.prisma 中为模型添加 deletedAt DateTime? 字段,运行 pnpm db:generate 后自动生效。
通信加密
基于 AES-256-GCM 的请求/响应 Body 加密,通过 ENABLE_ENCRYPTION 环境变量控制:
- 后端:
CryptoModule提供CryptoService和EncryptionInterceptor - 前端:
apps/web/src/lib/crypto.ts封装加密客户端 - 共享:
packages/shared/src/crypto/提供跨平台加密实现 - 跳过加密: 使用
@SkipEncryption()装饰器标记不需要加密的接口
CRUD 基类
apps/api/src/common/crud/ 提供通用 CRUD 服务和 DTO:
CrudService<T>- 泛型基类,提供分页查询、软删除、恢复等方法PaginationQueryDto- 分页查询参数 DTOcreatePaginatedResponseDto(ItemDto)- 分页响应 DTO 工厂函数
分页响应 DTO 使用示例:
import { createPaginatedResponseDto } from '@/common/crud';
export class UserResponseDto { ... }
export class PaginatedUserResponseDto extends createPaginatedResponseDto(UserResponseDto) {}
Swagger 文档规范
所有 API 接口必须完整定义 Swagger 文档:
Controller 装饰器要求:
@ApiTags()- 接口分组标签@ApiOperation()- 接口描述@ApiOkResponse()/@ApiCreatedResponse()- 成功响应类型@ApiBearerAuth()- 需要认证的接口(非@Public()接口)
DTO 定义要求:
- 所有请求和响应都必须定义对应的 DTO class
- 使用
@ApiProperty()/@ApiPropertyOptional()装饰每个字段 - 必须包含
example和description属性
示例:
// dto/example.dto.ts
export class ExampleResponseDto implements ExampleResponse {
@ApiProperty({ example: 'clxxx', description: '记录 ID' })
id: string;
}
// example.controller.ts
@ApiTags('示例')
@ApiBearerAuth()
@Controller('example')
export class ExampleController {
@Get(':id')
@ApiOperation({ summary: '获取示例详情' })
@ApiOkResponse({ type: ExampleResponseDto, description: '示例详情' })
findOne(@Param('id') id: string): Promise<ExampleResponseDto> {
return this.service.findOne(id);
}
}
共享包使用
// 类型导入
import type { User, AuthUser, UserResponse, TokenPayload } from '@seclusion/shared';
import type { ApiResponse, PaginatedResponse } from '@seclusion/shared';
// 工具函数
import { formatDate, generateId } from '@seclusion/shared';
// 加密模块
import { createBrowserCrypto, createNodeCrypto } from '@seclusion/shared/crypto';
// 常量(使用 const + type 模式)
import { CaptchaScene } from '@seclusion/shared';
注意: packages/shared 中的工具函数应优先使用 lodash-es 实现。
前后端共享类型设计规范
为避免类型重复定义和前后端不一致,遵循以下设计原则:
类型定义位置
| 类型 | 定义位置 | 说明 |
|---|---|---|
| 枚举/常量 | packages/shared |
使用 const + type 模式 |
| 接口类型 | packages/shared |
纯类型定义,无装饰器 |
| 后端 DTO | apps/api |
实现共享接口,添加 Swagger/验证装饰器 |
| 前端特有类型 | apps/web/src/types/ |
组件 Props、表单状态等 |
枚举定义方式(使用 const + type 替代 enum)
// packages/shared/src/types/index.ts
export const CaptchaScene = {
LOGIN: 'login',
REGISTER: 'register',
} as const;
export type CaptchaScene = (typeof CaptchaScene)[keyof typeof CaptchaScene];
后端 DTO 实现共享接口
// apps/api/src/xxx/dto/example.dto.ts
import type { ExampleResponse } from '@seclusion/shared';
export class ExampleResponseDto implements ExampleResponse {
@ApiProperty({ description: '示例字段', example: 'value' })
field: string;
}
新增共享类型检查清单
- 类型定义在
packages/shared/src/types/中 - 枚举使用
const + type模式 - 后端 DTO 使用
implements实现共享接口 - 前端直接从
@seclusion/shared导入使用
Environment Variables
环境变量在各应用目录下独立配置:
| 位置 | 文件 | 用途 | 是否提交 |
|---|---|---|---|
| apps/api | .env.example |
后端配置模板 | ✅ |
| apps/api | .env |
后端默认配置 | ✅ |
| apps/api | .env.local |
后端本地覆盖(敏感信息) | ❌ |
| apps/web | .env |
前端默认配置 | ✅ |
| apps/web | .env.local |
前端本地覆盖 | ❌ |
加密相关配置
# apps/api/.env
ENABLE_ENCRYPTION=false
ENCRYPTION_KEY=<32字节Base64密钥>
# apps/web/.env
NEXT_PUBLIC_ENABLE_ENCRYPTION=false
NEXT_PUBLIC_ENCRYPTION_KEY=<与后端相同的密钥>
生成密钥: openssl rand -base64 32
Documentation
项目文档统一存放在 docs/ 目录下:
docs/
├── web/ # 前端相关文档
│ └── DESIGN.md # Web 前端设计文档
├── api/ # 后端相关文档
└── shared/ # 共享模块文档
| 文档 | 说明 |
|---|---|
docs/web/DESIGN.md |
Web 前端整体设计(技术栈、目录结构、状态管理、实施计划) |
Key Files
deploy/docker-compose.yml- PostgreSQL + Redis 容器配置apps/api/prisma/schema.prisma- 数据库模型定义(PostgreSQL,ID 使用 cuid2)apps/api/src/prisma/prisma.service.ts- Prisma 服务,含软删除扩展apps/api/src/common/crud/- CRUD 基类和分页 DTOapps/api/src/common/crypto/- 后端加密模块packages/shared/src/types/- 共享类型定义packages/shared/src/crypto/- 跨平台加密实现apps/web/src/types/- 前端特有类型定义
代码生成器
pnpm generate 启动交互式代码生成器,可一键生成完整 CRUD 模块:
- 后端: Controller、Service、DTO、Module
- 前端: Service、Hooks、Table、CreateDialog、EditDialog、Page
- 共享类型: TypeScript 接口定义
- Prisma Model: 数据库模型
- 种子脚本: 菜单和权限初始化数据
生成的代码自动集成权限控制(@RequirePermission + PermissionGuard)。详见 plop/README.md。
通用组件
DataTable (components/shared/DataTable/): 封装 TanStack Table 的通用数据表格组件
- 支持服务端分页、排序、搜索
- 内置加载状态、错误状态、空状态渲染
- 使用示例见
components/*/XxxTable.tsx