Skip to content

Commit

Permalink
[BE -#166] 벌크 삽입으로 리팩토링 (#167)
Browse files Browse the repository at this point in the history
* feat: 서비스 레이어 - 벌크 삽입 코드 리팩토링

* feat: 레포지토리 레이어 - 벌크 삽입 코드 분리

* feat: 컨트롤러, class 레포지토리 메서드명 수정
  • Loading branch information
glaxyt authored Dec 12, 2024
1 parent b7c8e11 commit e2ec59d
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 41 deletions.
2 changes: 1 addition & 1 deletion packages/server/src/module/quiz/quizzes/quiz.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class QuizController {
@Post('classes/:classId/quizzes')
@UseInterceptors(new TransformInterceptor(ResponseDto))
async createQuiz(@Param('classId') classId: number, @Body() dto: CreateQuizListRequestDto) {
return await this.quizService.createQuiz(classId, dto);
return await this.quizService.createBulkQuizWithChoices(classId, dto);
}

@Get('classes')
Expand Down
77 changes: 40 additions & 37 deletions packages/server/src/module/quiz/quizzes/quiz.service.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import {
Injectable,
HttpException,
HttpStatus,
Param,
NotFoundException,
InternalServerErrorException,
} from '@nestjs/common';
import { DataSource } from 'typeorm';
import { Injectable, HttpException, HttpStatus, NotFoundException } from '@nestjs/common';
import { DataSource, InsertResult } from 'typeorm';
import { QuizRepository } from './repositories/quiz.repository';
import { ChoiceRepository } from './repositories/choice.repository';
import { ClassRepository } from './repositories/class.repository';
import { CreateClassRequestDto } from './dto/request/create-class.request.dto';
import { CreateClassResponseDto } from './dto/response/create-class.response.dto';
import { CreateQuizListRequestDto } from './dto/request/create-quizlist.request.dto';
import { ResponseDto } from '../../utils/dto/response.dto';
import { Quiz } from './entities/quiz.entity';
import { QuizResponseDto } from './dto/response/quiz.response.dto';
import { UpdateClassRequestDto } from './dto/request/update-class.request.dto';
import { UpdateQuizRequestDto } from './dto/request/update-quiz.request.dto';
import { UpdateQuizListRequestDto } from './dto/request/update-quizlist.request.dto';
import { GetClassResponseDto } from './dto/response/get-class.response.dto';
import { Class } from './entities/class.entity';
Expand All @@ -39,31 +29,44 @@ export class QuizService {
return responseDto;
}

async createQuiz(classId: number, quizData: CreateQuizListRequestDto): Promise<void> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

try {
async createBulkQuizWithChoices(
classId: number,
quizData: CreateQuizListRequestDto,
): Promise<void> {
return this.dataSource.transaction(async (manager) => {
await this.classRepository.findClassById(classId);
await Promise.all(
quizData.quizzes.map(async (quiz) => {
const quizEntity = await this.quizRepository.create(classId, quiz);
await Promise.all(
quiz.choices.map(async (choice) => {
await this.choiceRepository.create(quizEntity.id, choice);
}),
);
}),
);
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException('Failed to create quiz');
} finally {
await queryRunner.release();
}

const quizValues = this.prepareQuizData(classId, quizData);
const insertedQuizzes = await this.quizRepository.createBulkQuizzes(manager, quizValues);

const choiceValues = this.prepareChoiceData(quizData, insertedQuizzes);
await this.choiceRepository.createBulkChoices(manager, choiceValues);
});
}

private prepareQuizData(classId: number, quizData: CreateQuizListRequestDto) {
return quizData.quizzes.map((quiz) => ({
classId,
content: quiz.content,
quizType: quiz.quizType,
timeLimit: quiz.timeLimit,
point: quiz.point,
position: quiz.position,
createdAt: new Date(),
}));
}

private prepareChoiceData(quizData: CreateQuizListRequestDto, insertedQuizzes: InsertResult) {
const quizIds = insertedQuizzes.identifiers.map((identifier) => identifier.id);
return quizData.quizzes.flatMap((quiz, index) =>
quiz.choices.map((choice) => ({
quizId: quizIds[index],
content: choice.content,
isCorrect: choice.isCorrect,
position: choice.position,
createdAt: new Date(),
})),
);
}

async getAllClasses(): Promise<GetClassResponseDto[]> {
Expand All @@ -72,7 +75,7 @@ export class QuizService {
return classEntities.map((classEntity: Class) => GetClassResponseDto.fromEntity(classEntity));
}

async getQuizzesByClassId(classId: number): Promise<any> {
async getQuizzesByClassId(classId: number): Promise<QuizResponseDto[]> {
const quizzes = await this.quizRepository.findByClassId(classId);

if (!quizzes || quizzes.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ export class ChoiceRepository {
}
}

async createBulkChoices(manager: EntityManager, choiceData: any[]) {
try {
if (!choiceData.length) {
throw new Error('Choice data array is empty');
}

return await manager.createQueryBuilder().insert().into(Choice).values(choiceData).execute();
} catch (error) {
throw new InternalServerErrorException('Failed to create choices');
}
}

async findById(id: number): Promise<Choice> {
try {
const choice = await this.repository.findOne({ where: { id } });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { Injectable, InternalServerErrorException, NotFoundException } from '@ne
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import { Class } from '../entities/class.entity';
import { Quiz } from '../entities/quiz.entity';
import { Choice } from '../entities/choice.entity';

@Injectable()
export class ClassRepository {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, In, Repository } from 'typeorm';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { Quiz } from '../entities/quiz.entity';
import { Choice } from '../entities/choice.entity';
import { CreateQuizRequestDto } from '../dto/request/create-quiz.request.dto';
Expand Down Expand Up @@ -32,6 +32,18 @@ export class QuizRepository {
}
}

async createBulkQuizzes(manager: EntityManager, quizData: any[]) {
try {
if (!quizData.length) {
throw new Error('Quiz data array is empty');
}

return await manager.createQueryBuilder().insert().into(Quiz).values(quizData).execute();
} catch (error) {
throw new InternalServerErrorException('Failed to create quizzes');
}
}

async findAll(): Promise<Quiz[]> {
try {
const result = await this.repository.find();
Expand Down

0 comments on commit e2ec59d

Please sign in to comment.