diff --git a/apps/api/src/booking.dto.ts b/apps/api/src/booking.dto.ts new file mode 100644 index 0000000..d5e0421 --- /dev/null +++ b/apps/api/src/booking.dto.ts @@ -0,0 +1,34 @@ +import { Type } from "class-transformer"; +import { + IsArray, + IsIn, + IsInt, + IsISO8601, + IsOptional, + IsString, + Max, + Min, + ValidateNested, +} from "class-validator"; + +export class BookingItemDto { + @IsString() serviceId!: string; + @IsInt() @Min(1) @Max(50) quantity!: number; +} + +export class CreateBookingDto { + @IsIn(["package", "private_chef"]) requestType!: "package" | "private_chef"; + @IsString() customerName!: string; + @IsOptional() @IsString() phone?: string; + @IsOptional() @IsString() wechatId?: string; + @IsIn(["wechat", "phone", "none"]) contactPreference!: "wechat" | "phone" | "none"; + @IsOptional() @IsIn(["pickup", "delivery"]) fulfillmentType?: "pickup" | "delivery"; + @IsOptional() @IsString() serviceId?: string; + @IsOptional() @IsISO8601() requestedDate?: string; + @IsOptional() @IsString() requestedTime?: string; + @IsOptional() @IsString() deliveryAddress?: string; + @IsOptional() @IsString() note?: string; + @IsOptional() extraData?: Record; + @IsArray() @ValidateNested({ each: true }) @Type(() => BookingItemDto) items: BookingItemDto[] = + []; +} diff --git a/apps/api/src/booking.service.ts b/apps/api/src/booking.service.ts index 8bb6506..a80e95f 100644 --- a/apps/api/src/booking.service.ts +++ b/apps/api/src/booking.service.ts @@ -1,9 +1,9 @@ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; -import { BookingRequestType, Prisma } from "@prisma/client"; +import { Prisma } from "@prisma/client"; import { PrismaService } from "./prisma.service.js"; export interface CreateBookingInput { - requestType: BookingRequestType; + requestType: "package" | "private_chef"; customerName: string; phone?: string; wechatId?: string; diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index a00a3f4..7e5c486 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -8,14 +8,16 @@ import { Param, Post, Req, + ValidationPipe, } from "@nestjs/common"; import { NestFactory } from "@nestjs/core"; +import { Prisma } from "@prisma/client"; import { FastifyAdapter } from "@nestjs/platform-fastify"; import type { NestFastifyApplication } from "@nestjs/platform-fastify"; import { PrismaService } from "./prisma.service.js"; import { WechatAuthService } from "./wechat-auth.js"; import { BookingService } from "./booking.service.js"; -import type { CreateBookingInput } from "./booking.service.js"; +import { CreateBookingDto } from "./booking.dto.js"; import type { FastifyRequest } from "fastify"; import { createHmac } from "node:crypto"; @@ -115,13 +117,16 @@ export class CustomerBookingController { ) {} @Post() - async create(@Req() request: FastifyRequest, @Body() body: CreateBookingInput): Promise { + async create(@Req() request: FastifyRequest, @Body() body: CreateBookingDto): Promise { const identity = this.identity(request); const customer = await this.prisma.customerUser.findUnique({ where: { brandId_openid: identity }, }); if (!customer) throw new BadRequestException("请先完成微信登录"); - return this.bookings.create(identity.brandId, customer.id, body); + return this.bookings.create(identity.brandId, customer.id, { + ...body, + extraData: body.extraData as Prisma.InputJsonObject, + }); } @Get() @@ -198,6 +203,9 @@ export class AppModule {} export async function createApp(): Promise { const app = await NestFactory.create(AppModule, new FastifyAdapter()); + app.useGlobalPipes( + new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }), + ); app.enableCors(); return app; }