From 8b02c128132cda81f23f34d009d2fb1aff03179b Mon Sep 17 00:00:00 2001 From: Haebin Date: Sat, 23 Mar 2024 00:14:07 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=B6=94=EC=B2=9C=20=EB=B0=B1?= =?UTF-8?q?=EC=8B=A0=20=EC=A1=B0=ED=9A=8C=20API=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../disease/domain/constants/AgeCondition.kt | 3 +- .../security/matcher/CustomRequestMatcher.kt | 3 +- .../persistence/InoculationRepository.kt | 5 +++ .../presentation/search/SearchController.kt | 14 ++++--- .../search/application/SearchService.kt | 40 ++++++++++++++++--- 5 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt b/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt index 73a3b26..eb9d093 100644 --- a/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt +++ b/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt @@ -10,8 +10,7 @@ enum class AgeCondition( AGE40TO49("만 40-49세", 0b001000), AGE50TO59("만 50-59세", 0b000100), AGE60TO64("만 60-64세", 0b000010), - AGEOVER65("만 65세 이상", 0b000001), - ; + AGEOVER65("만 65세 이상", 0b000001); fun isMatching(value: Int): Boolean { return value and this.value == this.value diff --git a/src/main/kotlin/com/vacgom/backend/global/security/matcher/CustomRequestMatcher.kt b/src/main/kotlin/com/vacgom/backend/global/security/matcher/CustomRequestMatcher.kt index c606516..1939349 100644 --- a/src/main/kotlin/com/vacgom/backend/global/security/matcher/CustomRequestMatcher.kt +++ b/src/main/kotlin/com/vacgom/backend/global/security/matcher/CustomRequestMatcher.kt @@ -24,7 +24,8 @@ class CustomRequestMatcher { fun userEndpoints(): RequestMatcher { return OrRequestMatcher( - AntPathRequestMatcher("/api/v1/inoculation/**") + AntPathRequestMatcher("/api/v1/inoculation/**"), + AntPathRequestMatcher("/api/v1/search/**"), ) } } diff --git a/src/main/kotlin/com/vacgom/backend/inoculation/infrastructure/persistence/InoculationRepository.kt b/src/main/kotlin/com/vacgom/backend/inoculation/infrastructure/persistence/InoculationRepository.kt index 51d74fc..dec78fe 100644 --- a/src/main/kotlin/com/vacgom/backend/inoculation/infrastructure/persistence/InoculationRepository.kt +++ b/src/main/kotlin/com/vacgom/backend/inoculation/infrastructure/persistence/InoculationRepository.kt @@ -14,6 +14,11 @@ interface InoculationRepository : JpaRepository { "and i.vaccination.vaccinationType = :vaccinationType") fun findInoculationsByMemberIdAndVaccinationType(memberId: UUID, vaccinationType: VaccinationType): List + @Query("select distinct i.vaccination.diseaseName " + + "from Inoculation i " + + "where i.member.id = :memberId") + fun findDistinctDiseaseNameByMemberId(memberId: UUID): List + @Query("select i " + "from Inoculation i " + "where i.member.id = :memberId and i.vaccination.diseaseName = :diseaseName " + diff --git a/src/main/kotlin/com/vacgom/backend/presentation/search/SearchController.kt b/src/main/kotlin/com/vacgom/backend/presentation/search/SearchController.kt index 5d75836..b6c03b8 100644 --- a/src/main/kotlin/com/vacgom/backend/presentation/search/SearchController.kt +++ b/src/main/kotlin/com/vacgom/backend/presentation/search/SearchController.kt @@ -3,6 +3,7 @@ package com.vacgom.backend.presentation.search import com.vacgom.backend.disease.application.dto.request.FilterRequest import com.vacgom.backend.disease.domain.constants.AgeCondition import com.vacgom.backend.disease.domain.constants.HealthCondition +import com.vacgom.backend.global.security.annotation.AuthId import com.vacgom.backend.search.application.SearchService import com.vacgom.backend.search.application.dto.DiseaseSearchResponse import com.vacgom.backend.search.application.dto.VaccinationSearchResponse @@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import java.util.* @RestController @RequestMapping("/api/v1/search") @@ -33,11 +35,11 @@ class SearchController( fun vaccination( @RequestBody body: FilterRequest, ): ResponseEntity> { - return ResponseEntity.ok( - searchService.searchVaccination( - body.age.map { AgeCondition.valueOf(it) }, - body.condition.map { HealthCondition.valueOf(it) }, - ), - ) + return ResponseEntity.ok(searchService.searchVaccination(body)) + } + + @GetMapping("/recommend") + fun recommendVaccination(@AuthId id: UUID): ResponseEntity> { + return ResponseEntity.ok(searchService.searchRecommendVaccination(id)) } } diff --git a/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt b/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt index 9c7facb..ae69c09 100644 --- a/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt +++ b/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt @@ -1,18 +1,27 @@ package com.vacgom.backend.search.application import com.vacgom.backend.disease.application.DiseaseService +import com.vacgom.backend.disease.application.dto.request.FilterRequest import com.vacgom.backend.disease.domain.Disease import com.vacgom.backend.disease.domain.constants.AgeCondition import com.vacgom.backend.disease.domain.constants.HealthCondition +import com.vacgom.backend.global.exception.error.BusinessException import com.vacgom.backend.inoculation.domain.Vaccination +import com.vacgom.backend.inoculation.infrastructure.persistence.InoculationRepository import com.vacgom.backend.inoculation.infrastructure.persistence.VaccinationRepository +import com.vacgom.backend.member.exception.MemberError +import com.vacgom.backend.member.infrastructure.persistence.MemberRepository import com.vacgom.backend.search.application.dto.DiseaseSearchResponse import com.vacgom.backend.search.application.dto.VaccinationSearchResponse import org.springframework.stereotype.Service +import java.util.* + @Service class SearchService( val vaccinationRepository: VaccinationRepository, + val inoculationRepository: InoculationRepository, + val memberRepository: MemberRepository, val diseaseService: DiseaseService, ) { private fun findAllVaccinations(): List { @@ -31,17 +40,38 @@ class SearchService( } fun searchVaccination( - age: List, - condition: List, + request: FilterRequest, ): List { + val age = request.age.map { AgeCondition.valueOf(it) } + val condition = request.condition.map { HealthCondition.valueOf(it) } val diseases = this.searchDisease(age, condition) val vaccinations = findAllVaccinations() - return vaccinations.filter { - diseases.any { disease -> it.diseaseName.contains(disease.name) } - }.map { VaccinationSearchResponse.of(it) } + return filterByDisease(vaccinations, diseases) + } + + fun searchRecommendVaccination(memberId: UUID): List { + val ageCondition = AgeCondition.AGE19TO29 + + val vaccinations = findAllVaccinations() + val inoculatedDiseaseName = inoculationRepository.findDistinctDiseaseNameByMemberId(memberId).flatMap { it.split("·") }.toSet() + val recommendedVaccinations = vaccinations.filter { vaccination -> !inoculatedDiseaseName.contains(vaccination.vaccineName) } + + val healthProfiles = memberRepository.findById(memberId).orElseThrow { + BusinessException(MemberError.NOT_FOUND) + }.healthProfiles.map { it.healthCondition }.toList() + + val diseases = this.searchDisease(listOf(ageCondition), healthProfiles).filter { response -> + !inoculatedDiseaseName.contains(response.name) + }.toList() + return filterByDisease(recommendedVaccinations, diseases) } + private fun filterByDisease(vaccinations: List, diseases: List) = + vaccinations.filter { + diseases.any { disease -> it.diseaseName.contains(disease.name) } + }.map { VaccinationSearchResponse.of(it) } + fun isMatched( disease: Disease, age: List, From 4e33e5bd454c73d4411b1096fa75799c838be95e Mon Sep 17 00:00:00 2001 From: Haebin Date: Sat, 23 Mar 2024 00:44:23 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=83=9D=EB=85=84=EC=9B=94=EC=9D=BC=20=EA=B8=B0=EB=B0=98=20?= =?UTF-8?q?=EC=B6=94=EC=B2=9C=20=EB=B0=B1=EC=8B=A0=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 추천 백신 조회 API * feat: 사용자 연령 기반 백신 추천 --- .../disease/domain/constants/AgeCondition.kt | 16 ++++++++++------ .../backend/search/application/SearchService.kt | 10 ++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt b/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt index eb9d093..b7faebe 100644 --- a/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt +++ b/src/main/kotlin/com/vacgom/backend/disease/domain/constants/AgeCondition.kt @@ -12,13 +12,17 @@ enum class AgeCondition( AGE60TO64("만 60-64세", 0b000010), AGEOVER65("만 65세 이상", 0b000001); - fun isMatching(value: Int): Boolean { - return value and this.value == this.value - } - companion object { - fun getConditions(value: Int): List { - return entries.filter { it.isMatching(value) } + fun getAgeCondition(value: Int): AgeCondition { + return when { + value in 19..29 -> AGE19TO29 + value in 30..39 -> AGE30TO39 + value in 40..49 -> AGE40TO49 + value in 50..59 -> AGE50TO59 + value in 60..64 -> AGE60TO64 + value >= 65 -> AGEOVER65 + else -> AGE19TO29 + } } } } diff --git a/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt b/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt index ae69c09..56a3d39 100644 --- a/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt +++ b/src/main/kotlin/com/vacgom/backend/search/application/SearchService.kt @@ -14,6 +14,7 @@ import com.vacgom.backend.member.infrastructure.persistence.MemberRepository import com.vacgom.backend.search.application.dto.DiseaseSearchResponse import com.vacgom.backend.search.application.dto.VaccinationSearchResponse import org.springframework.stereotype.Service +import java.time.LocalDate import java.util.* @@ -51,15 +52,16 @@ class SearchService( } fun searchRecommendVaccination(memberId: UUID): List { - val ageCondition = AgeCondition.AGE19TO29 + val member = memberRepository.findById(memberId).orElseThrow { + BusinessException(MemberError.NOT_FOUND) + } + val ageCondition = AgeCondition.getAgeCondition(member.memberDetails?.birthday?.year!!.minus(LocalDate.now().year)) val vaccinations = findAllVaccinations() val inoculatedDiseaseName = inoculationRepository.findDistinctDiseaseNameByMemberId(memberId).flatMap { it.split("·") }.toSet() val recommendedVaccinations = vaccinations.filter { vaccination -> !inoculatedDiseaseName.contains(vaccination.vaccineName) } - val healthProfiles = memberRepository.findById(memberId).orElseThrow { - BusinessException(MemberError.NOT_FOUND) - }.healthProfiles.map { it.healthCondition }.toList() + val healthProfiles = member.healthProfiles.map { it.healthCondition }.toList() val diseases = this.searchDisease(listOf(ageCondition), healthProfiles).filter { response -> !inoculatedDiseaseName.contains(response.name)