diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 6898149..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.eslintrc.js b/.eslintrc.js index 0289ccd..a082022 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,26 +1,26 @@ module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': 'off', - }, + parser: "@typescript-eslint/parser", + parserOptions: { + project: "tsconfig.json", + tsconfigRootDir: __dirname, + sourceType: "module", + }, + plugins: ["@typescript-eslint/eslint-plugin"], + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: [".eslintrc.js"], + rules: { + "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + }, }; diff --git a/.gitignore b/.gitignore index 2a41ca9..ae229e2 100644 --- a/.gitignore +++ b/.gitignore @@ -182,4 +182,4 @@ dist # End of https://www.toptal.com/developers/gitignore/api/node,macos -gcp-file-storage.json +gcp-file-storage.json \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..d897c1b --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,25 @@ +version: '3.7' + +# 컴퓨터들 +services: + # 컴퓨터이름 + my-backend: + build: + context: . + dockerfile: Dockerfile + volumes: + - ./src:/myfolder/src + ports: + - 3000:3000 + env_file: + - ./.env + + # 컴퓨터이름 + my-database: + # platform: linux/86_64 + image: mysql:latest + environment: + MYSQL_DATABASE: 'groo-meong' + MYSQL_ROOT_PASSWORD: 'root' + ports: + - 3306:3306 diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..779ad19 --- /dev/null +++ b/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/src/apis/shops/dto/create-shop.input.ts b/src/apis/shops/dto/create-shop.input.ts new file mode 100644 index 0000000..5e95c0b --- /dev/null +++ b/src/apis/shops/dto/create-shop.input.ts @@ -0,0 +1,19 @@ +import { Field, InputType } from "@nestjs/graphql"; + +@InputType() +export class CreateShopInput { + @Field(() => String) + name: string; + + @Field(() => String) + phone: string; + + @Field(() => String) + openHour: string; + + @Field(() => String) + closeHour: string; + + @Field(() => String) + address: string; +} diff --git a/src/apis/shops/dto/update-shop.input.ts b/src/apis/shops/dto/update-shop.input.ts new file mode 100644 index 0000000..d3c3344 --- /dev/null +++ b/src/apis/shops/dto/update-shop.input.ts @@ -0,0 +1,5 @@ +import { InputType, PartialType } from "@nestjs/graphql"; +import { CreateShopInput } from "./create-shop.input"; + +@InputType() +export class UpdateShopInput extends PartialType(CreateShopInput) {} diff --git a/src/apis/shops/entities/shop.entity.ts b/src/apis/shops/entities/shop.entity.ts new file mode 100644 index 0000000..4803532 --- /dev/null +++ b/src/apis/shops/entities/shop.entity.ts @@ -0,0 +1,47 @@ +import { Field, ObjectType } from '@nestjs/graphql'; +import { + Column, + DeleteDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, +} from 'typeorm'; + +@Entity() +@ObjectType() +export class Shop { + @PrimaryGeneratedColumn('uuid') + @Field(() => String) + id: string; + + @Column({ length: 10 }) + @Field(() => String) + name: string; + + @Column({ length: 13 }) + @Field(() => String) + phone: string; + + @Column() + @Field(() => String) + openHour: string; + + @Column() + @Field(() => String) + closeHour: string; + + @Column({ length: 100 }) + @Field(() => String) + address: string; + + @DeleteDateColumn({ nullable: true }) + @Field(() => Date) + deleteAt?: Date; + + // // 가게(own):예약 = 1:N + // @OneToMany(() => Reservation, (reservation) => reservation.id, { + // nullable: true, + // }) + // @Field(() => [Reservation]) + // reservation?: Reservation[]; +} \ No newline at end of file diff --git a/src/apis/shops/interface/shops-service.interface.ts b/src/apis/shops/interface/shops-service.interface.ts new file mode 100644 index 0000000..12c65fc --- /dev/null +++ b/src/apis/shops/interface/shops-service.interface.ts @@ -0,0 +1,28 @@ +import { CreateShopInput } from '../dto/create-shop.input'; +import { UpdateShopInput } from '../dto/update-shop.input'; + +export interface IShopsServiceCreate { + phone: string; + createShopInput: CreateShopInput; +} + +export interface IShopsServiceFindOne { + shopId: string; +} + +export interface IShopsServiceFindOneByPhone { + phone: string; +} + +export interface IShopsServiceUpdate { + shopId: string; + updateShopInput: UpdateShopInput; +} + +export interface IShopsServiceDelete { + shopId: string; +} + +export interface IShopsServiceRestore { + shopId: string; +} diff --git a/src/apis/shops/shops.module.ts b/src/apis/shops/shops.module.ts new file mode 100644 index 0000000..a6777a2 --- /dev/null +++ b/src/apis/shops/shops.module.ts @@ -0,0 +1,18 @@ +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { Shop } from "./entities/shop.entity"; +import { ShopsResolver } from "./shops.resolver"; +import { ShopsService } from "./shops.service"; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + Shop, // + ]), + ], + providers: [ + ShopsResolver, // + ShopsService, + ], +}) +export class ShopsModule {} diff --git a/src/apis/shops/shops.resolver.ts b/src/apis/shops/shops.resolver.ts new file mode 100644 index 0000000..5c5c13a --- /dev/null +++ b/src/apis/shops/shops.resolver.ts @@ -0,0 +1,66 @@ +import { Args, Mutation, Resolver, Query } from '@nestjs/graphql'; +import { CreateShopInput } from './dto/create-shop.input'; +import { UpdateShopInput } from './dto/update-shop.input'; +import { Shop } from './entities/shop.entity'; +import { ShopsService } from './shops.service'; + +@Resolver() +export class ShopsResolver { + constructor( + private readonly shopsService: ShopsService, // + ) {} + + @Query(() => [Shop]) + fetchShops(): Promise { + return this.shopsService.findAll(); + } + + @Query(() => Shop) + fetchShop( + @Args('shopId') shopId: string, // + ): Promise { + return this.shopsService.findOne({ shopId }); + } + + @Query(() => [Shop]) + fetchShopsWithDeleted(): Promise { + return this.shopsService.findAllDeleted(); + } + + @Query(() => Shop) + fetchShopWithDeleted( + @Args('shopId') shopId: string, // + ): Promise { + return this.shopsService.findOneDeleted({ shopId }); + } + + @Mutation(() => Shop) + createShop( + @Args('createShopInput') createShopInput: CreateShopInput, + ): Promise { + const phone = createShopInput.phone; + return this.shopsService.create({ phone, createShopInput }); + } + + @Mutation(() => Shop) + async updateShop( + @Args('shopId') shopId: string, + @Args('updateShopInput') updateShopInput: UpdateShopInput, + ): Promise { + return this.shopsService.update({ shopId, updateShopInput }); + } + + @Mutation(() => Boolean) + deleteShop( + @Args('shopId') shopId: string, // + ): Promise { + return this.shopsService.delete({ shopId }); + } + + @Mutation(() => Boolean) + restoreShop( + @Args('shopId') shopId: string, // + ): Promise { + return this.shopsService.restore({ shopId }); + } +} diff --git a/src/apis/shops/shops.service.ts b/src/apis/shops/shops.service.ts new file mode 100644 index 0000000..8c3dc57 --- /dev/null +++ b/src/apis/shops/shops.service.ts @@ -0,0 +1,88 @@ +import { Injectable, UnprocessableEntityException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Shop } from './entities/shop.entity'; +import { + IShopsServiceCreate, + IShopsServiceDelete, + IShopsServiceFindOne, + IShopsServiceFindOneByPhone, + IShopsServiceRestore, + IShopsServiceUpdate, +} from './interface/shops-service.interface'; + +@Injectable() +export class ShopsService { + constructor( + @InjectRepository(Shop) + private readonly shopsRepository: Repository, // + ) {} + + async create({ phone, createShopInput }: IShopsServiceCreate): Promise { + const checkShop = await this.findOneByPhone({ phone }); + if (checkShop === undefined) { + throw new UnprocessableEntityException('이미 등록된 가게입니다'); + } + + return await this.shopsRepository.save({ ...createShopInput }); + } + + async findAll(): Promise { + return await this.shopsRepository.find({ + // relations: ['reservation'], + }); + } + + async findOne({ shopId }: IShopsServiceFindOne): Promise { + return await this.shopsRepository.findOne({ + where: { id: shopId }, + // relations: ['reservation'], + }); + } + + async findOneByPhone({ phone }: IShopsServiceFindOneByPhone): Promise { + return await this.shopsRepository.findOne({ + where: { phone: phone }, + }); + } + + async findAllDeleted(): Promise { + return await this.shopsRepository.find({ + withDeleted: true, + // relations: ['reservation'], + }); + } + + async findOneDeleted({ shopId }): Promise { + return await this.shopsRepository.findOne({ + where: { id: shopId }, + withDeleted: true, + // relations: ['reservation'], + }); + } + + async update({ + shopId, + updateShopInput, + }: IShopsServiceUpdate): Promise { + await this.shopsRepository.findOne({ + where: { id: shopId }, + }); + + return this.shopsRepository.save({ + ...updateShopInput, + }); + } + + async delete({ shopId }: IShopsServiceDelete): Promise { + const result = await this.shopsRepository.softDelete({ id: shopId }); + + return result.affected ? true : false; + } + + async restore({ shopId }: IShopsServiceRestore): Promise { + const result = await this.shopsRepository.restore({ id: shopId }); + + return result.affected ? true : false; + } +} diff --git a/src/app.module.ts b/src/app.module.ts index 2380097..91ff3b1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,15 +1,17 @@ -import { Module } from '@nestjs/common'; +import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { ConfigModule } from '@nestjs/config'; -import { TypeOrmModule } from '@nestjs/typeorm'; +import { DogsModule } from './apis/dogs/dogs.module'; import { GraphQLModule } from '@nestjs/graphql'; -import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; +import { Module } from '@nestjs/common'; +import { ShopsModule } from './apis/shops/shops.module'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { UsersModule } from './apis/users/user.module'; -import { DogsModule } from './apis/dogs/dogs.module'; @Module({ imports: [ - DogsModule, - UsersModule, // + DogsModule, // + ShopsModule, + UsersModule, GraphQLModule.forRoot({ driver: ApolloDriver,