|
|
@@ -0,0 +1,308 @@
|
|
|
+import { z } from '@hono/zod-openapi';
|
|
|
+import { GoodsCategorySchema } from './goods-category.schema.js';
|
|
|
+import { SupplierSchema } from '@d8d/supplier-module';
|
|
|
+import { FileSchema } from '@d8d/file-module';
|
|
|
+import { MerchantSchema } from '@d8d/merchant-module';
|
|
|
+
|
|
|
+// 管理员专用商品Schema - 保留完整权限字段
|
|
|
+export const AdminGoodsSchema = 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
|
|
|
+ }),
|
|
|
+ merchantId: z.number().int().positive().nullable().openapi({
|
|
|
+ description: '所属商户id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ imageFileId: z.number().int().positive().nullable().openapi({
|
|
|
+ description: '商品主图文件ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ slideImages: z.array(FileSchema).nullable().optional().openapi({
|
|
|
+ description: '商品轮播图文件列表',
|
|
|
+ example: [{
|
|
|
+ id: 1,
|
|
|
+ name: 'image1.jpg',
|
|
|
+ fullUrl: 'https://example.com/image1.jpg',
|
|
|
+ type: 'image/jpeg',
|
|
|
+ size: 102400
|
|
|
+ }]
|
|
|
+ }),
|
|
|
+ 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: '供应商信息'
|
|
|
+ }),
|
|
|
+ merchant: MerchantSchema.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'
|
|
|
+ }),
|
|
|
+ createdBy: z.number().int().positive().nullable().openapi({
|
|
|
+ description: '创建用户ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ updatedBy: z.number().int().positive().nullable().openapi({
|
|
|
+ description: '更新用户ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+});
|
|
|
+
|
|
|
+// 管理员创建商品DTO - 保留完整权限字段
|
|
|
+export const AdminCreateGoodsDto = z.object({
|
|
|
+ name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').openapi({
|
|
|
+ description: '商品名称',
|
|
|
+ example: 'iPhone 15'
|
|
|
+ }),
|
|
|
+ price: z.coerce.number<number>().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').default(0).openapi({
|
|
|
+ description: '售卖价',
|
|
|
+ example: 5999.99
|
|
|
+ }),
|
|
|
+ costPrice: z.coerce.number<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
|
|
|
+ }),
|
|
|
+ merchantId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '所属商户id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ imageFileId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '商品主图文件ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ slideImageIds: z.array(z.number().int().positive()).nullable().optional().openapi({
|
|
|
+ description: '商品轮播图文件ID数组',
|
|
|
+ example: [1, 2, 3]
|
|
|
+ }),
|
|
|
+ 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<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
|
|
|
+ }),
|
|
|
+ // 管理员可以指定创建人和更新人
|
|
|
+ createdBy: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '创建用户ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ updatedBy: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '更新用户ID',
|
|
|
+ example: 1
|
|
|
+ })
|
|
|
+});
|
|
|
+
|
|
|
+// 管理员更新商品DTO - 保留完整权限字段
|
|
|
+export const AdminUpdateGoodsDto = z.object({
|
|
|
+ name: z.string().min(1, '商品名称不能为空').max(255, '商品名称最多255个字符').optional().openapi({
|
|
|
+ description: '商品名称',
|
|
|
+ example: 'iPhone 15'
|
|
|
+ }),
|
|
|
+ price: z.coerce.number<number>().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能为负数').optional().openapi({
|
|
|
+ description: '售卖价',
|
|
|
+ example: 5999.99
|
|
|
+ }),
|
|
|
+ costPrice: z.coerce.number<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
|
|
|
+ }),
|
|
|
+ merchantId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '所属商户id',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ imageFileId: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '商品主图文件ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ slideImageIds: z.array(z.number().int().positive()).nullable().optional().openapi({
|
|
|
+ description: '商品轮播图文件ID数组',
|
|
|
+ example: [1, 2, 3]
|
|
|
+ }),
|
|
|
+ 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<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
|
|
|
+ }),
|
|
|
+ // 管理员可以指定更新人
|
|
|
+ updatedBy: z.number().int().positive().nullable().optional().openapi({
|
|
|
+ description: '更新用户ID',
|
|
|
+ example: 1
|
|
|
+ })
|
|
|
+});
|