create-dp-koa 1.0.0 → 1.0.2
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.
- package/package.json +2 -2
- package/template/.cursor/commands/cheatsheet-backend-controller.md +45 -0
- package/template/.cursor/commands/implement-backend-api-controller.md +60 -0
- package/template/.cursor/commands/plan-backend.md +97 -0
- package/template/.cursor/rules/00-backend-core.skill.md +61 -0
- package/template/.cursor/rules/01-backend-skill-router.skill.md +57 -0
- package/template/.cursor/rules/10-backend-api.skill.md +55 -0
- package/template/.cursor/rules/11-backend-controller-recipes.skill.md +188 -0
- package/template/.cursor/rules/20-backend-repository.skill.md +25 -0
- package/template/.cursor/rules/21-backend-service.skill.md +137 -0
- package/template/.cursor/rules/25-backend-comments-and-doc.skill.md +98 -0
- package/template/.cursor/rules/30-backend-validation.skill.md +342 -0
- package/template/.cursor/rules/40-backend-error-logging.skill.md +21 -0
- package/template/.cursor/rules/50-backend-bootstrap-lifecycle.skill.md +105 -0
- package/template/.cursor/rules/60-backend-router-registration.skill.md +73 -0
- package/template/.cursor/rules/70-backend-middleware.skill.md +100 -0
- package/template/.cursor/rules/80-backend-utils-and-libs.skill.md +108 -0
- package/template/.cursor/rules/85-backend-plugins.rule.md +65 -0
- package/template/.cursor/rules/90-backend-testing.skill.md +29 -0
- package/template/.cursor/rules/README.md +49 -0
- package/template/.trae/skills/11-backend-controller-recipes.skill.md +91 -10
- package/template/scripts/sync-template.mjs +0 -1
- package/template/src/controllers/example/ExampleController.ts +14 -0
- package/template/src/entity/index.ts +1 -15
- package/template/src/framework/decorator/processor/AnnotationProcessor.ts +5 -1
- package/template/src/routers/index.ts +0 -35
- package/template/src/utils/testDataInitializer.ts +2 -269
- package/template/test/controllers/example/ExampleController.test.ts +29 -31
- package/template/test/framework/annotation/AnnotationDecorators.test.ts +15 -15
- package/template/test/framework/annotation/AnnotationExecutor.test.ts +27 -32
- package/template/test/framework/annotation/AnnotationProcessor.test.ts +25 -24
- package/template/test/framework/annotation/CustomProcessors.test.ts +15 -25
- package/template/test/framework/annotation/NewRouter.test.ts +9 -7
- package/template/test/framework/annotation/ProcessorManager.test.ts +14 -27
- package/template/test/framework/databaseConfig.test.ts +2 -2
- package/template/test/integration/integration.test.ts +15 -72
- package/template/src/controllers/cacheManagement.controller.ts +0 -131
- package/template/src/controllers/captcha.controller.ts +0 -57
- package/template/src/controllers/example/NewAnnotationExampleController.ts +0 -159
- package/template/src/controllers/example/SwaggerExampleController.ts +0 -205
- package/template/src/controllers/example/TransactionExample.controller.ts +0 -336
- package/template/src/controllers/health.controller.ts +0 -235
- package/template/src/controllers/home/register.controller.ts +0 -58
- package/template/src/controllers/home/ytGoods.controller.ts +0 -92
- package/template/src/controllers/home/ytShop.controller.ts +0 -135
- package/template/src/controllers/home/ytUser.controller.ts +0 -89
- package/template/src/controllers/logManagement.controller.ts +0 -396
- package/template/src/controllers/public/emailSend.controller.ts +0 -65
- package/template/src/controllers/public/ytUserAuth.controller.ts +0 -174
- package/template/src/controllers/testData.controller.ts +0 -253
- package/template/src/dto/controller/example/NewAnnotationExampleController.dto.ts +0 -73
- package/template/src/dto/controller/home/emailSend.controller.dto.ts +0 -40
- package/template/src/dto/controller/home/register.controller.dto.ts +0 -45
- package/template/src/dto/controller/home/ytGoods.controller.dto.ts +0 -55
- package/template/src/dto/controller/home/ytShop.controller.dto.ts +0 -69
- package/template/src/dto/controller/home/ytUser.controller.dto.ts +0 -44
- package/template/src/dto/controller/public/ytUserAuth.controller.dto.ts +0 -63
- package/template/src/dto/goods.dto.ts +0 -212
- package/template/src/dto/service/ytService.dto.ts +0 -13
- package/template/src/dto/user.dto.ts +0 -177
- package/template/src/entity/columnTypes.ts +0 -13
- package/template/src/entity/goodsImagesUnlockKey.entity.ts +0 -33
- package/template/src/entity/goodsUnlocker.entity.ts +0 -34
- package/template/src/entity/shop.entity.ts +0 -52
- package/template/src/entity/shopUser.entity.ts +0 -41
- package/template/src/entity/ytGoods.entity.ts +0 -94
- package/template/src/entity/ytUser.entity.ts +0 -96
- package/template/src/examples/SwaggerProcessorExample.ts +0 -169
- package/template/src/examples/TransactionManagerDemo.ts +0 -377
- package/template/src/framework/utils/dynamicSwagger.ts +0 -410
- package/template/src/repository/UserRepository.ts +0 -122
- package/template/src/service/paramValidateTest.service.ts +0 -139
- package/template/src/service/ytGoods.service.ts +0 -42
- package/template/src/service/ytShop.service.ts +0 -90
- package/template/src/service/ytUser.service.ts +0 -451
- package/template/src/test/swaggerParameterTest.ts +0 -90
- package/template/test/controllers/controllers.test.ts +0 -173
- package/template/test/controllers/example/NewAnnotationExampleController.test.ts +0 -200
- package/template/test/framework/TransactionManagerDemo.test.ts +0 -363
- package/template/test/service/business.test.ts +0 -87
- package/template/test/service/paramValidateTest.service.test.ts +0 -184
- package/template/test/service/ytUser.service.test.ts +0 -566
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: false
|
|
3
|
+
---
|
|
4
|
+
# Skill:Service 层开发规范
|
|
5
|
+
|
|
6
|
+
## 适用与触发(重要)
|
|
7
|
+
- 当需要新增/修改 Service 类(`src/service/**.service.ts` 或等价命名)时启用本 Skill。
|
|
8
|
+
- 典型场景:
|
|
9
|
+
- 新增业务用例(用户、店铺、商品等)的 Service 方法
|
|
10
|
+
- 重构/增强现有 Service(增加校验、事务、日志等)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 一、Service 职责定位(必须)
|
|
15
|
+
- Service 负责:
|
|
16
|
+
- 业务规则/用例编排(多 Repository、多实体、多步骤)
|
|
17
|
+
- 参数校验(通过 `@ParamValidate` 装饰器,由 dp-ioc2 自动处理)
|
|
18
|
+
- 事务边界控制(通过 `transactionManager.executeInTransaction`)
|
|
19
|
+
- 组合/组装领域数据,返回适合 Controller 消费的结果结构
|
|
20
|
+
- Service 不负责:
|
|
21
|
+
- HTTP/路由层细节(status code、header、ctx 等)
|
|
22
|
+
- 直接与 Koa `Context` 交互
|
|
23
|
+
- 直接返回 Controller 统一响应结构(`ControllerResponse`)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 二、类定义与依赖(必须)
|
|
28
|
+
- Service 类必须:
|
|
29
|
+
- 使用 `@Injectable()` 装饰器
|
|
30
|
+
- 继承 `BaseService`
|
|
31
|
+
- Repository 访问:
|
|
32
|
+
- 优先使用 `BaseService` 提供的:
|
|
33
|
+
- `getDataRepository(EntityClass)`(简单 CRUD)
|
|
34
|
+
- `createLazyRepository(EntityClass, RepositoryClass, cache)`(自定义 Repository,参考 `YtUserService`)
|
|
35
|
+
- 禁止在 Service 中直接获取 DataSource:
|
|
36
|
+
- ❌ `getDataSource()` 直接操作
|
|
37
|
+
- ✅ 通过 Repository 完成数据访问(见 `20-backend-repository.skill.md`)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 三、返回值规范(必须)
|
|
42
|
+
- 对外公开的 Service 方法,应优先返回:
|
|
43
|
+
- `Promise<CommonServiceResult<T>>`
|
|
44
|
+
- 使用统一工厂方法:
|
|
45
|
+
- 成功:`CommonServiceResult.success(data, message?)`
|
|
46
|
+
- 各种错误:`validationError / notFound / conflict / unauthorized / forbidden / error` 等
|
|
47
|
+
- 仅在兼容旧接口或内部辅助方法时,可返回原始实体/值(需有明确注释说明原因)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 四、参数校验规范(必须)
|
|
52
|
+
|
|
53
|
+
### 4.1 使用 `@ParamValidate` 装饰器
|
|
54
|
+
- **必须**在方法参数上使用 `@ParamValidate` 装饰器进行自动校验
|
|
55
|
+
- 导入:`import { ParamValidate } from "dp-ioc2"`
|
|
56
|
+
- 对象参数(DTO)校验:
|
|
57
|
+
```typescript
|
|
58
|
+
async createUser(@ParamValidate(CreateUserDto) dto: CreateUserDto): Promise<CommonServiceResult<T>> {
|
|
59
|
+
// dp-ioc2 会自动使用 class-validator 校验 DTO
|
|
60
|
+
// 校验失败会抛出 IOCValidationError
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
- 非对象参数(基本类型)校验:
|
|
64
|
+
```typescript
|
|
65
|
+
import Joi from "joi";
|
|
66
|
+
async getUserById(@ParamValidate(undefined, Joi.string().min(3).required()) id: string): Promise<CommonServiceResult<T>> {
|
|
67
|
+
// dp-ioc2 会自动使用 joi 校验基本类型参数
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
- 混合使用:
|
|
71
|
+
```typescript
|
|
72
|
+
async updateUser(
|
|
73
|
+
@ParamValidate(undefined, Joi.number().positive().required()) userId: number,
|
|
74
|
+
@ParamValidate(UpdateUserDto) dto: UpdateUserDto
|
|
75
|
+
): Promise<CommonServiceResult<T>> {
|
|
76
|
+
// 可以同时校验基本类型和对象参数
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4.2 错误处理
|
|
81
|
+
- `@ParamValidate` 校验失败会抛出 `IOCValidationError` 异常
|
|
82
|
+
- **必须**在方法内部捕获并转换为 `CommonServiceResult`:
|
|
83
|
+
```typescript
|
|
84
|
+
import { IOCValidationError } from "dp-ioc2";
|
|
85
|
+
|
|
86
|
+
async createUser(@ParamValidate(CreateUserDto) dto: CreateUserDto): Promise<CommonServiceResult<T>> {
|
|
87
|
+
try {
|
|
88
|
+
// 业务逻辑...
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error instanceof IOCValidationError) {
|
|
91
|
+
// 将 IOCValidationError 转换为统一的错误响应
|
|
92
|
+
return CommonServiceResult.validationError(
|
|
93
|
+
error.message || "参数校验失败",
|
|
94
|
+
error.errors // 可选:传递详细错误信息
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
// 其他错误处理...
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 4.3 DTO 定义要求
|
|
103
|
+
- DTO 类必须使用 `class-validator` 装饰器(`@IsString`、`@IsEmail`、`@MinLength` 等)
|
|
104
|
+
- DTO 定义与目录规范遵循:`30-backend-validation.skill.md`
|
|
105
|
+
- 禁止在 Service 方法内部手动调用 `DtoValidator`(已由 `@ParamValidate` 自动处理)
|
|
106
|
+
|
|
107
|
+
### 4.4 注意事项
|
|
108
|
+
- `@ParamValidate` 在方法调用前自动执行,无需手动调用校验方法
|
|
109
|
+
- 对象参数会自动转换为 DTO 类实例(支持传入普通对象)
|
|
110
|
+
- 参数校验在 `@Before` hook 之前执行
|
|
111
|
+
- 如果方法有多个参数需要校验,每个参数都需要单独添加 `@ParamValidate` 装饰器
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 五、事务与错误处理(必须)
|
|
116
|
+
|
|
117
|
+
### 5.1 事务使用
|
|
118
|
+
- 涉及多表、多步写操作时,必须使用:
|
|
119
|
+
- `transactionManager.executeInTransaction(async (manager) => { ... })`
|
|
120
|
+
- 不允许自行手动开启/提交/回滚事务(由 `TransactionManager` 统一处理)
|
|
121
|
+
- 事务内如果需要 Repository,优先通过自定义 Repository/manager 适配(见现有示例)
|
|
122
|
+
|
|
123
|
+
### 5.2 错误处理与日志
|
|
124
|
+
- Service 方法内部应使用 `try/catch`:
|
|
125
|
+
- `catch` 中使用 `logger.error(...)` 记录上下文信息
|
|
126
|
+
- 返回 `CommonServiceResult.error(...)` 或更具体的错误码
|
|
127
|
+
- 生产环境避免将详细错误信息/堆栈返回给调用方(参考 `YtUserService` 的错误处理模式)
|
|
128
|
+
- 详细错误处理规范参见:`40-backend-error-logging.skill.md`
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 六、调用关系约束(必须)
|
|
133
|
+
- Controller 只能调用 Service 的公开方法,不应调用 Repository
|
|
134
|
+
- Service 不应调用 Controller,也不应依赖 Koa `Context`
|
|
135
|
+
- Service 之间如需调用,应通过依赖注入(`@Inject(OtherService)`),避免静态调用/循环依赖
|
|
136
|
+
|
|
137
|
+
|
|
@@ -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
|
+
- 敏感信息必须脱敏
|