diff --git a/src/main/kotlin/com/swm_standard/phote/entity/ExamResult.kt b/src/main/kotlin/com/swm_standard/phote/entity/ExamResult.kt index f7d0d65..6026739 100644 --- a/src/main/kotlin/com/swm_standard/phote/entity/ExamResult.kt +++ b/src/main/kotlin/com/swm_standard/phote/entity/ExamResult.kt @@ -3,6 +3,7 @@ package com.swm_standard.phote.entity import jakarta.persistence.CascadeType import jakarta.persistence.Column import jakarta.persistence.Entity +import jakarta.persistence.FetchType import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id @@ -16,12 +17,13 @@ import java.util.UUID @SQLDelete(sql = "UPDATE exam_result SET deleted_at = NOW() WHERE exam_result_id = ?") data class ExamResult( @JoinColumn(name = "member_id") - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) val member: Member, val time: Int, @JoinColumn(name = "exam_id") @ManyToOne val exam: Exam, + val totalQuantity: Int, ) : BaseTimeEntity() { @Id @GeneratedValue(strategy = GenerationType.UUID) @@ -33,8 +35,6 @@ data class ExamResult( @OneToMany(mappedBy = "examResult", cascade = [(CascadeType.REMOVE)]) val answers: MutableList = mutableListOf() - fun calculateTotalQuantity(): Int = answers.size - fun increaseTotalCorrect(count: Int) { totalCorrect += count } @@ -44,6 +44,7 @@ data class ExamResult( member: Member, time: Int, exam: Exam, - ) = ExamResult(member, time, exam) + totalQuantity: Int, + ) = ExamResult(member, time, exam, totalQuantity) } } diff --git a/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/CustomExamResultRepository.kt b/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/CustomExamResultRepository.kt new file mode 100644 index 0000000..078284e --- /dev/null +++ b/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/CustomExamResultRepository.kt @@ -0,0 +1,11 @@ +package com.swm_standard.phote.repository.examresultrepository + +import com.swm_standard.phote.entity.ExamResult +import java.util.UUID + +interface CustomExamResultRepository { + fun findAllByExamIdListAndMemberId( + examIdList: List, + memberId: UUID, + ): List +} diff --git a/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/CustomExamResultRepositoryImpl.kt b/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/CustomExamResultRepositoryImpl.kt new file mode 100644 index 0000000..8a421d2 --- /dev/null +++ b/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/CustomExamResultRepositoryImpl.kt @@ -0,0 +1,22 @@ +package com.swm_standard.phote.repository.examresultrepository + +import com.querydsl.jpa.impl.JPAQueryFactory +import com.swm_standard.phote.entity.ExamResult +import com.swm_standard.phote.entity.QExamResult.examResult +import org.springframework.stereotype.Repository +import java.util.UUID + +@Repository +class CustomExamResultRepositoryImpl( + private val jpaQueryFactory: JPAQueryFactory, +) : CustomExamResultRepository { + override fun findAllByExamIdListAndMemberId( + examIdList: List, + memberId: UUID, + ): List = + jpaQueryFactory + .selectFrom(examResult) + .where(examResult.exam.id.`in`(examIdList)) + .where(examResult.member.id.eq(memberId)) + .fetch() +} diff --git a/src/main/kotlin/com/swm_standard/phote/repository/ExamResultRepository.kt b/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/ExamResultRepository.kt similarity index 67% rename from src/main/kotlin/com/swm_standard/phote/repository/ExamResultRepository.kt rename to src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/ExamResultRepository.kt index 0965b06..1472d6a 100644 --- a/src/main/kotlin/com/swm_standard/phote/repository/ExamResultRepository.kt +++ b/src/main/kotlin/com/swm_standard/phote/repository/examresultrepository/ExamResultRepository.kt @@ -1,16 +1,18 @@ -package com.swm_standard.phote.repository +package com.swm_standard.phote.repository.examresultrepository import com.swm_standard.phote.entity.ExamResult import org.springframework.data.jpa.repository.JpaRepository import java.util.UUID -interface ExamResultRepository : JpaRepository { +interface ExamResultRepository : + JpaRepository, + CustomExamResultRepository { fun findByExamId(examId: UUID): ExamResult? fun findByExamIdAndMemberId( examId: UUID, memberId: UUID, - ): ExamResult + ): ExamResult? fun findAllByExamId(examId: UUID): List diff --git a/src/main/kotlin/com/swm_standard/phote/service/ExamService.kt b/src/main/kotlin/com/swm_standard/phote/service/ExamService.kt index 212b8a2..bd99907 100644 --- a/src/main/kotlin/com/swm_standard/phote/service/ExamService.kt +++ b/src/main/kotlin/com/swm_standard/phote/service/ExamService.kt @@ -9,7 +9,6 @@ import com.swm_standard.phote.dto.CreateSharedExamRequest import com.swm_standard.phote.dto.GradeExamRequest import com.swm_standard.phote.dto.GradeExamResponse import com.swm_standard.phote.dto.ReadAllSharedExamsResponse -import com.swm_standard.phote.dto.ReadSharedExamInfoResponse import com.swm_standard.phote.dto.ReadExamHistoryDetail import com.swm_standard.phote.dto.ReadExamHistoryDetailResponse import com.swm_standard.phote.dto.ReadExamHistoryListResponse @@ -17,6 +16,7 @@ import com.swm_standard.phote.dto.ReadExamResultDetail import com.swm_standard.phote.dto.ReadExamResultDetailResponse import com.swm_standard.phote.dto.ReadExamResultsResponse import com.swm_standard.phote.dto.ReadExamStudentResult +import com.swm_standard.phote.dto.ReadSharedExamInfoResponse import com.swm_standard.phote.dto.RegradeExamRequest import com.swm_standard.phote.dto.RegradeExamResponse import com.swm_standard.phote.dto.SubmittedAnswerRequest @@ -30,10 +30,10 @@ import com.swm_standard.phote.entity.ParticipationType import com.swm_standard.phote.entity.SharedExam import com.swm_standard.phote.entity.Workbook import com.swm_standard.phote.repository.AnswerRepository -import com.swm_standard.phote.repository.ExamResultRepository import com.swm_standard.phote.repository.MemberRepository import com.swm_standard.phote.repository.SharedExamRepository import com.swm_standard.phote.repository.examrepository.ExamRepository +import com.swm_standard.phote.repository.examresultrepository.ExamResultRepository import com.swm_standard.phote.repository.questionrepository.QuestionRepository import com.swm_standard.phote.repository.workbookrepository.WorkbookRepository import kotlinx.coroutines.CoroutineScope @@ -108,21 +108,29 @@ class ExamService( ) } - fun readExamHistoryList(workbookId: UUID, memberId: UUID): List { - val exams = examRepository.findAllByWorkbookId(workbookId) + fun readExamHistoryList( + workbookId: UUID, + memberId: UUID, + ): List { + val exams = + examRepository + .findAllByWorkbookId(workbookId) + .filter { exam -> !sharedExamRepository.findById(exam.id!!).isPresent } - return exams.filter { exam -> - !sharedExamRepository.findById(exam.id!!).isPresent - }.map { exam -> - val examResult = examResultRepository.findByExamIdAndMemberId(exam.id!!, memberId) - ?: throw NotFoundException(fieldName = "examResult") + val examResults = + examResultRepository + .findAllByExamIdListAndMemberId(exams.map { it.id!! }, memberId) + .also { + if (it.size != exams.size) throw NotFoundException(fieldName = "examResult") + } + return examResults.map { ReadExamHistoryListResponse( - examId = exam.id!!, - totalQuantity = examResult.calculateTotalQuantity(), - totalCorrect = examResult.totalCorrect, - time = examResult.time, - sequence = exam.sequence, + examId = it.exam.id!!, + totalCorrect = it.totalCorrect, + totalQuantity = it.totalQuantity, + time = it.time, + sequence = it.exam.sequence, ) } } @@ -148,7 +156,9 @@ class ExamService( examId: UUID, memberId: UUID, ): ReadExamResultDetailResponse { - val examResult = examResultRepository.findByExamIdAndMemberId(examId, memberId) + val examResult = + examResultRepository.findByExamIdAndMemberId(examId, memberId) + ?: throw NotFoundException(fieldName = "examResult") val responses = buildList { examResult.answers.forEach { answer -> @@ -221,6 +231,7 @@ class ExamService( member = member, time = request.time, exam = exam, + totalQuantity = request.answers.size, ), ) @@ -293,7 +304,9 @@ class ExamService( memberId: UUID, request: RegradeExamRequest, ): RegradeExamResponse { - val examResult = examResultRepository.findByExamIdAndMemberId(examId, memberId) + val examResult = + examResultRepository.findByExamIdAndMemberId(examId, memberId) + ?: throw NotFoundException(fieldName = "examResult") val answer = answerRepository.findByExamResultIdAndQuestionId(examResult.id!!, request.questionId) examResult.increaseTotalCorrect(if (request.isCorrect) 1 else -1) @@ -355,14 +368,17 @@ class ExamService( status = sharedExam.checkStatus(), role = ParticipationType.EXAMINEE, totalCorrect = examResult.totalCorrect, - questionQuantity = examResult.calculateTotalQuantity(), + questionQuantity = examResult.totalQuantity, ) } return examsAsCreator + examsAsExaminee } - fun readSharedExamInfo(examId: UUID, memberId: UUID): ReadSharedExamInfoResponse { + fun readSharedExamInfo( + examId: UUID, + memberId: UUID, + ): ReadSharedExamInfoResponse { val sharedExam = sharedExamRepository.findById(examId).orElseThrow { NotFoundException(fieldName = "examId") } return ReadSharedExamInfoResponse( examId = examId, diff --git a/src/test/kotlin/com/swm_standard/phote/entity/ExamResultTest.kt b/src/test/kotlin/com/swm_standard/phote/entity/ExamResultTest.kt index 573c97f..943e1e2 100644 --- a/src/test/kotlin/com/swm_standard/phote/entity/ExamResultTest.kt +++ b/src/test/kotlin/com/swm_standard/phote/entity/ExamResultTest.kt @@ -5,9 +5,7 @@ import com.navercorp.fixturemonkey.api.introspector.FieldReflectionArbitraryIntr import com.navercorp.fixturemonkey.kotlin.KotlinPlugin import com.navercorp.fixturemonkey.kotlin.giveMeBuilder import com.navercorp.fixturemonkey.kotlin.setExp -import com.navercorp.fixturemonkey.kotlin.sizeExp import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test class ExamResultTest { @@ -18,22 +16,6 @@ class ExamResultTest { .objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE) .build() - @Test - fun `문제 풀이한 총 문제수를 구한다`() { - // given - val exam: ExamResult = - fixtureMonkey - .giveMeBuilder() - .sizeExp(ExamResult::answers, 2) - .sample() - - // when - val totalQuantity = exam.calculateTotalQuantity() - - // then - assertEquals(totalQuantity, 2) - } - @Test fun `totalCorrect가 증가한다`() { val count = 3