From 96749e9e76e4e63b8e35f4adcd3dcdd091245679 Mon Sep 17 00:00:00 2001 From: Yong-Hyeon <104913654+whereami2048@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:31:13 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EB=B0=9C=EA=B8=89=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 백신 접종내역 생성 * feat: unclassified Vaccination Discord Webhook * chore: 테스트용 토큰 발급 API 구현 (#45) --------- Co-authored-by: Haebin --- build.gradle.kts | 3 + dump.rdb | Bin 6375 -> 0 bytes .../kotlin/kr/co/vacgom/api/TestController.kt | 43 ++++++++++++ .../api/baby/repository/BabyRepository.kt | 1 + .../baby/repository/BabyRepositoryAdapter.kt | 8 ++- .../api/global/discord/DiscordSender.kt | 65 ++++++++++++++++++ .../api/global/discord/RestTemplateConfig.kt | 14 ++++ .../global/exception/ApiExceptionHandler.kt | 17 +++-- .../exception/ApiExceptionHandlingFilter.kt | 3 +- .../api/user/repository/UserRepository.kt | 1 + .../user/repository/UserRepositoryAdapter.kt | 6 +- .../vaccine/application/VaccinationService.kt | 56 ++++++++++++++- .../vaccine/domain/UnclassifiedVaccination.kt | 5 +- .../vaccine/domain/constants/VaccineType.kt | 2 +- .../api/vaccine/exception/VaccinationError.kt | 13 ++++ .../vaccine/presentation/VaccinationApi.kt | 2 +- .../presentation/VaccinationController.kt | 11 +-- .../presentation/dto/VaccinationDto.kt | 8 ++- .../UnclassifiedVaccinationJpaRepository.kt | 9 +++ .../UnclassifiedVaccinationRepository.kt | 8 +++ ...nclassifiedVaccinationRepositoryAdapter.kt | 14 ++++ .../repository/VaccinationRepository.kt | 4 ++ .../VaccinationRepositoryAdapter.kt | 9 ++- .../vaccine/VaccineJpaRepository.kt | 9 +++ .../repository/vaccine/VaccineRepository.kt | 9 +++ .../vaccine/VaccineRepositoryAdapter.kt | 20 ++++++ 26 files changed, 321 insertions(+), 19 deletions(-) delete mode 100644 dump.rdb create mode 100644 src/main/kotlin/kr/co/vacgom/api/TestController.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/global/discord/DiscordSender.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/global/discord/RestTemplateConfig.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/exception/VaccinationError.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationJpaRepository.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepository.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepositoryAdapter.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineJpaRepository.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepository.kt create mode 100644 src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepositoryAdapter.kt diff --git a/build.gradle.kts b/build.gradle.kts index a79f74b..e6dbe59 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -76,6 +76,9 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + + // discord + implementation("dev.kord:kord-core:0.15.0") } kotlin { diff --git a/dump.rdb b/dump.rdb deleted file mode 100644 index c4527f915d37093a2959bbc63dacc85fd9630c40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6375 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 diff --git a/src/main/kotlin/kr/co/vacgom/api/TestController.kt b/src/main/kotlin/kr/co/vacgom/api/TestController.kt new file mode 100644 index 0000000..7fc694c --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/TestController.kt @@ -0,0 +1,43 @@ +package kr.co.vacgom.api + +import kr.co.vacgom.api.auth.oauth.enums.SocialLoginProvider +import kr.co.vacgom.api.global.presentation.GlobalPath.BASE_V3 +import kr.co.vacgom.api.user.application.UserTokenService +import kr.co.vacgom.api.user.repository.UserRepository +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import kotlin.random.Random + +@RestController +@RequestMapping(BASE_V3 + "/TEST") +class TestController( + private val userRepository: UserRepository, + private val userTokenService: UserTokenService +) { + + @PostMapping + fun test(@RequestParam tokenType: String): ResponseEntity { + val user = userRepository.findAll()[0] + + when (tokenType) { + "ACCESS" -> { + val accessToken = userTokenService.createAccessToken(user.id, user.role) + return ResponseEntity.ok(accessToken) + } + "REFRESH" -> { + val refreshToken = userTokenService.createRefreshToken(user.id) + return ResponseEntity.ok(refreshToken) + } + "REGISTER" -> { + val socialId = "testSocialId${Random(System.currentTimeMillis()).nextInt()}" + val registerToken = userTokenService.createRegisterToken(socialId, SocialLoginProvider.KAKAO.name) + return ResponseEntity.ok(registerToken) + } + } + + return ResponseEntity.internalServerError().build() + } +} diff --git a/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepository.kt b/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepository.kt index c632394..9942f5d 100644 --- a/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepository.kt +++ b/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepository.kt @@ -8,4 +8,5 @@ interface BabyRepository { fun saveAll(babies: List): List fun findBabiesById(ids: List): List fun findById(id: UUID): Baby + fun findAll(): List } diff --git a/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepositoryAdapter.kt b/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepositoryAdapter.kt index ba95cba..381b29d 100644 --- a/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepositoryAdapter.kt +++ b/src/main/kotlin/kr/co/vacgom/api/baby/repository/BabyRepositoryAdapter.kt @@ -8,7 +8,7 @@ import org.springframework.stereotype.Repository import java.util.* @Repository -class BabyRepositoryAdapter(private val babyJpaRepository: BabyJpaRepository): BabyRepository { +class BabyRepositoryAdapter(private val babyJpaRepository: BabyJpaRepository) : BabyRepository { override fun save(newBaby: Baby): Baby { return babyJpaRepository.save(newBaby) } @@ -20,8 +20,12 @@ class BabyRepositoryAdapter(private val babyJpaRepository: BabyJpaRepository): B override fun findBabiesById(ids: List): List { return babyJpaRepository.findAllById(ids) } - + override fun findById(id: UUID): Baby { return babyJpaRepository.findByIdOrNull(id) ?: throw BusinessException(BabyError.BABY_NOT_FOUND) } + + override fun findAll(): List { + return babyJpaRepository.findAll() + } } diff --git a/src/main/kotlin/kr/co/vacgom/api/global/discord/DiscordSender.kt b/src/main/kotlin/kr/co/vacgom/api/global/discord/DiscordSender.kt new file mode 100644 index 0000000..80984da --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/global/discord/DiscordSender.kt @@ -0,0 +1,65 @@ +package kr.co.vacgom.api.global.discord + +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vacgom.api.baby.domain.Baby +import kr.co.vacgom.api.vaccine.domain.UnclassifiedVaccination +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.HttpEntity +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.stereotype.Component +import org.springframework.web.client.RestTemplate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +@Component +class DiscordSender( + @Value("\${discord.deployWebhookURL}") + private val webhookURL: String, + + @Value("\${discord.welcomeWebhookURL}") + private val welcomeURL: String, + + private val objectMapper: ObjectMapper, + + private val restTemplate: RestTemplate +) { + fun sendVaccinationError( + baby: Baby, + unclassifiedVaccination: UnclassifiedVaccination + ) { + val headers = HttpHeaders() + headers.contentType = MediaType.APPLICATION_JSON + + val now = LocalDateTime.now() + val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초") + val formattedNow = now.format(formatter) + + val embeds = mapOf( + "title" to "**[ \uD83D\uDEA8 긴급 \uD83D\uDEA8 ] ERR-001** 미분류 백신 추가 필요", + "description" to "**미분류 백신** : ${unclassifiedVaccination.name}\n\n" + + "**요청시간** : " + formattedNow + "\n\n" + + "**LOG**\n\n" + + "```json\n{\n" + + "\t\"id\" : ${unclassifiedVaccination.id},\n" + + "\t\"name\" : ${unclassifiedVaccination.name},\n" + + "\t\"doseRound\" : ${unclassifiedVaccination.doseRound},\n" + + "\t\"doseRoundDescription\" : ${unclassifiedVaccination.doseRoundDescription},\n" + + "\t\"vaccinatedAt\" : ${unclassifiedVaccination.vaccinatedAt},\n" + + "\t\"facility\" : ${unclassifiedVaccination.facility},\n" + + "\t\"manufacturer\" : ${unclassifiedVaccination.manufacturer},\n" + + "\t\"productName\" : ${unclassifiedVaccination.productName},\n" + + "\t\"lotNumber\" : ${unclassifiedVaccination.lotNumber},\n" + + "\t\"babyId\" : ${unclassifiedVaccination.baby.id}\n}```", + "color" to "15548997" + ) + val payload = mapOf( + "content" to null, + "embeds" to listOf(embeds) + ) + + val jsonPayload = objectMapper.writeValueAsString(payload) + val entity = HttpEntity(jsonPayload, headers) + restTemplate.postForLocation(webhookURL, entity) + } +} diff --git a/src/main/kotlin/kr/co/vacgom/api/global/discord/RestTemplateConfig.kt b/src/main/kotlin/kr/co/vacgom/api/global/discord/RestTemplateConfig.kt new file mode 100644 index 0000000..974154a --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/global/discord/RestTemplateConfig.kt @@ -0,0 +1,14 @@ +package kr.co.vacgom.api.global.discord + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.client.RestTemplate + +@Configuration +class RestTemplateConfig { + + @Bean + fun restTemplate(): RestTemplate { + return RestTemplate() + } +} diff --git a/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandler.kt b/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandler.kt index 021af18..00ddff2 100644 --- a/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandler.kt +++ b/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandler.kt @@ -4,6 +4,8 @@ import kr.co.vacgom.api.global.exception.error.BusinessException import kr.co.vacgom.api.global.exception.error.ErrorCode import kr.co.vacgom.api.global.exception.error.ErrorResponse import kr.co.vacgom.api.global.exception.error.GlobalError +import lombok.extern.slf4j.Slf4j +import org.slf4j.Logger import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.MethodArgumentNotValidException @@ -13,23 +15,29 @@ import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestControllerAdvice import org.springframework.web.servlet.resource.NoResourceFoundException +@Slf4j @RestControllerAdvice -class ApiExceptionHandler { +class ApiExceptionHandler( + private val log: Logger +) { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException::class) - fun handleMethodArgumentNotValidException(ex: MethodArgumentNotValidException): ErrorResponse { - return ErrorResponse(ex.fieldErrors) + fun handleMethodArgumentNotValidException(exception: MethodArgumentNotValidException): ErrorResponse { + log.warn(exception.message) + return ErrorResponse(exception.fieldErrors) } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MissingServletRequestParameterException::class) - fun missingServletRequestParameterException(): ErrorResponse { + fun missingServletRequestParameterException(exception: MissingServletRequestParameterException): ErrorResponse { + log.warn(exception.message) return ErrorResponse(GlobalError.INVALID_REQUEST_PARAM) } @ExceptionHandler(NoResourceFoundException::class) fun handleUnexpectedException(exception: NoResourceFoundException): ErrorResponse { + log.warn(exception.message) return ErrorResponse(GlobalError.GLOBAL_NOT_FOUND); } @@ -37,6 +45,7 @@ class ApiExceptionHandler { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(BusinessException::class) fun handleBusinessException(exception: BusinessException): ResponseEntity { + log.warn(exception.message) return convert(exception.errorCode) } diff --git a/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandlingFilter.kt b/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandlingFilter.kt index e30a68d..093efc5 100644 --- a/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandlingFilter.kt +++ b/src/main/kotlin/kr/co/vacgom/api/global/exception/ApiExceptionHandlingFilter.kt @@ -43,10 +43,11 @@ class ApiExceptionHandlingFilter( } responseWrapper.copyBodyToResponse() - } catch (exception: BusinessException) { + log.warn(exception.message) setErrorResponse(response, exception) } catch (exception: Exception) { + log.error(exception.message) setErrorResponse(response, BusinessException(GlobalError.INTERNAL_SERVER_ERROR)) } diff --git a/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepository.kt b/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepository.kt index b3ed1f6..315da23 100644 --- a/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepository.kt +++ b/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepository.kt @@ -9,4 +9,5 @@ interface UserRepository { fun findBySocialId(socialId: String): User? fun findById(userId: UUID): User? fun deleteById(userId: UUID) + fun findAll(): List } diff --git a/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepositoryAdapter.kt b/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepositoryAdapter.kt index 9291b4e..8f2c81e 100644 --- a/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepositoryAdapter.kt +++ b/src/main/kotlin/kr/co/vacgom/api/user/repository/UserRepositoryAdapter.kt @@ -8,7 +8,7 @@ import java.util.* @Repository class UserRepositoryAdapter( private val userJpaRepository: UserJpaRepository -): UserRepository { +) : UserRepository { override fun save(user: User): User { return userJpaRepository.save(user) } @@ -24,4 +24,8 @@ class UserRepositoryAdapter( override fun deleteById(userId: UUID) { userJpaRepository.deleteById(userId) } + + override fun findAll(): List { + return userJpaRepository.findAll() + } } diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/application/VaccinationService.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/application/VaccinationService.kt index d126760..0957726 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/application/VaccinationService.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/application/VaccinationService.kt @@ -1,9 +1,63 @@ package kr.co.vacgom.api.vaccine.application +import kr.co.vacgom.api.baby.repository.BabyRepository +import kr.co.vacgom.api.global.discord.DiscordSender +import kr.co.vacgom.api.global.exception.error.BusinessException +import kr.co.vacgom.api.vaccine.domain.UnclassifiedVaccination +import kr.co.vacgom.api.vaccine.domain.Vaccination +import kr.co.vacgom.api.vaccine.presentation.dto.VaccinationDto +import kr.co.vacgom.api.vaccine.repository.UnclassifiedVaccinationRepository +import kr.co.vacgom.api.vaccine.repository.VaccinationRepository +import kr.co.vacgom.api.vaccine.repository.vaccine.VaccineRepository import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service @Transactional -class VaccinationService { +class VaccinationService( + private val unclassifiedVaccinationRepository: UnclassifiedVaccinationRepository, + private val vaccinationRepository: VaccinationRepository, + private val vaccineRepository: VaccineRepository, + private val babyRepository: BabyRepository, + private val discordSender: DiscordSender +) { + fun createVaccinations(request: VaccinationDto.Request.Create) { + + val baby = babyRepository.findById(request.babyId) + + request.vaccinationRequests.forEach { + try { + val vaccine = vaccineRepository.findByName(it.name) + + val classifiedVaccination = Vaccination( + doseRound = it.doseRound, + doseRoundDescription = it.doseRoundDescription, + vaccinatedAt = it.vaccinatedAt, + facility = it.facility, + manufacturer = it.manufacturer, + productName = it.productName, + lotNumber = it.lotNumber, + vaccine = vaccine, + baby = baby + ) + + vaccinationRepository.save(classifiedVaccination) + } catch (businessException: BusinessException) { + val unclassifiedVaccination = UnclassifiedVaccination( + name = it.name, + doseRound = it.doseRound, + doseRoundDescription = it.doseRoundDescription, + vaccinatedAt = it.vaccinatedAt, + facility = it.facility, + manufacturer = it.manufacturer, + productName = it.productName, + lotNumber = it.lotNumber, + baby = baby + ) + + val savedUnclassifiedVaccination = unclassifiedVaccinationRepository.save(unclassifiedVaccination) + discordSender.sendVaccinationError(baby, savedUnclassifiedVaccination) + } + } + } } diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/UnclassifiedVaccination.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/UnclassifiedVaccination.kt index 48d3608..66cdca4 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/UnclassifiedVaccination.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/UnclassifiedVaccination.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat.Shape.STRING import jakarta.persistence.* import jakarta.persistence.FetchType.LAZY import kr.co.vacgom.api.baby.domain.Baby +import kr.co.vacgom.api.global.common.domain.BaseTimeEntity import kr.co.vacgom.api.global.util.UuidCreator import org.hibernate.annotations.Comment import java.time.LocalDate @@ -12,7 +13,7 @@ import java.util.* @Entity @Table(name = "TB_VACCINATION_UNCLASSIFIED") -class UnclassifiedVaccination private constructor( +class UnclassifiedVaccination( @Column(nullable = false) @Comment("[Not Null] 미분류 백신 이름") val name: String, @@ -46,7 +47,7 @@ class UnclassifiedVaccination private constructor( @JoinColumn(name = "BABY_ID") @Comment("[NotNull] 아기 Id") val baby: Baby -) { +) : BaseTimeEntity() { @Id @Column( diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/constants/VaccineType.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/constants/VaccineType.kt index a8eba56..0b87ef2 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/constants/VaccineType.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/domain/constants/VaccineType.kt @@ -1,7 +1,7 @@ package kr.co.vacgom.api.vaccine.domain.constants enum class VaccineType { - NATIONAL, + NATION, GENERAL, EVENT } diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/exception/VaccinationError.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/exception/VaccinationError.kt new file mode 100644 index 0000000..33aa75a --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/exception/VaccinationError.kt @@ -0,0 +1,13 @@ +package kr.co.vacgom.api.vaccine.exception + +import kr.co.vacgom.api.global.exception.error.ErrorCode +import org.springframework.http.HttpStatus +import org.springframework.http.HttpStatus.NOT_FOUND + +enum class VaccinationError( + override val message: String, + override val status: HttpStatus, + override val code: String, +) : ErrorCode { + VACCINE_NOT_FOUND("해당 백신이 존재하지 않습니다.", NOT_FOUND, "V_001") +} diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationApi.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationApi.kt index 0e37761..b3c4b58 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationApi.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationApi.kt @@ -23,7 +23,7 @@ interface VaccinationApi { ), ] ) - fun createVaccinations(request: List) + fun createVaccinations(request: VaccinationDto.Request.Create) companion object { const val VACCINATIONS = "/vaccinations" diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationController.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationController.kt index 544dbea..cde11bb 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationController.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/VaccinationController.kt @@ -1,19 +1,22 @@ package kr.co.vacgom.api.vaccine.presentation import kr.co.vacgom.api.global.presentation.GlobalPath.BASE_V3 +import kr.co.vacgom.api.vaccine.application.VaccinationService import kr.co.vacgom.api.vaccine.presentation.VaccinationApi.Companion.VACCINATIONS import kr.co.vacgom.api.vaccine.presentation.dto.VaccinationDto -import kr.co.vacgom.api.vaccine.repository.VaccinationRepository +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping(BASE_V3 + VACCINATIONS) class VaccinationController( - private val vaccinationRepository: VaccinationRepository + private val vaccinationService: VaccinationService ) : VaccinationApi { - override fun createVaccinations(request: List) { - + @PostMapping + override fun createVaccinations(@RequestBody request: VaccinationDto.Request.Create) { + vaccinationService.createVaccinations(request) } } diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/dto/VaccinationDto.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/dto/VaccinationDto.kt index fa23ddb..5572c44 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/dto/VaccinationDto.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/presentation/dto/VaccinationDto.kt @@ -2,15 +2,21 @@ package kr.co.vacgom.api.vaccine.presentation.dto import io.swagger.v3.oas.annotations.media.Schema import java.time.LocalDate +import java.util.* class VaccinationDto { class Request { @Schema(name = "VaccinationDto.Request.Create") data class Create( + val babyId: UUID, + val vaccinationRequests: List + ) + + data class VaccinationRequest( val doseRound: Long?, val doseRoundDescription: String?, val facility: String?, - val logNumber: String?, + val lotNumber: String?, val manufacturer: String?, val productName: String?, val name: String, diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationJpaRepository.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationJpaRepository.kt new file mode 100644 index 0000000..e71ddeb --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationJpaRepository.kt @@ -0,0 +1,9 @@ +package kr.co.vacgom.api.vaccine.repository + +import kr.co.vacgom.api.vaccine.domain.UnclassifiedVaccination +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + +interface UnclassifiedVaccinationJpaRepository : JpaRepository { + +} diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepository.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepository.kt new file mode 100644 index 0000000..fb7a774 --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepository.kt @@ -0,0 +1,8 @@ +package kr.co.vacgom.api.vaccine.repository + +import kr.co.vacgom.api.vaccine.domain.UnclassifiedVaccination + +interface UnclassifiedVaccinationRepository { + + fun save(unclassifiedVaccination: UnclassifiedVaccination): UnclassifiedVaccination +} diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepositoryAdapter.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepositoryAdapter.kt new file mode 100644 index 0000000..9a4b317 --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/UnclassifiedVaccinationRepositoryAdapter.kt @@ -0,0 +1,14 @@ +package kr.co.vacgom.api.vaccine.repository + +import kr.co.vacgom.api.vaccine.domain.UnclassifiedVaccination +import org.springframework.stereotype.Repository + +@Repository +class UnclassifiedVaccinationRepositoryAdapter( + private val unclassifiedVaccinationJpaRepository: UnclassifiedVaccinationJpaRepository +) : UnclassifiedVaccinationRepository { + + override fun save(unclassifiedVaccination: UnclassifiedVaccination): UnclassifiedVaccination { + return unclassifiedVaccinationJpaRepository.save(unclassifiedVaccination) + } +} diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepository.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepository.kt index 428e18c..a15a0ba 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepository.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepository.kt @@ -1,4 +1,8 @@ package kr.co.vacgom.api.vaccine.repository +import kr.co.vacgom.api.vaccine.domain.Vaccination + interface VaccinationRepository { + + fun save(vaccination: Vaccination): Vaccination } diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepositoryAdapter.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepositoryAdapter.kt index e1c9887..9fcddc2 100644 --- a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepositoryAdapter.kt +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/VaccinationRepositoryAdapter.kt @@ -1,7 +1,14 @@ package kr.co.vacgom.api.vaccine.repository +import kr.co.vacgom.api.vaccine.domain.Vaccination import org.springframework.stereotype.Repository @Repository -class VaccinationRepositoryAdapter : VaccinationRepository { +class VaccinationRepositoryAdapter( + private val vaccinationJpaRepository: VaccinationJpaRepository +) : VaccinationRepository { + + override fun save(vaccination: Vaccination): Vaccination { + return vaccinationJpaRepository.save(vaccination) + } } diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineJpaRepository.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineJpaRepository.kt new file mode 100644 index 0000000..dedb5fa --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineJpaRepository.kt @@ -0,0 +1,9 @@ +package kr.co.vacgom.api.vaccine.repository.vaccine + +import kr.co.vacgom.api.vaccine.domain.Vaccine +import org.springframework.data.jpa.repository.JpaRepository + +interface VaccineJpaRepository : JpaRepository { + + fun findByName(name: String): Vaccine? +} diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepository.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepository.kt new file mode 100644 index 0000000..7110277 --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepository.kt @@ -0,0 +1,9 @@ +package kr.co.vacgom.api.vaccine.repository.vaccine + +import kr.co.vacgom.api.vaccine.domain.Vaccine + +interface VaccineRepository { + + fun findByName(name: String): Vaccine + fun findAll(): List +} diff --git a/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepositoryAdapter.kt b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepositoryAdapter.kt new file mode 100644 index 0000000..06ba34d --- /dev/null +++ b/src/main/kotlin/kr/co/vacgom/api/vaccine/repository/vaccine/VaccineRepositoryAdapter.kt @@ -0,0 +1,20 @@ +package kr.co.vacgom.api.vaccine.repository.vaccine + +import kr.co.vacgom.api.global.exception.error.BusinessException +import kr.co.vacgom.api.vaccine.domain.Vaccine +import kr.co.vacgom.api.vaccine.exception.VaccinationError +import org.springframework.stereotype.Repository + +@Repository +class VaccineRepositoryAdapter( + private val vaccineJpaRepository: VaccineJpaRepository +) : VaccineRepository { + + override fun findByName(name: String): Vaccine { + return vaccineJpaRepository.findByName(name) ?: throw BusinessException(VaccinationError.VACCINE_NOT_FOUND) + } + + override fun findAll(): List { + return vaccineJpaRepository.findAll() + } +}