Skip to content

Commit

Permalink
Merge: remote-tracking branch 'origin' into seyeon/swm-106
Browse files Browse the repository at this point in the history
  • Loading branch information
adorableco committed Aug 1, 2024
2 parents 1da4695 + c33eafb commit 2ac5d01
Show file tree
Hide file tree
Showing 20 changed files with 520 additions and 88 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ dependencies {
runtimeOnly("com.mysql:mysql-connector-j")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("org.mockito:mockito-core:4.6.1")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.swm_standard.phote.controller

import com.swm_standard.phote.common.responsebody.BaseResponse
import com.swm_standard.phote.dto.ReadExamHistoryDetailResponse
import com.swm_standard.phote.dto.ReadExamHistoryListResponse
import com.swm_standard.phote.service.ExamService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import java.util.UUID

@RestController
@RequestMapping("/api")
@Tag(name = "Exam", description = "Exam API Document")
class ExamController(
private val examService: ExamService
) {
@Operation(summary = "readExamHistoryDetail", description = "문제풀이 기록 상세조회")
@SecurityRequirement(name = "bearer Auth")
@GetMapping("/exam/{id}")
fun readExamHistoryDetail(
@PathVariable(
required = true
) id: UUID
): BaseResponse<ReadExamHistoryDetailResponse> =
BaseResponse(msg = "문제풀이 기록 상세조회 성공", data = examService.readExamHistoryDetail(id))

@Operation(summary = "readExamHistoryList", description = "문제풀이 기록 리스트 조회")
@SecurityRequirement(name = "bearer Auth")
@GetMapping("/exams/{workbookId}")
fun readExamHistoryList(
@PathVariable(
required = true
) workbookId: UUID
): BaseResponse<List<ReadExamHistoryListResponse>> =
BaseResponse(msg = "문제풀이 기록 리스트 조회 성공", data = examService.readExamHistoryList(workbookId))
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import com.swm_standard.phote.dto.CreateQuestionResponse
import com.swm_standard.phote.dto.DeleteQuestionResponse
import com.swm_standard.phote.dto.ReadQuestionDetailResponse
import com.swm_standard.phote.dto.SearchQuestionsToAddResponse
import com.swm_standard.phote.dto.SearchQuestionsResponse
import com.swm_standard.phote.dto.TransformQuestionResponse
import com.swm_standard.phote.entity.Question
import com.swm_standard.phote.external.aws.S3Service
import com.swm_standard.phote.service.QuestionService
import io.swagger.v3.oas.annotations.Operation
Expand Down Expand Up @@ -63,7 +63,7 @@ class QuestionController(
@Parameter(hidden = true) @MemberId memberId: UUID,
@RequestParam(required = false) tags: List<String>? = null,
@RequestParam(required = false) keywords: List<String>? = null,
): BaseResponse<List<Question>> {
): BaseResponse<List<SearchQuestionsResponse>> {
questionService.searchQuestions(memberId, tags, keywords)
return BaseResponse(msg = "문제 검색 성공", data = questionService.searchQuestions(memberId, tags, keywords))
}
Expand Down
30 changes: 30 additions & 0 deletions src/main/kotlin/com/swm_standard/phote/dto/ExamDtos.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.swm_standard.phote.dto

import com.swm_standard.phote.entity.Category
import java.util.UUID

data class ReadExamHistoryDetail(
val statement: String,
val options: List<String>?,
val image: String?,
val category: Category,
val answer: String,
val submittedAnswer: String,
val isCorrect: Boolean,
val sequence: Int
)

data class ReadExamHistoryDetailResponse(
val id: UUID,
val totalCorrect: Int,
val time: Int,
val questions: List<ReadExamHistoryDetail>
)

data class ReadExamHistoryListResponse(
val examId: UUID,
val totalQuantity: Int,
val totalCorrect: Int,
val time: Int,
val sequence: Int
)
44 changes: 35 additions & 9 deletions src/main/kotlin/com/swm_standard/phote/dto/QuestionDtos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ import java.util.*

data class ReadQuestionDetailResponse(
val createdAt: LocalDateTime,
val modifiedAt: LocalDateTime? = null,
val modifiedAt: LocalDateTime?,
val statement: String,
val image: String? = null,
val options: JsonNode? = null,
val image: String?,
val options: List<String>?,
val answer: String,
val category: Category,
val tags: List<Tag>? = null,
val memo: String? = null,
val tags: List<Tag>?,
val memo: String?,
) {
constructor(question: Question) : this(
constructor(question: Question, options: List<String>?) : this(
createdAt = question.createdAt,
modifiedAt = question.modifiedAt,
statement = question.statement,
image = question.image,
options = question.options,
options = options,
answer = question.answer,
tags = question.tags,
category = question.category,
Expand Down Expand Up @@ -54,15 +54,41 @@ data class CreateQuestionResponse(
val id: UUID,
)

data class SearchQuestionsResponse(
val id: UUID,
val statement: String,
val options: List<String>?,
val image: String?,
val answer: String,
val category: Category,
val tags: List<Tag>?,
val memo: String?,
val createdAt: LocalDateTime,
val modifiedAt: LocalDateTime?,
) {
constructor(question: Question, options: List<String>?) : this(
id = question.id,
statement = question.statement,
options = options,
image = question.image,
answer = question.answer,
category = question.category,
tags = question.tags,
memo = question.memo,
createdAt = question.createdAt,
modifiedAt = question.modifiedAt
)
}

data class SearchQuestionsToAddResponse(
val createdAt: LocalDateTime,
val modifiedAt: LocalDateTime? = null,
val statement: String,
val image: String? = null,
val options: JsonNode? = null,
val options: List<String>?,
val answer: String,
val category: Category,
val tags: List<Tag>? = null,
val tags: List<Tag>?,
val memo: String? = null,
var isContain: Boolean = false,
)
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/swm_standard/phote/entity/Answer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import jakarta.persistence.ManyToOne
data class Answer(
@ManyToOne
@JoinColumn(name = "question_id")
private val question: Question,
val question: Question,
@ManyToOne
@JoinColumn(name = "exam_id")
private val exam: Exam,
val exam: Exam,
@Column(name = "submitted_answer")
private val submittedAnswer: String,
private val isCorrect: Boolean,
val submittedAnswer: String,
val isCorrect: Boolean,
) : BaseTimeEntity() {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down
20 changes: 13 additions & 7 deletions src/main/kotlin/com/swm_standard/phote/entity/Exam.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,35 @@ package com.swm_standard.phote.entity
import jakarta.persistence.CascadeType
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.OneToMany
import java.util.UUID

@Entity
data class Exam(
@ManyToOne
@JoinColumn(name = "member_id")
private val member: Member,
val member: Member,
@ManyToOne
@JoinColumn(name = "workbook_id")
private val workbook: Workbook,
val workbook: Workbook,
) : BaseTimeEntity() {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "exam_id")
val id: Long = 0
@Column(name = "exam_id", nullable = false, unique = true)
val id: UUID = UUID.randomUUID()

val totalCorrect: Int = 0

@OneToMany(mappedBy = "exam", cascade = [(CascadeType.REMOVE)])
val answers: MutableList<Answer> = mutableListOf()

val time: Int = 0

val sequence: Int = 0

fun calculateTotalQuantity(): Int {
return answers.size
}
}
12 changes: 11 additions & 1 deletion src/main/kotlin/com/swm_standard/phote/entity/Question.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,14 @@ data class Question(
@OneToMany(mappedBy = "question", cascade = [CascadeType.REMOVE])
var tags: MutableList<Tag> = mutableListOf(),
val memo: String?,
) : BaseTimeEntity()
) : BaseTimeEntity() {

fun deserializeOptions(): MutableList<String> {
val optionsList = mutableListOf<String>()
options!!.fields().forEach { option ->
optionsList.add(option.value.asText())
}

return optionsList
}
}
8 changes: 8 additions & 0 deletions src/main/kotlin/com/swm_standard/phote/entity/QuestionSet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,12 @@ data class QuestionSet(
fun updateSequence(seq: Int) {
this.sequence = seq
}

companion object {
fun createSequence(
question: Question,
workbook: Workbook,
nextSequence: Int,
) = QuestionSet(question, workbook, nextSequence)
}
}
48 changes: 34 additions & 14 deletions src/main/kotlin/com/swm_standard/phote/entity/Workbook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ data class Workbook(
@JoinColumn(name = "member_id")
@JsonIgnore
val member: Member,
var emoji: String,
) : BaseTimeEntity() {
@Id
@Column(name = "workbook_uuid", nullable = false, unique = true)
Expand All @@ -34,11 +35,35 @@ data class Workbook(
@OrderBy("sequence asc")
val questionSet: List<QuestionSet>? = null

var emoji: String = "📚"

@ColumnDefault(value = "0")
var quantity: Int = 0

companion object {
fun createWorkbook(
title: String,
description: String?,
member: Member,
) = Workbook(
title,
description,
member,
matchEmojiByTitle(title),
)

private fun matchEmojiByTitle(title: String): String {
val math: List<String> = listOf("수학", "math", "미적분", "확통", "수1", "수2", "기하", "대수")
val language: List<String> = listOf("국어", "언매", "화작", "비문학", "문학", "독서", "듣기", "영어", "eng", "토익", "외국")
val science: List<String> = listOf("과학", "화학", "생물", "생명", "물리", "지구")

return when {
math.size != math.filter { !title.contains(it) }.size -> ""
language.size != language.filter { !title.contains(it) }.size -> "💬"
science.size != science.filter { !title.contains(it) }.size -> "🧪"
else -> "📚"
}
}
}

fun decreaseQuantity() {
this.quantity -= 1
modifiedAt = LocalDateTime.now()
Expand All @@ -51,17 +76,12 @@ data class Workbook(

fun compareQuestionQuantity(num: Int) = num == this.quantity

fun matchEmojiByTitle() {
val math: List<String> = listOf("수학", "math", "미적분", "확통", "수1", "수2", "기하", "대수")
val language: List<String> = listOf("국어", "언매", "화작", "비문학", "문학", "독서", "듣기", "영어", "eng", "토익", "외국")
val science: List<String> = listOf("과학", "화학", "생물", "생명", "물리", "지구")

emoji =
when {
math.size != math.filter { !title.contains(it) }.size -> ""
language.size != language.filter { !title.contains(it) }.size -> "💬"
science.size != science.filter { !title.contains(it) }.size -> "🧪"
else -> "📚"
}
fun updateWorkbook(
title: String,
description: String?,
) {
this.title = title
this.description = description
this.emoji = matchEmojiByTitle(title)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.swm_standard.phote.repository

import com.swm_standard.phote.entity.Exam
import org.springframework.data.jpa.repository.JpaRepository
import java.util.UUID

interface ExamRepository : JpaRepository<Exam, UUID> {
fun findAllByWorkbookId(workbookId: UUID): List<Exam>
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,13 @@ class QuestionCustomRepositoryImpl(
val questions = searchQuestionsList(memberId, tags, keywords)

return questions.map { question ->
val options = question.options?.let { question.deserializeOptions() }
SearchQuestionsToAddResponse(
createdAt = question.createdAt,
modifiedAt = question.modifiedAt,
statement = question.statement,
image = question.image,
options = question.options,
options = options,
answer = question.answer,
category = question.category,
tags = question.tags,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ package com.swm_standard.phote.repository

import com.swm_standard.phote.entity.QuestionSet
import org.springframework.data.jpa.repository.JpaRepository

import java.util.UUID

interface QuestionSetRepository : JpaRepository<QuestionSet, UUID>, QuestionSetCustomRepository {
interface QuestionSetRepository :
JpaRepository<QuestionSet, UUID>,
QuestionSetCustomRepository {
fun findByQuestionIdAndWorkbookId(
questionId: UUID,
workbookId: UUID,
): QuestionSet?

fun findByQuestionIdAndWorkbookId(questionId: UUID, workbookId: UUID): QuestionSet?
fun existsByQuestionIdAndWorkbookId(
questionId: UUID,
workbookId: UUID,
): Boolean

fun findByWorkbookIdOrderBySequence(workbookId: UUID): List<QuestionSet>
}
Loading

0 comments on commit 2ac5d01

Please sign in to comment.