-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat: quiz controller, service 파일 초기 설정 * feat: class entity, requestDTO 구현 * feat: quiz entity, requestDTO 구현 * feat: quiz Controller, Service 구현 * refactor: Entity Class 네이밍 테이블 명으로 수정 * feat: quiz, class repository 구현 * feat: class, quiz, choice nested DTO 객체로 구성 * feat: choice entity 구현 * feat: choice repository 구현 * feat: repository 상속-> @InjectRepository 수정 * feat: app, quiz module 연결 * feat: quiz controller, service 구현 * feat: controller, service 퀴즈 생성 API 구현 * feat: 퀴즈 생성 API 관련 DTO 구현 * feat: 퀴즈 생성 API 관련 entity, repository 구현 * feat: 기본 responseDto 구현 * refactor: 클래스, 퀴즈 생성 api를 restful하게 수정 * refactor: Entity 저장 시에 객체 구조 분해 할당으로 가독성 증가
- Loading branch information
Showing
15 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { Quiz } from './quizzes/entities/quiz.entity'; | ||
import { Choice } from './quizzes/entities/choice.entity'; | ||
import { Class } from './quizzes/entities/class.entity'; | ||
import { QuizRepository } from './quizzes/repositories/quiz.repository'; | ||
import { ChoiceRepository } from './quizzes/repositories/choice.repository'; | ||
import { ClassRepository } from './quizzes/repositories/class.repository'; | ||
import { QuizService } from './quizzes/quiz.service'; | ||
import { QuizController } from './quizzes/quiz.controller'; | ||
|
||
|
||
@Module({ | ||
imports: [ | ||
TypeOrmModule.forFeature([Quiz, Choice, Class]) | ||
], | ||
controllers: [QuizController], | ||
providers: [ | ||
QuizService, | ||
QuizRepository, | ||
ChoiceRepository, | ||
ClassRepository | ||
], | ||
exports: [ | ||
QuizRepository, | ||
ChoiceRepository, | ||
ClassRepository | ||
] | ||
}) | ||
export class QuizModule {} |
15 changes: 15 additions & 0 deletions
15
packages/BE/src/module/quiz/quizzes/dto/create-choice.request.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { IsNotEmpty, IsString, IsBoolean, IsNumber } from 'class-validator'; | ||
|
||
export class CreateChoiceRequestDto { | ||
@IsNumber() | ||
@IsNotEmpty() | ||
position: number; | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
content: string; | ||
|
||
@IsBoolean() | ||
@IsNotEmpty() | ||
isCorrect: boolean; | ||
} |
11 changes: 11 additions & 0 deletions
11
packages/BE/src/module/quiz/quizzes/dto/create-class.request.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { IsNotEmpty, IsString } from 'class-validator'; | ||
|
||
export class CreateClassRequestDto { | ||
@IsString() | ||
@IsNotEmpty() | ||
title: string; | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
description: string; | ||
} |
34 changes: 34 additions & 0 deletions
34
packages/BE/src/module/quiz/quizzes/dto/create-quiz.request.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { IsNotEmpty, IsString, IsNumber, IsArray, ValidateNested } from "class-validator"; | ||
import { Type } from "class-transformer"; | ||
import { CreateChoiceRequestDto } from "./create-choice.request.dto"; | ||
|
||
export class CreateQuizRequestDto { | ||
@IsNumber() | ||
@IsNotEmpty() | ||
position: number; | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
content: string; | ||
|
||
@IsNumber() | ||
@IsNotEmpty() | ||
timeLimit: number; | ||
|
||
@IsNumber() | ||
@IsNotEmpty() | ||
point: number; | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
questionType: string; | ||
|
||
// @IsNumber() | ||
// @IsNotEmpty() | ||
// position: number; | ||
|
||
@IsArray() | ||
@ValidateNested({ each: true }) | ||
@Type(() => CreateChoiceRequestDto) | ||
choices: CreateChoiceRequestDto[]; | ||
} |
10 changes: 10 additions & 0 deletions
10
packages/BE/src/module/quiz/quizzes/dto/create-quizlist.request.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { IsNumber, IsNotEmpty, IsArray, ValidateNested } from 'class-validator'; | ||
import { Type } from 'class-transformer'; | ||
import { CreateQuizRequestDto } from './create-quiz.request.dto'; | ||
|
||
export class CreateQuizListRequestDto { | ||
@IsArray() | ||
@ValidateNested({ each: true }) | ||
@Type(() => CreateQuizRequestDto) | ||
quizzes: CreateQuizRequestDto[]; | ||
} |
19 changes: 19 additions & 0 deletions
19
packages/BE/src/module/quiz/quizzes/entities/choice.entity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; | ||
|
||
@Entity() | ||
export class Choice { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
@Column() | ||
quiz_id: number; | ||
|
||
@Column() | ||
position: number; | ||
|
||
@Column() | ||
content: string; | ||
|
||
@Column() | ||
is_correct: boolean; | ||
} |
19 changes: 19 additions & 0 deletions
19
packages/BE/src/module/quiz/quizzes/entities/class.entity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; | ||
|
||
@Entity() | ||
export class Class { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
@Column() | ||
creator_id: number; | ||
|
||
@Column() | ||
title: string; | ||
|
||
@Column() | ||
description: string; | ||
|
||
@Column() | ||
created_at: Date; | ||
} |
28 changes: 28 additions & 0 deletions
28
packages/BE/src/module/quiz/quizzes/entities/quiz.entity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; | ||
|
||
@Entity() | ||
export class Quiz { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
@Column() | ||
class_id: number; | ||
|
||
@Column() | ||
position: number | ||
|
||
@Column() | ||
content: string; | ||
|
||
@Column() | ||
time_limit: number; | ||
|
||
@Column() | ||
point: number; | ||
|
||
@Column() | ||
question_type: string; // 퀴즈 타입에 따른 구분이 가능한 String Enum 혹은 정수로 해도 좋을 것 같음 | ||
|
||
// @Column() | ||
// position: number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Body, Controller, Param, Post, UsePipes, ValidationPipe } from "@nestjs/common"; | ||
import { QuizService } from "./quiz.service"; | ||
// index.ts barrel file로 처리해도 좋을 듯. | ||
import { CreateClassRequestDto } from "./dto/create-class.request.dto"; | ||
import { CreateQuizListRequestDto } from "./dto/create-quizlist.request.dto"; | ||
|
||
@Controller('api') | ||
export class QuizController { | ||
constructor( private readonly quizService : QuizService) {} | ||
|
||
// class 생성 | ||
@Post('classes') | ||
@UsePipes(ValidationPipe) | ||
async createClass(@Body() dto : CreateClassRequestDto) { | ||
return await this.quizService.createClass(dto); | ||
} | ||
|
||
@Post('classes/:classId/quizzes') | ||
@UsePipes(ValidationPipe) | ||
async createQuiz(@Param('classId') classId: number, | ||
@Body() dto : CreateQuizListRequestDto) { | ||
return await this.quizService.createQuiz(classId, dto); | ||
} | ||
|
||
// @Delete('delete-class') | ||
// @UsePipes(ValidationPipe) | ||
// async deleteQuiz(@Body() dto : CreateClassRequestDto) { | ||
// return await this.quizService.deleteClass(dto); | ||
// } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { Injectable, HttpException, HttpStatus, Param } from '@nestjs/common'; | ||
import { DataSource } from 'typeorm'; | ||
import { QuizRepository } from './repositories/quiz.repository'; | ||
import { ChoiceRepository } from './repositories/choice.repository'; | ||
import { ClassRepository } from './repositories/class.repository'; | ||
import { CreateClassRequestDto } from './dto/create-class.request.dto'; | ||
import { CreateQuizListRequestDto } from './dto/create-quizlist.request.dto'; | ||
import { CreateQuizRequestDto } from './dto/create-quiz.request.dto'; | ||
import { CreateChoiceRequestDto } from './dto/create-choice.request.dto'; | ||
import { ResponseDto } from '../../utils/dto/response.dto'; | ||
import { Quiz } from './entities/quiz.entity'; | ||
import { Class } from './entities/class.entity'; | ||
import { Choice } from './entities/choice.entity'; | ||
|
||
@Injectable() | ||
export class QuizService { | ||
constructor( | ||
private readonly quizRepository: QuizRepository, | ||
private readonly choiceRepository: ChoiceRepository, | ||
private readonly classRepository: ClassRepository, | ||
private readonly dataSource: DataSource, | ||
) {} | ||
|
||
async createClass(createClassRequestDto: CreateClassRequestDto): Promise<void> { | ||
try { | ||
const classData = await this.classRepository.create({ | ||
title: createClassRequestDto.title, | ||
description: createClassRequestDto.description, | ||
}); | ||
} catch (error) { | ||
console.error('error:', error); | ||
} | ||
} | ||
|
||
// dto가 여러개라서 처리하기 좀 그러네 quiz, choice는 dto가 아니라 인터페이스로 구현하는게 좋지않을까라는 생각...? | ||
async createQuiz(classId: number, quizData: CreateQuizListRequestDto): Promise<ResponseDto> { | ||
// 그럼 이 컨트롤러에서 dto 구분이 힘들다 | ||
// 너무 이 메서드에 책임이 많은게 아닌가 라는 생각도 든다. | ||
// const queryRunner = this.dataSource.createQueryRunner(); | ||
// await queryRunner.connect(); | ||
// await queryRunner.startTransaction(); | ||
try { | ||
// class_id가 유효한지 확인 | ||
const is_valid_class = await this.classRepository.findClassById(classId); | ||
if (!is_valid_class) { | ||
throw new Error(`Class with ID ${classId} not found`); | ||
} | ||
|
||
await Promise.all( | ||
quizData.quizzes.map(async (quiz) => { | ||
const quizEntity = await this.quizRepository.create(classId, quiz); | ||
quiz.choices.map(async (choice) => { | ||
this.choiceRepository.create(quizEntity.id, choice); | ||
}); | ||
} | ||
)); | ||
|
||
// await queryRunner.commitTransaction(); | ||
|
||
|
||
return { | ||
success: true, | ||
message: 'Quiz created successfully', | ||
}; | ||
} catch (error) { | ||
// await queryRunner.rollbackTransaction(); | ||
throw new HttpException({ | ||
status: HttpStatus.FORBIDDEN, | ||
error: `${error}`, | ||
}, HttpStatus.FORBIDDEN, { | ||
cause: error | ||
}); | ||
} | ||
// } finally { | ||
// await queryRunner.release(); | ||
// } | ||
} | ||
|
||
async findAll(): Promise<Quiz[]> { | ||
return this.quizRepository.findAll(); | ||
} | ||
|
||
// async findById(id: number): Promise<Quiz> { | ||
// const quiz = await this.quizRepository.findById(id); | ||
// if (!quiz) { | ||
// throw new NotFoundException(`Quiz with ID ${id} not found`); | ||
// } | ||
// return quiz; | ||
// } | ||
// async updateQuiz(id: number, updateQuizDto: UpdateQuizDto): Promise<Quiz> {} | ||
|
||
// async deleteQuiz(id: number): Promise<void> {} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
packages/BE/src/module/quiz/quizzes/repositories/choice.repository.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
import { InjectRepository } from "@nestjs/typeorm"; | ||
import { Repository } from 'typeorm'; | ||
import { Choice } from '../entities/choice.entity'; | ||
import { CreateChoiceRequestDto } from "../dto/create-choice.request.dto"; | ||
|
||
@Injectable() | ||
export class ChoiceRepository { | ||
constructor( | ||
@InjectRepository(Choice) | ||
private readonly repository: Repository<Choice> | ||
) {} | ||
|
||
async create(quiz_id: number, choiceData: CreateChoiceRequestDto): Promise<Choice> { | ||
const { position, content, isCorrect: is_correct } = choiceData; | ||
const choiceEntity = this.repository.create({ | ||
quiz_id, | ||
position, | ||
content, | ||
is_correct, | ||
}); | ||
return await this.repository.save(choiceEntity); | ||
} | ||
|
||
|
||
async findById(id: number): Promise<Choice> { | ||
return this.repository.findOne({ where: { id } }); | ||
} | ||
|
||
async findAll(): Promise<Choice[]> { | ||
return this.repository.find(); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
packages/BE/src/module/quiz/quizzes/repositories/class.repository.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Repository } from 'typeorm'; | ||
import { Class } from '../entities/class.entity'; | ||
|
||
@Injectable() | ||
export class ClassRepository { | ||
constructor( | ||
@InjectRepository(Class) | ||
private readonly repository: Repository<Class> | ||
) {} | ||
|
||
async create(classData: Partial<Class>): Promise<Class> { | ||
return this.repository.save(classData); | ||
} | ||
|
||
// async delete(classData: Partial<Class>): Promise<void> { | ||
// return this.repository.delete(classData); | ||
// } | ||
|
||
async findById(id: number): Promise<Class> { | ||
return this.repository.findOne({ where: { id } }); | ||
} | ||
|
||
async findClassById(id: number): Promise<Class> { | ||
return this.repository.findOne({ where: { id } }); | ||
} | ||
|
||
async findAll(): Promise<Class[]> { | ||
return this.repository.find(); | ||
} | ||
} |
Oops, something went wrong.