From 697eb50bad0611fe013ab4848dcdf499dc29287c Mon Sep 17 00:00:00 2001
From: anjdydhody <a01066444534@gmail.com>
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 <a01066444534@gmail.com>
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<Order> {
+  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({