From 4910002c4658ec533d381167011e0eb823f37195 Mon Sep 17 00:00:00 2001 From: anjdydhody Date: Wed, 13 Nov 2024 18:03:29 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A3=BC=EB=AC=B8=20?= =?UTF-8?q?=EC=B2=B4=EA=B2=B0=20=EC=8B=9C=20=EC=9E=90=EC=82=B0=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=ED=8A=B8?= =?UTF-8?q?=EB=9E=9C=EC=9E=AD=EC=85=98=EC=9C=BC=EB=A1=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20#53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/stock/order/stock-order.module.ts | 7 +- BE/src/stock/order/stock-order.repository.ts | 74 +++++++++++++++++++- BE/src/stock/order/stock-order.service.ts | 31 +++++--- 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/BE/src/stock/order/stock-order.module.ts b/BE/src/stock/order/stock-order.module.ts index a31eee97..944813d5 100644 --- a/BE/src/stock/order/stock-order.module.ts +++ b/BE/src/stock/order/stock-order.module.ts @@ -5,9 +5,14 @@ import { StockOrderService } from './stock-order.service'; import { Order } from './stock-order.entity'; import { StockOrderRepository } from './stock-order.repository'; import { SocketModule } from '../../websocket/socket.module'; +import { AssetModule } from '../../asset/asset.module'; @Module({ - imports: [TypeOrmModule.forFeature([Order]), forwardRef(() => SocketModule)], + imports: [ + TypeOrmModule.forFeature([Order]), + forwardRef(() => SocketModule), + AssetModule, + ], controllers: [StockOrderController], providers: [StockOrderService, StockOrderRepository], exports: [StockOrderService], diff --git a/BE/src/stock/order/stock-order.repository.ts b/BE/src/stock/order/stock-order.repository.ts index cec0080a..6b0dc184 100644 --- a/BE/src/stock/order/stock-order.repository.ts +++ b/BE/src/stock/order/stock-order.repository.ts @@ -1,11 +1,81 @@ import { DataSource, Repository } from 'typeorm'; import { InjectDataSource } from '@nestjs/typeorm'; -import { Injectable } from '@nestjs/common'; +import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { Order } from './stock-order.entity'; +import { StatusType } from './enum/status-type'; +import { Asset } from '../../asset/asset.entity'; @Injectable() export class StockOrderRepository extends Repository { - constructor(@InjectDataSource() dataSource: DataSource) { + constructor(@InjectDataSource() private dataSource: DataSource) { super(Order, dataSource.createEntityManager()); } + + async updateOrderAndAssetWhenBuy(order, realPrice) { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.startTransaction(); + + try { + await queryRunner.manager.update( + Order, + { id: order.id }, + { status: StatusType.COMPLETE, completed_at: new Date() }, + ); + await queryRunner.manager + .createQueryBuilder() + .update(Asset) + .set({ + cash_balance: () => 'cash_balance - :realPrice', + stock_balance: () => 'stock_balance - :realPrice', + total_asset: () => 'total_asset - :realPrice', + total_profit: () => 'total_profit - :realPrice', + total_profit_rate: () => `total_profit / 10000000`, + last_updated: new Date(), + }) + .where({ user_id: order.user_id }) + .setParameter('realPrice', realPrice) + .execute(); + + await queryRunner.commitTransaction(); + } catch (err) { + await queryRunner.rollbackTransaction(); + throw new InternalServerErrorException(); + } finally { + await queryRunner.release(); + } + } + + async updateOrderAndAssetWhenSell(order, realPrice) { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.startTransaction(); + + try { + await queryRunner.manager.update( + Order, + { id: order.id }, + { status: StatusType.COMPLETE, completed_at: new Date() }, + ); + await queryRunner.manager + .createQueryBuilder() + .update(Asset) + .set({ + cash_balance: () => 'cash_balance + :realPrice', + stock_balance: () => 'stock_balance + :realPrice', + total_asset: () => 'total_asset + :realPrice', + total_profit: () => 'total_profit + :realPrice', + total_profit_rate: () => `total_profit / 10000000`, + last_updated: new Date(), + }) + .where({ user_id: order.user_id }) + .setParameter('realPrice', realPrice) + .execute(); + + await queryRunner.commitTransaction(); + } catch (err) { + await queryRunner.rollbackTransaction(); + throw new InternalServerErrorException(); + } finally { + await queryRunner.release(); + } + } } diff --git a/BE/src/stock/order/stock-order.service.ts b/BE/src/stock/order/stock-order.service.ts index 1aba3347..c756e8d2 100644 --- a/BE/src/stock/order/stock-order.service.ts +++ b/BE/src/stock/order/stock-order.service.ts @@ -114,20 +114,35 @@ export class StockOrderService { } private async executeBuy(order) { - // TODO: 매수 체결 로직 필요... this.logger.log(`${order.id}번 매수 예약이 체결되었습니다.`, 'BUY'); - await this.stockOrderRepository.update( - { id: order.id }, - { status: StatusType.COMPLETE, completed_at: new Date() }, + + const totalPrice = order.price * order.amount; + const fee = this.calculateFee(totalPrice); + await this.stockOrderRepository.updateOrderAndAssetWhenBuy( + order, + totalPrice + fee, ); } private async executeSell(order) { - // TODO: 매도 체결 로직 필요... this.logger.log(`${order.id}번 매도 예약이 체결되었습니다.`, 'SELL'); - await this.stockOrderRepository.update( - { id: order.id }, - { status: StatusType.COMPLETE, completed_at: new Date() }, + + const totalPrice = order.price * order.amount; + const fee = this.calculateFee(totalPrice); + await this.stockOrderRepository.updateOrderAndAssetWhenSell( + order, + totalPrice - fee, ); } + + private calculateFee(totalPrice: number) { + if (totalPrice <= 10000000) return totalPrice * 0.16; + if (totalPrice > 10000000 && totalPrice <= 50000000) + return totalPrice * 0.14; + if (totalPrice > 50000000 && totalPrice <= 100000000) + return totalPrice * 0.12; + if (totalPrice > 100000000 && totalPrice <= 300000000) + return totalPrice * 0.1; + return totalPrice * 0.08; + } }