Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

restaurants api 추가 #15

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

generator client {
provider = "prisma-client-js"
binaryTargets = ["windows", "darwin", "debian-openssl-1.1.x"]
binaryTargets = ["windows", "darwin", "debian-openssl-1.1.x", "darwin-arm64"]
}

datasource db {
Expand All @@ -13,14 +13,13 @@ datasource db {

model User {
id BigInt @id @default(autoincrement())
email String @unique
email String? @unique
password String?
provider Provider @default(KAKAO)
oauthId String? @unique
nickname String
defaultPhotoId Int @db.TinyInt // 1~6
userPhotoUuid String?
birthYear DateTime?
birthYear Int?
gender Int? @db.TinyInt
isActivated Boolean
createdAt DateTime @default(now())
Expand Down Expand Up @@ -88,7 +87,7 @@ model Registration {
}

model Category {
id BigInt @id @default(autoincrement())
id Int @id @default(autoincrement())
name String
Restaurants Restaurant[]
}
Expand All @@ -115,14 +114,14 @@ model Restaurant {
VoteItems VoteItem[]
userId BigInt
groupId BigInt
categoryId BigInt
categoryId Int
user User @relation(fields: [userId], references: [id])
group Group @relation(fields: [groupId], references: [id])
category Category @relation(fields: [categoryId], references: [id])
}

model Tag {
id BigInt @id @default(autoincrement())
id Int @id @default(autoincrement())
name String
RestaurantTagAs RestaurantTagA[]
}
Expand All @@ -133,7 +132,7 @@ model RestaurantTagA {
updatedAt DateTime @default(now()) @updatedAt
deletedAt DateTime?
restaurantId String
tagId BigInt
tagId Int
restaurant Restaurant @relation(fields: [restaurantId], references: [id])
tag Tag @relation(fields: [tagId], references: [id])
}
Expand Down
3 changes: 2 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { AppConfigModule } from "./config/app/config.module";
import { AuthModule } from "./modules/auth/auth.module";
import { RestaurantsModule } from "./modules/restaurants/restaurants.module";

@Module({
imports: [AppConfigModule, AuthModule],
imports: [AppConfigModule, AuthModule, RestaurantsModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
25 changes: 25 additions & 0 deletions src/common/dtos/page-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsInt, IsPositive } from "class-validator";

export class PageQueryDto {
@ApiProperty({ description: "페이지 번호" })
@Type(() => Number)
@IsInt()
@IsPositive()
pageNumber: number;

@ApiProperty({ description: "페이지 크기" })
@Type(() => Number)
@IsInt()
@IsPositive()
pageSize: number;

getOffset(): number {
return (+this.pageNumber - 1) * +this.pageSize;
}

getLimit(): number {
return +this.pageSize;
}
}
22 changes: 22 additions & 0 deletions src/common/dtos/page-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ApiProperty } from "@nestjs/swagger";

export class PageResponseDto<T> {
@ApiProperty({ description: "페이지 번호" })
pageNumber: number;
@ApiProperty({ description: "페이지 크기" })
pageSize: number;
@ApiProperty({ description: "콘텐츠 총 개수" })
totalCount: number;
@ApiProperty({ description: "전체 페이지 수" })
totalPages: number;
@ApiProperty({ description: "콘텐츠" })
content: T[];

constructor(pageNumber: number, pageSize: number, totalCount: number, content: T[]) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.totalCount = totalCount;
this.totalPages = Math.ceil(totalCount / pageSize);
this.content = content;
}
}
11 changes: 11 additions & 0 deletions src/common/pipes/pipes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { BadRequestException, Injectable, PipeTransform } from "@nestjs/common";

@Injectable()
export class ParseBigIntPipe implements PipeTransform {
async transform(value: string) {
if (!/^\d+$/.test(value)) {
throw new BadRequestException("형변환이 불가능한 형식입니다.");
}
return BigInt(value);
}
}
18 changes: 18 additions & 0 deletions src/common/utils/soft-delete-records.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { InternalServerErrorException } from "@nestjs/common";

export async function softDeleteRecords(model, filter) {
try {
await model.updateMany({
where: {
...filter,
deletedAt: null,
},
data: {
deletedAt: new Date(),
},
});
} catch (e) {
console.log(e);
throw new InternalServerErrorException("데이터 삭제를 실패했습니다.");
}
}
9 changes: 9 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AppModule } from "./app.module";
import { winstonLogger } from "./config/logger/winston/logger";
import { PrismaService } from "./config/database/prisma.service";
import * as cookieParser from "cookie-parser";
import { ValidationPipe } from "@nestjs/common";

async function bootstrap() {
const app = await NestFactory.create(AppModule, {
Expand All @@ -25,6 +26,14 @@ async function bootstrap() {
app.use(cookieParser());
const prismaService = app.get(PrismaService);

app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
skipMissingProperties: true,
})
);

await app.listen(appConfig.get("app.port"));
}
bootstrap();
2 changes: 1 addition & 1 deletion src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, Get, Query, Res, UseGuards } from "@nestjs/common";
import { Controller, Get, Query, Res } from "@nestjs/common";
import { AuthService } from "./auth.service";
import { Response } from "express";
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
Expand Down
1 change: 1 addition & 0 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import { CacheModule } from "@nestjs/cache-manager";
imports: [CacheModule.register()],
controllers: [AuthController],
providers: [AuthService, Oauth2Strategy],
exports: [Oauth2Strategy],
})
export class AuthModule {}
98 changes: 98 additions & 0 deletions src/modules/restaurants/dtos/create-restaurant.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import {
ArrayNotEmpty,
IsArray,
IsBoolean,
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
IsString,
IsUUID,
ValidateNested,
} from "class-validator";

class File {
@IsNotEmpty()
@IsString()
@IsUUID()
uuid: string;
}

export class CreateRestaurantDto {
@ApiProperty({ description: "맛집 이름" })
@IsNotEmpty()
@IsString()
name: string;

@ApiProperty({ description: "맛집 공개 여부" })
@IsNotEmpty()
@IsBoolean()
isPublic: boolean;

@ApiProperty({ description: "카테고리 id", example: 1 })
@IsNotEmpty()
@IsInt()
@IsPositive()
categoryId: number;

@ApiProperty({ description: "태그 id 배열", example: [1, 2, 3] })
@IsArray()
@ArrayNotEmpty()
@IsInt({ each: true })
@IsPositive({ each: true })
tagIds: number[];

@ApiProperty({ description: "위치 정보", required: false })
@IsOptional()
@IsString()
address?: string;

@ApiProperty({ description: "관련 링크", required: false })
@IsOptional()
@IsString()
link?: string;

@ApiProperty({ description: "배달 가능 여부", required: false })
@IsOptional()
@IsBoolean()
delivery?: boolean;

@ApiProperty({ description: "한줄평", required: false })
@IsOptional()
@IsString()
comment?: string;

@ApiProperty({ description: "수용 인원", required: false, example: 30 })
@IsOptional()
@IsInt()
@IsPositive()
capacity?: number;

@ApiProperty({ description: "운영 시간 정보", required: false })
@IsOptional()
@IsString()
openingHour?: string;

@ApiProperty({ description: "추천 메뉴", required: false })
@IsOptional()
@IsString()
recommendedMenu?: string;

@ApiProperty({ description: "주문 팁", required: false })
@IsOptional()
@IsString()
orderTip?: string;

@ApiProperty({
description: "맛집 사진 UUID 배열",
required: false,
example: [{ uuid: "11111111-1111-1111-1111-111111111111" }],
})
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => File)
files?: File[];
}
11 changes: 11 additions & 0 deletions src/modules/restaurants/dtos/get-restaurants-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsIn, IsOptional, IsString } from "class-validator";
import { PageQueryDto } from "src/common/dtos/page-query.dto";

export class GetRestaurantsQueryDto extends PageQueryDto {
@IsString()
@IsOptional()
@IsIn(["latest", "topRated"])
@ApiProperty({ description: "정렬 기준", required: false, enum: ["latest", "topRated"] })
sort?: string;
}
84 changes: 84 additions & 0 deletions src/modules/restaurants/dtos/patch-restaurant.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import {
ArrayNotEmpty,
IsArray,
IsBoolean,
IsInt,
IsNotEmpty,
IsPositive,
IsString,
IsUUID,
ValidateNested,
} from "class-validator";

class File {
@IsNotEmpty()
@IsString()
@IsUUID()
uuid: string;
}

export class PatchRestaurantDto {
@ApiPropertyOptional({ description: "맛집 이름" })
@IsString()
name?: string;

@ApiPropertyOptional({ description: "맛집 공개 여부" })
@IsBoolean()
isPublic?: boolean;

@ApiPropertyOptional({ description: "카테고리 id", example: 1 })
@IsInt()
@IsPositive()
categoryId?: number;

@ApiPropertyOptional({ description: "태그 id 배열", example: [1, 2, 3] })
@IsArray()
@ArrayNotEmpty()
@IsInt({ each: true })
@IsPositive({ each: true })
tagIds?: number[];

@ApiPropertyOptional({ description: "위치 정보" })
@IsString()
address?: string;

@ApiPropertyOptional({ description: "관련 링크" })
@IsString()
link?: string;

@ApiPropertyOptional({ description: "배달 가능 여부" })
@IsBoolean()
delivery?: boolean;

@ApiPropertyOptional({ description: "한줄평" })
@IsString()
comment?: string;

@ApiPropertyOptional({ description: "수용 인원", example: 30 })
@IsInt()
@IsPositive()
capacity?: number;

@ApiPropertyOptional({ description: "운영 시간 정보" })
@IsString()
openingHour?: string;

@ApiPropertyOptional({ description: "추천 메뉴" })
@IsString()
recommendedMenu?: string;

@ApiPropertyOptional({ description: "주문 팁" })
@IsString()
orderTip?: string;

@ApiPropertyOptional({
description: "맛집 사진 UUID 배열",
example: [{ uuid: "11111111-1111-1111-1111-111111111111" }],
})
@IsArray()
@ValidateNested({ each: true })
@Type(() => File)
files?: File[];
}
Loading