Skip to content

Commit

Permalink
Merge pull request #8 from erik1110/feature/api
Browse files Browse the repository at this point in the history
[feat] add order and room api
  • Loading branch information
erik1110 committed Jan 4, 2024
2 parents 3024264 + aa8d439 commit 7772ab5
Show file tree
Hide file tree
Showing 18 changed files with 990 additions and 440 deletions.
3 changes: 3 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import { NewsModule } from './features/news/news.module';
import { CulinaryModule } from './features/culinary/culinary.module';
import { AppController } from './app.controller';
import { RoomModule } from './features/room/room.module';
import { OrderService } from './features/order/order.service';
import { OrderModule } from './features/order/order.module';

@Module({
imports: [
ConfigModule.forRoot(),
MongooseModule.forRoot(process.env.MONGO_URI),
UserModule,
OrderModule,
NewsModule,
CulinaryModule,
RoomModule,
Expand Down
43 changes: 43 additions & 0 deletions src/common/decorator/validation/dto.decorator.ts
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;
},
},
});
};
}
158 changes: 158 additions & 0 deletions src/features/order/dto/order.dto.ts
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;
}
51 changes: 51 additions & 0 deletions src/features/order/dto/user.dto.ts
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;
}
21 changes: 21 additions & 0 deletions src/features/order/interfaces/order.interface.ts
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;
}
18 changes: 18 additions & 0 deletions src/features/order/order.controller.spec.ts
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();
});
});
86 changes: 86 additions & 0 deletions src/features/order/order.controller.ts
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);
}
}
Loading

0 comments on commit 7772ab5

Please sign in to comment.