user.schema.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import { z } from '@hono/zod-openapi';
  2. import { DeleteStatus, DisabledStatus } from '../../share/types';
  3. import { RoleSchema } from './role.schema';
  4. // import { FileSchema } from './modules/files/file.schema';
  5. // 基础用户 schema(包含所有字段)
  6. export const UserSchema = z.object({
  7. id: z.number().int().positive().openapi({ description: '用户ID' }),
  8. username: z.string().min(3, '用户名至少3个字符').max(255, '用户名最多255个字符').openapi({
  9. example: 'admin',
  10. description: '用户名,3-255个字符'
  11. }),
  12. password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').openapi({
  13. example: 'password123',
  14. description: '密码,最少6位'
  15. }),
  16. phone: z.string().max(255, '手机号最多255个字符').nullable().openapi({
  17. example: '13800138000',
  18. description: '手机号'
  19. }),
  20. email: z.email('请输入正确的邮箱格式').max(255, '邮箱最多255个字符').nullable().openapi({
  21. example: 'user@example.com',
  22. description: '邮箱'
  23. }),
  24. nickname: z.string().max(255, '昵称最多255个字符').nullable().openapi({
  25. example: '昵称',
  26. description: '用户昵称'
  27. }),
  28. name: z.string().max(255, '姓名最多255个字符').nullable().openapi({
  29. example: '张三',
  30. description: '真实姓名'
  31. }),
  32. avatarFileId: z.number().int().positive().nullable().openapi({
  33. example: 1,
  34. description: '头像文件ID'
  35. }),
  36. avatarFile: z.object({
  37. id: z.number().int().positive().openapi({ description: '文件ID' }),
  38. name: z.string().max(255).openapi({ description: '文件名', example: 'avatar.jpg' }),
  39. fullUrl: z.string().openapi({ description: '文件完整URL', example: 'https://example.com/avatar.jpg' }),
  40. type: z.string().nullable().openapi({ description: '文件类型', example: 'image/jpeg' }),
  41. size: z.number().nullable().openapi({ description: '文件大小(字节)', example: 102400 })
  42. }).nullable().optional().openapi({
  43. description: '头像文件信息'
  44. }),
  45. openid: z.string().max(255).nullable().optional().openapi({
  46. example: 'oABCDEFGH123456789',
  47. description: '微信小程序openid'
  48. }),
  49. unionid: z.string().max(255).nullable().optional().openapi({
  50. example: 'unionid123456789',
  51. description: '微信unionid'
  52. }),
  53. registrationSource: z.string().max(20).default('web').openapi({
  54. example: 'miniapp',
  55. description: '注册来源: web, miniapp'
  56. }),
  57. isDisabled: z.nativeEnum(DisabledStatus).default(DisabledStatus.ENABLED).openapi({
  58. example: DisabledStatus.ENABLED,
  59. description: '是否禁用(0:启用,1:禁用)'
  60. }),
  61. isDeleted: z.number().int().min(0).max(1).default(DeleteStatus.NOT_DELETED).openapi({
  62. example: DeleteStatus.NOT_DELETED,
  63. description: '是否删除(0:未删除,1:已删除)'
  64. }),
  65. roles: z.array(RoleSchema).optional().openapi({
  66. example: [
  67. {
  68. id: 1,
  69. name: 'admin',
  70. description: '管理员',
  71. permissions: ['user:create'],
  72. createdAt: new Date(),
  73. updatedAt: new Date()
  74. }
  75. ],
  76. description: '用户角色列表'
  77. }),
  78. createdAt: z.coerce.date().openapi({ description: '创建时间' }),
  79. updatedAt: z.coerce.date().openapi({ description: '更新时间' })
  80. });
  81. // 创建用户请求 schema
  82. export const CreateUserDto = z.object({
  83. username: z.string().min(3, '用户名至少3个字符').max(255, '用户名最多255个字符').openapi({
  84. example: 'admin',
  85. description: '用户名,3-255个字符'
  86. }),
  87. password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').openapi({
  88. example: 'password123',
  89. description: '密码,最少6位'
  90. }),
  91. phone: z.string().max(255, '手机号最多255个字符').nullable().optional().openapi({
  92. example: '13800138000',
  93. description: '手机号'
  94. }),
  95. email: z.email('请输入正确的邮箱格式').max(255, '邮箱最多255个字符').nullable().optional().openapi({
  96. example: 'user@example.com',
  97. description: '邮箱'
  98. }),
  99. nickname: z.string().max(255, '昵称最多255个字符').nullable().optional().openapi({
  100. example: '昵称',
  101. description: '用户昵称'
  102. }),
  103. name: z.string().max(255, '姓名最多255个字符').nullable().optional().openapi({
  104. example: '张三',
  105. description: '真实姓名'
  106. }),
  107. avatarFileId: z.number().int().positive().nullable().optional().openapi({
  108. example: 1,
  109. description: '头像文件ID'
  110. }),
  111. isDisabled: z.number().int().min(0, '状态值只能是0或1').max(1, '状态值只能是0或1').default(DisabledStatus.ENABLED).optional().openapi({
  112. example: DisabledStatus.ENABLED,
  113. description: '是否禁用(0:启用,1:禁用)'
  114. })
  115. });
  116. // 更新用户请求 schema
  117. export const UpdateUserDto = z.object({
  118. username: z.string().min(3, '用户名至少3个字符').max(255, '用户名最多255个字符').optional().openapi({
  119. example: 'admin',
  120. description: '用户名,3-255个字符'
  121. }),
  122. password: z.string().min(6, '密码至少6位').max(255, '密码最多255位').optional().openapi({
  123. example: 'password123',
  124. description: '密码,最少6位'
  125. }),
  126. phone: z.string().max(255, '手机号最多255个字符').nullable().optional().openapi({
  127. example: '13800138000',
  128. description: '手机号'
  129. }),
  130. email: z.email('请输入正确的邮箱格式').max(255, '邮箱最多255个字符').nullable().optional().openapi({
  131. example: 'user@example.com',
  132. description: '邮箱'
  133. }),
  134. nickname: z.string().max(255, '昵称最多255个字符').nullable().optional().openapi({
  135. example: '昵称',
  136. description: '用户昵称'
  137. }),
  138. name: z.string().max(255, '姓名最多255个字符').nullable().optional().openapi({
  139. example: '张三',
  140. description: '真实姓名'
  141. }),
  142. avatarFileId: z.number().int().positive().nullable().optional().openapi({
  143. example: 1,
  144. description: '头像文件ID'
  145. }),
  146. isDisabled: z.number().int().min(0, '状态值只能是0或1').max(1, '状态值只能是0或1').optional().openapi({
  147. example: DisabledStatus.ENABLED,
  148. description: '是否禁用(0:启用,1:禁用)'
  149. })
  150. });
  151. // 用户列表响应 schema
  152. export const UserListResponse = z.object({
  153. data: z.array(UserSchema.omit({ password: true })),
  154. pagination: z.object({
  155. total: z.number().openapi({
  156. example: 100,
  157. description: '总记录数'
  158. }),
  159. current: z.number().openapi({
  160. example: 1,
  161. description: '当前页码'
  162. }),
  163. pageSize: z.number().openapi({
  164. example: 10,
  165. description: '每页数量'
  166. })
  167. })
  168. });
  169. // 单个用户查询响应 schema
  170. export const UserResponseSchema = UserSchema.omit({password:true})
  171. // 类型导出
  172. export type User = z.infer<typeof UserSchema>;
  173. export type CreateUserRequest = z.infer<typeof CreateUserDto>;
  174. export type UpdateUserRequest = z.infer<typeof UpdateUserDto>;
  175. export type UserListResponseType = z.infer<typeof UserListResponse>;