goods.schema.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import { z } from '@hono/zod-openapi';
  2. import { GoodsCategorySchema } from './goods-category.schema';
  3. import { SupplierSchema } from '@/server/modules/supplier/supplier.schema';
  4. import { FileSchema } from '@/server/modules/files/file.schema';
  5. import { MerchantSchema } from '@/server/modules/merchant/merchant.schema';
  6. export const GoodsSchema = z.object({
  7. id: z.number().int().positive().openapi({ description: '商品ID' }),
  8. name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').openapi({
  9. description: '商品名称',
  10. example: 'iPhone 15'
  11. }),
  12. price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').default(0).openapi({
  13. description: '售卖价',
  14. example: 5999.99
  15. }),
  16. costPrice: z.coerce.number().multipleOf(0.01, '成本价最多保留两位小数').min(0, '成本价不能为负数').default(0).openapi({
  17. description: '成本价',
  18. example: 4999.99
  19. }),
  20. salesNum: z.coerce.number().int().nonnegative('销售数量必须为非负数').default(0).openapi({
  21. description: '销售数量',
  22. example: 100
  23. }),
  24. clickNum: z.coerce.number().int().nonnegative('点击次数必须为非负数').default(0).openapi({
  25. description: '点击次数',
  26. example: 1000
  27. }),
  28. categoryId1: z.number().int().nonnegative('一级类别ID必须为非负数').default(0).openapi({
  29. description: '一级类别id',
  30. example: 1
  31. }),
  32. categoryId2: z.number().int().nonnegative('二级类别ID必须为非负数').default(0).openapi({
  33. description: '二级类别id',
  34. example: 2
  35. }),
  36. categoryId3: z.number().int().nonnegative('三级类别ID必须为非负数').default(0).openapi({
  37. description: '三级类别id',
  38. example: 3
  39. }),
  40. goodsType: z.number().int().min(1).max(2).default(1).openapi({
  41. description: '订单类型 1实物产品 2虚拟产品',
  42. example: 1
  43. }),
  44. supplierId: z.number().int().positive().nullable().openapi({
  45. description: '所属供应商id',
  46. example: 1
  47. }),
  48. merchantId: z.number().int().positive().nullable().openapi({
  49. description: '所属商户id',
  50. example: 1
  51. }),
  52. imageFileId: z.number().int().positive().nullable().openapi({
  53. description: '商品主图文件ID',
  54. example: 1
  55. }),
  56. slideImages: z.array(FileSchema).nullable().optional().openapi({
  57. description: '商品轮播图文件列表',
  58. example: [{
  59. id: 1,
  60. name: 'image1.jpg',
  61. fullUrl: 'https://example.com/image1.jpg',
  62. type: 'image/jpeg',
  63. size: 102400
  64. }]
  65. }),
  66. detail: z.string().nullable().optional().openapi({
  67. description: '商品详情',
  68. example: '这是商品详情内容'
  69. }),
  70. instructions: z.string().max(255, '简介最多255个字符').nullable().optional().openapi({
  71. description: '简介',
  72. example: '高品质智能手机'
  73. }),
  74. sort: z.number().int().nonnegative('排序值必须为非负数').default(0).openapi({
  75. description: '排序',
  76. example: 0
  77. }),
  78. state: z.number().int().min(1).max(2).default(1).openapi({
  79. description: '状态 1可用 2不可用',
  80. example: 1
  81. }),
  82. stock: z.coerce.number().int().nonnegative('库存必须为非负数').default(0).openapi({
  83. description: '库存',
  84. example: 100
  85. }),
  86. spuId: z.number().int().nonnegative('主商品ID必须为非负数').default(0).openapi({
  87. description: '主商品ID',
  88. example: 0
  89. }),
  90. spuName: z.string().max(255, '主商品名称最多255个字符').nullable().optional().openapi({
  91. description: '主商品名称',
  92. example: 'iPhone系列'
  93. }),
  94. lowestBuy: z.number().int().positive('最小起购量必须为正整数').default(1).openapi({
  95. description: '最小起购量',
  96. example: 1
  97. }),
  98. category1: GoodsCategorySchema.nullable().optional().openapi({
  99. description: '一级分类信息'
  100. }),
  101. category2: GoodsCategorySchema.nullable().optional().openapi({
  102. description: '二级分类信息'
  103. }),
  104. category3: GoodsCategorySchema.nullable().optional().openapi({
  105. description: '三级分类信息'
  106. }),
  107. supplier: SupplierSchema.nullable().optional().openapi({
  108. description: '供应商信息'
  109. }),
  110. merchant: MerchantSchema.nullable().optional().openapi({
  111. description: '商户信息'
  112. }),
  113. imageFile: FileSchema.nullable().optional().openapi({
  114. description: '商品主图信息'
  115. }),
  116. createdAt: z.coerce.date().openapi({
  117. description: '创建时间',
  118. example: '2024-01-01T12:00:00Z'
  119. }),
  120. updatedAt: z.coerce.date().openapi({
  121. description: '更新时间',
  122. example: '2024-01-01T12:00:00Z'
  123. }),
  124. createdBy: z.number().int().positive().nullable().openapi({
  125. description: '创建用户ID',
  126. example: 1
  127. }),
  128. updatedBy: z.number().int().positive().nullable().openapi({
  129. description: '更新用户ID',
  130. example: 1
  131. }),
  132. });
  133. export const CreateGoodsDto = z.object({
  134. name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').openapi({
  135. description: '商品名称',
  136. example: 'iPhone 15'
  137. }),
  138. price: z.coerce.number<number>().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').default(0).openapi({
  139. description: '售卖价',
  140. example: 5999.99
  141. }),
  142. costPrice: z.coerce.number<number>().multipleOf(0.01, '成本价最多保留两位小数').min(0, '成本价不能为负数').default(0).openapi({
  143. description: '成本价',
  144. example: 4999.99
  145. }),
  146. categoryId1: z.number().int().nonnegative('一级类别ID必须为非负数').default(0).openapi({
  147. description: '一级类别id',
  148. example: 1
  149. }),
  150. categoryId2: z.number().int().nonnegative('二级类别ID必须为非负数').default(0).openapi({
  151. description: '二级类别id',
  152. example: 2
  153. }),
  154. categoryId3: z.number().int().nonnegative('三级类别ID必须为非负数').default(0).openapi({
  155. description: '三级类别id',
  156. example: 3
  157. }),
  158. goodsType: z.number().int().min(1).max(2).default(1).openapi({
  159. description: '订单类型 1实物产品 2虚拟产品',
  160. example: 1
  161. }),
  162. supplierId: z.number().int().positive().nullable().optional().openapi({
  163. description: '所属供应商id',
  164. example: 1
  165. }),
  166. merchantId: z.number().int().positive().nullable().optional().openapi({
  167. description: '所属商户id',
  168. example: 1
  169. }),
  170. imageFileId: z.number().int().positive().nullable().optional().openapi({
  171. description: '商品主图文件ID',
  172. example: 1
  173. }),
  174. slideImageIds: z.array(z.number().int().positive()).nullable().optional().openapi({
  175. description: '商品轮播图文件ID数组',
  176. example: [1, 2, 3]
  177. }),
  178. detail: z.string().nullable().optional().openapi({
  179. description: '商品详情',
  180. example: '这是商品详情内容'
  181. }),
  182. instructions: z.string().max(255, '简介最多255个字符').nullable().optional().openapi({
  183. description: '简介',
  184. example: '高品质智能手机'
  185. }),
  186. sort: z.number().int().nonnegative('排序值必须为非负数').default(0).openapi({
  187. description: '排序',
  188. example: 0
  189. }),
  190. state: z.number().int().min(1).max(2).default(1).openapi({
  191. description: '状态 1可用 2不可用',
  192. example: 1
  193. }),
  194. stock: z.coerce.number<number>().int().nonnegative('库存必须为非负数').default(0).openapi({
  195. description: '库存',
  196. example: 100
  197. }),
  198. spuId: z.number().int().nonnegative('主商品ID必须为非负数').default(0).openapi({
  199. description: '主商品ID',
  200. example: 0
  201. }),
  202. spuName: z.string().max(255, '主商品名称最多255个字符').nullable().optional().openapi({
  203. description: '主商品名称',
  204. example: 'iPhone系列'
  205. }),
  206. lowestBuy: z.number().int().positive('最小起购量必须为正整数').default(1).openapi({
  207. description: '最小起购量',
  208. example: 1
  209. })
  210. });
  211. export const UpdateGoodsDto = z.object({
  212. name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').optional().openapi({
  213. description: '商品名称',
  214. example: 'iPhone 15'
  215. }),
  216. price: z.coerce.number<number>().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').optional().openapi({
  217. description: '售卖价',
  218. example: 5999.99
  219. }),
  220. costPrice: z.coerce.number<number>().multipleOf(0.01, '成本价最多保留两位小数').min(0, '成本价不能为负数').optional().openapi({
  221. description: '成本价',
  222. example: 4999.99
  223. }),
  224. categoryId1: z.number().int().nonnegative('一级类别ID必须为非负数').optional().openapi({
  225. description: '一级类别id',
  226. example: 1
  227. }),
  228. categoryId2: z.number().int().nonnegative('二级类别ID必须为非负数').optional().openapi({
  229. description: '二级类别id',
  230. example: 2
  231. }),
  232. categoryId3: z.number().int().nonnegative('三级类别ID必须为非负数').optional().openapi({
  233. description: '三级类别id',
  234. example: 3
  235. }),
  236. goodsType: z.number().int().min(1).max(2).optional().openapi({
  237. description: '订单类型 1实物产品 2虚拟产品',
  238. example: 1
  239. }),
  240. supplierId: z.number().int().positive().nullable().optional().openapi({
  241. description: '所属供应商id',
  242. example: 1
  243. }),
  244. merchantId: z.number().int().positive().nullable().optional().openapi({
  245. description: '所属商户id',
  246. example: 1
  247. }),
  248. imageFileId: z.number().int().positive().nullable().optional().openapi({
  249. description: '商品主图文件ID',
  250. example: 1
  251. }),
  252. slideImageIds: z.array(z.number().int().positive()).nullable().optional().openapi({
  253. description: '商品轮播图文件ID数组',
  254. example: [1, 2, 3]
  255. }),
  256. detail: z.string().nullable().optional().openapi({
  257. description: '商品详情',
  258. example: '这是商品详情内容'
  259. }),
  260. instructions: z.string().max(255, '简介最多255个字符').nullable().optional().openapi({
  261. description: '简介',
  262. example: '高品质智能手机'
  263. }),
  264. sort: z.number().int().nonnegative('排序值必须为非负数').optional().openapi({
  265. description: '排序',
  266. example: 0
  267. }),
  268. state: z.number().int().min(1).max(2).optional().openapi({
  269. description: '状态 1可用 2不可用',
  270. example: 1
  271. }),
  272. stock: z.coerce.number<number>().int().nonnegative('库存必须为非负数').optional().openapi({
  273. description: '库存',
  274. example: 100
  275. }),
  276. spuId: z.number().int().nonnegative('主商品ID必须为非负数').optional().openapi({
  277. description: '主商品ID',
  278. example: 0
  279. }),
  280. spuName: z.string().max(255, '主商品名称最多255个字符').nullable().optional().openapi({
  281. description: '主商品名称',
  282. example: 'iPhone系列'
  283. }),
  284. lowestBuy: z.number().int().positive('最小起购量必须为正整数').optional().openapi({
  285. description: '最小起购量',
  286. example: 1
  287. })
  288. });