From 2a27661b329b90f7706398caaa974f5811a532cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=84=B1=ED=98=84?= <31495131+SeongHyeon0409@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:28:57 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80data=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EB=B0=8F=20api=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/src/account/account.controller.ts | 2 + packages/server/src/account/account.module.ts | 7 ++- .../server/src/account/account.service.ts | 2 + packages/server/src/auth/auth.controller.ts | 11 ++-- packages/server/src/auth/auth.module.ts | 17 +++-- packages/server/src/auth/user.controller.ts | 52 ++++++++++++++++ packages/server/src/auth/user.service.ts | 62 +++++++++++++++++++ .../src/trade-history/trade-history.module.ts | 1 + packages/server/src/trade/trade.module.ts | 1 + 9 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 packages/server/src/auth/user.controller.ts create mode 100644 packages/server/src/auth/user.service.ts diff --git a/packages/server/src/account/account.controller.ts b/packages/server/src/account/account.controller.ts index 0f216ea1..b86baca9 100644 --- a/packages/server/src/account/account.controller.ts +++ b/packages/server/src/account/account.controller.ts @@ -59,4 +59,6 @@ export class AccountController { throw error; } } + + } diff --git a/packages/server/src/account/account.module.ts b/packages/server/src/account/account.module.ts index 2878fad6..2740f907 100644 --- a/packages/server/src/account/account.module.ts +++ b/packages/server/src/account/account.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module, forwardRef } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Account } from './account.entity'; import { AccountRepository } from './account.repository'; @@ -6,9 +6,10 @@ import { User } from 'src/auth/user.entity'; import { AccountController } from './account.controller'; import { AccountService } from './account.service'; import { AssetRepository } from '@src/asset/asset.repository'; + @Module({ - imports: [TypeOrmModule.forFeature([Account, User])], + imports: [TypeOrmModule.forFeature([Account, User]),], controllers: [AccountController], providers: [AccountRepository, AccountService, AssetRepository], }) -export class AccountModule {} +export class AccountModule { } diff --git a/packages/server/src/account/account.service.ts b/packages/server/src/account/account.service.ts index b7608c08..0d3cf4b0 100644 --- a/packages/server/src/account/account.service.ts +++ b/packages/server/src/account/account.service.ts @@ -2,6 +2,7 @@ import { HttpStatus, Injectable, Logger, + NotFoundException, UnauthorizedException, } from '@nestjs/common'; import { AssetRepository } from '@src/asset/asset.repository'; @@ -11,6 +12,7 @@ import { CURRENCY_CONSTANTS } from './constants/currency.constants'; import { AccountResponseDto, MyAccountResponseDto, UserDto } from './dtos/my-account.response.dto'; import { AccountRepository } from './account.repository'; import { Asset } from '@src/asset/asset.entity'; +import { UserRepository } from '@src/auth/user.repository'; @Injectable() export class AccountService { diff --git a/packages/server/src/auth/auth.controller.ts b/packages/server/src/auth/auth.controller.ts index 36613dbf..4369d544 100644 --- a/packages/server/src/auth/auth.controller.ts +++ b/packages/server/src/auth/auth.controller.ts @@ -5,6 +5,7 @@ import { Get, HttpCode, HttpStatus, + Logger, Post, Request, Res, @@ -25,7 +26,9 @@ import { FRONTEND_URL } from './constants'; @Controller('auth') export class AuthController { - constructor(private authService: AuthService) {} + + private readonly logger = new Logger(AuthController.name); + constructor(private authService: AuthService) { } @ApiBody({ type: SignInDto }) @HttpCode(HttpStatus.OK) @@ -42,7 +45,7 @@ export class AuthController { @Get('google') @UseGuards(PassportAuthGuard('google')) - async googleLogin() {} + async googleLogin() { } @Get('google/callback') @UseGuards(PassportAuthGuard('google')) @@ -53,7 +56,7 @@ export class AuthController { @Get('kakao') @UseGuards(PassportAuthGuard('kakao')) - async kakaoLogin() {} + async kakaoLogin() { } @Get('kakao/callback') @UseGuards(PassportAuthGuard('kakao')) @@ -122,7 +125,6 @@ export class AuthController { return this.authService.validateOAuthLogin(signUpDto); } - // Helper method to redirect with tokens private redirectWithTokens(res: any, tokens: any): void { const redirectURL = new URL('/auth/callback', FRONTEND_URL); @@ -131,5 +133,4 @@ export class AuthController { res.redirect(redirectURL.toString()); } - } diff --git a/packages/server/src/auth/auth.module.ts b/packages/server/src/auth/auth.module.ts index 60fb3026..46e9012c 100644 --- a/packages/server/src/auth/auth.module.ts +++ b/packages/server/src/auth/auth.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module, forwardRef } from '@nestjs/common'; import { UserRepository } from './user.repository'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './user.entity'; @@ -11,26 +11,35 @@ import { AccountModule } from 'src/account/account.module'; import { KakaoStrategy } from './strategies/kakao.strategy'; import { GoogleStrategy } from './strategies/google.strategy'; import { PassportModule } from '@nestjs/passport'; +import { UserController } from './user.controller'; +import { UserService } from './user.service'; +import { TradeModule } from '@src/trade/trade.module'; +import { TradehistoryModule } from '@src/trade-history/trade-history.module'; +import { AssetRepository } from '@src/asset/asset.repository'; +import { AssetModule } from '@src/asset/asset.module'; @Module({ imports: [ - TypeOrmModule.forFeature([User]), + TypeOrmModule.forFeature([User, UserRepository]), JwtModule.register({ global: true, secret: jwtConstants.secret, signOptions: { expiresIn: '6000s' }, }), AccountModule, + TradeModule, + TradehistoryModule, PassportModule, ], providers: [ UserRepository, AccountRepository, AuthService, + UserService, JwtService, GoogleStrategy, KakaoStrategy, ], - controllers: [AuthController], + controllers: [AuthController, UserController], exports: [UserRepository], }) -export class AuthModule {} +export class AuthModule { } diff --git a/packages/server/src/auth/user.controller.ts b/packages/server/src/auth/user.controller.ts new file mode 100644 index 00000000..a100bfbb --- /dev/null +++ b/packages/server/src/auth/user.controller.ts @@ -0,0 +1,52 @@ +import { + Controller, + Delete, + HttpCode, + HttpStatus, + UseGuards, + Logger, + Request, + } from '@nestjs/common'; + import { AuthGuard } from './auth.guard'; + import { UserService } from './user.service'; + import { ApiBearerAuth, ApiSecurity, ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; + + @ApiTags('유저 API') + @Controller('user') + export class UserController { + private readonly logger = new Logger(UserController.name); + + constructor( + private readonly userService: UserService, + ) {} + + @ApiOperation({ + summary: '유저 데이터 초기화 및 새 계정 생성', + description: '유저의 계정, 에셋, 트레이드, 트레이드 히스토리를 삭제하고 새 계정을 생성합니다.', + }) + @ApiResponse({ + status: HttpStatus.OK, + description: '유저 데이터 초기화 성공 및 새 계정 생성 완료', + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: '유저를 찾을 수 없음', + }) + @HttpCode(HttpStatus.OK) + @ApiBearerAuth('access-token') + @ApiSecurity('access-token') + @UseGuards(AuthGuard) + @Delete('reset') + async resetUserData(@Request() req): Promise<{ message: string }> { + this.logger.log(`유저 데이터 초기화 시작: User ID ${req.user.userId}`); + try { + await this.userService.resetUserData(req.user.userId); + this.logger.log(`유저 데이터 초기화 완료: User ID ${req.user.userId}`); + return { message: '유저 데이터 초기화 및 새 계정 생성이 완료되었습니다.' }; + } catch (error) { + this.logger.error(`유저 데이터 초기화 실패: ${error.message}`, error.stack); + throw error; + } + } + } + \ No newline at end of file diff --git a/packages/server/src/auth/user.service.ts b/packages/server/src/auth/user.service.ts new file mode 100644 index 00000000..ebc5b7c9 --- /dev/null +++ b/packages/server/src/auth/user.service.ts @@ -0,0 +1,62 @@ +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { UserRepository } from './user.repository'; +import { TradeRepository } from '../trade/trade.repository'; +import { TradeHistoryRepository } from '../trade-history/trade-history.repository'; +import { AccountRepository } from '@src/account/account.repository'; +import { DEFAULT_BTC, DEFAULT_KRW, DEFAULT_USDT } from './constants'; +import { DataSource } from 'typeorm'; + +@Injectable() +export class UserService { + private readonly logger = new Logger(UserService.name); + + constructor( + private readonly userRepository: UserRepository, + private readonly tradeRepository: TradeRepository, + private readonly accountRepository: AccountRepository, + private readonly tradeHistoryRepository: TradeHistoryRepository, + private readonly dataSource: DataSource, + ) { } + + async resetUserData(userId: number): Promise { + await this.dataSource.transaction(async (manager) => { + const user = await manager.findOne(this.userRepository.target, { + where: { id: userId }, + relations: ['account'], // account 관계를 로드 + }); + + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + this.logger.log(`유저 데이터 삭제 시작: User ID ${userId}`); + + await manager.delete(this.tradeRepository.target, { user }); + + await manager.delete(this.tradeHistoryRepository.target, { user }); + + if (user.account) { + await manager.delete(this.accountRepository.target, { id: user.account.id }); + user.account = null; + await manager.save(this.userRepository.target, user); + } + await manager.save(this.userRepository.target, user); + + this.logger.log(`유저 데이터 삭제 완료: User ID ${userId}`); + + this.logger.log(`새 어카운트 생성 시작: User ID ${userId}`); + + const newAccount = manager.create(this.accountRepository.target, { + KRW: DEFAULT_KRW, + USDT: DEFAULT_USDT, + BTC: DEFAULT_BTC, + }); + + user.account = newAccount; + + await manager.save(this.userRepository.target, user); + + this.logger.log(`새 어카운트 생성 완료: User ID ${userId}`); + }); + } +} diff --git a/packages/server/src/trade-history/trade-history.module.ts b/packages/server/src/trade-history/trade-history.module.ts index f5038acf..c361003d 100644 --- a/packages/server/src/trade-history/trade-history.module.ts +++ b/packages/server/src/trade-history/trade-history.module.ts @@ -11,5 +11,6 @@ import { UpbitModule } from '@src/upbit/upbit.module'; imports: [TypeOrmModule.forFeature([TradeHistory]), HttpModule, UpbitModule], providers: [TradeHistoryRepository, TradeHistoryService], controllers: [TradeHistoryController], + exports: [TradeHistoryRepository] }) export class TradehistoryModule {} diff --git a/packages/server/src/trade/trade.module.ts b/packages/server/src/trade/trade.module.ts index ca7d21d2..3ce0f204 100644 --- a/packages/server/src/trade/trade.module.ts +++ b/packages/server/src/trade/trade.module.ts @@ -26,5 +26,6 @@ import { TradeService } from './trade.service'; TradeService, ], controllers: [TradeController], + exports: [TradeRepository] }) export class TradeModule {} From 3c0bcd2a75f5f6fd3410cdd751e77ef8ef551b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=84=B1=ED=98=84?= <31495131+SeongHyeon0409@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:39:36 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20account=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/server/src/account/account.entity.ts | 3 +++ .../server/src/account/account.service.ts | 2 ++ .../server/src/account/dtos/my-account.dto.ts | 3 +++ .../account/dtos/my-account.response.dto.ts | 6 ++++++ packages/server/src/auth/auth.service.ts | 19 ++----------------- .../server/src/trade/trade-ask.service.ts | 11 +++++++++++ .../server/src/trade/trade-bid.service.ts | 16 ++++++++++++---- packages/server/src/trade/trade.service.ts | 4 ++-- 8 files changed, 41 insertions(+), 23 deletions(-) diff --git a/packages/server/src/account/account.entity.ts b/packages/server/src/account/account.entity.ts index fe87b050..7c635987 100644 --- a/packages/server/src/account/account.entity.ts +++ b/packages/server/src/account/account.entity.ts @@ -17,6 +17,9 @@ export class Account { @Column('double') KRW: number; + @Column('double') + availableKRW: number; + @Column('double') USDT: number; diff --git a/packages/server/src/account/account.service.ts b/packages/server/src/account/account.service.ts index 0d3cf4b0..daecc7ff 100644 --- a/packages/server/src/account/account.service.ts +++ b/packages/server/src/account/account.service.ts @@ -49,6 +49,7 @@ export class AccountService { totalPrice = this.calculateTotalPrice(coins); accountData.KRW = account.KRW; + accountData.availableKRW = account.availableKRW; accountData.total_bid = totalPrice; accountData.coins = coins; @@ -56,6 +57,7 @@ export class AccountService { return { KRW: accountData.KRW, + availableKRW: accountData.availableKRW, total_bid: accountData.total_bid, coins: accountData.coins }; diff --git a/packages/server/src/account/dtos/my-account.dto.ts b/packages/server/src/account/dtos/my-account.dto.ts index 59e08334..5fd73cb2 100644 --- a/packages/server/src/account/dtos/my-account.dto.ts +++ b/packages/server/src/account/dtos/my-account.dto.ts @@ -25,6 +25,9 @@ export class MyAccountDto { @IsNumber() KRW: number; + @IsNumber() + availableKRW: number; + @IsNumber() total_bid: number; diff --git a/packages/server/src/account/dtos/my-account.response.dto.ts b/packages/server/src/account/dtos/my-account.response.dto.ts index 3108aea4..0294fd70 100644 --- a/packages/server/src/account/dtos/my-account.response.dto.ts +++ b/packages/server/src/account/dtos/my-account.response.dto.ts @@ -22,6 +22,12 @@ export class AccountResponseDto { description: '계좌 잔액', }) KRW: number; + + @ApiProperty({ + example: 2000000, + description: '매수가능한 계좌 잔액', + }) + availableKRW: number; @ApiProperty({ type: MyAccountDto, diff --git a/packages/server/src/auth/auth.service.ts b/packages/server/src/auth/auth.service.ts index 416e42eb..4fb358db 100644 --- a/packages/server/src/auth/auth.service.ts +++ b/packages/server/src/auth/auth.service.ts @@ -30,9 +30,7 @@ export class AuthService { private accountRepository: AccountRepository, private jwtService: JwtService, private readonly redisRepository: RedisRepository, - ) { - this.createAdminUser(); - } + ) {} async signIn( username: string, @@ -120,20 +118,6 @@ export class AuthService { return { message: 'User logged out successfully' }; } - async createAdminUser() { - const user = await this.userRepository.findOneBy({ username: 'admin' }); - - if (!user) { - const adminUser = new User(); - adminUser.username = 'admin'; - await this.userRepository.save(adminUser); - await this.accountRepository.createAccountForAdmin(adminUser); - this.logger.log('Admin user created successfully.'); - } else { - this.logger.log('Admin user already exists.'); - } - } - private async generateTokens( userId: number, username: string, @@ -223,6 +207,7 @@ export class AuthService { await this.accountRepository.save({ user, KRW: DEFAULT_KRW, + availableKRW: DEFAULT_KRW, USDT: DEFAULT_USDT, BTC: DEFAULT_BTC, }); diff --git a/packages/server/src/trade/trade-ask.service.ts b/packages/server/src/trade/trade-ask.service.ts index 2250aa6e..004e8879 100644 --- a/packages/server/src/trade/trade-ask.service.ts +++ b/packages/server/src/trade/trade-ask.service.ts @@ -291,12 +291,23 @@ export class AskService extends TradeAskBidService implements OnModuleInit { account[askDto.typeReceived] + buyData.price * buyData.quantity, ); + const availableChange = formatQuantity( + account.availableKRW + buyData.price * buyData.quantity, + ); + await this.accountRepository.updateAccountCurrency( askDto.typeReceived, change, account.id, queryRunner, ); + + await this.accountRepository.updateAccountCurrency( + 'availableKRW', + availableChange, + account.id, + queryRunner, + ); } private async waitForTransaction( diff --git a/packages/server/src/trade/trade-bid.service.ts b/packages/server/src/trade/trade-bid.service.ts index 00103987..61a2a1bd 100644 --- a/packages/server/src/trade/trade-bid.service.ts +++ b/packages/server/src/trade/trade-bid.service.ts @@ -74,7 +74,7 @@ export class BidService extends TradeAskBidService implements OnModuleInit { ); await this.accountRepository.updateAccountCurrency( - bidDto.typeGiven, + 'availableKRW', accountBalance, userAccount.id, queryRunner, @@ -101,8 +101,8 @@ export class BidService extends TradeAskBidService implements OnModuleInit { bidDto: TradeData, account: any, ): Promise { - const { typeGiven, receivedPrice, receivedAmount } = bidDto; - const balance = account[typeGiven]; + const { receivedPrice, receivedAmount } = bidDto; + const balance = account.availableKRW; const givenAmount = formatQuantity(receivedPrice * receivedAmount); const remaining = formatQuantity(balance - givenAmount); @@ -253,7 +253,8 @@ export class BidService extends TradeAskBidService implements OnModuleInit { const change = formatQuantity( (bidDto.receivedPrice - buyData.price) * buyData.quantity, ); - const returnChange = formatQuantity(change + account[typeGiven]); + const returnChange = formatQuantity(account[typeGiven] - (buyData.price * buyData.quantity)); + const returnAvailableChange = formatQuantity(change + account.availableKRW); await this.accountRepository.updateAccountCurrency( typeGiven, @@ -261,6 +262,13 @@ export class BidService extends TradeAskBidService implements OnModuleInit { account.id, queryRunner, ); + + await this.accountRepository.updateAccountCurrency( + 'availableKRW', + returnAvailableChange, + account.id, + queryRunner, + ); } private async waitForTransaction( diff --git a/packages/server/src/trade/trade.service.ts b/packages/server/src/trade/trade.service.ts index b26fb4dc..09fab343 100644 --- a/packages/server/src/trade/trade.service.ts +++ b/packages/server/src/trade/trade.service.ts @@ -160,7 +160,7 @@ export class TradeService { const accountBalance = this.calculateAccountBalance(trade, userAccount); await this.accountRepository.updateAccountCurrency( - trade.tradeCurrency, + 'availableKRW', accountBalance, userAccount.id, queryRunner, @@ -238,7 +238,7 @@ export class TradeService { private calculateAccountBalance(trade: any, userAccount: any): number { return parseFloat( - (trade.price * trade.quantity + userAccount[trade.tradeCurrency]).toFixed( + (trade.price * trade.quantity + userAccount.availableKRW).toFixed( 8, ), );