Skip to content

Commit

Permalink
Merge pull request #79 from boostcampwm-2024/be/feature/get_my_rank
Browse files Browse the repository at this point in the history
[BE/feature] 랭킹 조회 api 구현
  • Loading branch information
HBLEEEEE authored Nov 21, 2024
2 parents a51702c + c69bd8b commit 803ba4b
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 16 deletions.
14 changes: 10 additions & 4 deletions apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}

Expand All @@ -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 };
Expand Down
3 changes: 2 additions & 1 deletion apps/backend/src/global/successhandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export const successMessage = {
CREATE_ORDER_SUCCESS: { code: 201, message: '주문이 성공적으로 생성되었습니다.' },
DELETE_ORDER_SUCCESS: { code: 201, message: '주문이 성공적으로 삭제되었습니다.' },
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<T>(success: SuccessMessage, data: Nullable<T> = null) {
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/global/utils/jwtAuthGuard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions apps/backend/src/rank/decorator/getRank.decorator.ts
Original file line number Diff line number Diff line change
@@ -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
})
);
}
4 changes: 2 additions & 2 deletions apps/backend/src/rank/decorator/top5rank.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
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(
TokenDecorator(),
ApiResponse({
status: 200,
description: '5명 조회 성공',
type: rank5SuccessResponseDto
type: Rank5SuccessResponseDto
})
);
}
29 changes: 29 additions & 0 deletions apps/backend/src/rank/dto/getRank.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
}
8 changes: 4 additions & 4 deletions apps/backend/src/rank/dto/top5rank.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';

export class rank5DataDto {
export class Rank5DataDto {
@ApiProperty({
description: '닉네임',
example: '홍길동'
Expand All @@ -14,7 +14,7 @@ export class rank5DataDto {
score: number;
}

export class rank5SuccessResponseDto {
export class Rank5SuccessResponseDto {
@ApiProperty({
description: '응답 코드',
example: 200
Expand All @@ -29,7 +29,7 @@ export class rank5SuccessResponseDto {

@ApiProperty({
description: '응답 데이터',
type: [rank5DataDto],
type: [Rank5DataDto],
example: [
{ nickname: '파이썬', score: 50000 },
{ nickname: '자바', score: 40000 },
Expand All @@ -38,5 +38,5 @@ export class rank5SuccessResponseDto {
{ nickname: 'C 언어', score: 10000 }
]
})
data: rank5DataDto;
data: Rank5DataDto;
}
16 changes: 14 additions & 2 deletions apps/backend/src/rank/rank.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion apps/backend/src/rank/rank.queries.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 12 additions & 0 deletions apps/backend/src/rank/rank.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '해당 유저의 랭킹이 존재 하지 않습니다.'
};
}
}

0 comments on commit 803ba4b

Please sign in to comment.