Skip to content

Commit

Permalink
fix: guest계정 db삭제 버그 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
SeongHyeon0409 committed Nov 18, 2024
1 parent a7fb86e commit 111ec9e
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 291 deletions.
7 changes: 5 additions & 2 deletions packages/server/src/account/account.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ export class Account {
@Column('double')
BTC: number;

@OneToOne(() => User, (user) => user.account)
@JoinColumn()
@OneToOne(() => User, (user) => user.account, {
nullable: true,
onDelete: 'CASCADE',
})
@JoinColumn()
user: User;

@OneToMany(() => Asset, (asset) => asset.account, {
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/schedule/clean-up.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export class CleanupService {
this.logger.log('Expired guest cleanup finished.');
}

@Cron('0 */30 * * * *')
//@Cron('*/30 * * * *')
@Cron('* * * * *')
async handleCron(): Promise<void> {
this.logger.log('Running scheduled guest cleanup...');
await this.cleanupExpiredGuests();
Expand Down
298 changes: 149 additions & 149 deletions packages/server/src/trade/trade.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
Injectable,
OnModuleInit,
UnprocessableEntityException,
Injectable,
OnModuleInit,
UnprocessableEntityException,
} from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AccountRepository } from 'src/account/account.repository';
Expand All @@ -13,159 +13,159 @@ import { UPBIT_UPDATED_COIN_INFO_TIME } from 'common/upbit';

@Injectable()
export class TradeService implements OnModuleInit {
private transactionBuy: boolean = false;
private matchPendingTradesTimeoutId: NodeJS.Timeout | null = null;
private transactionBuy: boolean = false;
private matchPendingTradesTimeoutId: NodeJS.Timeout | null = null;

constructor(
private accountRepository: AccountRepository,
private assetRepository: AssetRepository,
private tradeRepository: TradeRepository,
private coinDataUpdaterService: CoinDataUpdaterService,
private readonly dataSource: DataSource,
private tradeHistoryRepository: TradeHistoryRepository,
) {}
constructor(
private accountRepository: AccountRepository,
private assetRepository: AssetRepository,
private tradeRepository: TradeRepository,
private coinDataUpdaterService: CoinDataUpdaterService,
private readonly dataSource: DataSource,
private tradeHistoryRepository: TradeHistoryRepository,
) {}

onModuleInit() {
this.matchPendingTrades();
}
onModuleInit() {
this.matchPendingTrades();
}

async calculatePercentBuy(user, moneyType: string, percent: number) {
const money = await this.accountRepository.getMyMoney(user, moneyType);
async calculatePercentBuy(user, moneyType: string, percent: number) {
const money = await this.accountRepository.getMyMoney(user, moneyType);

return Number(money) * (percent / 100);
}
async createBuyTrade(buyDto) {
const tradeId = await this.tradeRepository.createTrade(buyDto);
buyDto.tradeId = tradeId;
return buyDto;
}
return Number(money) * (percent / 100);
}
async createBuyTrade(buyDto) {
const tradeId = await this.tradeRepository.createTrade(buyDto);
buyDto.tradeId = tradeId;
return buyDto;
}

async buyTradeService(buyDto) {
if (this.transactionBuy) await this.waitForTransactionOrder();
this.transactionBuy = true;
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const [accountBalance, account] = await this.checkCurrency(buyDto);
// 일단 호가창 체크 안하고 현재가 기준으로만 체결
const currentCoinPrice = this.coinDataUpdaterService.getCoinPrice(buyDto);
if (buyDto.receivedPrice < currentCoinPrice) {
await queryRunner.commitTransaction();
return {
code: 204,
message: '아직 체결되지 않았습니다.',
};
}
async buyTradeService(buyDto) {
if (this.transactionBuy) await this.waitForTransactionOrder();
this.transactionBuy = true;
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const [accountBalance, account] = await this.checkCurrency(buyDto);
// 일단 호가창 체크 안하고 현재가 기준으로만 체결
const currentCoinPrice = this.coinDataUpdaterService.getCoinPrice(buyDto);
if (buyDto.receivedPrice < currentCoinPrice) {
await queryRunner.commitTransaction();
return {
code: 204,
message: '아직 체결되지 않았습니다.',
};
}

const tradeData = await this.tradeRepository.deleteTrade(
buyDto.tradeId,
queryRunner,
);
await this.tradeHistoryRepository.createTradeHistory(
buyDto.userId,
tradeData,
queryRunner,
);
const tradeData = await this.tradeRepository.deleteTrade(
buyDto.tradeId,
queryRunner,
);
await this.tradeHistoryRepository.createTradeHistory(
buyDto.userId,
tradeData,
queryRunner,
);

const afterTradeBalance =
accountBalance - currentCoinPrice * buyDto.receivedAmount;
await this.assetRepository.createAsset(
buyDto,
currentCoinPrice,
account,
queryRunner,
);
const afterTradeBalance =
accountBalance - currentCoinPrice * buyDto.receivedAmount;
await this.assetRepository.createAsset(
buyDto,
currentCoinPrice,
account,
queryRunner,
);

const accountId = account.accountId;
await this.accountRepository.succesBuy(
buyDto,
afterTradeBalance,
accountId,
queryRunner,
);
const accountId = account.accountId;
await this.accountRepository.successBuy(
buyDto,
afterTradeBalance,
accountId,
queryRunner,
);

await queryRunner.commitTransaction();
return {
code: 200,
message: '거래가 체결되었습니다.',
};
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
this.transactionBuy = false;
}
}
async checkCurrency(buyDto) {
const { userId, typeGiven, receivedPrice, receivedAmount } = buyDto;
const givenAmount = receivedPrice * receivedAmount;
const userAccount = await this.accountRepository.findOne({
where: {
user: { id: userId },
},
});
if (!userAccount) {
throw new UnprocessableEntityException({
response: {
message: '유저가 존재하지 않습니다.',
statusCode: 422,
},
});
}
const accountBalance = userAccount[typeGiven];
const accountResult = givenAmount <= accountBalance;
if (!accountResult)
throw new UnprocessableEntityException({
response: {
message: '자산이 부족합니다.',
statusCode: 422,
},
});
return [accountBalance, userAccount];
}
async waitForTransactionOrder() {
return new Promise<void>((resolve) => {
const check = () => {
if (!this.transactionBuy) resolve();
else setTimeout(check, 100);
};
check();
});
}
async matchPendingTrades() {
try {
const coinLatestInfo = this.coinDataUpdaterService.getCoinLatestInfo();
if (coinLatestInfo.size === 0) return;
const coinPrice = [];
coinLatestInfo.forEach((value, key) => {
const price = value.trade_price;
const [give, receive] = key.split('-');
coinPrice.push({ give: give, receive: receive, price: price });
});
const availableTrades = await this.tradeRepository.searchTrade(coinPrice);
availableTrades.forEach((trade) => {
const buyDto = {
userId: trade.user.id,
typeGiven: trade.tradeCurrency,
typeReceived: trade.assetName,
receivedPrice: trade.price,
receivedAmount: trade.quantity,
};
this.buyTradeService(buyDto);
});
} catch (error) {
console.error('미체결 거래 처리 오류:', error);
} finally {
console.log(`미체결 거래 처리 완료: ${Date()}`);
await queryRunner.commitTransaction();
return {
code: 200,
message: '거래가 체결되었습니다.',
};
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
this.transactionBuy = false;
}
}
async checkCurrency(buyDto) {
const { userId, typeGiven, receivedPrice, receivedAmount } = buyDto;
const givenAmount = receivedPrice * receivedAmount;
const userAccount = await this.accountRepository.findOne({
where: {
user: { id: userId },
},
});
if (!userAccount) {
throw new UnprocessableEntityException({
response: {
message: '유저가 존재하지 않습니다.',
statusCode: 422,
},
});
}
const accountBalance = userAccount[typeGiven];
const accountResult = givenAmount <= accountBalance;
if (!accountResult)
throw new UnprocessableEntityException({
response: {
message: '자산이 부족합니다.',
statusCode: 422,
},
});
return [accountBalance, userAccount];
}
async waitForTransactionOrder() {
return new Promise<void>((resolve) => {
const check = () => {
if (!this.transactionBuy) resolve();
else setTimeout(check, 100);
};
check();
});
}
async matchPendingTrades() {
try {
const coinLatestInfo = this.coinDataUpdaterService.getCoinLatestInfo();
if (coinLatestInfo.size === 0) return;
const coinPrice = [];
coinLatestInfo.forEach((value, key) => {
const price = value.trade_price;
const [give, receive] = key.split('-');
coinPrice.push({ give: give, receive: receive, price: price });
});
const availableTrades = await this.tradeRepository.searchTrade(coinPrice);
availableTrades.forEach((trade) => {
const buyDto = {
userId: trade.user.id,
typeGiven: trade.tradeCurrency, //건네주는 통화
typeReceived: trade.assetName, //건네받을 통화 타입
receivedPrice: trade.price, //건네받을 통화 가격
receivedAmount: trade.quantity, //건네 받을 통화 갯수
};
this.buyTradeService(buyDto);
});
} catch (error) {
console.error('미체결 거래 처리 오류:', error);
} finally {
console.log(`미체결 거래 처리 완료: ${Date()}`);

if (this.matchPendingTradesTimeoutId)
clearTimeout(this.matchPendingTradesTimeoutId);
this.matchPendingTradesTimeoutId = setTimeout(
() => this.matchPendingTrades(),
UPBIT_UPDATED_COIN_INFO_TIME,
);
}
}
}
if (this.matchPendingTradesTimeoutId)
clearTimeout(this.matchPendingTradesTimeoutId);
this.matchPendingTradesTimeoutId = setTimeout(
() => this.matchPendingTrades(),
UPBIT_UPDATED_COIN_INFO_TIME,
);
}
}
}
Loading

0 comments on commit 111ec9e

Please sign in to comment.