feat: 开发broker相关代码,开发全局代码
This commit is contained in:
714
apps/api/NESTJS-GENERATE-GUIDE.md
Normal file
714
apps/api/NESTJS-GENERATE-GUIDE.md
Normal file
@@ -0,0 +1,714 @@
|
||||
# NestJS 模块生成指南
|
||||
|
||||
## 快速生成完整模块
|
||||
|
||||
### 方法一:使用 `resource` 命令(推荐)
|
||||
|
||||
一次性生成包含 module、controller、service、entity 的完整 CRUD 资源:
|
||||
|
||||
```bash
|
||||
# 生成 user 模块(会在 src/modules/user 目录下创建所有文件)
|
||||
pnpm nest g resource modules/user
|
||||
|
||||
# 或者使用别名
|
||||
pnpm nest g res modules/user
|
||||
```
|
||||
|
||||
**交互式选项:**
|
||||
|
||||
- `What transport layer do you use?` → 选择 `REST API`
|
||||
- `Would you like to generate CRUD entry points?` → 选择 `Yes`
|
||||
|
||||
**生成的文件结构:**
|
||||
|
||||
```
|
||||
src/modules/user/
|
||||
├── user.controller.ts # 控制器
|
||||
├── user.controller.spec.ts # 控制器测试
|
||||
├── user.service.ts # 服务
|
||||
├── user.service.spec.ts # 服务测试
|
||||
├── user.module.ts # 模块
|
||||
└── entities/
|
||||
└── user.entity.ts # 实体(TypeORM)
|
||||
```
|
||||
|
||||
### 方法二:分别生成各个文件
|
||||
|
||||
```bash
|
||||
# 生成模块
|
||||
pnpm nest g module modules/user
|
||||
|
||||
# 生成控制器
|
||||
pnpm nest g controller modules/user
|
||||
|
||||
# 生成服务
|
||||
pnpm nest g service modules/user
|
||||
|
||||
# 生成实体(需要手动创建,或使用 TypeORM CLI)
|
||||
```
|
||||
|
||||
### 方法三:使用 TypeORM 生成实体
|
||||
|
||||
```bash
|
||||
# 安装 TypeORM CLI(如果还没有)
|
||||
pnpm add -D typeorm
|
||||
|
||||
# 生成实体(需要先配置 TypeORM)
|
||||
pnpm typeorm entity:create -n User
|
||||
```
|
||||
|
||||
## 文件组织模式
|
||||
|
||||
### 模式一:按功能模块组织(推荐)⭐
|
||||
|
||||
```
|
||||
src/
|
||||
├── modules/
|
||||
│ ├── user/
|
||||
│ │ ├── user.module.ts
|
||||
│ │ ├── user.controller.ts
|
||||
│ │ ├── user.service.ts
|
||||
│ │ ├── user.entity.ts
|
||||
│ │ ├── user.dto.ts # DTO 文件
|
||||
│ │ ├── user.controller.spec.ts
|
||||
│ │ └── user.service.spec.ts
|
||||
│ ├── order/
|
||||
│ │ ├── order.module.ts
|
||||
│ │ ├── order.controller.ts
|
||||
│ │ ├── order.service.ts
|
||||
│ │ ├── order.entity.ts
|
||||
│ │ └── ...
|
||||
│ └── product/
|
||||
│ └── ...
|
||||
├── database/
|
||||
│ ├── database.module.ts
|
||||
│ └── database.config.ts
|
||||
└── app.module.ts
|
||||
```
|
||||
|
||||
**优点:**
|
||||
|
||||
- 模块化清晰,每个功能独立
|
||||
- 易于维护和扩展
|
||||
- 符合 NestJS 最佳实践
|
||||
|
||||
**适用场景:**
|
||||
|
||||
- 大型企业级应用
|
||||
- 需要复杂业务逻辑
|
||||
- 需要清晰的架构分层
|
||||
|
||||
## 推荐的文件组织(当前项目)
|
||||
|
||||
基于你的项目结构,推荐使用**模式一**:
|
||||
|
||||
```
|
||||
src/
|
||||
├── modules/ # 业务模块
|
||||
│ ├── user/
|
||||
│ │ ├── user.module.ts
|
||||
│ │ ├── user.controller.ts
|
||||
│ │ ├── user.service.ts
|
||||
│ │ ├── user.entity.ts
|
||||
│ │ ├── dto/ # DTO 文件(可选)
|
||||
│ │ │ ├── create-user.dto.ts
|
||||
│ │ │ └── update-user.dto.ts
|
||||
│ │ └── interfaces/ # 接口定义(可选)
|
||||
│ │ └── user.interface.ts
|
||||
│ ├── order/
|
||||
│ └── product/
|
||||
├── database/ # 数据库配置
|
||||
│ ├── database.module.ts
|
||||
│ └── database.config.ts
|
||||
├── common/ # 公共模块(可选)
|
||||
│ ├── filters/
|
||||
│ ├── guards/
|
||||
│ ├── interceptors/
|
||||
│ └── pipes/
|
||||
└── app.module.ts
|
||||
```
|
||||
|
||||
## 实际生成示例
|
||||
|
||||
### 生成 user 模块
|
||||
|
||||
```bash
|
||||
# 1. 生成完整资源
|
||||
cd /Users/joey-xd/sites/vest-mind/vest-mind-backend/apps/api
|
||||
pnpm nest g resource modules/user
|
||||
|
||||
# 2. 选择选项:
|
||||
# - REST API
|
||||
# - Yes (生成 CRUD)
|
||||
```
|
||||
|
||||
### 生成后的文件内容示例
|
||||
|
||||
**user.entity.ts:**
|
||||
|
||||
```typescript
|
||||
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
```
|
||||
|
||||
**user.service.ts:**
|
||||
|
||||
```typescript
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
create(createUserDto: CreateUserDto) {
|
||||
return 'This action adds a new user';
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all user`;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} user`;
|
||||
}
|
||||
|
||||
update(id: number, updateUserDto: UpdateUserDto) {
|
||||
return `This action updates a #${id} user`;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} user`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**user.controller.ts:**
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
|
||||
@Controller('user')
|
||||
export class UserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createUserDto: CreateUserDto) {
|
||||
return this.userService.create(createUserDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.userService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.userService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
|
||||
return this.userService.update(+id, updateUserDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.userService.remove(+id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 常用生成命令速查
|
||||
|
||||
```bash
|
||||
# 生成资源(完整 CRUD)
|
||||
pnpm nest g resource modules/user
|
||||
|
||||
# 生成模块
|
||||
pnpm nest g module modules/user
|
||||
|
||||
# 生成控制器
|
||||
pnpm nest g controller modules/user
|
||||
|
||||
# 生成服务
|
||||
pnpm nest g service modules/user
|
||||
|
||||
# 生成守卫
|
||||
pnpm nest g guard modules/user/guards/auth
|
||||
|
||||
# 生成拦截器
|
||||
pnpm nest g interceptor modules/user/interceptors/logging
|
||||
|
||||
# 生成过滤器
|
||||
pnpm nest g filter modules/user/filters/http-exception
|
||||
|
||||
# 生成管道
|
||||
pnpm nest g pipe modules/user/pipes/validation
|
||||
|
||||
# 生成装饰器
|
||||
pnpm nest g decorator modules/user/decorators/roles
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **路径规范:**
|
||||
- 使用 `modules/模块名` 作为路径
|
||||
- 会自动创建目录结构
|
||||
|
||||
2. **自动导入:**
|
||||
- 生成的文件会自动导入到相应的模块中
|
||||
- 如果不想自动导入,使用 `--skip-import` 选项
|
||||
|
||||
3. **测试文件:**
|
||||
- 默认会生成 `.spec.ts` 测试文件
|
||||
- 使用 `--no-spec` 可以跳过测试文件生成
|
||||
|
||||
4. **扁平结构:**
|
||||
- 使用 `--flat` 可以生成扁平结构(所有文件在同一目录)
|
||||
- 不推荐使用,会破坏模块化结构
|
||||
|
||||
## DTO(Data Transfer Object)详解
|
||||
|
||||
### 什么是 DTO?
|
||||
|
||||
DTO(Data Transfer Object)是数据传输对象,用于在不同层之间传输数据。在 NestJS 中,DTO 主要用于:
|
||||
|
||||
1. **定义 API 请求和响应的数据结构**
|
||||
2. **数据验证**(结合 `class-validator`)
|
||||
3. **类型安全**
|
||||
4. **API 文档生成**(结合 Swagger)
|
||||
|
||||
### 为什么需要 DTO?
|
||||
|
||||
#### 1. 数据验证
|
||||
|
||||
```typescript
|
||||
// 没有 DTO - 不安全
|
||||
@Post()
|
||||
create(@Body() body: any) {
|
||||
// body 可能是任何数据,没有验证
|
||||
return this.userService.create(body);
|
||||
}
|
||||
|
||||
// 使用 DTO - 安全
|
||||
@Post()
|
||||
create(@Body() createUserDto: CreateUserDto) {
|
||||
// 数据已经验证,类型安全
|
||||
return this.userService.create(createUserDto);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 类型安全
|
||||
|
||||
```typescript
|
||||
// DTO 定义了明确的数据结构
|
||||
export class CreateUserDto {
|
||||
username: string;
|
||||
email: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
// TypeScript 会检查类型
|
||||
const user = new CreateUserDto();
|
||||
user.username = 'john'; // ✅ 正确
|
||||
user.age = '25'; // ❌ TypeScript 错误
|
||||
```
|
||||
|
||||
#### 3. API 文档
|
||||
|
||||
使用 Swagger 时,DTO 会自动生成 API 文档:
|
||||
|
||||
```typescript
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ description: '用户名', example: 'john' })
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ description: '邮箱', example: 'john@example.com' })
|
||||
email: string;
|
||||
}
|
||||
```
|
||||
|
||||
### DTO 的类型
|
||||
|
||||
#### 1. Create DTO(创建数据)
|
||||
|
||||
用于创建新资源时的数据验证:
|
||||
|
||||
```typescript
|
||||
// dto/create-user.dto.ts
|
||||
import {
|
||||
IsString,
|
||||
IsEmail,
|
||||
IsOptional,
|
||||
MinLength,
|
||||
MaxLength,
|
||||
} from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ description: '用户名', example: 'john' })
|
||||
@IsString()
|
||||
@MinLength(3)
|
||||
@MaxLength(20)
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ description: '邮箱', example: 'john@example.com' })
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ description: '密码', example: 'password123' })
|
||||
@IsString()
|
||||
@MinLength(6)
|
||||
password: string;
|
||||
|
||||
@ApiProperty({ description: '年龄', example: 25, required: false })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
age?: number;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Update DTO(更新数据)
|
||||
|
||||
用于更新资源时的数据验证:
|
||||
|
||||
```typescript
|
||||
// dto/update-user.dto.ts
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
|
||||
// 方式一:使用 PartialType(推荐)
|
||||
export class UpdateUserDto extends PartialType(CreateUserDto) {}
|
||||
|
||||
// 方式二:手动定义(更灵活)
|
||||
// export class UpdateUserDto {
|
||||
// @IsOptional()
|
||||
// @IsString()
|
||||
// username?: string;
|
||||
//
|
||||
// @IsOptional()
|
||||
// @IsEmail()
|
||||
// email?: string;
|
||||
// }
|
||||
```
|
||||
|
||||
#### 3. Query DTO(查询参数)
|
||||
|
||||
用于查询列表时的参数验证:
|
||||
|
||||
```typescript
|
||||
// dto/query-user.dto.ts
|
||||
import { IsOptional, IsNumber, Min, Max } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class QueryUserDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
page?: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
limit?: number = 10;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
search?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sortBy?: string = 'createdAt';
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sortOrder?: 'ASC' | 'DESC' = 'DESC';
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Response DTO(响应数据)
|
||||
|
||||
用于定义 API 响应的数据结构:
|
||||
|
||||
```typescript
|
||||
// dto/user-response.dto.ts
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UserResponseDto {
|
||||
@ApiProperty({ description: '用户ID', example: 1 })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: '用户名', example: 'john' })
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ description: '邮箱', example: 'john@example.com' })
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ description: '创建时间', example: '2024-01-01T00:00:00Z' })
|
||||
createdAt: Date;
|
||||
|
||||
@ApiProperty({ description: '更新时间', example: '2024-01-01T00:00:00Z' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
```
|
||||
|
||||
### 在 Controller 中使用 DTO
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { QueryUserDto } from './dto/query-user.dto';
|
||||
|
||||
@Controller('users')
|
||||
export class UserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createUserDto: CreateUserDto) {
|
||||
return this.userService.create(createUserDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll(@Query() queryUserDto: QueryUserDto) {
|
||||
return this.userService.findAll(queryUserDto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.userService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
|
||||
return this.userService.update(+id, updateUserDto);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 启用全局验证管道
|
||||
|
||||
在 `main.ts` 中启用全局验证:
|
||||
|
||||
```typescript
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
// 启用全局验证管道
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true, // 自动删除不在 DTO 中的属性
|
||||
forbidNonWhitelisted: true, // 如果请求包含未定义的属性,抛出错误
|
||||
transform: true, // 自动转换类型(如字符串转数字)
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.listen(3000);
|
||||
}
|
||||
```
|
||||
|
||||
### 常用验证装饰器
|
||||
|
||||
```typescript
|
||||
import {
|
||||
IsString, // 字符串
|
||||
IsNumber, // 数字
|
||||
IsBoolean, // 布尔值
|
||||
IsEmail, // 邮箱
|
||||
IsUrl, // URL
|
||||
IsDate, // 日期
|
||||
IsOptional, // 可选字段
|
||||
IsNotEmpty, // 非空
|
||||
MinLength, // 最小长度
|
||||
MaxLength, // 最大长度
|
||||
Min, // 最小值
|
||||
Max, // 最大值
|
||||
IsEnum, // 枚举
|
||||
IsArray, // 数组
|
||||
IsObject, // 对象
|
||||
ValidateNested, // 嵌套对象验证
|
||||
IsUUID, // UUID
|
||||
Matches, // 正则匹配
|
||||
} from 'class-validator';
|
||||
```
|
||||
|
||||
### DTO 示例:完整的用户模块
|
||||
|
||||
```typescript
|
||||
// dto/create-user.dto.ts
|
||||
import {
|
||||
IsString,
|
||||
IsEmail,
|
||||
IsOptional,
|
||||
MinLength,
|
||||
IsNumber,
|
||||
Min,
|
||||
Max,
|
||||
} from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ description: '用户名', example: 'john' })
|
||||
@IsString()
|
||||
@MinLength(3)
|
||||
@MaxLength(20)
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ description: '邮箱', example: 'john@example.com' })
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ description: '密码', example: 'password123' })
|
||||
@IsString()
|
||||
@MinLength(6)
|
||||
password: string;
|
||||
|
||||
@ApiProperty({ description: '年龄', example: 25, required: false })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(18)
|
||||
@Max(100)
|
||||
age?: number;
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// dto/update-user.dto.ts
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
|
||||
export class UpdateUserDto extends PartialType(CreateUserDto) {}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// dto/query-user.dto.ts
|
||||
import { IsOptional, IsNumber, IsString, Min, Max } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class QueryUserDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
page?: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
limit?: number = 10;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
search?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 安装必要的依赖
|
||||
|
||||
```bash
|
||||
# 安装验证库
|
||||
pnpm add class-validator class-transformer
|
||||
|
||||
# 如果使用 Swagger(可选)
|
||||
pnpm add @nestjs/swagger swagger-ui-express
|
||||
```
|
||||
|
||||
### DTO vs Entity
|
||||
|
||||
| 特性 | DTO | Entity |
|
||||
| -------- | ---------------------- | ------------------- |
|
||||
| **用途** | 数据传输和验证 | 数据库模型 |
|
||||
| **位置** | `dto/` 目录 | `entities/` 目录 |
|
||||
| **验证** | 使用 `class-validator` | 使用 TypeORM 装饰器 |
|
||||
| **暴露** | 暴露给 API 客户端 | 不直接暴露 |
|
||||
| **示例** | `CreateUserDto` | `User` entity |
|
||||
|
||||
**最佳实践:**
|
||||
|
||||
- ✅ Entity 包含数据库字段(如 `id`, `createdAt`)
|
||||
- ✅ DTO 只包含客户端需要传递的字段
|
||||
- ✅ 使用 DTO 转换 Entity,避免暴露敏感信息
|
||||
|
||||
### DTO 转换示例
|
||||
|
||||
```typescript
|
||||
// service 中使用
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
async create(createUserDto: CreateUserDto): Promise<User> {
|
||||
// DTO → Entity
|
||||
const user = this.userRepository.create({
|
||||
username: createUserDto.username,
|
||||
email: createUserDto.email,
|
||||
// 不直接传递 password,需要加密
|
||||
passwordHash: await this.hashPassword(createUserDto.password),
|
||||
});
|
||||
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
async findAll(queryDto: QueryUserDto): Promise<User[]> {
|
||||
// 使用 DTO 中的查询参数
|
||||
return this.userRepository.find({
|
||||
skip: (queryDto.page - 1) * queryDto.limit,
|
||||
take: queryDto.limit,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. ✅ **使用 `resource` 命令生成完整模块**
|
||||
2. ✅ **按功能模块组织文件**
|
||||
3. ✅ **每个模块包含:module、controller、service、entity**
|
||||
4. ✅ **DTO 文件放在模块目录下的 `dto/` 子目录**
|
||||
5. ✅ **为每个操作创建对应的 DTO(Create、Update、Query、Response)**
|
||||
6. ✅ **使用 `class-validator` 进行数据验证**
|
||||
7. ✅ **使用 `PartialType` 创建 Update DTO**
|
||||
8. ✅ **启用全局验证管道**
|
||||
9. ✅ **区分 DTO 和 Entity,不要混用**
|
||||
10. ✅ **公共组件放在 `common/` 目录**
|
||||
11. ❌ **避免使用扁平结构**
|
||||
12. ❌ **避免按类型组织文件**
|
||||
13. ❌ **避免在 Entity 中直接暴露给 API**
|
||||
Reference in New Issue
Block a user