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 @@ -2,13 +2,17 @@ package org.example.beyondubackend.domain.university.application

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.Parameters
import io.swagger.v3.oas.annotations.enums.ParameterIn
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.tags.Tag
import org.example.beyondubackend.common.annotation.ExamScoreParams
import org.example.beyondubackend.common.dto.ApiResponse
import org.example.beyondubackend.domain.university.application.dto.UniversitySearchRequest
import org.example.beyondubackend.domain.university.business.UniversityService
import org.example.beyondubackend.domain.university.business.dto.UniversityDetailResponse
import org.example.beyondubackend.domain.university.business.dto.UniversityListResponse
import org.springdoc.core.annotations.ParameterObject
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.http.ResponseEntity
Expand All @@ -25,18 +29,61 @@ class UniversityController(
summary = "대학교 목록 조회",
description = """
대학교 목록을 조회합니다.
- 필터링: nation, isExchange, isVisit, gpa, nations, major, hasReview
- 언어 점수: TOEFL, IELTS, TOEIC, JLPT 등
- 페이지네이션: page
- 필터링: nation, isExchange, isVisit, gpa, major, hasReview
- 어학 점수 필터: TOEFL_IBT, TOEFL_ITP, IELTS, TOEIC, TOEIC_Speaking, HSK, JLPT, JPT, DELF, ZD
- 페이지네이션: page (기본값 0), size (기본값 12)
- 모든 파라미터는 optional이며, null이면 전체 조회
"""
)
@Parameters(value = [
Parameter(
name = "TOEFL_IBT", description = "TOEFL iBT 점수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "80")
),
Parameter(
name = "TOEFL_ITP", description = "TOEFL ITP 점수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "550")
),
Parameter(
name = "IELTS", description = "IELTS 점수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "6.5")
),
Parameter(
name = "TOEIC", description = "TOEIC 점수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "800")
),
Parameter(
name = "TOEIC_Speaking", description = "TOEIC Speaking 점수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "160")
),
Parameter(
name = "HSK", description = "HSK 급수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "4")
),
Parameter(
name = "JLPT", description = "JLPT 레벨 (1=N1 ~ 5=N5, 숫자가 낮을수록 높은 레벨)",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "2")
),
Parameter(
name = "JPT", description = "JPT 점수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "700")
),
Parameter(
name = "DELF", description = "DELF 급수",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "4")
),
Parameter(
name = "ZD", description = "ZD 급수 (독어 자격증)",
required = false, `in` = ParameterIn.QUERY, schema = Schema(type = "number", example = "4")
)
])
@GetMapping
fun getUniversities(
@ModelAttribute request: UniversitySearchRequest,
@Parameter(description = "언어 시험 점수 (예: TOEFL=80, IELTS=6.5)", required = false)
@ExamScoreParams examScores: Map<String, Double>,
@ParameterObject @ModelAttribute request: UniversitySearchRequest,
@Parameter(hidden = true) @ExamScoreParams examScores: Map<String, Double>,
@Parameter(description = "페이지 번호", example = "0")
@RequestParam(defaultValue = "0") page: Int,
@Parameter(description = "페이지 크기", example = "12")
@RequestParam(defaultValue = "12") size: Int
): ResponseEntity<ApiResponse<UniversityListResponse>> {
val pageable = PageRequest.of(page, size, Sort.by(Sort.Order.asc("nameEng"), Sort.Order.asc("nameKor")))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package org.example.beyondubackend.domain.university.application.dto

import io.swagger.v3.oas.annotations.media.Schema
import org.example.beyondubackend.domain.university.business.query.UniversityQuery

data class UniversitySearchRequest(
@Schema(description = "국가 필터 (예: 미국, 일본)")
val nation: String? = null,
@Schema(description = "교환학생 가능 여부")
val isExchange: Boolean? = null,
@Schema(description = "방문학생 가능 여부")
val isVisit: Boolean? = null,
//TO DO: 대학 이름 검색 사용시 추가
@Schema(hidden = true)
val search: String? = null,
@Schema(description = "최소 GPA (입력한 GPA 이상 지원 가능한 학교 조회)", example = "3.5")
val gpa: Double? = null,
val nations: String? = null,
@Schema(description = "전공 필터")
val major: String? = null,
@Schema(description = "후기 보유 여부")
val hasReview: Boolean? = null
) {
fun toQuery(examScores: Map<String, Double>): UniversityQuery {
Expand All @@ -19,7 +27,6 @@ data class UniversitySearchRequest(
isVisit = isVisit,
search = search,
gpa = gpa,
nations = nations,
major = major,
hasReview = hasReview,
examScores = examScores
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class UniversityServiceImpl(
isVisit = query.isVisit,
search = query.search,
gpa = query.gpa,
nations = query.nations,
major = query.major,
hasReview = query.hasReview,
examScores = query.examScores,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ data class UniversityDetailResponse(
val region: String,
val isExchange: Boolean,
val isVisit: Boolean,
val programType: String,
val badge: String,
val hasReview: Boolean,
val minGpa: Double,
Expand All @@ -20,12 +21,19 @@ data class UniversityDetailResponse(
val availableMajors: List<String>?,
val courseListUrl: String?,
val studentCount: String?,
val location: String?,
) {
companion object {
fun from(
university: University,
languageRequirements: List<LanguageRequirementResponse>
): UniversityDetailResponse {
val programType = when {
university.isExchange -> "일반교환"
university.isVisit -> "방문교환"
else -> ""
}

return UniversityDetailResponse(
id = university.id!!,
nameKor = university.nameKor,
Expand All @@ -34,6 +42,7 @@ data class UniversityDetailResponse(
region = university.region,
isExchange = university.isExchange,
isVisit = university.isVisit,
programType = programType,
badge = university.badge,
hasReview = university.hasReview,
minGpa = university.minGpa,
Expand All @@ -44,6 +53,7 @@ data class UniversityDetailResponse(
availableMajors = parseAvailableMajors(university.availableMajors),
courseListUrl = parseCourseListUrl(university.availableMajors),
studentCount = parseStudentCount(university.remark),
location = parseLocation(university.remark),
)
}

Expand Down Expand Up @@ -74,6 +84,18 @@ data class UniversityDetailResponse(
return regex.find(availableMajors)?.groupValues?.get(1)?.trim()
}

/**
* remark에서 위치 파싱
* 예: "* 위치: Vechta\n* 특징: ..." -> "Vechta"
* 예: "* 위치: Brühl (쾰른에서 기차로 10분) * 특징: ..." -> "Brühl (쾰른에서 기차로 10분)"
* 없으면 null 반환
*/
private fun parseLocation(remark: String?): String? {
if (remark == null) return null
val regex = """\*\s*위치\s*:\s*([^*\n]+)""".toRegex()
return regex.find(remark)?.groupValues?.get(1)?.trim()?.takeIf { it.isNotBlank() }
}

/**
* remark에서 학생 수 파싱
* 예: "... 학생 수 약 28,600명 ..." -> "약 28,600명"
Expand All @@ -83,7 +105,7 @@ data class UniversityDetailResponse(
if (remark == null) return "학생 수 정보 없음"
val regex = """학생\s*수\s*약?\s*([\d,]+)\s*명""".toRegex()
val count = regex.find(remark)?.groupValues?.get(1)?.trim()
return if (count != null) "약 ${count} 명" else "학생 수 정보 없음"
return if (count != null) "약 $count 명" else "학생 수 정보 없음"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ data class UniversityQuery(
val isVisit: Boolean? = null,
val search: String? = null,
val gpa: Double? = null,
val nations: String? = null,
val major: String? = null,
val hasReview: Boolean? = null,
val examScores: Map<String, Double> = emptyMap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ class UniversityReader(
isVisit: Boolean?,
search: String?,
gpa: Double?,
nations: String?,
major: String?,
hasReview: Boolean?,
examScores: Map<String, Double>,
pageable: Pageable
): Page<University> {
return universityRepository.findAllWithFilters(
nation, isExchange, isVisit, search, gpa, nations, major, hasReview, examScores, pageable
nation, isExchange, isVisit, search, gpa, major, hasReview, examScores, pageable
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ interface UniversityRepository {
isVisit: Boolean?,
search: String?,
gpa: Double?,
nations: String?,
major: String?,
hasReview: Boolean?,
examScores: Map<String, Double>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class UniversityRepositoryImpl(
isVisit: Boolean?,
search: String?,
gpa: Double?,
nations: String?,
major: String?,
hasReview: Boolean?,
examScores: Map<String, Double>,
Expand All @@ -39,7 +38,6 @@ class UniversityRepositoryImpl(
isVisitEq(isVisit),
searchKeyword(search),
gpaLoe(gpa),
nationsIn(nations),
majorContains(major),
hasReviewEq(hasReview),
examScoresMatch(examScores)
Expand All @@ -66,7 +64,6 @@ class UniversityRepositoryImpl(
isVisitEq(isVisit),
searchKeyword(search),
gpaLoe(gpa),
nationsIn(nations),
majorContains(major),
hasReviewEq(hasReview),
examScoresMatch(examScores)
Expand Down
Loading