create-dp-koa 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ ---
2
+ alwaysApply: false
3
+ ---
4
+ # Skill:后端注释与文档规范(Comments & Docs)
5
+
6
+ ## 适用与触发
7
+ - 当你**新增或重构业务代码**时,尤其是以下文件:
8
+ - `src/controllers/**/*.ts`
9
+ - `src/service/**/*.ts`
10
+ - `src/entity/**/*.ts`
11
+ - `src/dto/**/*.ts`
12
+ - 本 Skill 只约束**代码注释/文档风格**,不改变分层职责(仍遵循 `00/10/11/20/21` 等规则)。
13
+
14
+ ---
15
+
16
+ ## 一、总体原则(必须)
17
+ - **中文为主,面向“后来的业务开发同学”解释意图**,而不是翻译代码本身。
18
+ - 注释重点回答三件事:
19
+ 1. **这段代码在业务上要解决什么问题?**
20
+ 2. **为什么要这么做 / 有什么约束和边界?**
21
+ 3. **有哪些容易踩坑的地方?(例如依赖环境变量、数据库特性、幂等性、并发等)**
22
+ - 禁止:
23
+ - ❌ 逐行翻译式注释(如 `// 定义一个变量 a`、`// 调用 userService`)。
24
+ - ❌ 与代码含义完全重复、没有信息增量的注释。
25
+
26
+ ---
27
+
28
+ ## 二、不同层级的注释要求
29
+
30
+ ### 2.1 Controller(必须)
31
+ - 每个 `Controller` 类必须有**类级别注释**:
32
+ - 说明该 Controller 覆盖的业务域(例如“项目管理相关接口”)。
33
+ - 对外暴露的大类接口(如“项目列表/创建/更新”等)。
34
+ - 每个对外路由方法(带 `@Get/@Post/...` 的方法)必须有**方法注释**:
35
+ - 粗略说明:**请求来源场景**(前端页面/模块)+ **业务动作**。
36
+ - 标出关键入参(DTO 字段)和关键返回值(例如分页结构、是否依赖登录态)。
37
+ - 如有**权限要求 / 限制条件 / 重要副作用**,必须写清楚。
38
+
39
+ ### 2.2 Service(必须)
40
+ - Service 是业务逻辑核心,注释要尽量详细:
41
+ - 类注释:说明该 Service 管理的领域/聚合根(例如“项目聚合的用例层服务,负责项目生命周期管理”)。
42
+ - 方法注释:
43
+ - 业务用例名称(如“创建项目并绑定负责人”、“按标签过滤项目动态”等)。
44
+ - 输入输出约定:关键参数的语义(例如 `currentUserId` 是否可空、`tags` 的筛选逻辑是“任一命中”还是“全部命中”)。
45
+ - 事务性/幂等性/一致性说明(如果涉及多表、多步写操作)。
46
+ - 对外可见的**错误分支**(对应哪些 `CommonServiceResultCode`)。
47
+
48
+ ### 2.3 Entity(推荐)
49
+ - 实体类上推荐有简短注释:
50
+ - 该表在业务中的角色(例如“项目主表,承载看板列表数据”)。
51
+ - 与前端/其他系统的强耦合字段(如 status 枚举、对外展示字段)。
52
+ - 对于**容易产生歧义的字段**必须补充注释,例如:
53
+ - 标记是否是**展示用字段**(如 `iconColor`),而不是业务逻辑字段。
54
+ - 说明枚举取值与 PRD 或其他文档的对应关系。
55
+
56
+ ### 2.4 DTO(推荐)
57
+ - 对暴露给前端/第三方的 DTO,建议:
58
+ - DTO 类注释:说明在哪些接口中被使用。
59
+ - 对约束较强的字段(必填/长度/格式)补充解释用途(例如“前端用于搜索项目标题和描述的关键字”)。
60
+
61
+ ---
62
+
63
+ ## 三、注释风格与格式
64
+
65
+ ### 3.1 统一使用 TypeScript 风格块注释
66
+ - 类和方法使用 `/** ... */` 块注释,方便 IDE 悬浮提示:
67
+ ```ts
68
+ /**
69
+ * 【业务含义】创建项目并自动绑定负责人
70
+ * - 从当前登录用户中推导 ownerUser
71
+ * - 如果请求体未显式传 owner,则默认使用用户昵称或邮箱
72
+ * - 只负责写入项目主表,不负责创建任何子资源
73
+ */
74
+ async createProject(...) { ... }
75
+ ```
76
+
77
+ ### 3.2 注释粒度
78
+ - **优先注释“函数/逻辑块”,其次才是字段**:
79
+ - 对复杂 if/循环/过滤条件,使用行上方的简短注释解释“业务规则”。
80
+ - 不建议每一行都写注释,保持“高信息密度”。
81
+
82
+ ---
83
+
84
+ ## 四、与测试 & 文档的关系
85
+
86
+ - 当 Service 方法被测试覆盖时,注释可以引用测试用例说明典型场景:
87
+ - 例如:“见 `project.service.test.ts` 中关于关键字搜索和分页的用例”。
88
+ - PRD 中有明确约束的字段/行为,注释应当**引用 PRD 段落或小节标题**,方便对照。
89
+
90
+ ---
91
+
92
+ ## 五、应用建议
93
+
94
+ - 新增业务时:
95
+ - 先写 Controller / Service 框架,再补完整注释,最后写测试。
96
+ - 重构旧代码时:
97
+ - 优先为“复杂/关键路径”补注释,如鉴权、事务、资金/安全相关逻辑。
98
+
@@ -0,0 +1,342 @@
1
+ ---
2
+ alwaysApply: false
3
+ ---
4
+ # Skill:参数校验与 DTO 规范
5
+
6
+ ## 适用与触发(重要)
7
+ - 当需要新增/修改 DTO 类时启用本 Skill
8
+ - 典型场景:
9
+ - 新增 API 接口需要定义请求/响应数据结构
10
+ - 新增 Service 方法需要定义业务输入参数
11
+ - 重构现有 DTO 结构
12
+
13
+ ---
14
+
15
+ ## 一、DTO 分层架构(必须)
16
+
17
+ ### 1.1 分层原则
18
+ - **Controller DTO**:处理 HTTP 层的数据结构(请求参数、响应数据)
19
+ - **Service DTO**:处理业务领域的数据结构(业务操作输入、查询条件)
20
+ - **必须进行转换**:Controller 接收的 DTO 需要转换为 Service DTO 后再调用 Service
21
+
22
+ ### 1.2 目录结构(必须遵守)
23
+
24
+ ```
25
+ src/dto/
26
+ ├── controller/ # Controller 层 DTO(HTTP 层)
27
+ │ ├── home/ # 需要认证的接口
28
+ │ │ ├── ytUser.controller.dto.ts
29
+ │ │ ├── ytGoods.controller.dto.ts
30
+ │ │ └── ytShop.controller.dto.ts
31
+ │ ├── public/ # 公开接口
32
+ │ │ └── ytUserAuth.controller.dto.ts
33
+ │ └── example/ # 示例
34
+ │ └── NewAnnotationExampleController.dto.ts
35
+ └── service/ # Service 层 DTO(业务领域)
36
+ ├── user.service.dto.ts
37
+ ├── goods.service.dto.ts
38
+ └── shop.service.dto.ts
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 二、命名规范(必须)
44
+
45
+ ### 2.1 Controller DTO 命名
46
+
47
+ #### 请求 DTO(接收 HTTP 参数)
48
+ - **格式**:`{操作}{实体}ControllerDto` 或 `{操作}{实体}RequestDto`
49
+ - **示例**:
50
+ - `CreateUserControllerDto` - 创建用户请求
51
+ - `UpdateUserControllerDto` - 更新用户请求
52
+ - `LoginUserControllerDto` - 登录请求
53
+ - `GetUserInfoControllerDto` - 获取用户信息请求(Query 参数)
54
+ - `UnlockGoodsImageControllerDto` - 解锁商品图片请求
55
+
56
+ #### 响应 DTO(API 返回数据)
57
+ - **格式**:`{操作}{实体}ResponseDto`
58
+ - **示例**:
59
+ - `GetUserInfoResponseDto` - 获取用户信息响应
60
+ - `CreateUserResponseDto` - 创建用户响应
61
+ - `GetYtUserBasicInfoResponseDto` - 获取用户基本信息响应
62
+
63
+ #### 命名规则总结
64
+ - 请求 DTO:以 `ControllerDto` 或 `RequestDto` 结尾
65
+ - 响应 DTO:以 `ResponseDto` 结尾
66
+ - 文件命名:`{模块}.controller.dto.ts`
67
+
68
+ ### 2.2 Service DTO 命名
69
+
70
+ #### 业务操作 DTO
71
+ - **格式**:`{操作}{实体}ServiceDto` 或 `{操作}{实体}Dto`(简洁形式)
72
+ - **示例**:
73
+ - `CreateUserServiceDto` 或 `CreateUserDto` - 创建用户业务输入
74
+ - `UpdateUserServiceDto` 或 `UpdateUserDto` - 更新用户业务输入
75
+ - `LoginUserServiceDto` 或 `LoginUserDto` - 登录业务输入
76
+ - `ChangePasswordServiceDto` 或 `ChangePasswordDto` - 修改密码业务输入
77
+
78
+ #### 查询 DTO
79
+ - **格式**:`Query{实体}ServiceDto` 或 `Query{实体}Dto`
80
+ - **示例**:
81
+ - `QueryUserServiceDto` 或 `QueryUserDto` - 用户查询条件
82
+ - `QueryGoodsServiceDto` 或 `QueryGoodsDto` - 商品查询条件
83
+
84
+ #### 命名规则总结
85
+ - 业务操作 DTO:以 `ServiceDto` 或 `Dto` 结尾(推荐使用 `ServiceDto` 以明确区分)
86
+ - 查询 DTO:以 `Query{实体}ServiceDto` 或 `Query{实体}Dto` 命名
87
+ - 文件命名:`{模块}.service.dto.ts`
88
+
89
+ ---
90
+
91
+ ## 三、使用规范(必须)
92
+
93
+ ### 3.1 Controller 层使用
94
+
95
+ #### 请求 DTO 使用
96
+ ```typescript
97
+ import { CreateUserControllerDto, CreateUserResponseDto } from '@src/dto/controller/home/ytUser.controller.dto';
98
+ import { CreateUserServiceDto } from '@src/dto/service/user.service.dto';
99
+
100
+ @Post()
101
+ async createUser(@Body(CreateUserControllerDto) body: CreateUserControllerDto): Promise<ControllerResponse<CreateUserResponseDto>> {
102
+ // 必须:将 Controller DTO 转换为 Service DTO
103
+ const serviceDto: CreateUserServiceDto = {
104
+ nickName: body.name, // 字段名可能不同
105
+ email: body.email,
106
+ password: body.password,
107
+ // ... 其他字段映射
108
+ };
109
+
110
+ // 调用 Service(使用 Service DTO)
111
+ const result = await this.userService.createUser(serviceDto);
112
+
113
+ // 将 Service 结果转换为 Response DTO
114
+ return this.success(mapToResponseDto(result.data));
115
+ }
116
+ ```
117
+
118
+ **重要**:
119
+ - `@Body(CreateUserControllerDto)` - 将 DTO 类传给装饰器,框架会自动校验
120
+ - `@Query(QueryDto)` - Query 参数同样将 DTO 类传给装饰器
121
+ - `@Params(ParamsDto)` - 路径参数同样将 DTO 类传给装饰器
122
+ - 框架会在方法执行前自动校验,校验失败会直接返回错误响应
123
+
124
+ #### 响应 DTO 使用
125
+ ```typescript
126
+ import { GetUserInfoResponseDto } from '@src/dto/controller/home/ytUser.controller.dto';
127
+ import { ResponseValidateIf } from '@src/framework/decorator/controller';
128
+
129
+ // 使用 @ResponseValidateIf 装饰器校验响应数据
130
+ @ResponseValidateIf(GetUserInfoResponseDto, (data) => data && data.data)
131
+ @Get()
132
+ async getUserInfo(@State() state: any): Promise<ControllerResponse<GetUserInfoResponseDto>> {
133
+ // 返回数据必须符合 ResponseDto 结构
134
+ return this.success({
135
+ id: user.id,
136
+ nickName: user.nickName,
137
+ // ... 其他字段
138
+ });
139
+ }
140
+ ```
141
+
142
+ **响应校验说明**:
143
+ - `@ResponseValidateIf(ResponseDto, condition)` - **推荐使用**,条件校验响应数据
144
+ - 第一个参数:Response DTO 类
145
+ - 第二个参数:条件函数,返回 `true` 时才进行校验
146
+ - **必须传 condition 函数**,不传 condition 默认不校验
147
+ - 示例:`@ResponseValidateIf(GetUserInfoResponseDto, (data) => data && data.data)`
148
+ - `@ResponseValidator(ResponseDto, objectKey?)` - 无条件校验响应数据(不推荐使用)
149
+ - 第一个参数:Response DTO 类
150
+ - 第二个参数:可选,指定要校验的数据对象的 key(如 `"data"`)
151
+ - **注意**:虽然存在,但无法控制校验时机,建议使用 `@ResponseValidateIf`
152
+ - **默认行为**:如果不使用任何响应校验装饰器,默认不进行响应校验
153
+ - 校验失败会在返回前抛出异常或返回错误响应
154
+
155
+ ### 3.2 Service 层使用
156
+
157
+ ```typescript
158
+ import { CreateUserServiceDto } from '@src/dto/service/user.service.dto';
159
+ import { ParamValidate } from 'dp-ioc2';
160
+
161
+ async createUser(@ParamValidate(CreateUserServiceDto) dto: CreateUserServiceDto): Promise<CommonServiceResult<YtUserEntity>> {
162
+ // 业务逻辑(校验已在方法调用前自动完成)
163
+ // ...
164
+ }
165
+ ```
166
+
167
+ ### 3.3 DTO 转换规范
168
+
169
+ #### 转换位置
170
+ - **必须在 Controller 方法内部进行转换**
171
+ - 禁止在 Service 层接收 Controller DTO
172
+ - 禁止在 Controller 层直接传递 Controller DTO 给 Service
173
+
174
+ #### 转换示例
175
+ ```typescript
176
+ // ✅ 正确:在 Controller 中转换
177
+ @Post()
178
+ async createUser(@Body(CreateUserControllerDto) body: CreateUserControllerDto) {
179
+ // 框架已自动校验 body,校验失败不会进入方法体
180
+ const serviceDto: CreateUserServiceDto = {
181
+ // 字段映射
182
+ };
183
+ return await this.userService.createUser(serviceDto);
184
+ }
185
+
186
+ // ❌ 错误:直接传递 Controller DTO
187
+ @Post()
188
+ async createUser(@Body(CreateUserControllerDto) body: CreateUserControllerDto) {
189
+ return await this.userService.createUser(body); // 禁止!必须转换为 Service DTO
190
+ }
191
+ ```
192
+
193
+ ---
194
+
195
+ ## 四、DTO 定义规范(必须)
196
+
197
+ ### 4.1 必须使用 class-validator
198
+ - 所有 DTO 类必须使用 `class-validator` 装饰器
199
+ - 常用装饰器:`@IsString()`, `@IsEmail()`, `@IsNotEmpty()`, `@MinLength()`, `@IsOptional()` 等
200
+
201
+ ### 4.2 Controller DTO 特殊处理
202
+ - 可以使用 `@Transform` 进行数据转换(如字符串转数字、trim)
203
+ - 可以包含 HTTP 层特定的字段(如 `vcodeToken`、`requestId`)
204
+
205
+ ### 4.3 Service DTO 要求
206
+ - **禁止**包含 HTTP 层特性(如 `@Transform`、HTTP 特定字段)
207
+ - 专注于业务领域的数据结构
208
+ - 可被多个 Controller 复用
209
+
210
+ ### 4.4 示例对比
211
+
212
+ #### Controller DTO(包含 HTTP 层转换)
213
+ ```typescript
214
+ export class CreateUserControllerDto {
215
+ @Transform((val) => Trim(String(val)))
216
+ @IsString()
217
+ @IsEmail()
218
+ email: string;
219
+
220
+ @Transform((val) => Number(val))
221
+ @IsNumber()
222
+ age: number;
223
+ }
224
+ ```
225
+
226
+ #### Service DTO(纯业务结构)
227
+ ```typescript
228
+ export class CreateUserServiceDto {
229
+ @IsString()
230
+ @IsEmail()
231
+ email: string;
232
+
233
+ @IsNumber()
234
+ age: number;
235
+ }
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 五、校验流程
241
+
242
+ ### 5.1 Controller 层校验
243
+
244
+ #### 请求参数校验
245
+ - **必须**将 DTO 类传给装饰器,框架会自动校验:
246
+ ```typescript
247
+ // ✅ 正确:将 DTO 类传给装饰器
248
+ @Get()
249
+ async getUserInfo(@Query(GetUserInfoQueryDto) query: GetUserInfoQueryDto) { }
250
+
251
+ @Post()
252
+ async createUser(@Body(CreateUserControllerDto) body: CreateUserControllerDto) { }
253
+
254
+ @Get('/:id')
255
+ async getUserById(@Params(GetUserByIdParamsDto) params: GetUserByIdParamsDto) { }
256
+
257
+ // ❌ 错误:不传 DTO 类,框架无法自动校验
258
+ @Get()
259
+ async getUserInfo(@Query() query: GetUserInfoQueryDto) { }
260
+ ```
261
+ - 校验失败自动返回错误响应,不会进入方法体
262
+
263
+ #### 响应数据校验
264
+ - 使用 `@ResponseValidateIf` 或 `@ResponseValidator` 装饰器:
265
+ ```typescript
266
+ // 条件校验(推荐)
267
+ @ResponseValidateIf(GetUserInfoResponseDto, (data) => data && data.data)
268
+ @Get()
269
+ async getUserInfo(): Promise<ControllerResponse<GetUserInfoResponseDto>> {
270
+ return this.success({ /* ... */ });
271
+ }
272
+
273
+ // 无条件校验(始终校验)
274
+ @ResponseValidator(GetUserInfoResponseDto)
275
+ @Get()
276
+ async getUserInfo(): Promise<ControllerResponse<GetUserInfoResponseDto>> {
277
+ return this.success({ /* ... */ });
278
+ }
279
+ ```
280
+ - 校验失败会在返回前抛出异常或返回错误响应
281
+
282
+ ### 5.2 Service 层校验
283
+ - **必须**使用 `@ParamValidate` 装饰器进行参数校验(由 dp-ioc2 自动处理)
284
+ - 对象参数(DTO):使用 `@ParamValidate(DtoClass)`,自动使用 class-validator 校验
285
+ - 非对象参数:使用 `@ParamValidate(undefined, JoiSchema)`,使用 joi 校验
286
+ - 校验失败会抛出 `IOCValidationError`,需要在 Service 方法中捕获并转换为 `CommonServiceResult.validationError(...)`
287
+ - 详细规范参见:`21-backend-service.skill.md` 的"四、参数校验规范"
288
+
289
+ ---
290
+
291
+ ## 六、迁移指南
292
+
293
+ ### 6.1 现有 DTO 迁移
294
+ - `src/dto/user.dto.ts` → `src/dto/service/user.service.dto.ts`
295
+ - `src/dto/goods.dto.ts` → `src/dto/service/goods.service.dto.ts`
296
+ - 重命名:`CreateUserDto` → `CreateUserServiceDto`(或保持 `CreateUserDto`,但文件放在 service 目录)
297
+
298
+ ### 6.2 迁移步骤
299
+ 1. 创建 Service DTO 文件(`src/dto/service/{module}.service.dto.ts`)
300
+ 2. 将业务 DTO 移动到 Service DTO 文件
301
+ 3. 在 Controller 中创建对应的 Controller DTO
302
+ 4. 在 Controller 方法中添加 DTO 转换逻辑
303
+ 5. 更新所有引用
304
+
305
+ ---
306
+
307
+ ## 七、禁止事项
308
+
309
+ ### 7.1 禁止在 Service 层使用 Controller DTO
310
+ - ❌ `async createUser(dto: CreateUserControllerDto)` - 禁止
311
+ - ✅ `async createUser(dto: CreateUserServiceDto)` - 正确
312
+
313
+ ### 7.2 禁止直接传递 Controller DTO 给 Service
314
+ - ❌ `this.service.createUser(controllerDto)` - 禁止
315
+ - ✅ `this.service.createUser(convertToServiceDto(controllerDto))` - 正确
316
+
317
+ ### 7.3 禁止在 Service DTO 中使用 HTTP 层特性
318
+ - ❌ Service DTO 中使用 `@Transform` - 禁止
319
+ - ✅ Controller DTO 中使用 `@Transform` - 允许
320
+
321
+ ---
322
+
323
+ ## 八、最佳实践
324
+
325
+ ### 8.1 字段映射
326
+ - 如果 Controller DTO 和 Service DTO 字段名相同,可以直接使用展开运算符
327
+ - 如果字段名不同,必须显式映射
328
+
329
+ ### 8.2 响应 DTO
330
+ - 响应 DTO 应该只包含前端需要的数据
331
+ - 避免直接返回 Entity,应该转换为 Response DTO
332
+
333
+ ### 8.3 复用性
334
+ - Service DTO 应该设计为可被多个 Controller 复用
335
+ - Controller DTO 可以针对特定接口优化
336
+
337
+ ---
338
+
339
+ ## 九、相关规范
340
+
341
+ - **Service 层规范**:`21-backend-service.skill.md`
342
+ - **Controller 层规范**:`10-backend-api.skill.md`, `11-backend-controller-recipes.skill.md`
@@ -0,0 +1,21 @@
1
+ ---
2
+ alwaysApply: false
3
+ ---
4
+ # Skill:错误处理与日志规范
5
+
6
+ ## 一、错误处理
7
+
8
+ - 所有 async 方法必须 try/catch
9
+ - 业务错误:
10
+ - 使用 `CommonServiceResult.fail()`
11
+ - 系统错误:
12
+ - 记录日志
13
+ - 返回通用失败响应
14
+
15
+ ---
16
+
17
+ ## 二、日志规范
18
+
19
+ - 使用 `logger.info / warn / error`
20
+ - 日志必须包含上下文信息
21
+ - 敏感信息必须脱敏
@@ -0,0 +1,105 @@
1
+ ---
2
+ alwaysApply: false
3
+ ---
4
+ # Skill:启动生命周期规范(setBeforeBootstrap / setAfterBootstrap)
5
+
6
+ ## 适用与触发(重要)
7
+ - 仅当需要修改/新增启动逻辑(`src/app.ts`)或框架启动流程(`src/framework/utils/bootstrap.ts`)时启用本 Skill。
8
+ - 触发口令建议:
9
+ - “请按 `50-backend-bootstrap-lifecycle.skill.md` 调整启动流程”
10
+
11
+ ---
12
+
13
+ ## 一、真实执行顺序(必须理解并遵守)
14
+ 以 `src/framework/utils/bootstrap.ts` 为准:
15
+
16
+ 1. `bootstrap()` 内部 **先执行** `beforeBootstraps(app)`(如果已注册)
17
+ 2. 然后框架挂载:CORS → 日志中间件 → 框架错误处理中间件 → `koaBody()`
18
+ 3. 然后挂载:`router.routes()/allowedMethods()` 并 `app.listen(...)`
19
+ 4. `listen` 成功后 **再执行** `afterBootstraps(app)`(如果已注册)
20
+
21
+ 结论:
22
+ - **before**:发生在“框架中间件/路由挂载/监听端口”之前
23
+ - **after**:发生在“监听端口成功”之后
24
+
25
+ ---
26
+
27
+ ## 二、setBeforeBootstrap(fn) 规范(必须)
28
+
29
+ ### 2.1 允许做的事情(推荐放这里)
30
+ - **注册必须最先执行的中间件**(例如静态资源 `staticMiddleware`)
31
+ - **完成路由注册/绑定**(例如调用 `Router()` 让 `bindRouter` 把 Controller 注册到 router)
32
+ - **初始化框架级系统**(例如注解系统初始化、配置系统初始化)
33
+ - **初始化数据库与依赖它的组件**
34
+ - 连接数据库(`initDb`)
35
+ - 设置 `transactionManager/migrationManager` 的 DataSource
36
+ - 生产环境迁移(如有)
37
+ - 内存库模式初始化测试数据(如有)
38
+
39
+ ### 2.1.1 环境变量文件与 `.env` 选择(对齐 `bootstrap.ts`)
40
+
41
+ - **与 `NODE_ENV` 解耦**:`bootstrap.ts` 最早加载的 `.env` 文件由 **`isDebug()`** 决定:
42
+ - `--env=debug` → `.env.development`
43
+ - 否则 → `.env.production`
44
+ - **本地开发**:`package.json` 的 `dev` 建议在 `node dist/main.js` 后追加 `--env=debug`,避免误加载生产 env。
45
+ - **生产部署**:进程**不要**带 `--env=debug`,否则迁移与脱敏等行为会按“调试口径”执行。
46
+
47
+ ### 2.1.2 数据库迁移触发时机(对齐 `app.ts`)
48
+
49
+ - 迁移执行条件以 **`!isDebug()`** 为准(非 debug 即跑迁移),与 `NODE_ENV` 解耦。
50
+ - 迁移类需**显式注册**到 DataSource(如 `src/migrations/index.ts` 导出 `APP_MIGRATIONS`),避免 webpack 单文件部署后无法按路径扫描迁移文件。
51
+
52
+ ### 2.2 必须遵守的约束
53
+ - 回调函数签名必须能接收 `app: Koa`(框架会 `beforeBootstraps(app)` 调用)
54
+ - **只注册一次**:项目当前实现中 `beforeBootstraps` 只有一个槽位,后注册会覆盖先注册
55
+ - 发生异常必须:
56
+ - 记录日志
57
+ - 抛出异常(让启动失败),避免“半初始化”状态继续运行
58
+
59
+ ### 2.3 禁止行为
60
+ - ❌ 在这里启动“永不结束的任务”阻塞启动(例如无限循环、长期 await 不返回)
61
+ - ❌ 在这里写业务逻辑(仅做启动编排/初始化)
62
+
63
+ ---
64
+
65
+ ## 三、setAfterBootstrap(fn) 规范(必须)
66
+
67
+ ### 3.1 允许做的事情(推荐放这里)
68
+ - **启动后台/周期性任务**(例如数据库连接监控 `dbMonitor.startMonitoring()`)
69
+ - **启动只依赖服务器已监听的组件**(例如需要对外暴露端口后再启动的探针/上报)
70
+ - **输出启动完成日志**、打印运行信息
71
+
72
+ ### 3.2 必须遵守的约束
73
+ - 回调函数签名必须能接收 `app: Koa`
74
+ - after 内部出错:
75
+ - 必须记录日志
76
+ - 不应静默失败(至少可观测);必要时应触发优雅退出策略(按项目约定)
77
+ - **只注册一次**:项目当前实现中 `afterBootstraps` 只有一个槽位,后注册会覆盖先注册
78
+
79
+ ### 3.3 禁止行为(强烈)
80
+ - ❌ 在 after 阶段新增路由/中间件/全局行为(会导致启动行为不可预测)
81
+ - 路由与中间件应在 before 阶段完成,保证一致性
82
+ - ❌ 在 after 阶段再做“数据库 schema 级别变更/迁移”等破坏性操作
83
+
84
+ ---
85
+
86
+ ## 四、`src/app.ts` 推荐写法(对齐范式)
87
+
88
+ ### 4.1 before 推荐结构
89
+ - 注册静态中间件(如需要最前置)
90
+ - 初始化注解系统
91
+ - 调用 `Router()` 完成 Controller→router 绑定
92
+ - 初始化数据库、迁移、测试数据、事务管理器 DataSource
93
+
94
+ ### 4.2 after 推荐结构
95
+ - 启动 dbMonitor 等后台任务
96
+ - 打印启动后日志
97
+
98
+ ---
99
+
100
+ ## 五、常见坑(必须避免)
101
+ - before/after **不是数组**,目前实现是单个槽位:多次调用会覆盖
102
+ - `Router()` 必须在 `app.use(router.routes())` 之前执行;因此应放在 before
103
+ - 若在 before 里注册中间件,其执行顺序会早于框架默认中间件(因为 before 先 `app.use`)
104
+
105
+
@@ -0,0 +1,73 @@
1
+ ---
2
+ alwaysApply: false
3
+ ---
4
+ # Skill:路由注册规范(routers/index.ts + bindRouter)
5
+
6
+ ## 适用与触发(重要)
7
+ - 仅当需要新增/修改路由绑定(`src/routers/index.ts`)或调整路由系统(`src/framework/utils/router.ts`)时启用本 Skill。
8
+ - 触发口令建议:
9
+ - “请按 `60-backend-router-registration.skill.md` 规范注册路由”
10
+
11
+ ---
12
+
13
+ ## 一、路由注册机制(必须理解)
14
+ 本项目路由注册入口:`src/routers/index.ts`
15
+ 路由绑定工具:`src/framework/utils/router.ts` 的 `bindRouter(...)`
16
+
17
+ ### 1.1 bindRouter(...) 的真实行为(以代码为准)
18
+ - 调用形式:
19
+ - `bindRouter(prefixPath, ControllerClass)`
20
+ - `bindRouter(prefixPath, middleware1, middleware2, ..., ControllerClass)`
21
+ - Controller 实例化:
22
+ - 由 `Provider<any>(ControllerClass)` 创建(依赖注入容器)
23
+ - 路由生成:
24
+ - 读取 Controller 上的 `@Get/@Post/@Put/@Del/@All` 装饰器元数据
25
+ - 将每个方法的 url 与 `prefixPath` 通过 `path.join` 拼接,并规范化为 `/`
26
+ - middleware 注入顺序:
27
+ - 传入的 middlewares 会按顺序对该 prefix 下的每个具体 `_url` 执行 `_router.use(_url, middleware)`
28
+ - 再注册对应 method 的路由处理函数(`_router.get/post/...`)
29
+ - 尾斜杠兼容:
30
+ - 若生成的 `_url` 以 `/` 结尾,会同时注册“去掉尾斜杠”的版本(避免 `/a/` 与 `/a` 不兼容)
31
+
32
+ ---
33
+
34
+ ## 二、`src/routers/index.ts` 注册规范(必须遵守)
35
+
36
+ ### 2.1 只做“路由绑定”
37
+ - `routers/index.ts` 只负责:
38
+ - 导入 Controller
39
+ - 选择是否需要 middlewares(鉴权/日志等)
40
+ - 调用 `bindRouter(prefix, ...middlewares, Controller)`
41
+ - 禁止在此文件写业务逻辑或数据库操作
42
+
43
+ ### 2.2 路由前缀命名(推荐统一)
44
+ 建议用“模块/领域 + 子模块 + 资源”的层级前缀,保持稳定:
45
+ - `"/public/..."`:公开接口(一般不加“认证/鉴权 middleware”)
46
+ - `"/home/..."`:业务接口(按需加“认证/鉴权 middleware”)
47
+ - `"/admin/..."`:管理接口(一般需要认证/鉴权;可在此基础上再加权限/审计中间件)
48
+ - `"/health"`:健康检查(通常不鉴权)
49
+ - `"/test"`:测试/调试接口(仅测试环境或内存库模式有效时暴露)
50
+
51
+ ### 2.3 middleware 使用规范(必须)
52
+ - 需要认证/鉴权的路由必须在 `bindRouter` 里显式挂“认证/鉴权 middleware”(名称不固定,以项目实际为准):
53
+ - 示例(本项目当前实现):`bindRouter("/home/user/yt_user", tokenMiddleware(), YtUserController);`
54
+ - middlewares 必须传“可执行的 koa middleware 函数”:
55
+ - 例如 `authMiddleware()` / `tokenMiddleware()`(注意是调用结果,而不是函数引用本身)
56
+ - middleware 顺序必须从“通用/鉴权”到“更具体/业务”:
57
+ - 例如:`authMiddleware()` → 其他业务中间件
58
+
59
+ ### 2.4 禁止重复绑定(必须)
60
+ - ❌ 禁止对同一个 `prefixPath + Controller` 重复调用 `bindRouter`
61
+ - 重复绑定会导致路由重复注册、重复执行中间件、行为不可预测
62
+
63
+ ### 2.5 尾斜杠(推荐)
64
+ - 推荐 `bindRouter` 的 `prefixPath` **不要以 `/` 结尾**,保持统一(除非明确需要)
65
+ - 若使用了尾斜杠,框架会额外注册无尾斜杠版本;应避免产生重复/歧义的路由
66
+
67
+ ---
68
+
69
+ ## 三、与启动流程的配合(必须)
70
+ - `Router()`(即 `src/routers/index.ts` 的默认导出函数)必须在 `app.use(router.routes())` 之前执行
71
+ - 因此通常应在 `setBeforeBootstrap(...)` 中调用(见 `50-backend-bootstrap-lifecycle.skill.md`)
72
+
73
+