From 697eb50bad0611fe013ab4848dcdf499dc29287c Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Mon, 11 Nov 2024 15:03:35 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20feat:=20jwt=20authGuard=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/auth/jwt-auth-guard.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 BE/src/auth/jwt-auth-guard.ts diff --git a/BE/src/auth/jwt-auth-guard.ts b/BE/src/auth/jwt-auth-guard.ts new file mode 100644 index 00000000..0aa58fd8 --- /dev/null +++ b/BE/src/auth/jwt-auth-guard.ts @@ -0,0 +1,5 @@ +import { AuthGuard } from '@nestjs/passport'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') {} From e1d5582f19f6ebf9317e88b2dace8629a4742324 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Mon, 11 Nov 2024 15:04:16 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EC=8B=9D=20?= =?UTF-8?q?=EB=A7=A4=EC=88=98=20API=20=EA=B5=AC=ED=98=84=20#49?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/app.module.ts | 5 +- .../order/dto/stock-order-request.dto.ts | 19 ++++++++ BE/src/stock/order/enum/status-type.ts | 4 ++ BE/src/stock/order/enum/trade-type.ts | 4 ++ .../order/interface/request.interface.ts | 9 ++++ BE/src/stock/order/stock-order.controller.ts | 42 +++++++++++++++++ BE/src/stock/order/stock-order.entity.ts | 46 +++++++++++++++++++ BE/src/stock/order/stock-order.module.ts | 13 ++++++ BE/src/stock/order/stock-order.repository.ts | 11 +++++ BE/src/stock/order/stock-order.service.ts | 23 ++++++++++ BE/src/util/swagger.ts | 1 + 11 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 BE/src/stock/order/dto/stock-order-request.dto.ts create mode 100644 BE/src/stock/order/enum/status-type.ts create mode 100644 BE/src/stock/order/enum/trade-type.ts create mode 100644 BE/src/stock/order/interface/request.interface.ts create mode 100644 BE/src/stock/order/stock-order.controller.ts create mode 100644 BE/src/stock/order/stock-order.entity.ts create mode 100644 BE/src/stock/order/stock-order.module.ts create mode 100644 BE/src/stock/order/stock-order.repository.ts create mode 100644 BE/src/stock/order/stock-order.service.ts diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index 114ecadf..dee9d8fa 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -10,6 +10,8 @@ import { StockIndexModule } from './stock/index/stock-index.module'; import { StockTopfiveModule } from './stock/topfive/stock-topfive.module'; import { KoreaInvestmentModule } from './koreaInvestment/korea-investment.module'; import { SocketModule } from './websocket/socket.module'; +import { StockOrderModule } from './stock/order/stock-order.module'; +import { Order } from './stock/order/stock-order.entity'; @Module({ imports: [ @@ -22,7 +24,7 @@ import { SocketModule } from './websocket/socket.module'; username: process.env.DB_USERNAME, password: process.env.DB_PASSWD, database: process.env.DB_DATABASE, - entities: [User], + entities: [User, Order], synchronize: true, }), KoreaInvestmentModule, @@ -30,6 +32,7 @@ import { SocketModule } from './websocket/socket.module'; StockIndexModule, StockTopfiveModule, SocketModule, + StockOrderModule, ], controllers: [AppController], providers: [AppService], diff --git a/BE/src/stock/order/dto/stock-order-request.dto.ts b/BE/src/stock/order/dto/stock-order-request.dto.ts new file mode 100644 index 00000000..b06a3dcb --- /dev/null +++ b/BE/src/stock/order/dto/stock-order-request.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt, IsNumber, IsPositive } from 'class-validator'; + +export class StockOrderRequestDto { + @ApiProperty({ description: '주식 id' }) + @IsInt() + @IsPositive() + stock_id: number; + + @ApiProperty({ description: '매수/매도 희망 가격' }) + @IsNumber() + @IsPositive() + price: number; + + @ApiProperty({ description: '매수/매도 희망 수량' }) + @IsInt() + @IsPositive() + amount: number; +} diff --git a/BE/src/stock/order/enum/status-type.ts b/BE/src/stock/order/enum/status-type.ts new file mode 100644 index 00000000..04b3caeb --- /dev/null +++ b/BE/src/stock/order/enum/status-type.ts @@ -0,0 +1,4 @@ +export enum StatusType { + PENDING = 'PENDING', + COMPLETE = 'COMPLETE', +} diff --git a/BE/src/stock/order/enum/trade-type.ts b/BE/src/stock/order/enum/trade-type.ts new file mode 100644 index 00000000..c07270e3 --- /dev/null +++ b/BE/src/stock/order/enum/trade-type.ts @@ -0,0 +1,4 @@ +export enum TradeType { + SELL = 'SELL', + BUY = 'BUY', +} diff --git a/BE/src/stock/order/interface/request.interface.ts b/BE/src/stock/order/interface/request.interface.ts new file mode 100644 index 00000000..d7616da0 --- /dev/null +++ b/BE/src/stock/order/interface/request.interface.ts @@ -0,0 +1,9 @@ +export interface RequestInterface { + user: { + id: number; + email: string; + password: string; + tutorial: boolean; + kakaoId: number; + }; +} diff --git a/BE/src/stock/order/stock-order.controller.ts b/BE/src/stock/order/stock-order.controller.ts new file mode 100644 index 00000000..218e5ce2 --- /dev/null +++ b/BE/src/stock/order/stock-order.controller.ts @@ -0,0 +1,42 @@ +import { + Body, + Controller, + Post, + Req, + UseGuards, + ValidationPipe, +} from '@nestjs/common'; +import { + ApiBearerAuth, + ApiOperation, + ApiResponse, + ApiTags, +} from '@nestjs/swagger'; +import { StockOrderService } from './stock-order.service'; +import { StockOrderRequestDto } from './dto/stock-order-request.dto'; +import { JwtAuthGuard } from '../../auth/jwt-auth-guard'; +import { RequestInterface } from './interface/request.interface'; + +@Controller('/api/stocks/trade') +@ApiTags('주식 매수/매도 API') +export class StockOrderController { + constructor(private readonly stockTradeService: StockOrderService) {} + + @Post('/buy') + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @ApiOperation({ + summary: '주식 매수 API', + description: '주식 id, 매수 가격, 수량으로 주식을 매수한다.', + }) + @ApiResponse({ + status: 201, + description: '주식 매수 성공', + }) + async buy( + @Req() request: RequestInterface, + @Body(ValidationPipe) stockOrderRequest: StockOrderRequestDto, + ) { + await this.stockTradeService.buy(request.user.id, stockOrderRequest); + } +} diff --git a/BE/src/stock/order/stock-order.entity.ts b/BE/src/stock/order/stock-order.entity.ts new file mode 100644 index 00000000..a88de180 --- /dev/null +++ b/BE/src/stock/order/stock-order.entity.ts @@ -0,0 +1,46 @@ +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { TradeType } from './enum/trade-type'; +import { StatusType } from './enum/status-type'; + +@Entity('orders') +export class Order { + @PrimaryGeneratedColumn() + id: number; + + @Column({ nullable: false }) + user_id: number; + + @Column({ nullable: false }) + stock_id: number; + + @Column({ + type: 'enum', + enum: TradeType, + nullable: false, + }) + trade_type: TradeType; + + @Column({ nullable: false }) + amount: number; + + @Column({ nullable: false }) + price: number; + + @Column({ + type: 'enum', + enum: StatusType, + nullable: false, + }) + status: StatusType; + + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + completed_at?: Date; +} diff --git a/BE/src/stock/order/stock-order.module.ts b/BE/src/stock/order/stock-order.module.ts new file mode 100644 index 00000000..b5c54337 --- /dev/null +++ b/BE/src/stock/order/stock-order.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StockOrderController } from './stock-order.controller'; +import { StockOrderService } from './stock-order.service'; +import { Order } from './stock-order.entity'; +import { StockOrderRepository } from './stock-order.repository'; + +@Module({ + imports: [TypeOrmModule.forFeature([Order])], + controllers: [StockOrderController], + providers: [StockOrderService, StockOrderRepository], +}) +export class StockOrderModule {} diff --git a/BE/src/stock/order/stock-order.repository.ts b/BE/src/stock/order/stock-order.repository.ts new file mode 100644 index 00000000..cec0080a --- /dev/null +++ b/BE/src/stock/order/stock-order.repository.ts @@ -0,0 +1,11 @@ +import { DataSource, Repository } from 'typeorm'; +import { InjectDataSource } from '@nestjs/typeorm'; +import { Injectable } from '@nestjs/common'; +import { Order } from './stock-order.entity'; + +@Injectable() +export class StockOrderRepository extends Repository { + constructor(@InjectDataSource() dataSource: DataSource) { + super(Order, dataSource.createEntityManager()); + } +} diff --git a/BE/src/stock/order/stock-order.service.ts b/BE/src/stock/order/stock-order.service.ts new file mode 100644 index 00000000..a8b771f1 --- /dev/null +++ b/BE/src/stock/order/stock-order.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { StockOrderRequestDto } from './dto/stock-order-request.dto'; +import { StockOrderRepository } from './stock-order.repository'; +import { TradeType } from './enum/trade-type'; +import { StatusType } from './enum/status-type'; + +@Injectable() +export class StockOrderService { + constructor(private readonly stockOrderRepository: StockOrderRepository) {} + + async buy(userId: number, stockOrderRequest: StockOrderRequestDto) { + const order = this.stockOrderRepository.create({ + user_id: userId, + stock_id: stockOrderRequest.stock_id, + trade_type: TradeType.BUY, + amount: stockOrderRequest.amount, + price: stockOrderRequest.price, + status: StatusType.PENDING, + }); + + await this.stockOrderRepository.save(order); + } +} diff --git a/BE/src/util/swagger.ts b/BE/src/util/swagger.ts index 44de0c96..e820d85e 100644 --- a/BE/src/util/swagger.ts +++ b/BE/src/util/swagger.ts @@ -13,6 +13,7 @@ export function setupSwagger(app: INestApplication): void { .setTitle('Juga API') .setDescription('Juga API 문서입니다.') .setVersion('1.0.0') + .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, options); From cce5466cd25f9adef3a81429eda88ef1c7f028f0 Mon Sep 17 00:00:00 2001 From: sieun <147706431+sieunie@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:14:40 +0900 Subject: [PATCH 3/4] merge conflict resolve app.module.ts --- BE/src/app.module.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index d3c27a75..99a51b90 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -17,16 +17,6 @@ import { typeOrmConfig } from './configs/typeorm.config'; imports: [ ScheduleModule.forRoot(), ConfigModule.forRoot(), - TypeOrmModule.forRoot({ - type: 'mysql', // 데이터베이스 타입 - host: process.env.DB_HOST, - port: 3306, - username: process.env.DB_USERNAME, - password: process.env.DB_PASSWD, - database: process.env.DB_DATABASE, - entities: [User, Order], - synchronize: true, - }), TypeOrmModule.forRoot(typeOrmConfig), KoreaInvestmentModule, AuthModule, From 8ecc4225172285cf2c68702cb72f4dc7ed0bb721 Mon Sep 17 00:00:00 2001 From: sieun <147706431+sieunie@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:16:23 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20remove=20u?= =?UTF-8?q?nused=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/app.module.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index 99a51b90..ac06a59c 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -10,7 +10,6 @@ import { StockTopfiveModule } from './stock/topfive/stock-topfive.module'; import { KoreaInvestmentModule } from './koreaInvestment/korea-investment.module'; import { SocketModule } from './websocket/socket.module'; import { StockOrderModule } from './stock/order/stock-order.module'; -import { Order } from './stock/order/stock-order.entity'; import { typeOrmConfig } from './configs/typeorm.config'; @Module({