-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from erik1110/feature/api
[feat] add order and room api
- Loading branch information
Showing
18 changed files
with
990 additions
and
440 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { ValidationOptions, registerDecorator, ValidationArguments } from "class-validator"; | ||
|
||
export function IsNotBeforeToday(validationOptions?: ValidationOptions): PropertyDecorator { | ||
return function (object: Record<string, any>, propertyName: string) { | ||
registerDecorator({ | ||
name: 'isNotBeforeToday', | ||
target: object.constructor, | ||
propertyName: propertyName, | ||
options: validationOptions, | ||
validator: { | ||
validate(value: any): boolean { | ||
const today = new Date(); | ||
today.setHours(0, 0, 0, 0); | ||
const inputDate = new Date(value); | ||
inputDate.setHours(0, 0, 0, 0); | ||
return inputDate >= today; | ||
}, | ||
}, | ||
}); | ||
}; | ||
} | ||
|
||
|
||
export function IsBefore(property: string, validationOptions?: ValidationOptions) { | ||
return function (object: Record<string, any>, propertyName: string) { | ||
registerDecorator({ | ||
name: 'isBefore', | ||
target: object.constructor, | ||
propertyName: propertyName, | ||
constraints: [property], | ||
options: validationOptions, | ||
validator: { | ||
validate(value: any, args: ValidationArguments): boolean { | ||
const relatedValue = args.object[property]; | ||
if (value instanceof Date && relatedValue instanceof Date) { | ||
return value > relatedValue; | ||
} | ||
return false; | ||
}, | ||
}, | ||
}); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { Transform, Type } from 'class-transformer'; | ||
import { IsDate, IsNotEmpty, Matches, ValidateNested, ValidationOptions, registerDecorator } from 'class-validator'; | ||
import { Schema } from 'mongoose'; | ||
import { UserDto } from './user.dto'; | ||
import { IsBefore, IsNotBeforeToday } from 'src/common/decorator/validation/dto.decorator'; | ||
|
||
export class CreateOrderDto { | ||
@ApiProperty({ | ||
example: '6579516fcd9cb68b22599e9e', | ||
description: 'roomId', | ||
}) | ||
@IsNotEmpty({ message: 'roomId 未填寫' }) | ||
roomId: Schema.Types.ObjectId; | ||
|
||
@ApiProperty({ | ||
example: '2024/06/18', | ||
description: 'checkInDate', | ||
}) | ||
@IsNotEmpty() | ||
@Transform(({ value }) => new Date(value)) | ||
@IsNotBeforeToday({ message: 'checkInDate 不得小於今天' }) | ||
@IsDate({ message: 'checkInDate 格式不正確' }) | ||
checkInDate: Date; | ||
|
||
@ApiProperty({ | ||
example: '2024/06/20', | ||
description: 'checkOutDate', | ||
}) | ||
@IsNotEmpty() | ||
@Transform(({ value }) => new Date(value)) | ||
@IsDate({ message: 'checkOutDate 格式不正確' }) | ||
@IsBefore('checkInDate', { message: 'checkInDate 不得大於等於 checkOutDate' }) | ||
checkOutDate: Date; | ||
|
||
@ApiProperty({ | ||
example: 2, | ||
description: 'peopleNum', | ||
}) | ||
@IsNotEmpty({ message: 'peopleNum 未填寫' }) | ||
peopleNum: number; | ||
|
||
@ApiProperty({ | ||
type: UserDto, | ||
description: 'userInfo', | ||
}) | ||
@ValidateNested({ each: true }) | ||
@Type(() => UserDto) | ||
userInfo: UserDto; | ||
|
||
} | ||
|
||
export class CreateOrderSuccessDto { | ||
@ApiProperty({ example: true }) | ||
status: boolean; | ||
|
||
@ApiProperty({ example: '新增訂單' }) | ||
message: string; | ||
|
||
@ApiProperty({ | ||
example: { | ||
userInfo: { | ||
address: { | ||
zipcode: 100, | ||
county: '中正區', | ||
city: '臺北市', | ||
}, | ||
name: 'john', | ||
phone: '0912345678', | ||
email: '[email protected]', | ||
}, | ||
_id: '6579516fcd9cb68b22599e9e', | ||
roomId: '6593d28d455420d4f19b23f8', | ||
checkInDate: '2023-06-17T16:00:00.000Z', | ||
checkOutDate: '2023-06-18T16:00:00.000Z', | ||
peopleNum: 2, | ||
orderUserId: '658b9367df4b59a38f24e143', | ||
createdAt: '2023-12-27T03:00:55.922Z', | ||
updatedAt: '2023-12-28T04:01:21.006Z', | ||
}, | ||
}) | ||
data: object; | ||
} | ||
|
||
export class GetOrderSuccessDto { | ||
@ApiProperty({ example: true }) | ||
status: boolean; | ||
|
||
@ApiProperty({ example: '取得所有訂單' }) | ||
message: string; | ||
|
||
@ApiProperty({ | ||
example: [ | ||
{ | ||
userInfo: { | ||
address: { | ||
zipcode: 100, | ||
county: '中正區', | ||
city: '臺北市', | ||
}, | ||
name: 'john', | ||
phone: '0912345678', | ||
email: '[email protected]', | ||
}, | ||
_id: '6579516fcd9cb68b22599e9e', | ||
roomId: '6593d28d455420d4f19b23f8', | ||
checkInDate: '2023-06-17T16:00:00.000Z', | ||
checkOutDate: '2023-06-18T16:00:00.000Z', | ||
peopleNum: 2, | ||
orderUserId: '658b9367df4b59a38f24e143', | ||
createdAt: '2023-12-27T03:00:55.922Z', | ||
updatedAt: '2023-12-28T04:01:21.006Z', | ||
status: 1, | ||
}, | ||
], | ||
}) | ||
data: object; | ||
} | ||
|
||
export class UpdateOrderSuccessDto { | ||
@ApiProperty({ example: true }) | ||
status: boolean; | ||
|
||
@ApiProperty({ example: '更新訂單' }) | ||
message: string; | ||
|
||
@ApiProperty({ | ||
example: { | ||
userInfo: { | ||
address: { | ||
zipcode: 100, | ||
county: '中正區', | ||
city: '臺北市', | ||
}, | ||
name: 'john', | ||
phone: '0912345678', | ||
email: '[email protected]', | ||
}, | ||
_id: '6579516fcd9cb68b22599e9e', | ||
roomId: '6593d28d455420d4f19b23f8', | ||
checkInDate: '2023-06-17T16:00:00.000Z', | ||
checkOutDate: '2023-06-18T16:00:00.000Z', | ||
peopleNum: 2, | ||
orderUserId: '658b9367df4b59a38f24e143', | ||
createdAt: '2023-12-27T03:00:55.922Z', | ||
updatedAt: '2023-12-28T04:01:21.006Z', | ||
}, | ||
}) | ||
data: object; | ||
} | ||
|
||
export class DeleteOrderSuccessDto { | ||
@ApiProperty({ example: true }) | ||
status: boolean; | ||
|
||
@ApiProperty({ example: '刪除訂單' }) | ||
message: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { Type } from 'class-transformer'; | ||
import { IsEmail, IsNotEmpty, IsString, Matches, MaxLength, MinLength, ValidateNested } from 'class-validator'; | ||
import { AddressDto } from 'src/features/user/dto/address'; | ||
|
||
export class UserDto { | ||
|
||
@ApiProperty({ | ||
type: AddressDto, | ||
description: 'Address', | ||
}) | ||
@ValidateNested({ each: true }) | ||
@Type(() => AddressDto) | ||
address: AddressDto; | ||
|
||
@ApiProperty({ | ||
example: 'john', | ||
description: 'Name', | ||
format: 'string', | ||
minLength: 2, | ||
maxLength: 255, | ||
}) | ||
@IsNotEmpty() | ||
@IsString() | ||
@MinLength(2) | ||
@MaxLength(255) | ||
readonly name: string; | ||
|
||
@ApiProperty({ | ||
example: '[email protected]', | ||
description: 'Email', | ||
format: 'email', | ||
uniqueItems: true, | ||
minLength: 5, | ||
maxLength: 255, | ||
}) | ||
@IsNotEmpty() | ||
@IsString() | ||
@MinLength(5) | ||
@MaxLength(255) | ||
@IsEmail() | ||
readonly email: string; | ||
|
||
@ApiProperty({ | ||
example: '0912345678', | ||
description: 'Phone', | ||
}) | ||
@IsNotEmpty() | ||
@Matches(/^09\d{8}$/, { message: 'Invalid Phone' }) | ||
phone: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Schema, Document } from 'mongoose'; | ||
|
||
export interface IOrder extends Document { | ||
roomId: Schema.Types.ObjectId; | ||
checkInDate: Date, | ||
checkOutDate: Date; | ||
peopleNum: number; | ||
orderUserId: Schema.Types.ObjectId; | ||
userInfo: { | ||
name: string; | ||
phone: string; | ||
email: string; | ||
address: { | ||
zipcode: number; | ||
county: string; | ||
city: string; | ||
}; | ||
}; | ||
// 可使用:1,已刪除:-1 | ||
status: number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { OrderController } from './order.controller'; | ||
|
||
describe('OrderController', () => { | ||
let controller: OrderController; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [OrderController], | ||
}).compile(); | ||
|
||
controller = module.get<OrderController>(OrderController); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Req, UseGuards } from '@nestjs/common'; | ||
import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; | ||
import { RolesGuard } from 'src/auth/guards/roles.guard'; | ||
import { ApiErrorDecorator } from 'src/common/decorator/error/error.decorator'; | ||
import { OrderService } from './order.service'; | ||
import { Roles } from 'src/auth/decorators/roles.decorator'; | ||
import { CreateOrderDto, CreateOrderSuccessDto, DeleteOrderSuccessDto, GetOrderSuccessDto } from './dto/order.dto'; | ||
import { AuthGuard } from '@nestjs/passport'; | ||
import { IsObjectIdPipe } from 'nestjs-object-id'; | ||
import { DeleteRoomSuccessDto } from '../room/dto/room.dto'; | ||
|
||
|
||
@ApiTags('Orders - 訂單') | ||
@ApiErrorDecorator( | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
'CriticalError', | ||
'系統錯誤,請洽系統管理員', | ||
) | ||
@UseGuards(AuthGuard('jwt')) | ||
@ApiBearerAuth() | ||
@Controller('/api/v1/orders') | ||
export class OrderController { | ||
constructor(private readonly orderService: OrderService) {} | ||
|
||
|
||
@Get('') | ||
@HttpCode(HttpStatus.OK) | ||
@ApiOperation({ summary: '取得自己的訂單列表 Get My Orders' }) | ||
@ApiOkResponse({ type: GetOrderSuccessDto }) | ||
async getMyOrders(@Req() req: Request) { | ||
return await this.orderService.getMyOrders(req); | ||
} | ||
|
||
@Post('') | ||
@HttpCode(HttpStatus.OK) | ||
@ApiOperation({ summary: '新增訂單 Add an order' }) | ||
@ApiOkResponse({ type: CreateOrderSuccessDto }) | ||
async addOrder(@Req() req: Request, @Body() createOrderDto: CreateOrderDto) { | ||
return await this.orderService.createOrder(req, createOrderDto); | ||
} | ||
|
||
@Get(':id') | ||
@HttpCode(HttpStatus.OK) | ||
@ApiOperation({ summary: '取得自己訂單詳細資料 Get My Orders Detail ' }) | ||
@ApiOkResponse({ type: GetOrderSuccessDto }) | ||
async getMyOrderDetail( | ||
@Param('id', IsObjectIdPipe) id: string, | ||
@Req() req: Request) { | ||
return await this.orderService.getMyOrderDetail(id, req); | ||
} | ||
|
||
@Delete(':id') | ||
@HttpCode(HttpStatus.OK) | ||
@ApiOperation({ summary: '刪除自己訂單 Delete My order' }) | ||
@ApiOkResponse({ type: DeleteOrderSuccessDto }) | ||
async deleteNews( | ||
@Param('id', IsObjectIdPipe) id: string, | ||
@Req() req: Request, | ||
) { | ||
return await this.orderService.deleteMyOrder(id, req); | ||
} | ||
} | ||
|
||
|
||
@ApiTags('Admin/Orders - 訂單管理') | ||
@UseGuards(RolesGuard) | ||
@ApiBearerAuth() | ||
@ApiErrorDecorator(HttpStatus.FORBIDDEN, 'ForbiddenException', 'Forbidden') | ||
@ApiErrorDecorator( | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
'CriticalError', | ||
'系統錯誤,請洽系統管理員', | ||
) | ||
@Controller('/api/v1/admin/orders') | ||
export class OrderAdminController { | ||
constructor(private readonly orderService: OrderService) {} | ||
|
||
@Get('') | ||
@Roles('admin') | ||
@HttpCode(HttpStatus.OK) | ||
@ApiOperation({ summary: '取得所有訂單 Get all orders' }) | ||
@ApiOkResponse({ type: GetOrderSuccessDto }) | ||
async getallNews(@Req() req: Request) { | ||
return await this.orderService.getallOrders(req); | ||
} | ||
} |
Oops, something went wrong.