|
|
@@ -0,0 +1,257 @@
|
|
|
+import { z } from '@hono/zod-openapi';
|
|
|
+import { GoodsCategorySchema } from './goods-category.schema';
|
|
|
+import { SupplierSchema } from '@/server/modules/supplier/supplier.schema';
|
|
|
+import { FileSchema } from '@/server/modules/files/file.schema';
|
|
|
+
|
|
|
+const SlideImagesSchema = z.array(z.string()).nullable().optional().openapi({
|
|
|
+ description: '商品轮播图URL数组',
|
|
|
+ example: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg']
|
|
|
+});
|
|
|
+
|
|
|
+export const GoodsSchema = z.object({
|
|
|
+ id: z.number().int().positive().openapi({ description: '商品ID' }),
|
|
|
+ name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').openapi({
|
|
|
+ description: '商品名称',
|
|
|
+ example: 'iPhone 15'
|
|
|
+ }),
|
|
|
+ price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').default(0).openapi({
|
|
|
+ description: '售卖价',
|
|
|
+ example: 5999.99
|
|
|
+ }),
|
|
|
+ costPrice: z.coerce.number().multipleOf(0.01, '成本价最多保留两位小数').min(0, '成本价不能为负数').default(0).openapi({
|
|
|
+ description: '成本价',
|
|
|
+ example: 4999.99
|
|
|
+ }),
|
|
|
+ salesNum: z.coerce.number().int().nonnegative('销售数量必须为非负数').default(0).openapi({
|
|
|
+ description: '销售数量',
|
|
|
+ example: 100
|
|
|
+ }),
|
|
|
+ clickNum: z.coerce.number().int().nonnegative('点击次数必须为非负数').default(0).openapi({
|
|
|
+ description: '点击次数',
|
|
|
+ example: 1000
|
|
|
+ }),
|
|
|
+ categoryId1: z.number().int().nonnegative('一级类别ID必须为非负数').default(0).openapi({
|
|
|
+ description: '一级类别id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ categoryId2: z.number().int().nonnegative('二级类别ID必须为非负数').default(0).openapi({
|
|
|
+ description: '二级类别id',
|
|
|
+ example: 2
|
|
|
+ }),
|
|
|
+ categoryId3: z.number().int().nonnegative('三级类别ID必须为非负数').default(0).openapi({
|
|
|
+ description: '三级类别id',
|
|
|
+ example: 3
|
|
|
+ }),
|
|
|
+ goodsType: z.number().int().min(1).max(2).default(1).openapi({
|
|
|
+ description: '订单类型 1实物产品 2虚拟产品',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ supplierId: z.number().int().positive().nullable().openapi({
|
|
|
+ description: '所属供应商id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ imageFileId: z.number().int().positive().nullable().openapi({
|
|
|
+ description: '商品主图文件ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ slideImages: SlideImagesSchema,
|
|
|
+ detail: z.string().nullable().optional().openapi({
|
|
|
+ description: '商品详情',
|
|
|
+ example: '这是商品详情内容'
|
|
|
+ }),
|
|
|
+ instructions: z.string().max(255, '简介最多255个字符').nullable().optional().openapi({
|
|
|
+ description: '简介',
|
|
|
+ example: '高品质智能手机'
|
|
|
+ }),
|
|
|
+ sort: z.number().int().nonnegative('排序值必须为非负数').default(0).openapi({
|
|
|
+ description: '排序',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ state: z.number().int().min(1).max(2).default(1).openapi({
|
|
|
+ description: '状态 1可用 2不可用',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ stock: z.coerce.number().int().nonnegative('库存必须为非负数').default(0).openapi({
|
|
|
+ description: '库存',
|
|
|
+ example: 100
|
|
|
+ }),
|
|
|
+ spuId: z.number().int().nonnegative('主商品ID必须为非负数').default(0).openapi({
|
|
|
+ description: '主商品ID',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ spuName: z.string().max(255, '主商品名称最多255个字符').nullable().optional().openapi({
|
|
|
+ description: '主商品名称',
|
|
|
+ example: 'iPhone系列'
|
|
|
+ }),
|
|
|
+ lowestBuy: z.number().int().positive('最小起购量必须为正整数').default(1).openapi({
|
|
|
+ description: '最小起购量',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ category1: GoodsCategorySchema.nullable().optional().openapi({
|
|
|
+ description: '一级分类信息'
|
|
|
+ }),
|
|
|
+ category2: GoodsCategorySchema.nullable().optional().openapi({
|
|
|
+ description: '二级分类信息'
|
|
|
+ }),
|
|
|
+ category3: GoodsCategorySchema.nullable().optional().openapi({
|
|
|
+ description: '三级分类信息'
|
|
|
+ }),
|
|
|
+ supplier: SupplierSchema.nullable().optional().openapi({
|
|
|
+ description: '供应商信息'
|
|
|
+ }),
|
|
|
+ imageFile: FileSchema.nullable().optional().openapi({
|
|
|
+ description: '商品主图信息'
|
|
|
+ }),
|
|
|
+ createdAt: z.coerce.date().openapi({
|
|
|
+ description: '创建时间',
|
|
|
+ example: '2024-01-01T12:00:00Z'
|
|
|
+ }),
|
|
|
+ updatedAt: z.coerce.date().openapi({
|
|
|
+ description: '更新时间',
|
|
|
+ example: '2024-01-01T12:00:00Z'
|
|
|
+ })
|
|
|
+});
|
|
|
+
|
|
|
+export const CreateGoodsDto = z.object({
|
|
|
+ name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').openapi({
|
|
|
+ description: '商品名称',
|
|
|
+ example: 'iPhone 15'
|
|
|
+ }),
|
|
|
+ price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').default(0).openapi({
|
|
|
+ description: '售卖价',
|
|
|
+ example: 5999.99
|
|
|
+ }),
|
|
|
+ costPrice: z.coerce.number().multipleOf(0.01, '成本价最多保留两位小数').min(0, '成本价不能为负数').default(0).openapi({
|
|
|
+ description: '成本价',
|
|
|
+ example: 4999.99
|
|
|
+ }),
|
|
|
+ categoryId1: z.number().int().nonnegative('一级类别ID必须为非负数').default(0).openapi({
|
|
|
+ description: '一级类别id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ categoryId2: z.number().int().nonnegative('二级类别ID必须为非负数').default(0).openapi({
|
|
|
+ description: '二级类别id',
|
|
|
+ example: 2
|
|
|
+ }),
|
|
|
+ categoryId3: z.number().int().nonnegative('三级类别ID必须为非负数').default(0).openapi({
|
|
|
+ description: '三级类别id',
|
|
|
+ example: 3
|
|
|
+ }),
|
|
|
+ goodsType: z.number().int().min(1).max(2).default(1).openapi({
|
|
|
+ description: '订单类型 1实物产品 2虚拟产品',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ supplierId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '所属供应商id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ imageFileId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '商品主图文件ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ slideImages: SlideImagesSchema,
|
|
|
+ detail: z.string().nullable().optional().openapi({
|
|
|
+ description: '商品详情',
|
|
|
+ example: '这是商品详情内容'
|
|
|
+ }),
|
|
|
+ instructions: z.string().max(255, '简介最多255个字符').nullable().optional().openapi({
|
|
|
+ description: '简介',
|
|
|
+ example: '高品质智能手机'
|
|
|
+ }),
|
|
|
+ sort: z.number().int().nonnegative('排序值必须为非负数').default(0).openapi({
|
|
|
+ description: '排序',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ state: z.number().int().min(1).max(2).default(1).openapi({
|
|
|
+ description: '状态 1可用 2不可用',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ stock: z.coerce.number().int().nonnegative('库存必须为非负数').default(0).openapi({
|
|
|
+ description: '库存',
|
|
|
+ example: 100
|
|
|
+ }),
|
|
|
+ spuId: z.number().int().nonnegative('主商品ID必须为非负数').default(0).openapi({
|
|
|
+ description: '主商品ID',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ spuName: z.string().max(255, '主商品名称最多255个字符').nullable().optional().openapi({
|
|
|
+ description: '主商品名称',
|
|
|
+ example: 'iPhone系列'
|
|
|
+ }),
|
|
|
+ lowestBuy: z.number().int().positive('最小起购量必须为正整数').default(1).openapi({
|
|
|
+ description: '最小起购量',
|
|
|
+ example: 1
|
|
|
+ })
|
|
|
+});
|
|
|
+
|
|
|
+export const UpdateGoodsDto = z.object({
|
|
|
+ name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').optional().openapi({
|
|
|
+ description: '商品名称',
|
|
|
+ example: 'iPhone 15'
|
|
|
+ }),
|
|
|
+ price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').optional().openapi({
|
|
|
+ description: '售卖价',
|
|
|
+ example: 5999.99
|
|
|
+ }),
|
|
|
+ costPrice: z.coerce.number().multipleOf(0.01, '成本价最多保留两位小数').min(0, '成本价不能为负数').optional().openapi({
|
|
|
+ description: '成本价',
|
|
|
+ example: 4999.99
|
|
|
+ }),
|
|
|
+ categoryId1: z.number().int().nonnegative('一级类别ID必须为非负数').optional().openapi({
|
|
|
+ description: '一级类别id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ categoryId2: z.number().int().nonnegative('二级类别ID必须为非负数').optional().openapi({
|
|
|
+ description: '二级类别id',
|
|
|
+ example: 2
|
|
|
+ }),
|
|
|
+ categoryId3: z.number().int().nonnegative('三级类别ID必须为非负数').optional().openapi({
|
|
|
+ description: '三级类别id',
|
|
|
+ example: 3
|
|
|
+ }),
|
|
|
+ goodsType: z.number().int().min(1).max(2).optional().openapi({
|
|
|
+ description: '订单类型 1实物产品 2虚拟产品',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ supplierId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '所属供应商id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ imageFileId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '商品主图文件ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ slideImages: SlideImagesSchema,
|
|
|
+ detail: z.string().nullable().optional().openapi({
|
|
|
+ description: '商品详情',
|
|
|
+ example: '这是商品详情内容'
|
|
|
+ }),
|
|
|
+ instructions: z.string().max(255, '简介最多255个字符').nullable().optional().openapi({
|
|
|
+ description: '简介',
|
|
|
+ example: '高品质智能手机'
|
|
|
+ }),
|
|
|
+ sort: z.number().int().nonnegative('排序值必须为非负数').optional().openapi({
|
|
|
+ description: '排序',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ state: z.number().int().min(1).max(2).optional().openapi({
|
|
|
+ description: '状态 1可用 2不可用',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ stock: z.coerce.number().int().nonnegative('库存必须为非负数').optional().openapi({
|
|
|
+ description: '库存',
|
|
|
+ example: 100
|
|
|
+ }),
|
|
|
+ spuId: z.number().int().nonnegative('主商品ID必须为非负数').optional().openapi({
|
|
|
+ description: '主商品ID',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ spuName: z.string().max(255, '主商品名称最多255个字符').nullable().optional().openapi({
|
|
|
+ description: '主商品名称',
|
|
|
+ example: 'iPhone系列'
|
|
|
+ }),
|
|
|
+ lowestBuy: z.number().int().positive('最小起购量必须为正整数').optional().openapi({
|
|
|
+ description: '最小起购量',
|
|
|
+ example: 1
|
|
|
+ })
|
|
|
+});
|