From 4952676e79e221178b5e505735f6eb6cd5a62d04 Mon Sep 17 00:00:00 2001 From: edder773 Date: Thu, 21 Nov 2024 16:18:11 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20=ED=98=84=EC=9E=AC=20=EB=9E=AD?= =?UTF-8?q?=ED=82=B9=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/backend/src/auth/auth.service.ts | 14 ++++++--- apps/backend/src/global/successhandler.ts | 3 +- apps/backend/src/global/utils/jwtAuthGuard.ts | 4 +-- .../src/rank/decorator/getRank.decorator.ts | 15 ++++++++++ .../src/rank/decorator/top5rank.decorator.ts | 4 +-- apps/backend/src/rank/dto/getRank.dto.ts | 29 +++++++++++++++++++ apps/backend/src/rank/dto/top5rank.dto.ts | 8 ++--- apps/backend/src/rank/rank.controller.ts | 16 ++++++++-- apps/backend/src/rank/rank.queries.ts | 2 +- apps/backend/src/rank/rank.service.ts | 12 ++++++++ 10 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 apps/backend/src/rank/decorator/getRank.decorator.ts create mode 100644 apps/backend/src/rank/dto/getRank.dto.ts diff --git a/apps/backend/src/auth/auth.service.ts b/apps/backend/src/auth/auth.service.ts index 321187e..8deda82 100644 --- a/apps/backend/src/auth/auth.service.ts +++ b/apps/backend/src/auth/auth.service.ts @@ -70,7 +70,10 @@ export class AuthService { throw new HttpException('이메일 또는 비밀번호가 올바르지 않습니다.', HttpStatus.UNAUTHORIZED); const nickname = member.rows[0].nickname; - const { accessToken, refreshToken } = await this.generateTokens(member.rows[0].member_id); + const { accessToken, refreshToken } = await this.generateTokens( + member.rows[0].member_id, + member.rows[0].nickname + ); return { nickname, accessToken, refreshToken }; } @@ -88,12 +91,15 @@ export class AuthService { async SocialLogin(email: string, nickname: string) { const member = await this.verifyUser(email, nickname); - const { accessToken, refreshToken } = await this.generateTokens(member.rows[0].member_id); + const { accessToken, refreshToken } = await this.generateTokens( + member.rows[0].member_id, + member.rows[0].nickname + ); return { nickname, accessToken, refreshToken }; } - private async generateTokens(memberId: number) { - const payload = { memberId }; + private async generateTokens(memberId: number, nickname: string) { + const payload = { memberId, nickname }; const accessToken = this.jwtService.sign(payload, { expiresIn: '1h' }); const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' }); return { accessToken, refreshToken }; diff --git a/apps/backend/src/global/successhandler.ts b/apps/backend/src/global/successhandler.ts index 2d20b6e..55e71ac 100644 --- a/apps/backend/src/global/successhandler.ts +++ b/apps/backend/src/global/successhandler.ts @@ -16,7 +16,8 @@ export const successMessage = { DELETE_MAIL_SUCCESS: { code: 200, message: '메일 삭제를 완료했습니다.' }, GET_MAIL_ALARM_SUCCESS: { code: 200, message: 'Catch alarm!.' }, BUY_LOTTO_SUCCESS: { code: 200, message: '로또 구매를 완료했습니다.' }, - TOP5_RANK_GET_SUCCESS: { code: 200, message: '상위 5명을 조회했습니다.' } + GET_TOP5_RANK_SUCCESS: { code: 200, message: '상위 5명을 조회했습니다.' }, + GET_RANK_SUCCESS: { code: 200, message: '현재 랭킹을 조회했습니다.' } }; export function successhandler(success: SuccessMessage, data: Nullable = null) { diff --git a/apps/backend/src/global/utils/jwtAuthGuard.ts b/apps/backend/src/global/utils/jwtAuthGuard.ts index 06ae2cd..987affa 100644 --- a/apps/backend/src/global/utils/jwtAuthGuard.ts +++ b/apps/backend/src/global/utils/jwtAuthGuard.ts @@ -25,8 +25,8 @@ export class JwtAuthGuard { throw new HttpException('만료된 토큰입니다.', HttpStatus.UNAUTHORIZED); } - const { memberId } = decoded; - request.user = { memberId }; + const { memberId, nickname } = decoded; + request.user = { memberId, nickname }; return true; } catch { throw new HttpException('잘못된 토큰입니다.', HttpStatus.UNAUTHORIZED); diff --git a/apps/backend/src/rank/decorator/getRank.decorator.ts b/apps/backend/src/rank/decorator/getRank.decorator.ts new file mode 100644 index 0000000..d2b3845 --- /dev/null +++ b/apps/backend/src/rank/decorator/getRank.decorator.ts @@ -0,0 +1,15 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiResponse } from '@nestjs/swagger'; +import { TokenDecorator } from 'src/global/utils/tokenSwagger'; +import { GetRankSuccessResponseDto } from '../dto/getRank.dto'; + +export function getRankResponseDecorator() { + return applyDecorators( + TokenDecorator(), + ApiResponse({ + status: 200, + description: '랭킹 조회 성공', + type: GetRankSuccessResponseDto + }) + ); +} diff --git a/apps/backend/src/rank/decorator/top5rank.decorator.ts b/apps/backend/src/rank/decorator/top5rank.decorator.ts index a873716..5db2c20 100644 --- a/apps/backend/src/rank/decorator/top5rank.decorator.ts +++ b/apps/backend/src/rank/decorator/top5rank.decorator.ts @@ -1,7 +1,7 @@ import { applyDecorators } from '@nestjs/common'; import { ApiResponse } from '@nestjs/swagger'; import { TokenDecorator } from 'src/global/utils/tokenSwagger'; -import { rank5SuccessResponseDto } from '../dto/top5rank.dto'; +import { Rank5SuccessResponseDto } from '../dto/top5rank.dto'; export function top5rankResponseDecorator() { return applyDecorators( @@ -9,7 +9,7 @@ export function top5rankResponseDecorator() { ApiResponse({ status: 200, description: '5명 조회 성공', - type: rank5SuccessResponseDto + type: Rank5SuccessResponseDto }) ); } diff --git a/apps/backend/src/rank/dto/getRank.dto.ts b/apps/backend/src/rank/dto/getRank.dto.ts new file mode 100644 index 0000000..b2afc0c --- /dev/null +++ b/apps/backend/src/rank/dto/getRank.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class GetRankDataDto { + @ApiProperty({ + description: '순위', + example: '5' + }) + rank: number; +} + +export class GetRankSuccessResponseDto { + @ApiProperty({ + description: '응답 코드', + example: 200 + }) + code: number; + + @ApiProperty({ + description: '응답 메세지', + example: '현재 랭킹을 조회했습니다.' + }) + message: string; + + @ApiProperty({ + description: '응답 데이터', + type: GetRankDataDto + }) + data: GetRankDataDto; +} diff --git a/apps/backend/src/rank/dto/top5rank.dto.ts b/apps/backend/src/rank/dto/top5rank.dto.ts index 0e2e04f..32b5f8a 100644 --- a/apps/backend/src/rank/dto/top5rank.dto.ts +++ b/apps/backend/src/rank/dto/top5rank.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class rank5DataDto { +export class Rank5DataDto { @ApiProperty({ description: '닉네임', example: '홍길동' @@ -14,7 +14,7 @@ export class rank5DataDto { score: number; } -export class rank5SuccessResponseDto { +export class Rank5SuccessResponseDto { @ApiProperty({ description: '응답 코드', example: 200 @@ -29,7 +29,7 @@ export class rank5SuccessResponseDto { @ApiProperty({ description: '응답 데이터', - type: [rank5DataDto], + type: [Rank5DataDto], example: [ { nickname: '파이썬', score: 50000 }, { nickname: '자바', score: 40000 }, @@ -38,5 +38,5 @@ export class rank5SuccessResponseDto { { nickname: 'C 언어', score: 10000 } ] }) - data: rank5DataDto; + data: Rank5DataDto; } diff --git a/apps/backend/src/rank/rank.controller.ts b/apps/backend/src/rank/rank.controller.ts index 36f9e35..f896393 100644 --- a/apps/backend/src/rank/rank.controller.ts +++ b/apps/backend/src/rank/rank.controller.ts @@ -4,17 +4,29 @@ import { successhandler, successMessage } from 'src/global/successhandler'; import { JwtAuthGuard } from 'src/global/utils/jwtAuthGuard'; import { ApiOperation } from '@nestjs/swagger'; import { top5rankResponseDecorator } from './decorator/top5rank.decorator'; +import { User } from 'src/global/utils/memberData'; +import { getRankResponseDecorator } from './decorator/getRank.decorator'; -@UseGuards(JwtAuthGuard) @Controller('api/rank') export class RankController { constructor(private readonly rankService: RankService) {} + @UseGuards(JwtAuthGuard) @ApiOperation({ summary: '상위 랭킹 5명 반환 api' }) @top5rankResponseDecorator() @Get('top5') async top5rank() { const data = await this.rankService.getTopRankings(); - return successhandler(successMessage.TOP5_RANK_GET_SUCCESS, data); + return successhandler(successMessage.GET_TOP5_RANK_SUCCESS, data); + } + + @UseGuards(JwtAuthGuard) + @ApiOperation({ summary: '현재 랭킹 반환 api' }) + @getRankResponseDecorator() + @Get() + async getRanking(@User() user: { nickname: string }) { + const { nickname } = user; + const data = await this.rankService.getRanking(nickname); + return successhandler(successMessage.GET_RANK_SUCCESS, data); } } diff --git a/apps/backend/src/rank/rank.queries.ts b/apps/backend/src/rank/rank.queries.ts index 3cf32e2..fcbe73f 100644 --- a/apps/backend/src/rank/rank.queries.ts +++ b/apps/backend/src/rank/rank.queries.ts @@ -1,5 +1,5 @@ export const rankQueries = { - moneyDataQuery: `SELECT + moneyDataQuery: `SELECT m.nickname, m.total_cash + COALESCE(SUM(mc.quantity * cp.price), 0) AS total_asset FROM diff --git a/apps/backend/src/rank/rank.service.ts b/apps/backend/src/rank/rank.service.ts index 79790c6..b4c4c05 100644 --- a/apps/backend/src/rank/rank.service.ts +++ b/apps/backend/src/rank/rank.service.ts @@ -35,4 +35,16 @@ export class RankService { const members = await this.redisClient.zRangeWithScores('ranking', -5, -1); return members.reverse(); } + + async getRanking(nickname: string) { + const rank = await this.redisClient.zRevRank('ranking', nickname); + if (rank) { + return { + rank: rank + 1 + }; + } + return { + rank: '해당 유저의 랭킹이 존재 하지 않습니다.' + }; + } }