Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: readExamHistoryList (시험 결과 목록 조회) 조회 성능 개선 #267

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/main/kotlin/com/swm_standard/phote/entity/ExamResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -33,8 +35,6 @@ data class ExamResult(
@OneToMany(mappedBy = "examResult", cascade = [(CascadeType.REMOVE)])
val answers: MutableList<Answer> = mutableListOf()

fun calculateTotalQuantity(): Int = answers.size

fun increaseTotalCorrect(count: Int) {
totalCorrect += count
}
Expand All @@ -44,6 +44,7 @@ data class ExamResult(
member: Member,
time: Int,
exam: Exam,
) = ExamResult(member, time, exam)
totalQuantity: Int,
) = ExamResult(member, time, exam, totalQuantity)
}
}
Original file line number Diff line number Diff line change
@@ -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<UUID>,
memberId: UUID,
): List<ExamResult>
}
Original file line number Diff line number Diff line change
@@ -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<UUID>,
memberId: UUID,
): List<ExamResult> =
jpaQueryFactory
.selectFrom(examResult)
.where(examResult.exam.id.`in`(examIdList))
.where(examResult.member.id.eq(memberId))
.fetch()
}
Original file line number Diff line number Diff line change
@@ -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<ExamResult, UUID> {
interface ExamResultRepository :
JpaRepository<ExamResult, UUID>,
CustomExamResultRepository {
fun findByExamId(examId: UUID): ExamResult?

fun findByExamIdAndMemberId(
examId: UUID,
memberId: UUID,
): ExamResult
): ExamResult?

fun findAllByExamId(examId: UUID): List<ExamResult>

Expand Down
52 changes: 34 additions & 18 deletions src/main/kotlin/com/swm_standard/phote/service/ExamService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ 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
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
Expand All @@ -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
Expand Down Expand Up @@ -108,21 +108,29 @@ class ExamService(
)
}

fun readExamHistoryList(workbookId: UUID, memberId: UUID): List<ReadExamHistoryListResponse> {
val exams = examRepository.findAllByWorkbookId(workbookId)
fun readExamHistoryList(
workbookId: UUID,
memberId: UUID,
): List<ReadExamHistoryListResponse> {
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,
)
}
}
Expand All @@ -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 ->
Expand Down Expand Up @@ -221,6 +231,7 @@ class ExamService(
member = member,
time = request.time,
exam = exam,
totalQuantity = request.answers.size,
),
)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 0 additions & 18 deletions src/test/kotlin/com/swm_standard/phote/entity/ExamResultTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -18,22 +16,6 @@ class ExamResultTest {
.objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE)
.build()

@Test
fun `문제 풀이한 총 문제수를 구한다`() {
// given
val exam: ExamResult =
fixtureMonkey
.giveMeBuilder<ExamResult>()
.sizeExp(ExamResult::answers, 2)
.sample()

// when
val totalQuantity = exam.calculateTotalQuantity()

// then
assertEquals(totalQuantity, 2)
}

@Test
fun `totalCorrect가 증가한다`() {
val count = 3
Expand Down
Loading