Skip to content

Commit

Permalink
Merge pull request #88 from Boost-Coder/feature/RankAll-#87
Browse files Browse the repository at this point in the history
[#87] 모든 역량 랭크를 조회하는 기능 구현
  • Loading branch information
koomin1227 authored Apr 20, 2024
2 parents 4475ba9 + a3bad4f commit 3a6b0ee
Show file tree
Hide file tree
Showing 15 changed files with 243 additions and 185 deletions.
1 change: 0 additions & 1 deletion src/Entity/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
OneToOne,
PrimaryGeneratedColumn,
Expand Down
2 changes: 0 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import * as process from 'process';
import { HttpLoggerInterceptor } from './utils/httpLoggerInterceptor';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { HttpExceptionFilter } from './utils/httpExceptionFilter';
import { WinstonModule } from 'nest-winston';
import { transports } from 'winston';

@Module({
imports: [
Expand Down
2 changes: 1 addition & 1 deletion src/stat/dto/rank-list-option.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class RankListDto {
rank: number;

@ApiProperty()
user_id: string;
userId: string;

@ApiProperty()
point: number;
Expand Down
3 changes: 3 additions & 0 deletions src/stat/dto/stat-find.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ export class StatFindDto {

@ApiProperty()
grade: number;

@ApiProperty()
totalPoint: number;
}
84 changes: 81 additions & 3 deletions src/stat/rank.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,81 @@ export class RankController {
return await this.algorithmService.getAlgorithms(options);
}

@Get('/users/:id')
@Get('github')
@ApiTags('rank')
@ApiOperation({
summary: '깃허브 전체 랭킹 API',
description:
'깃허브 역량의 랭킹리스트를 반환한다. 페이지네이션이 가능하고 학과 별로 필터링 가능하다. (학번 필터링은 아직 미구현) 커서 사용시 cursorPoint, cursorUserId 두개를 동시에 넣어야한다. 각각 마지막으로 받은 유저의 점수와 유저 아이디이다.',
})
@ApiBearerAuth('accessToken')
@ApiOkResponse({
description: '깃허브 랭킹 반환',
type: [RankListDto],
})
@ApiUnauthorizedResponse({
description: 'jwt 관련 문제 (인증 시간이 만료됨, jwt를 보내지 않음)',
})
@ApiForbiddenResponse({
description: '허용되지 않은 자원에 접근한 경우. 즉, 권한이 없는 경우',
})
@ApiInternalServerErrorResponse({
description: '서버 오류',
})
async findGithubRank(@Query() options: RankListOptionDto) {
return await this.githubService.getGithubRank(options);
}

@Get('grade')
@ApiTags('rank')
@ApiOperation({
summary: '학점 전체 랭킹 API',
description:
'학점 역량의 랭킹리스트를 반환한다. 페이지네이션이 가능하고 학과 별로 필터링 가능하다. (학번 필터링은 아직 미구현) 커서 사용시 cursorPoint, cursorUserId 두개를 동시에 넣어야한다. 각각 마지막으로 받은 유저의 점수와 유저 아이디이다.',
})
@ApiBearerAuth('accessToken')
@ApiOkResponse({
description: '깃허브 랭킹 반환',
type: [RankListDto],
})
@ApiUnauthorizedResponse({
description: 'jwt 관련 문제 (인증 시간이 만료됨, jwt를 보내지 않음)',
})
@ApiForbiddenResponse({
description: '허용되지 않은 자원에 접근한 경우. 즉, 권한이 없는 경우',
})
@ApiInternalServerErrorResponse({
description: '서버 오류',
})
async findGradeRank(@Query() options: RankListOptionDto) {
return await this.gradeService.getGradeRank(options);
}

@Get('total')
@ApiTags('rank')
@ApiOperation({
summary: '종합 전체 랭킹 API',
description:
'학점 역량의 랭킹리스트를 반환한다. 페이지네이션이 가능하고 학과 별로 필터링 가능하다. (학번 필터링은 아직 미구현) 커서 사용시 cursorPoint, cursorUserId 두개를 동시에 넣어야한다. 각각 마지막으로 받은 유저의 점수와 유저 아이디이다.',
})
@ApiBearerAuth('accessToken')
@ApiOkResponse({
description: '종합 랭킹 반환',
type: [RankListDto],
})
@ApiUnauthorizedResponse({
description: 'jwt 관련 문제 (인증 시간이 만료됨, jwt를 보내지 않음)',
})
@ApiForbiddenResponse({
description: '허용되지 않은 자원에 접근한 경우. 즉, 권한이 없는 경우',
})
@ApiInternalServerErrorResponse({
description: '서버 오류',
})
async findTotalRank(@Query() options: RankListOptionDto) {
return await this.totalService.getTotalRank(options);
}

@ApiTags('rank')
@ApiOperation({
summary: '유저의 각 부문 별 랭킹 API',
Expand All @@ -73,10 +147,14 @@ export class RankController {
@ApiInternalServerErrorResponse({
description: '서버 오류',
})
async findUsersRank(@Param('id') userId, @Query() options: PointFindDto) {
@Get('/users/:id')
async findUsersRank(
@Param('id') userId: string,
@Query() options: PointFindDto,
) {
const user = await this.userService.findUserByUserId(userId);

if (options && user.major !== options.major) {
if (options.major && user.major !== options.major) {
return new RankFindDto(null, null, null, null);
} else {
const algorithmRank =
Expand Down
40 changes: 6 additions & 34 deletions src/stat/repository/algorithm.repository.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { BaseRepository } from '../../utils/base.repository';
import { Inject, Injectable, Scope } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { REQUEST } from '@nestjs/core';
import { Algorithm } from '../../Entity/algorithm';
import { RankListDto, RankListOptionDto } from '../dto/rank-list-option.dto';
import { RankListOptionDto } from '../dto/rank-list-option.dto';
import { User } from '../../Entity/user';
import { RankController } from '../rank.controller';
import { PointFindDto } from '../dto/rank-find.dto';
import { StatRepository } from '../../utils/stat.repository';

@Injectable({ scope: Scope.REQUEST })
export class AlgorithmRepository extends BaseRepository {
private repository: Repository<Algorithm>;
export class AlgorithmRepository extends StatRepository {
constructor(dataSource: DataSource, @Inject(REQUEST) req: Request) {
super(dataSource, req);
this.repository = this.getRepository(Algorithm);
super(dataSource, req, Algorithm);
}

async save(algorithm: Algorithm) {
Expand All @@ -23,31 +20,6 @@ export class AlgorithmRepository extends BaseRepository {
async findOneById(userId: string) {
return await this.repository.findOneBy({ userId: userId });
}

async findAlgorithmWithRank(
options: RankListOptionDto,
): Promise<[RankListDto]> {
const queryBuilder = this.repository
.createQueryBuilder()
.select(['b.rank', 'b.user_id', 'b.point', 'b.nickname'])
.distinct(true)
.from((sub) => {
return sub
.select('RANK() OVER (ORDER BY a.point DESC)', 'rank')
.addSelect('a.user_id', 'user_id')
.addSelect('a.point', 'point')
.addSelect('u.nickname', 'nickname')
.from(Algorithm, 'a')
.innerJoin(User, 'u', 'a.user_id = u.user_id')
.where(this.createClassificationOption(options));
}, 'b')
.where(this.createCursorOption(options))
.orderBy('point', 'DESC')
.addOrderBy('user_id')
.limit(3);
return await (<Promise<[RankListDto]>>queryBuilder.getRawMany());
}

public async findIndividualAlgorithmRank(
userId: string,
options: PointFindDto,
Expand All @@ -66,7 +38,7 @@ export class AlgorithmRepository extends BaseRepository {
.addSelect('u.major', 'major')
.where(this.createClassificationOption(options));
}, 'b')
.where(`b.user_id = ${userId}`);
.where(`b.user_id = '${userId}'`);

return await queryBuilder.getRawOne();
}
Expand Down
47 changes: 5 additions & 42 deletions src/stat/repository/github.repository.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { BaseRepository } from '../../utils/base.repository';
import { Inject, Injectable, Scope } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { Github } from '../../Entity/github';
import { DataSource, Repository } from 'typeorm';
import { DataSource } from 'typeorm';
import { REQUEST } from '@nestjs/core';
import { RankListOptionDto } from '../dto/rank-list-option.dto';
import { Algorithm } from '../../Entity/algorithm';
import { User } from '../../Entity/user';
import { PointFindDto } from '../dto/rank-find.dto';
import { StatRepository } from '../../utils/stat.repository';

@Injectable()
export class GithubRepository extends BaseRepository {
private repository: Repository<Github>;

export class GithubRepository extends StatRepository {
constructor(dataSource: DataSource, @Inject(REQUEST) req: Request) {
super(dataSource, req);
this.repository = this.getRepository(Github);
super(dataSource, req, Github);
}

public async save(github: Github) {
Expand Down Expand Up @@ -43,34 +36,4 @@ export class GithubRepository extends BaseRepository {
public async findAll() {
return await this.repository.find();
}

public async findIndividualGithubRank(
userId: string,
options: PointFindDto,
) {
const queryBuilder = this.repository
.createQueryBuilder()
.select(['b.rank', 'b.user_id'])
.distinct(true)
.from((sub) => {
return sub
.select('RANK() OVER (ORDER BY g.point DESC)', 'rank')
.addSelect('g.user_id', 'user_id')
.addSelect('g.point', 'point')
.from(Github, 'g')
.innerJoin(User, 'u', 'g.user_id = u.user_id')
.where(this.createClassificationOption(options));
}, 'b')
.where(`b.user_id = ${userId}`);

return await queryBuilder.getRawOne();
}

createClassificationOption(options: PointFindDto) {
if (options.major != null) {
return `u.major like '${options.major}'`;
} else {
return `u.id > 0`;
}
}
}
45 changes: 4 additions & 41 deletions src/stat/repository/grade.repository.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { BaseRepository } from '../../utils/base.repository';
import { Inject, Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { Github } from '../../Entity/github';
import { DataSource } from 'typeorm';
import { REQUEST } from '@nestjs/core';
import { Grade } from '../../Entity/grade';
import { RankListOptionDto } from '../dto/rank-list-option.dto';
import { User } from '../../Entity/user';
import { PointFindDto } from '../dto/rank-find.dto';
import { StatRepository } from '../../utils/stat.repository';

@Injectable()
export class GradeRepository extends BaseRepository {
private repository: Repository<Grade>;

export class GradeRepository extends StatRepository {
constructor(dataSource: DataSource, @Inject(REQUEST) req: Request) {
super(dataSource, req);
this.repository = this.getRepository(Grade);
super(dataSource, req, Grade);
}

public async save(grade: Grade) {
Expand All @@ -38,34 +31,4 @@ export class GradeRepository extends BaseRepository {
public async delete(id: string) {
await this.repository.delete({ userId: id });
}

public async findIndividualGradeRank(
userId: string,
options: PointFindDto,
) {
const queryBuilder = this.repository
.createQueryBuilder()
.select(['b.rank', 'b.user_id'])
.distinct(true)
.from((sub) => {
return sub
.select('RANK() OVER (ORDER BY g.point DESC)', 'rank')
.addSelect('g.user_id', 'user_id')
.addSelect('g.point', 'point')
.from(Grade, 'g')
.innerJoin(User, 'u', 'g.user_id = u.user_id')
.where(this.createClassificationOption(options));
}, 'b')
.where(`b.user_id = ${userId}`);

return await queryBuilder.getRawOne();
}

createClassificationOption(options: PointFindDto) {
if (options.major != null) {
return `u.major like '${options.major}'`;
} else {
return `u.id > 0`;
}
}
}
47 changes: 4 additions & 43 deletions src/stat/repository/total.repository.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import { BaseRepository } from '../../utils/base.repository';
import { DataSource, Repository } from 'typeorm';
import { Grade } from '../../Entity/grade';
import { DataSource } from 'typeorm';
import { Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { TotalPoint } from '../../Entity/totalPoint';
import { RankListOptionDto } from '../dto/rank-list-option.dto';
import { Algorithm } from '../../Entity/algorithm';
import { User } from '../../Entity/user';
import { PointFindDto } from '../dto/rank-find.dto';

export class TotalRepository extends BaseRepository {
private repository: Repository<TotalPoint>;
import { StatRepository } from '../../utils/stat.repository';

export class TotalRepository extends StatRepository {
constructor(dataSource: DataSource, @Inject(REQUEST) req: Request) {
super(dataSource, req);
this.repository = this.getRepository(TotalPoint);
super(dataSource, req, TotalPoint);
}

async findOneById(userId: string) {
Expand All @@ -28,35 +20,4 @@ export class TotalRepository extends BaseRepository {
async save(total: TotalPoint) {
return await this.repository.save(total);
}

public async findIndividualAlgorithmRank(
userId: string,
options: PointFindDto,
) {
const queryBuilder = this.repository
.createQueryBuilder()
.select(['b.rank', 'b.user_id', 'b.major'])
.distinct(true)
.from((sub) => {
return sub
.select('RANK() OVER (ORDER BY a.point DESC)', 'rank')
.addSelect('a.user_id', 'user_id')
.addSelect('a.point', 'point')
.from(Algorithm, 'a')
.innerJoin(User, 'u', 'a.user_id = u.user_id')
.addSelect('u.major', 'major')
.where(this.createClassificationOption(options));
}, 'b')
.where(`b.user_id = ${userId}`);

return await queryBuilder.getRawOne();
}

createClassificationOption(options: PointFindDto) {
if (options.major != null) {
return `u.major like '${options.major}'`;
} else {
return `u.id > 0`;
}
}
}
Loading

0 comments on commit 3a6b0ee

Please sign in to comment.