|
|
@@ -0,0 +1,128 @@
|
|
|
+import { z } from '@hono/zod-openapi';
|
|
|
+
|
|
|
+export enum MembershipType {
|
|
|
+ SINGLE = 'single',
|
|
|
+ MONTHLY = 'monthly',
|
|
|
+ YEARLY = 'yearly',
|
|
|
+ LIFETIME = 'lifetime'
|
|
|
+}
|
|
|
+
|
|
|
+// 基础会员套餐schema
|
|
|
+export const MembershipPlanSchema = z.object({
|
|
|
+ id: z.number().int().positive().openapi({
|
|
|
+ description: '套餐ID',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ name: z.string().min(1, '套餐名称不能为空').max(100, '套餐名称最多100个字符').openapi({
|
|
|
+ description: '套餐名称',
|
|
|
+ example: '月度会员'
|
|
|
+ }),
|
|
|
+ type: z.enum(MembershipType).openapi({
|
|
|
+ description: '套餐类型:single-单次,monthly-单月,yearly-年,lifetime-永久',
|
|
|
+ example: MembershipType.MONTHLY
|
|
|
+ }),
|
|
|
+ price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0, '价格不能小于0').max(999999.99, '价格不能超过999999.99').openapi({
|
|
|
+ description: '价格',
|
|
|
+ example: 29.99
|
|
|
+ }),
|
|
|
+ durationDays: z.coerce.number().int('有效期天数必须是整数').min(0, '有效期天数不能小于0').openapi({
|
|
|
+ description: '有效期天数(永久为0)',
|
|
|
+ example: 30
|
|
|
+ }),
|
|
|
+ description: z.string().max(1000, '套餐描述最多1000个字符').nullable().openapi({
|
|
|
+ description: '套餐描述',
|
|
|
+ example: '月度会员套餐,包含所有基础功能'
|
|
|
+ }),
|
|
|
+ features: z.array(z.string()).openapi({
|
|
|
+ description: '套餐功能列表',
|
|
|
+ example: ['无限制文档处理', '高级AI功能', '优先客服支持']
|
|
|
+ }),
|
|
|
+ isActive: z.coerce.number().int().min(0).max(1).default(1).openapi({
|
|
|
+ description: '是否启用(0-禁用,1-启用)',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ sortOrder: z.coerce.number().int().default(0).openapi({
|
|
|
+ description: '排序',
|
|
|
+ example: 0
|
|
|
+ }),
|
|
|
+ createdAt: z.coerce.date().openapi({
|
|
|
+ description: '创建时间',
|
|
|
+ example: '2025-01-01T00:00:00Z'
|
|
|
+ }),
|
|
|
+ updatedAt: z.coerce.date().openapi({
|
|
|
+ description: '更新时间',
|
|
|
+ example: '2025-01-01T00:00:00Z'
|
|
|
+ })
|
|
|
+});
|
|
|
+
|
|
|
+// 创建会员套餐DTO
|
|
|
+export const CreateMembershipPlanDto = z.object({
|
|
|
+ name: z.string().min(1, '套餐名称不能为空').max(100, '套餐名称最多100个字符').openapi({
|
|
|
+ description: '套餐名称',
|
|
|
+ example: '月度会员'
|
|
|
+ }),
|
|
|
+ type: z.nativeEnum(MembershipType).openapi({
|
|
|
+ description: '套餐类型:single-单次,monthly-单月,yearly-年,lifetime-永久',
|
|
|
+ example: MembershipType.MONTHLY
|
|
|
+ }),
|
|
|
+ price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0.01, '价格必须大于0').max(999999.99, '价格不能超过999999.99').openapi({
|
|
|
+ description: '价格',
|
|
|
+ example: 29.99
|
|
|
+ }),
|
|
|
+ durationDays: z.coerce.number().int('有效期天数必须是整数').min(0, '有效期天数不能小于0').openapi({
|
|
|
+ description: '有效期天数(永久为0)',
|
|
|
+ example: 30
|
|
|
+ }),
|
|
|
+ description: z.string().max(1000, '套餐描述最多1000个字符').nullable().optional().openapi({
|
|
|
+ description: '套餐描述(选填)',
|
|
|
+ example: '月度会员套餐,包含所有基础功能'
|
|
|
+ }),
|
|
|
+ features: z.array(z.string()).default([]).openapi({
|
|
|
+ description: '套餐功能列表',
|
|
|
+ example: ['无限制文档处理', '高级AI功能', '优先客服支持']
|
|
|
+ }),
|
|
|
+ isActive: z.coerce.number().int().min(0).max(1).default(1).optional().openapi({
|
|
|
+ description: '是否启用(0-禁用,1-启用)',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ sortOrder: z.coerce.number().int().default(0).optional().openapi({
|
|
|
+ description: '排序',
|
|
|
+ example: 0
|
|
|
+ })
|
|
|
+});
|
|
|
+
|
|
|
+// 更新会员套餐DTO
|
|
|
+export const UpdateMembershipPlanDto = z.object({
|
|
|
+ name: z.string().min(1, '套餐名称不能为空').max(100, '套餐名称最多100个字符').optional().openapi({
|
|
|
+ description: '套餐名称',
|
|
|
+ example: '更新后的月度会员'
|
|
|
+ }),
|
|
|
+ type: z.enum(MembershipType).optional().openapi({
|
|
|
+ description: '套餐类型:single-单次,monthly-单月,yearly-年,lifetime-永久',
|
|
|
+ example: MembershipType.YEARLY
|
|
|
+ }),
|
|
|
+ price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0.01, '价格必须大于0').max(999999.99, '价格不能超过999999.99').optional().openapi({
|
|
|
+ description: '价格',
|
|
|
+ example: 39.99
|
|
|
+ }),
|
|
|
+ durationDays: z.coerce.number().int('有效期天数必须是整数').min(0, '有效期天数不能小于0').optional().openapi({
|
|
|
+ description: '有效期天数(永久为0)',
|
|
|
+ example: 365
|
|
|
+ }),
|
|
|
+ description: z.string().max(1000, '套餐描述最多1000个字符').nullable().optional().openapi({
|
|
|
+ description: '套餐描述',
|
|
|
+ example: '更新后的套餐描述'
|
|
|
+ }),
|
|
|
+ features: z.array(z.string()).optional().openapi({
|
|
|
+ description: '套餐功能列表',
|
|
|
+ example: ['更新后的功能1', '更新后的功能2']
|
|
|
+ }),
|
|
|
+ isActive: z.coerce.number().int().min(0).max(1).optional().openapi({
|
|
|
+ description: '是否启用(0-禁用,1-启用)',
|
|
|
+ example: 1
|
|
|
+ }),
|
|
|
+ sortOrder: z.coerce.number().int().optional().openapi({
|
|
|
+ description: '排序',
|
|
|
+ example: 1
|
|
|
+ })
|
|
|
+});
|