Selaa lähdekoodia

📝 docs(schema): 优化schema错误提示文档和示例

- 更新schema-error-msg-cn.md文档,精简示例代码,保留核心字段展示
- 修改10-entity.md中schema规则示例,统一添加中文错误提示
- 为各类型字段添加明确的错误提示信息,如"必须是整数"、"必须是正整数"等
- 为字符串字段增加非空校验和长度限制提示
- 为数字字段增加范围限制和格式校验提示
- 为日期字段添加格式校验和范围限制
- 优化CreateEntityDto和UpdateEntityDto示例,完善错误提示信息
yourname 3 kuukautta sitten
vanhempi
sitoutus
20955a305a
2 muutettua tiedostoa jossa 34 lisäystä ja 171 poistoa
  1. 8 150
      .roo/commands/schema-error-msg-cn.md
  2. 26 21
      .roo/rules/10-entity.md

+ 8 - 150
.roo/commands/schema-error-msg-cn.md

@@ -1,158 +1,16 @@
 ---
-description: "给schema加上中文错误提示"
+description: "给schema加上中文错误提示指令"
 ---
 
 为每个验证字段添加 中文错误提示
 
-
 示例:
 ```typescript
-// 基础用户 schema(包含所有字段)
+// 用户 schema
 export const UserSchema = z.object({
-  id: z.number().int().positive().openapi({ description: '用户ID' }),
-  username: z.string().min(3, '用户名至少3个字符').max(255, '用户名最多255个字符').openapi({
-    example: 'admin',
-    description: '用户名,3-255个字符'
-  }),
-  password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').openapi({
-    example: 'password123',
-    description: '密码,最少6位'
-  }),
-  phone: z.string().max(255, '手机号最多255个字符').nullable().openapi({
-    example: '13800138000',
-    description: '手机号'
-  }),
-  email: z.email('请输入正确的邮箱格式').max(255, '邮箱最多255个字符').nullable().openapi({
-    example: 'user@example.com',
-    description: '邮箱'
-  }),
-  nickname: z.string().max(255, '昵称最多255个字符').nullable().openapi({
-    example: '昵称',
-    description: '用户昵称'
-  }),
-  name: z.string().max(255, '姓名最多255个字符').nullable().openapi({
-    example: '张三',
-    description: '真实姓名'
-  }),
-  avatarFileId: z.number().int().positive().nullable().openapi({
-    example: 1,
-    description: '头像文件ID'
-  }),
-  avatarFile: z.object({
-    id: z.number().int().positive().openapi({ description: '文件ID' }),
-    name: z.string().max(255).openapi({ description: '文件名', example: 'avatar.jpg' }),
-    fullUrl: z.string().openapi({ description: '文件完整URL', example: 'https://example.com/avatar.jpg' }),
-    type: z.string().nullable().openapi({ description: '文件类型', example: 'image/jpeg' }),
-    size: z.number().nullable().openapi({ description: '文件大小(字节)', example: 102400 })
-  }).nullable().optional().openapi({
-    description: '头像文件信息'
-  }),
-  openid: z.string().max(255).nullable().optional().openapi({
-    example: 'oABCDEFGH123456789',
-    description: '微信小程序openid'
-  }),
-  unionid: z.string().max(255).nullable().optional().openapi({
-    example: 'unionid123456789',
-    description: '微信unionid'
-  }),
-  registrationSource: z.string().max(20).default('web').openapi({
-    example: 'miniapp',
-    description: '注册来源: web, miniapp'
-  }),
-  isDisabled: z.number().int().min(0).max(1).default(DisabledStatus.ENABLED).openapi({
-    example: DisabledStatus.ENABLED,
-    description: '是否禁用(0:启用,1:禁用)'
-  }),
-  isDeleted: z.number().int().min(0).max(1).default(DeleteStatus.NOT_DELETED).openapi({
-    example: DeleteStatus.NOT_DELETED,
-    description: '是否删除(0:未删除,1:已删除)'
-  }),
-  roles: z.array(RoleSchema).optional().openapi({
-    example: [
-      {
-        id: 1,
-        name: 'admin',
-        description: '管理员',
-        permissions: ['user:create'],
-        createdAt: new Date(),
-        updatedAt: new Date()
-      }
-    ],
-    description: '用户角色列表'
-  }),
-  createdAt: z.coerce.date().openapi({ description: '创建时间' }),
-  updatedAt: z.coerce.date().openapi({ description: '更新时间' })
-});
-
-// 创建用户请求 schema
-export const CreateUserDto = z.object({
-  username: z.string().min(3, '用户名至少3个字符').max(255, '用户名最多255个字符').openapi({
-    example: 'admin',
-    description: '用户名,3-255个字符'
-  }),
-  password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').openapi({
-    example: 'password123',
-    description: '密码,最少6位'
-  }),
-  phone: z.string().max(255, '手机号最多255个字符').nullable().optional().openapi({
-    example: '13800138000',
-    description: '手机号'
-  }),
-  email: z.email('请输入正确的邮箱格式').max(255, '邮箱最多255个字符').nullable().optional().openapi({
-    example: 'user@example.com',
-    description: '邮箱'
-  }),
-  nickname: z.string().max(255, '昵称最多255个字符').nullable().optional().openapi({
-    example: '昵称',
-    description: '用户昵称'
-  }),
-  name: z.string().max(255, '姓名最多255个字符').nullable().optional().openapi({
-    example: '张三',
-    description: '真实姓名'
-  }),
-  avatarFileId: z.number().int().positive().nullable().optional().openapi({
-    example: 1,
-    description: '头像文件ID'
-  }),
-  isDisabled: z.number().int().min(0, '状态值只能是0或1').max(1, '状态值只能是0或1').default(DisabledStatus.ENABLED).optional().openapi({
-    example: DisabledStatus.ENABLED,
-    description: '是否禁用(0:启用,1:禁用)'
-  })
-});
-
-// 更新用户请求 schema
-export const UpdateUserDto = z.object({
-  username: z.string().min(3, '用户名至少3个字符').max(255, '用户名最多255个字符').optional().openapi({
-    example: 'admin',
-    description: '用户名,3-255个字符'
-  }),
-  password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').optional().openapi({
-    example: 'password123',
-    description: '密码,最少6位'
-  }),
-  phone: z.string().max(255, '手机号最多255个字符').nullable().optional().openapi({
-    example: '13800138000',
-    description: '手机号'
-  }),
-  email: z.email('请输入正确的邮箱格式').max(255, '邮箱最多255个字符').nullable().optional().openapi({
-    example: 'user@example.com',
-    description: '邮箱'
-  }),
-  nickname: z.string().max(255, '昵称最多255个字符').nullable().optional().openapi({
-    example: '昵称',
-    description: '用户昵称'
-  }),
-  name: z.string().max(255, '姓名最多255个字符').nullable().optional().openapi({
-    example: '张三',
-    description: '真实姓名'
-  }),
-  avatarFileId: z.number().int().positive().nullable().optional().openapi({
-    example: 1,
-    description: '头像文件ID'
-  }),
-  isDisabled: z.number().int().min(0, '状态值只能是0或1').max(1, '状态值只能是0或1').optional().openapi({
-    example: DisabledStatus.ENABLED,
-    description: '是否禁用(0:启用,1:禁用)'
-  })
-});
-```
+  username: z.string().min(3, '用户名至少3个字符').max(255, '最多255个字符'),
+  password: z.string().min(6, '密码至少6位').max(255, '最多255位'),
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号'),
+  email: z.email('请输入正确的邮箱格式'),
+  name: z.string().max(255, '姓名最多255个字符').optional()
+});

+ 26 - 21
.roo/rules/10-entity.md

@@ -108,10 +108,11 @@ updatedAt!: Date;
 ```typescript
 import { z } from '@hono/zod-openapi';
 export const EntitySchema = z.object({
-  id: z.number().int().positive().openapi({ description: 'ID说明' }),
+  id: z.number().int('必须是整数').positive('必须是正整数').openapi({ description: 'ID说明' }),
   // 字符串字段
   fieldName: z.string()
-    .max(255)
+    .min(1, '不能为空')
+    .max(255, '最多255个字符')
     .nullable()
     .openapi({
       description: '字段说明',
@@ -119,10 +120,14 @@ export const EntitySchema = z.object({
     }),
   // 数字字段
   numberField: z.number()
+    .int('必须是整数')
+    .positive('必须是正数')
+    .min(1, '最小值为1')
+    .max(9999, '最大值为9999')
     .default(默认值)
     .openapi({...}),
   // 日期字段
-  dateField: z.date().openapi({...})
+  dateField: z.coerce.date('日期格式不正确').openapi({...})
 });
 ```
 
@@ -134,19 +139,19 @@ export const EntitySchema = z.object({
 
 ```typescript
 // 整数类型
-z.coerce.number().int().positive().openapi({
+z.coerce.number().int('必须是整数').positive('必须是正整数').openapi({
   description: '正整数ID',
   example: 1
 });
 
 // 小数类型
-z.coerce.number().multipleOf(0.01).openapi({
+z.coerce.number().multipleOf(0.01, '最多保留两位小数').openapi({
   description: '金额,保留两位小数',
   example: 19.99
 });
 
 // 状态类型(0/1)
-z.coerce.number().int().min(0).max(1).openapi({
+z.coerce.number().int('必须是整数').min(0, '最小值为0').max(1, '最大值为1').openapi({
   description: '状态(0-禁用,1-启用)',
   example: 1
 });
@@ -158,18 +163,18 @@ z.coerce.number().int().min(0).max(1).openapi({
 
 ```typescript
 // 日期时间类型
-z.coerce.date().openapi({
+z.coerce.date('日期格式不正确').openapi({
   description: '创建时间',
   example: '2023-10-01T12:00:00Z'
 });
 
 // 日期范围查询
 const DateRangeSchema = z.object({
-  startDate: z.coerce.date().openapi({
+  startDate: z.coerce.date('开始日期格式不正确').openapi({
     description: '开始日期',
     example: '2023-10-01T00:00:00Z'
   }),
-  endDate: z.coerce.date().openapi({
+  endDate: z.coerce.date('结束日期格式不正确').openapi({
     description: '结束日期',
     example: '2023-10-31T23:59:59Z'
   })
@@ -182,7 +187,7 @@ const DateRangeSchema = z.object({
 
 ```typescript
 // 布尔类型
-z.coerce.boolean().openapi({
+z.coerce.boolean('必须是布尔值').openapi({
   description: '是否启用',
   example: true
 });
@@ -200,29 +205,29 @@ z.coerce.boolean().openapi({
 
 ```typescript
 export const CreateEntityDto = z.object({
-  name: z.string().max(255).openapi({
+  name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').openapi({
     description: '名称',
     example: '示例名称'
   }),
-  quantity: z.coerce.number().int().min(1).openapi({
+  quantity: z.coerce.number().int('数量必须是整数').min(1, '数量最小为1').max(9999, '数量最大为9999').openapi({
     description: '数量',
     example: 10
   }),
-  price: z.coerce.number().multipleOf(0.01).openapi({
+  price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0.01, '价格必须大于0').max(999999.99, '价格不能超过999999.99').openapi({
     description: '价格',
     example: 99.99
   }),
-  isActive: z.coerce.boolean().default(true).openapi({
+  isActive: z.coerce.boolean('是否激活必须是布尔值').default(true).openapi({
     description: '是否激活',
     example: true
   }),
-  expireDate: z.coerce.date().openapi({
+  expireDate: z.coerce.date('过期日期格式不正确').min(new Date(), '过期日期不能早于当前时间').openapi({
     description: '过期日期',
     example: '2024-12-31T23:59:59Z'
   }),
   // 选填字段示例
   // nullable字段必须显式添加optional()
-  description: z.string().max(500).nullable().optional().openapi({
+  description: z.string().max(500, '描述最多500个字符').nullable().optional().openapi({
     description: '商品描述(选填)',
     example: '这是一个可选的商品描述信息'
   })
@@ -236,23 +241,23 @@ export const CreateEntityDto = z.object({
 
 ```typescript
 export const UpdateEntityDto = z.object({
-  name: z.string().max(255).optional().openapi({
+  name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').optional().openapi({
     description: '名称',
     example: '更新后的名称'
   }),
-  quantity: z.coerce.number().int().min(1).optional().openapi({
+  quantity: z.coerce.number().int('数量必须是整数').min(1, '数量最小为1').max(9999, '数量最大为9999').optional().openapi({
     description: '数量',
     example: 20
   }),
-  price: z.coerce.number().multipleOf(0.01).optional().openapi({
+  price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0.01, '价格必须大于0').max(999999.99, '价格不能超过999999.99').optional().openapi({
     description: '价格',
     example: 89.99
   }),
-  isActive: z.coerce.boolean().optional().openapi({
+  isActive: z.coerce.boolean('是否激活必须是布尔值').optional().openapi({
     description: '是否激活',
     example: false
   }),
-  expireDate: z.coerce.date().optional().openapi({
+  expireDate: z.coerce.date('过期日期格式不正确').optional().openapi({
     description: '过期日期',
     example: '2025-12-31T23:59:59Z'
   })