|
|
@@ -305,12 +305,58 @@ export * from './channel-custom.routes';
|
|
|
export * from './channel-crud.routes';
|
|
|
```
|
|
|
|
|
|
-### 4.3 关键要点
|
|
|
+### 4.3 自定义路由响应规范
|
|
|
+
|
|
|
+**重要**: 自定义路由(非CRUD路由)在返回响应前**必须使用 `parseWithAwait`** 验证和转换数据。
|
|
|
+
|
|
|
+**实际实现**:
|
|
|
+```typescript
|
|
|
+import { parseWithAwait, createZodErrorResponse } from '@d8d/shared-utils';
|
|
|
+import { z } from '@hono/zod-openapi';
|
|
|
+import { ChannelSchema } from '../schemas/channel.schema';
|
|
|
+
|
|
|
+// 自定义路由
|
|
|
+channelCustomRoutes.get('/statistics/:id', async (c) => {
|
|
|
+ try {
|
|
|
+ const id = c.req.param('id');
|
|
|
+ const channelService = new ChannelService(AppDataSource);
|
|
|
+ const result = await channelService.getStatistics(Number(id));
|
|
|
+
|
|
|
+ // ✅ 必须:使用 parseWithAwait 验证和转换响应数据
|
|
|
+ const validatedResult = await parseWithAwait(ChannelSchema, result);
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ // ✅ 推荐:使用 createZodErrorResponse 处理Zod验证错误
|
|
|
+ return c.json(createZodErrorResponse(error), 400);
|
|
|
+ }
|
|
|
+ return c.json({ code: 500, message: error.message }, 500);
|
|
|
+ }
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**数组响应处理**:
|
|
|
+```typescript
|
|
|
+// 处理数组数据
|
|
|
+const validatedData = await Promise.all(
|
|
|
+ result.data.map(item => parseWithAwait(ChannelSchema, item))
|
|
|
+);
|
|
|
+return c.json({ data: validatedData, total: result.total }, 200);
|
|
|
+```
|
|
|
+
|
|
|
+**关键要点**:
|
|
|
+- **必须使用 `parseWithAwait`**: 所有自定义路由返回前必须使用 `parseWithAwait` 验证数据
|
|
|
+- **捕获 ZodError**: 在catch块中处理 `z.ZodError` 异常
|
|
|
+- **使用 `createZodErrorResponse`**: 提供统一的错误响应格式
|
|
|
+- **数组使用 `Promise.all`**: 批量验证数组数据
|
|
|
+
|
|
|
+### 4.4 关键要点
|
|
|
|
|
|
- **使用 `OpenAPIHono`**: 而非普通的 `Hono`
|
|
|
- **使用 `AuthContext` 泛型**: 提供类型安全的认证上下文
|
|
|
- **路由聚合**: 分别定义自定义路由和CRUD路由,然后聚合
|
|
|
- **不设置 `basePath`**: 在聚合路由时处理路径
|
|
|
+- **自定义路由必须使用 `parseWithAwait`**: 验证响应数据符合Schema定义
|
|
|
|
|
|
## 5. Schema规范
|
|
|
|
|
|
@@ -691,7 +737,10 @@ override async create(data: Partial<Channel>, userId?: string | number): Promise
|
|
|
- [ ] 软删除实现:使用 `status` 字段
|
|
|
- [ ] Schema使用 `.openapi()` 装饰器
|
|
|
- [ ] Schema不导出推断类型(类型由RPC自动推断)
|
|
|
+- [ ] Schema使用 `z.coerce.date<Date>()` 和 `z.coerce.number<number>()` 泛型语法
|
|
|
- [ ] 路由使用 `OpenAPIHono` 和 `AuthContext`
|
|
|
+- [ ] 自定义路由使用 `parseWithAwait` 验证响应数据
|
|
|
+- [ ] 自定义路由使用 `createZodErrorResponse` 处理Zod错误
|
|
|
- [ ] 测试数据工厂使用时间戳保证唯一性
|
|
|
- [ ] vitest.config.ts 设置 `fileParallelism: false`
|
|
|
- [ ] 类型检查通过
|