Skip to content

Commit

Permalink
✨ feat: 주문 체결 시 자산 업데이트 로직 트랜잭션으로 구현 #53
Browse files Browse the repository at this point in the history
  • Loading branch information
sieunie committed Nov 13, 2024
1 parent a6b6ea6 commit 4910002
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 11 deletions.
7 changes: 6 additions & 1 deletion BE/src/stock/order/stock-order.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
74 changes: 72 additions & 2 deletions BE/src/stock/order/stock-order.repository.ts
Original file line number Diff line number Diff line change
@@ -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<Order> {
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();
}
}
}
31 changes: 23 additions & 8 deletions BE/src/stock/order/stock-order.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

0 comments on commit 4910002

Please sign in to comment.