diff --git a/apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts b/apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts new file mode 100644 index 0000000000..37a8306fce --- /dev/null +++ b/apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts @@ -0,0 +1,8 @@ +export enum ContestSubmissionOrder { + studentIdASC = 'studentId-asc', + studentIdDESC = 'studentId-desc', + realNameASC = 'realName-asc', + realNameDESC = 'realName-desc', + usernameASC = 'username-asc', + usernameDESC = 'username-desc' +} diff --git a/apps/backend/apps/admin/src/submission/submission.resolver.ts b/apps/backend/apps/admin/src/submission/submission.resolver.ts index 91831bd8bb..d401dcd4d0 100644 --- a/apps/backend/apps/admin/src/submission/submission.resolver.ts +++ b/apps/backend/apps/admin/src/submission/submission.resolver.ts @@ -1,7 +1,7 @@ -import { InternalServerErrorException, Logger } from '@nestjs/common' import { Args, Int, Query, Resolver } from '@nestjs/graphql' -import { CursorValidationPipe } from '@libs/pipe' +import { ContestSubmissionOrderPipe, CursorValidationPipe } from '@libs/pipe' import { Submission } from '@admin/@generated' +import { ContestSubmissionOrder } from './enum/contest-submission-order.enum' import { ContestSubmission } from './model/contest-submission.model' import { GetContestSubmissionsInput } from './model/get-contest-submission.input' import { SubmissionDetail } from './model/submission-detail.output' @@ -9,7 +9,6 @@ import { SubmissionService } from './submission.service' @Resolver(() => Submission) export class SubmissionResolver { - private readonly logger = new Logger(SubmissionResolver.name) constructor(private readonly submissionService: SubmissionService) {} /** @@ -28,19 +27,22 @@ export class SubmissionResolver { @Args('cursor', { nullable: true, type: () => Int }, CursorValidationPipe) cursor: number | null, @Args('take', { nullable: true, defaultValue: 10, type: () => Int }) - take: number + take: number, + @Args( + 'order', + { nullable: true, type: () => String }, + ContestSubmissionOrderPipe + ) + order: ContestSubmissionOrder | null ): Promise { - try { - return await this.submissionService.getContestSubmissions( - input, - take, - cursor - ) - } catch (error) { - this.logger.error(error.error) - throw new InternalServerErrorException() - } + return await this.submissionService.getContestSubmissions( + input, + take, + cursor, + order + ) } + /** * 특정 Contest의 특정 제출 내역에 대한 상세 정보를 불러옵니다. */ diff --git a/apps/backend/apps/admin/src/submission/submission.service.ts b/apps/backend/apps/admin/src/submission/submission.service.ts index 90e1f8e6de..2c23699f4d 100644 --- a/apps/backend/apps/admin/src/submission/submission.service.ts +++ b/apps/backend/apps/admin/src/submission/submission.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common' +import type { Prisma } from '@prisma/client' import { plainToInstance } from 'class-transformer' import { EntityNotExistException } from '@libs/exception' import { PrismaService } from '@libs/prisma' import type { Language, ResultStatus } from '@admin/@generated' import { Snippet } from '@admin/problem/model/template.input' +import { ContestSubmissionOrder } from './enum/contest-submission-order.enum' import type { GetContestSubmissionsInput } from './model/get-contest-submission.input' @Injectable() @@ -13,7 +15,8 @@ export class SubmissionService { async getContestSubmissions( input: GetContestSubmissionsInput, take: number, - cursor: number | null + cursor: number | null, + order: ContestSubmissionOrder | null ) { const paginator = this.prisma.getPaginator(cursor) @@ -59,7 +62,8 @@ export class SubmissionService { } } } - } + }, + orderBy: order ? this.getOrderBy(order) : undefined }) const results = contestSubmissions.map((c) => { @@ -84,6 +88,33 @@ export class SubmissionService { return results } + getOrderBy( + order: ContestSubmissionOrder + ): Prisma.SubmissionOrderByWithRelationInput { + const [attr, value] = order.split('-') + + switch (order) { + case ContestSubmissionOrder.studentIdASC: + case ContestSubmissionOrder.studentIdDESC: + case ContestSubmissionOrder.usernameASC: + case ContestSubmissionOrder.usernameDESC: + return { + user: { + [attr]: value + } + } + case ContestSubmissionOrder.realNameASC: + case ContestSubmissionOrder.realNameDESC: + return { + user: { + userProfile: { + [attr]: value + } + } + } + } + } + async getSubmission(id: number) { const submission = await this.prisma.submission.findFirst({ where: { diff --git a/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts b/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts new file mode 100644 index 0000000000..42a93b5205 --- /dev/null +++ b/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts @@ -0,0 +1,24 @@ +import { + BadRequestException, + Injectable, + type PipeTransform +} from '@nestjs/common' +import { ContestSubmissionOrder } from '@admin/submission/enum/contest-submission-order.enum' + +@Injectable() +export class ContestSubmissionOrderPipe implements PipeTransform { + transform(value: unknown) { + if (!value) { + return null + } else if ( + !Object.values(ContestSubmissionOrder).includes( + value as ContestSubmissionOrder + ) + ) { + throw new BadRequestException( + 'Contest-submission-order validation failed' + ) + } + return value + } +} diff --git a/apps/backend/libs/pipe/src/index.ts b/apps/backend/libs/pipe/src/index.ts index 51df868689..fb1fa3d2bd 100644 --- a/apps/backend/libs/pipe/src/index.ts +++ b/apps/backend/libs/pipe/src/index.ts @@ -3,3 +3,4 @@ export * from './id-validation.pipe' export * from './group-id.pipe' export * from './required-int.pipe' export * from './problem-order.pipe' +export * from './contest-submission-order.pipe' diff --git a/collection/admin/Submission/Get Contest Submissions/Succeed.bru b/collection/admin/Submission/Get Contest Submissions/Succeed.bru index d7f3942608..0194d64067 100644 --- a/collection/admin/Submission/Get Contest Submissions/Succeed.bru +++ b/collection/admin/Submission/Get Contest Submissions/Succeed.bru @@ -14,12 +14,14 @@ body:graphql { query GetContestSubmissions( $input: GetContestSubmissionsInput!, $cursor: Int, - $take: Int + $take: Int, + $order: String ) { getContestSubmissions( input: $input, cursor: $cursor, - take: $take + take: $take, + order: $order ) { title studentId @@ -44,7 +46,8 @@ body:graphql:vars { "problemId": 1, "searchingName": "lee" }, - "take": 10 + "take": 10, + "order": "username-desc" } } @@ -55,13 +58,21 @@ docs { - https://github.com/skkuding/codedang/pull/1924 #### 필요 인자 - | `input` | `take` | `cursor` | - |----------|--------|----------| - | 밑에서 설명 | Pagination 구현을 위함 | Pagination 구현을 위함 | + | `input` | `take` | `cursor` |`order`| + |----------|--------|----------|--------| + | 밑에서 설명 | Pagination 구현을 위함 | Pagination 구현을 위함 | 정렬 | `input` - `contestId`: 제출 내역을 불러올 Contest의 ID - `problemId?`: 제출된 내역 중 특정 Problem의 제출만 필터링 - `searchingName?`: 필터링할 User의 realName(없으면 모든 유저) + `order` + - `studentId-asc`: 학번 오름차순 정렬 + - `studentId-desc`: 학번 내림차순 정렬 + - `realName-asc`: 학생이름 오름차순 정렬 + - `realName-desc`: 학생이름 내림차순 정렬 + - `username-asc`: username 오름차순 정렬 + - `username-desc`: username 내림차순 정렬 + }