Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ enum class ErrorCode(val code: String, val httpStatus: HttpStatus, val message:
DATABASE_ERROR("S503_001", HttpStatus.SERVICE_UNAVAILABLE, "데이터베이스 통신 오류가 발생했습니다."),

// UNIVERSITY
UNIVERSITY_NOT_FOUND("U404_001", HttpStatus.NOT_FOUND, "대학교를 찾을 수 없습니다.")
UNIVERSITY_NOT_FOUND("U404_001", HttpStatus.NOT_FOUND, "대학교를 찾을 수 없습니다."),

// LANGUAGE REQUIREMENT
INVALID_EXAM_SCORE("L400_001", HttpStatus.BAD_REQUEST, "어학 점수가 유효 범위를 벗어났습니다.")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.example.beyondubackend.common.enums

enum class ExamType(
val paramName: String,
val displayName: String,
val minScore: Double,
val maxScore: Double
) {
TOEFL_IBT("TOEFL_IBT", "TOEFL iBT", 0.0, 120.0),
TOEFL_ITP("TOEFL_ITP", "TOEFL ITP", 0.0, 677.0),
IELTS("IELTS", "IELTS", 0.0, 9.0),
TOEIC("TOEIC", "TOEIC", 0.0, 990.0),
TOEIC_SPEAKING("TOEIC_Speaking", "TOEIC Speaking", 0.0, 200.0),
HSK("HSK", "HSK", 1.0, 6.0),
JLPT("JLPT", "JLPT", 1.0, 5.0),
JPT("JPT", "JPT", 0.0, 990.0),
DELF("DELF", "DELF", 1.0, 6.0),
ZD("ZD", "ZD", 1.0, 6.0);

companion object {
fun fromParamName(paramName: String): ExamType? = entries.find { it.paramName == paramName }
fun fromDisplayName(displayName: String): ExamType? = entries.find { it.displayName == displayName }
}
}
67 changes: 67 additions & 0 deletions src/main/kotlin/org/example/beyondubackend/common/enums/Nation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.example.beyondubackend.common.enums

enum class Nation(val displayName: String) {
GUATEMALA("과테말라"),
GREECE("그리스"),
NETHERLANDS("네덜란드"),
NEW_ZEALAND("뉴질랜드"),
TAIWAN("대만"),
DENMARK("덴마크"),
DOMINICAN_REPUBLIC("도미니카공화국"),
GERMANY("독일"),
LATVIA("라트비아"),
RUSSIA("러시아"),
ROMANIA("루마니아"),
LITHUANIA("리투아니아"),
MALAYSIA("말레이시아"),
MEXICO("멕시코"),
MONGOLIA("몽골"),
USA("미국"),
MYANMAR("미얀마"),
BANGLADESH("방글라데시"),
VIETNAM("베트남"),
BELGIUM("벨기에"),
BRAZIL("브라질"),
SWEDEN("스웨덴"),
SWITZERLAND("스위스"),
SPAIN("스페인"),
SINGAPORE("싱가포르"),
ARGENTINA("아르헨티나"),
IRELAND("아일랜드"),
AZERBAIJAN("아제르바이잔"),
ESTONIA("에스토니아"),
ECUADOR("에콰도르"),
UK("영국"),
AUSTRIA("오스트리아"),
URUGUAY("우루과이"),
UZBEKISTAN("우즈베키스탄"),
UKRAINE("우크라이나"),
ITALY("이탈리아"),
INDIA("인도"),
INDONESIA("인도네시아"),
JAPAN("일본"),
CHINA("중국"),
CZECH("체코"),
CHILE("칠레"),
KAZAKHSTAN("카자흐스탄"),
CANADA("캐나다"),
COLOMBIA("콜롬비아"),
CROATIA("크로아티아"),
KYRGYZSTAN("키르기스스탄"),
THAILAND("태국"),
TUNISIA("튀니지"),
TURKEY("튀르키예(터키)"),
PERU("페루"),
PORTUGAL("포르투갈"),
POLAND("폴란드"),
FRANCE("프랑스"),
FINLAND("핀란드"),
PHILIPPINES("필리핀"),
HUNGARY("헝가리"),
AUSTRALIA("호주"),
HONG_KONG("홍콩");

companion object {
fun fromDisplayName(name: String): Nation? = entries.find { it.displayName == name }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.example.beyondubackend.common.resolver

import org.example.beyondubackend.common.annotation.ExamScoreParams
import org.example.beyondubackend.common.code.ErrorCode
import org.example.beyondubackend.common.enums.ExamType
import org.example.beyondubackend.common.exception.BusinessException
import org.springframework.core.MethodParameter
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.context.request.NativeWebRequest
Expand All @@ -13,25 +16,6 @@ import org.springframework.web.method.support.ModelAndViewContainer
*/
class ExamScoreArgumentResolver : HandlerMethodArgumentResolver {

companion object {
/**
* 지원하는 어학 시험 목록
* 새로운 시험 추가 시 이 목록에만 추가하면 됨
*/
private val SUPPORTED_EXAMS = mapOf(
"TOEFL_IBT" to "TOEFL iBT",
"TOEFL_ITP" to "TOEFL ITP",
"IELTS" to "IELTS",
"TOEIC" to "TOEIC",
"TOEIC_Speaking" to "TOEIC Speaking",
"HSK" to "HSK",
"JLPT" to "JLPT",
"JPT" to "JPT",
"DELF" to "DELF",
"ZD" to "ZD"
)
}

override fun supportsParameter(parameter: MethodParameter): Boolean {
return parameter.hasParameterAnnotation(ExamScoreParams::class.java)
}
Expand All @@ -42,9 +26,17 @@ class ExamScoreArgumentResolver : HandlerMethodArgumentResolver {
webRequest: NativeWebRequest,
binderFactory: WebDataBinderFactory?
): Map<String, Double> {
return SUPPORTED_EXAMS.mapNotNull { (paramName, displayName) ->
val value = webRequest.getParameter(paramName)?.toDoubleOrNull()
if (value != null) displayName to value else null
return ExamType.entries.mapNotNull { examType ->
val value = webRequest.getParameter(examType.paramName)?.toDoubleOrNull()
if (value != null) {
if (value < examType.minScore || value > examType.maxScore) {
throw BusinessException(
ErrorCode.INVALID_EXAM_SCORE,
"${examType.displayName} 점수는 ${examType.minScore} ~ ${examType.maxScore} 범위여야 합니다."
)
}
examType.displayName to value
} else null
}.toMap()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.example.beyondubackend.domain.languagerequirement.implement

import org.example.beyondubackend.common.enums.ExamType
import org.springframework.stereotype.Component

@Component
Expand All @@ -20,11 +21,11 @@ class LanguageRequirementReader(

return requirements.joinToString(" / ") { requirement ->
when {
requirement.examType.equals("HSK", ignoreCase = true) && requirement.levelCode != null -> {
"HSK ${requirement.levelCode}"
requirement.examType.equals(ExamType.HSK.displayName, ignoreCase = true) && requirement.levelCode != null -> {
"${ExamType.HSK.displayName} ${requirement.levelCode}"
}
requirement.examType.equals("HSK", ignoreCase = true) -> {
"HSK ${requirement.minScore.toInt()}"
requirement.examType.equals(ExamType.HSK.displayName, ignoreCase = true) -> {
"${ExamType.HSK.displayName} ${requirement.minScore.toInt()}"
}
else -> {
"${requirement.examType} ${formatScore(requirement.minScore)}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.example.beyondubackend.domain.meta.application

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.example.beyondubackend.common.dto.ApiResponse
import org.example.beyondubackend.common.enums.ExamType
import org.example.beyondubackend.common.enums.Nation
import org.example.beyondubackend.domain.meta.application.dto.ExamTypeResponse
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@Tag(name = "Meta", description = "공통 코드 조회 API")
@RestController
@RequestMapping("/api/v1/meta")
class MetaController {

@Operation(summary = "국가 목록 조회", description = "university 필터링에 사용 가능한 국가 목록을 반환합니다.")
@GetMapping("/nations")
fun getNations(): ResponseEntity<ApiResponse<List<String>>> {
val nations = Nation.entries.map { it.displayName }
return ApiResponse.success(nations)
}

@Operation(summary = "어학 시험 목록 조회", description = "지원하는 어학 시험 종류와 점수 범위를 반환합니다.")
@GetMapping("/exam-types")
fun getExamTypes(): ResponseEntity<ApiResponse<List<ExamTypeResponse>>> {
val examTypes = ExamType.entries.map { ExamTypeResponse.from(it) }
return ApiResponse.success(examTypes)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.example.beyondubackend.domain.meta.application.dto

import org.example.beyondubackend.common.enums.ExamType

data class ExamTypeResponse(
val paramName: String,
val displayName: String,
val minScore: Double,
val maxScore: Double
) {
companion object {
fun from(examType: ExamType) = ExamTypeResponse(
paramName = examType.paramName,
displayName = examType.displayName,
minScore = examType.minScore,
maxScore = examType.maxScore
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.example.beyondubackend.domain.university.storage
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.JPAExpressions
import com.querydsl.jpa.impl.JPAQueryFactory
import org.example.beyondubackend.common.enums.ExamType
import org.example.beyondubackend.domain.languagerequirement.storage.QLanguageRequirementEntity.languageRequirementEntity
import org.example.beyondubackend.domain.university.implement.University
import org.example.beyondubackend.domain.university.implement.UniversityRepository
Expand Down Expand Up @@ -120,7 +121,7 @@ class UniversityRepositoryImpl(

val examConditions = examScores.map { (examType, score) ->
// JLPT는 숫자가 낮을수록 높은 레벨 (N1 > N2 > ... > N5)
val scoreCondition = if (examType == "JLPT") {
val scoreCondition = if (examType == ExamType.JLPT.displayName) {
languageRequirementEntity.minScore.goe(score)
} else {
languageRequirementEntity.minScore.loe(score)
Expand Down
Loading