|
|
@@ -375,8 +375,59 @@ channelCustomRoutes.openapi(createChannelRoute, async (c) => {
|
|
|
|
|
|
#### 数组响应处理
|
|
|
|
|
|
+**推荐做法**: 定义数组响应 Schema
|
|
|
+
|
|
|
```typescript
|
|
|
-// 处理数组数据 - 使用 Promise.all 批量验证
|
|
|
+// Schema定义
|
|
|
+export const ChannelListResponseSchema = z.object({
|
|
|
+ data: z.array(ChannelSchema).openapi({
|
|
|
+ description: '渠道列表'
|
|
|
+ }),
|
|
|
+ total: z.number().int().min(0).openapi({
|
|
|
+ description: '总数',
|
|
|
+ example: 10
|
|
|
+ })
|
|
|
+});
|
|
|
+
|
|
|
+// 路由定义
|
|
|
+const listChannelRoute = createRoute({
|
|
|
+ method: 'get',
|
|
|
+ path: '/list',
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: '获取列表成功',
|
|
|
+ content: {
|
|
|
+ 'application/json': { schema: ChannelListResponseSchema }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 400: {
|
|
|
+ description: '参数错误',
|
|
|
+ content: { 'application/json': { schema: ZodErrorSchema } }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 路由实现
|
|
|
+channelCustomRoutes.openapi(listChannelRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const result = await channelService.getList(query);
|
|
|
+
|
|
|
+ // ✅ 推荐:直接使用 parseWithAwait 验证整个响应对象
|
|
|
+ const validatedResult = await parseWithAwait(ChannelListResponseSchema, result);
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json(createZodErrorResponse(error), 400);
|
|
|
+ }
|
|
|
+ return c.json({ code: 500, message: error.message }, 500);
|
|
|
+ }
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**❌ 不推荐**: 使用 `Promise.all` 循环验证
|
|
|
+
|
|
|
+```typescript
|
|
|
+// ❌ 不推荐:这种方式效率较低
|
|
|
const validatedData = await Promise.all(
|
|
|
result.data.map(item => parseWithAwait(ChannelSchema, item))
|
|
|
);
|
|
|
@@ -388,13 +439,14 @@ return c.json({ data: validatedData, total: result.total }, 200);
|
|
|
**响应Schema定义**:
|
|
|
- **400响应使用 `ZodErrorSchema`**: 因为使用 `createZodErrorResponse` 返回详细的验证错误
|
|
|
- **其他错误使用 `ErrorSchema`**: 401, 404, 500 等使用简单的错误格式
|
|
|
+- **数组响应定义专用Schema**: 使用 `z.array(ItemSchema)` 定义列表响应Schema
|
|
|
|
|
|
**代码实现**:
|
|
|
- **必须使用 `createRoute`**: 所有自定义路由必须使用 `createRoute` 定义
|
|
|
- **必须使用 `parseWithAwait`**: 所有自定义路由返回前必须验证数据
|
|
|
- **捕获 ZodError**: 在catch块中处理 `z.ZodError` 异常
|
|
|
- **使用 `createZodErrorResponse`**: 提供统一的Zod错误响应格式
|
|
|
-- **数组使用 `Promise.all`**: 批量验证数组数据
|
|
|
+- **避免使用 Promise.all 循环验证**: 应定义数组响应Schema,直接验证整个响应对象
|
|
|
|
|
|
### 4.4 关键要点
|
|
|
|
|
|
@@ -481,6 +533,17 @@ export const CreateChannelSchema = z.object({
|
|
|
|
|
|
// 更新渠道DTO(所有字段可选)
|
|
|
export const UpdateChannelSchema = CreateChannelSchema.partial();
|
|
|
+
|
|
|
+// 列表响应Schema
|
|
|
+export const ChannelListResponseSchema = z.object({
|
|
|
+ data: z.array(ChannelSchema).openapi({
|
|
|
+ description: '渠道列表'
|
|
|
+ }),
|
|
|
+ total: z.number().int().min(0).openapi({
|
|
|
+ description: '总数',
|
|
|
+ example: 10
|
|
|
+ })
|
|
|
+});
|
|
|
```
|
|
|
|
|
|
### 5.2 Zod 4.0 coerce使用说明
|
|
|
@@ -520,6 +583,7 @@ export type UpdateChannelDto = z.infer<typeof UpdateChannelSchema>;
|
|
|
- **使用 `.openapi()` 装饰器**: 添加描述和示例
|
|
|
- **使用 `z.coerce.date<Date>()` 和 `z.coerce.number<number>()`**: Zod 4.0需要添加泛型参数
|
|
|
- **使用 `.nullable().optional()`**: 处理可空字段
|
|
|
+- **定义列表响应Schema**: 使用 `z.array(ItemSchema)` 定义数组响应,而不是循环验证
|
|
|
- **不导出推断类型**: 类型由RPC自动推断,不需要手动导出
|
|
|
|
|
|
## 6. 软删除规范
|