计划覆盖对照
原计划(NestJS 学习计划,Java/Spring 背景)的每一项,都在这里对应到网站上讲解它的小节。读完这里列出的所有小节, 就覆盖了整个计划。
计划的通用章节
| 计划章节 | 内容 | 对应小节 |
|---|---|---|
| 开头 | 2026 年 11 月 9 日开始,共 11 天,前置要求,技术栈,贯穿始终的 TaskFlow 项目 | 计划与方法 |
| 1. 工作方式 | 上午理论(3 小时),下午实践(3 小时),30 分钟写 LEARNINGS.md + 打 jour-XX 标签,11 月 11 日放假,每天一次提交 | 方法、学习日志 |
| 2. 日程 | 11 天的主题和交付物 | 日程 |
| 3. Spring ↔ NestJS 对照表 | 34 条对应关系 | Web、依赖注入、数据、请求管道、配置、安全、异步、测试、工具链 |
| 4. 事件循环与 thread-per-request | 一个 JS 线程,处处 await,不做繁重计算,没有 await 的 Promise,Promise.all,WebFlux | 事件循环、带来的影响、CPU 计算、WebFlux |
| 4. 注入容器 | 类型被擦除,emitDecoratorMetadata,接口要用 token,声明与导出,forwardRef | 类型擦除、token、没有扫描、循环依赖 |
| 4. 生态 | npm 与 lockfile,没有 JVM,装饰器就是函数 | 生态 |
| 6. 领域模型 | USER、PROJECT、PROJECT_MEMBER、TASK;状态 | 模型、状态 |
| 7. Docker Compose | db、db-test(tmpfs)、adminer、可选的 redis;.env.example | docker-compose.yml、.env 文件 |
| 8. 仓库结构 | 目标目录结构,命名约定 | 目录结构、约定 |
| 9. 进度跟踪 | J1 到 J11 的勾选框 | 进度跟踪 |
| 10. 学习资源 | 官方文档、TypeORM、Fundamentals 课程、Node 事件循环 | 学习资源 |
| 11. 常见陷阱 | 8 个陷阱 | 漏写 await、synchronize、直接返回实体、循环依赖、单例里的状态、校验、相等性与 this、@Res() |
J1 · Node 与 Nest 基础
| 计划条目 | 对应小节 |
|---|---|
| Node、事件循环(各个阶段、微任务与宏任务) | 事件循环 |
Promise、async/await | Promise 与 async/await |
| ES 模块 / CommonJS | ES 模块与 CommonJS |
Nest 项目结构:main.ts、AppModule、AppController、AppService | Nest 项目结构 |
Nest CLI:nest new、nest g module/controller/service/resource | Nest CLI、Nest CLI 速查 |
npm i -g @nestjs/cli,然后 nest new taskflow | 生成项目 |
docker-compose.yml(PostgreSQL + Adminer) | Docker Compose |
返回 { status: 'ok' } 的 GET /health | GET /health |
git 仓库、.gitignore、README.md、LEARNINGS.md | git 仓库 |
练习:/block 对比 /ping,再改用 await sleep(5000) | 事件循环练习 |
SpringApplication.run ↔ NestFactory.create | Nest 项目结构 |
J2 · 控制器、DTO、校验
| 计划条目 | 对应小节 |
|---|---|
| 路由装饰器 | 路由 |
状态码(@HttpCode) | 状态码 |
| API 版本 | API 版本 |
用类写 DTO,class-validator,class-transformer | DTO 与校验 |
全局 ValidationPipe(whitelist、transform、forbidNonWhitelisted) | 全局 ValidationPipe |
@nestjs/mapped-types:PartialType、PickType、OmitType | Mapped types |
nest g resource projects | 生成资源 |
带校验的 CreateProjectDto、UpdateProjectDto | DTO |
| 内存版增删改查 | 内存版增删改查 |
通过 @Query() 和一个 QueryDto 实现分页和过滤 | 分页 |
@Valid + Bean Validation ↔ ValidationPipe + class-validator | Spring → NestJS |
ResponseEntity ↔ 对象 + @HttpCode(或 @Res(),应避免) | 状态码、陷阱 |
J3 · 依赖注入与模块
| 计划条目 | 对应小节 |
|---|---|
Provider:useClass、useValue、useFactory、useExisting | Provider |
自定义 token、@Inject、@Optional | Token |
| Scope:default、request、transient(代价与限制) | Scope |
模块:imports、exports、@Global | 模块 |
动态模块(forRoot、forRootAsync) | 动态模块 |
把逻辑独立到 ProjectsService 里 | ProjectsService |
ProjectsStore 抽象(接口 + token),内存实现 | ProjectsStore |
放共用工具的 CommonModule | CommonModule |
@Configuration + @Bean ↔ useFactory | Provider |
| 按条件生效的 profile ↔ 根据配置选择 provider | ProjectsStore |
J4 · PostgreSQL 与 TypeORM
| 计划条目 | 对应小节 |
|---|---|
@nestjs/typeorm、forRoot / forRootAsync / forFeature | @nestjs/typeorm |
| 实体、列 | 实体 |
PostgreSQL 类型:uuid、timestamptz、jsonb、枚举 | PostgreSQL 类型 |
Repository:find、findOne、save、softDelete | Repository |
QueryBuilder | QueryBuilder |
synchronize: true 只在开发环境用 | synchronize |
Project 实体 | Project 实体 |
| 用 TypeORM 替换内存实现,不改控制器 | ProjectsStore 背后的 TypeORM |
| 开发用种子数据 | 种子数据 |
JpaRepository ↔ Repository<T>;没有派生查询(ILike、QueryBuilder);没有 Page<T>(findAndCount + skip/take) | Spring → NestJS、搜索与分页 |
J5 · 关系、迁移、事务
| 计划条目 | 对应小节 |
|---|---|
OneToMany、ManyToOne、ManyToMany | 关系 |
加载方式:eager、lazy、relations: [] | 加载 |
N+1 问题、leftJoinAndSelect | N+1 |
迁移:generate、run、revert;关闭 synchronize | 迁移 |
事务:dataSource.transaction()、QueryRunner | 事务、QueryRunner |
关联到 Project 的 Task 实体 | Task 实体 |
POST /projects/:id/tasks、PATCH /tasks/:id/status | 接口 |
| 归档项目及其任务(事务) | 归档 |
| 生成第一个迁移并纳入版本控制 | 第一个迁移 |
@Transactional ↔ 没有声明式的对应物,要把 manager 传下去 | 事务 |
J6 · 请求管道
| 计划条目 | 对应小节 |
|---|---|
| 执行顺序 Middleware → … → Exception Filters | 执行顺序 |
| 内置 HTTP 异常 | HTTP 异常 |
ExceptionFilter、@Catch()、APP_FILTER | 全局过滤器 |
管道:ParseUUIDPipe、ParseEnumPipe、自定义管道 | 管道 |
| 拦截器:日志、计时、响应映射、缓存 | 拦截器 |
函数式或类形式的中间件、configure(consumer) | 中间件 |
problem+json 格式的全局 AllExceptionsFilter | AllExceptionsFilter |
| TypeORM 错误:唯一约束冲突 → 409,找不到 → 404 | TypeORM 错误 |
LoggingInterceptor(耗时、方法、路由) | LoggingInterceptor |
CorrelationIdMiddleware | CorrelationIdMiddleware |
@ControllerAdvice ↔ APP_FILTER;Filter ↔ 中间件;@Around ↔ 拦截器 | Spring → NestJS |
J7 · 配置与日志
| 计划条目 | 对应小节 |
|---|---|
ConfigModule.forRoot({ isGlobal, validate }) | ConfigModule |
registerAs、类型化配置 | 类型化配置 |
校验环境变量 schema(Joi 或 class-validator) | 校验 |
日志:Nest 的 Logger 或 nestjs-pino | 日志 |
OnModuleInit、OnApplicationShutdown、enableShutdownHooks() | 生命周期 |
.env.development、.env.test、.env.example(只有它纳入版本控制) | .env 文件 |
| 数据库、JWT、端口的类型化配置 | 类型化配置 |
通过 nestjs-pino 输出带 correlationId 的 JSON 日志 | JSON 日志 |
@ConfigurationProperties ↔ registerAs;@PostConstruct ↔ OnModuleInit;@PreDestroy ↔ OnModuleDestroy;profile ↔ NODE_ENV | Spring → NestJS、多环境配置 |
J8 · 认证与授权
| 计划条目 | 对应小节 |
|---|---|
@nestjs/passport + passport-jwt、@nestjs/jwt | Passport 与 JWT |
哈希:argon2 或 bcrypt | 密码哈希 |
AuthGuard、全局 guard、@Public() | 全局 guard、公开路由 |
参数装饰器(@CurrentUser()) | @CurrentUser() |
RBAC:@Roles() + 使用 Reflector 的 RolesGuard | RBAC |
users 模块(User,角色 ADMIN / MEMBER) | users 模块 |
POST /auth/register、POST /auth/login(refresh token 可选) | 注册与登录、refresh token |
全局 JWT guard,@Public() 公开路由 | 全局 JWT guard |
| 用户只能看到自己的项目 | 用户只能看到自己的项目 |
SecurityFilterChain ↔ 全局 guard;@PreAuthorize ↔ @Roles;SecurityContextHolder ↔ @CurrentUser() | Spring → NestJS |
J9 · 事件、定时任务、队列
| 计划条目 | 对应小节 |
|---|---|
@nestjs/event-emitter:emit、@OnEvent、异步监听器 | 应用事件 |
@nestjs/schedule:@Cron、@Interval、@Timeout | 定时任务 |
@nestjs/bullmq:processor、重试、退避 | Redis 任务 |
| 监听器抛出的异常不会传回调用方 | 异步错误 |
task.status.changed 事件 → NotificationsListener | 事件 |
每天运行的 OVERDUE 定时任务 | OVERDUE 定时任务 |
| 可选:用 Redis + BullMQ 处理通知 | BullMQ 队列 |
ApplicationEventPublisher ↔ EventEmitter2;@EventListener ↔ @OnEvent;@Scheduled ↔ @Cron;JMS / Batch ↔ BullMQ | Spring → NestJS |
J10 · 测试
| 计划条目 | 对应小节 |
|---|---|
Jest:describe、it、jest.fn()、jest.mock()、beforeEach | Jest |
带模拟 provider(useValue)的 Test.createTestingModule | Testing module |
| 用 supertest 和专用数据库做端到端测试(Testcontainers 或 Compose) | 端到端测试、测试数据库 |
模拟 repository:getRepositoryToken(Entity) | 模拟 repository |
ProjectsService 和 TasksService 的单元测试 | 单元测试 |
| 端到端:register → login → 项目 → 任务 → 状态 | 端到端流程 |
覆盖率阈值、npm run test:cov | 覆盖率阈值 |
Mockito ↔ jest.fn() / jest.Mocked<T>;@SpringBootTest + MockMvc ↔ testing module + supertest;@DataJpaTest ↔ 真实数据库 | Spring → NestJS |
J11 · 收尾与上线
| 计划条目 | 对应小节 |
|---|---|
@nestjs/swagger:CLI 插件、@ApiProperty、@ApiTags、@ApiBearerAuth | Swagger |
@nestjs/terminus:数据库、内存、磁盘 | 健康检查 |
helmet、CORS、@nestjs/throttler | 安全 |
多阶段 Dockerfile、非 root、npm ci --omit=dev | Dockerfile |
| GitLab CI:lint、测试、构建镜像 | CI |
/docs 上的 Swagger | /docs 上的 Swagger |
Terminus 的 /health(readiness 和 liveness) | /health |
Dockerfile 以及 Compose 里的 app 服务 | Docker |
.gitlab-ci.yml:install、lint、test、build | 流水线 |
最终回顾:LEARNINGS.md、README | 最终回顾 |
计划之外
下面这些小节不在计划里,它们来自用当前版本(NestJS 12、TypeORM 1.1、BullMQ 6)搭建 TaskFlow 时实际遇到的问题:
- 类型元数据与
design:type - 不启动 Nest 的测试里的
reflect-metadata - 用字符串调用
ConditionalModule.registerWhen - BullMQ 6 缺少
ioredis - 定时任务的日历日与时区
- Nest CLI 速查:在 CLI 12 上验证过的命令、别名和选项,以及生成 TaskFlow 骨架的命令
- 场景速查,从一个具体问题出发找到对应案例