--- description: NestJS 后端架构规范和模块结构 globs: - "backend/**/*.ts" alwaysApply: false --- # 后端架构规范 ## 模块结构 每个功能模块应包含: - `module.ts` - 模块定义 - `controller.ts` - 控制器 - `service.ts` - 服务层 - `dto/` - 数据传输对象目录 ### 命名规范 - 模块命名使用复数形式:`users`, `roles`, `contests` - 子模块放在父模块目录下:`contests/works/`, `contests/teams/` ### 目录结构示例 ``` src/ ├── contests/ │ ├── contests.module.ts │ ├── contests/ │ │ ├── contests.module.ts │ │ ├── contests.controller.ts │ │ ├── contests.service.ts │ │ └── dto/ │ ├── works/ │ │ ├── works.module.ts │ │ ├── works.controller.ts │ │ ├── works.service.ts │ │ └── dto/ │ └── teams/ │ └── ... ``` ## 服务层 (Service) ### 基本规范 - 使用 `@Injectable()` 装饰器 - 构造函数注入依赖,使用 `private readonly` - 所有数据库操作通过 PrismaService - **禁止直接使用 SQL** ### 标准方法命名 - `create` - 创建 - `findAll` - 查询列表(支持分页) - `findOne` - 查询单个 - `update` - 更新 - `remove` - 删除(软删除或级联) ### 示例 ```typescript @Injectable() export class UsersService { constructor(private readonly prisma: PrismaService) {} async create(createDto: CreateUserDto, tenantId: number) { return this.prisma.user.create({ data: { ...createDto, tenantId, }, }); } async findAll(tenantId: number, skip?: number, take?: number) { return this.prisma.user.findMany({ where: { tenantId, validState: 1 }, skip, take, include: { roles: { include: { role: true }, }, }, }); } } ``` ## 控制器层 (Controller) ### 基本规范 - 使用 `@Controller()` 装饰器,路径使用复数形式 - 所有路由默认需要认证(除非使用 `@Public()` 装饰器) - 使用 REST 风格的 HTTP 方法装饰器 ### 装饰器使用 ```typescript @Controller("users") export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() @RequirePermission("user:create") async create( @Body() createDto: CreateUserDto, @CurrentTenantId() tenantId: number, @CurrentUser() user: any ) { return this.usersService.create(createDto, tenantId); } @Get() @RequirePermission("user:read") async findAll( @CurrentTenantId() tenantId: number, @Query("skip") skip?: number, @Query("take") take?: number ) { return this.usersService.findAll(tenantId, skip, take); } @Public() @Get("public-info") async getPublicInfo() { return { version: "1.0.0" }; } } ``` ### 常用装饰器 - `@CurrentTenantId()` - 获取当前租户ID - `@CurrentUser()` - 获取当前用户信息 - `@RequirePermission('module:action')` - 权限检查 - `@Public()` - 公开接口,无需认证 ## DTO 规范 ### 命名规范 - 创建:`CreateXxxDto` - 更新:`UpdateXxxDto` - 查询:`QueryXxxDto` ### 验证规则 使用 `class-validator` 装饰器: ```typescript import { IsString, IsEmail, IsOptional, IsArray, IsNumber, } from "class-validator"; export class CreateUserDto { @IsString() username: string; @IsString() password: string; @IsString() nickname: string; @IsEmail() @IsOptional() email?: string; @IsArray() @IsNumber({}, { each: true }) @IsOptional() roleIds?: number[]; } ``` ## 错误处理 使用 NestJS 内置异常,消息使用中文: ```typescript import { NotFoundException, BadRequestException, UnauthorizedException, ForbiddenException, } from "@nestjs/common"; // 示例 if (!user) { throw new NotFoundException("用户不存在"); } if (!isValid) { throw new BadRequestException("数据验证失败"); } ``` ## 权限控制 权限字符串格式:`模块:操作` ```typescript @RequirePermission('contest:create') // 创建竞赛 @RequirePermission('user:update') // 更新用户 @RequirePermission('role:delete') // 删除角色 ``` ## 代码风格 - 导入顺序:NestJS 核心 → 第三方库 → 本地模块 - 使用 async/await,避免 Promise.then() - 使用解构赋值提高代码可读性 - 复杂逻辑必须添加注释