admin-goods.schema.ts 11 KB

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