From be7ea60d07478aae55b29628c3e6725f82b5466a Mon Sep 17 00:00:00 2001 From: Hank-Choi Date: Thu, 6 Jul 2023 18:28:31 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=EB=B9=88=EC=9E=90=EB=A6=AC=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=B0=95=EC=A2=8C=20=EC=A1=B0=ED=9A=8C/=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/VacancyNotifcationHandler.kt | 17 ++++-------- api/src/main/kotlin/router/MainRouter.kt | 5 ++-- .../VacancyNotificationRepository.kt | 2 +- .../service/VacancyNotificationService.kt | 27 +++++++++---------- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt b/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt index 4c247297..005fcd2d 100644 --- a/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt +++ b/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt @@ -1,7 +1,6 @@ package com.wafflestudio.snu4t.handler import com.wafflestudio.snu4t.middleware.SnuttRestApiDefaultMiddleware -import com.wafflestudio.snu4t.vacancynotification.dto.VacancyNotificationDto import com.wafflestudio.snu4t.vacancynotification.service.VacancyNotificationService import org.springframework.stereotype.Component import org.springframework.web.reactive.function.server.ServerRequest @@ -19,25 +18,19 @@ class VacancyNotifcationHandler( val lectureId = req.pathVariable("lectureId") vacancyNotificationService.addVacancyNotification(userId, lectureId) - .let { VacancyNotificationDto(it) } + null } - suspend fun getVacancyNotifications(req: ServerRequest): ServerResponse = handle(req) { + suspend fun getVacancyNotificationLectures(req: ServerRequest): ServerResponse = handle(req) { val userId = req.userId - vacancyNotificationService.getVacancyNotifications(userId) + vacancyNotificationService.getVacancyNotificationLectures(userId) } - suspend fun getVacancyNotification(req: ServerRequest): ServerResponse = handle(req) { - val userId = req.userId - val lectureId = req.pathVariable("lectureId") - - vacancyNotificationService.getVacancyNotification(userId, lectureId) - } suspend fun deleteVacancyNotification(req: ServerRequest): ServerResponse = handle(req) { - val id = req.pathVariable("id") + val lectureId = req.pathVariable("lectureId") - vacancyNotificationService.deleteVacancyNotification(id) + vacancyNotificationService.deleteVacancyNotification(lectureId) null } } diff --git a/api/src/main/kotlin/router/MainRouter.kt b/api/src/main/kotlin/router/MainRouter.kt index a621bc50..18f6010f 100644 --- a/api/src/main/kotlin/router/MainRouter.kt +++ b/api/src/main/kotlin/router/MainRouter.kt @@ -119,10 +119,9 @@ class MainRouter( fun vacancyNotificationRoute() = coRouter { path("/v1").nest { "/vacancy-notifications".nest { - GET("", vacancyNotificationHandler::getVacancyNotifications) - GET("/lectures/{lectureId}", vacancyNotificationHandler::getVacancyNotification) + GET("/lectures", vacancyNotificationHandler::getVacancyNotificationLectures) POST("/lectures/{lectureId}", vacancyNotificationHandler::addVacancyNotification) - DELETE("/{id}", vacancyNotificationHandler::deleteVacancyNotification) + DELETE("/lectures/{lectureId}", vacancyNotificationHandler::deleteVacancyNotification) } } } diff --git a/core/src/main/kotlin/vacancynotification/repository/VacancyNotificationRepository.kt b/core/src/main/kotlin/vacancynotification/repository/VacancyNotificationRepository.kt index f0f0e71e..d3805d7b 100644 --- a/core/src/main/kotlin/vacancynotification/repository/VacancyNotificationRepository.kt +++ b/core/src/main/kotlin/vacancynotification/repository/VacancyNotificationRepository.kt @@ -7,5 +7,5 @@ import org.springframework.data.repository.kotlin.CoroutineCrudRepository interface VacancyNotificationRepository : CoroutineCrudRepository { fun findAllByLectureId(lectureId: String): Flow fun findAllByUserId(userId: String): Flow - suspend fun findFirstByUserIdAndLectureId(userId: String, lectureId: String): VacancyNotification + suspend fun deleteByLectureId(lectureId: String) } diff --git a/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt b/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt index e9a37676..50bc8152 100644 --- a/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt +++ b/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt @@ -4,31 +4,36 @@ import com.wafflestudio.snu4t.common.exception.DuplicateVacancyNotificationExcep import com.wafflestudio.snu4t.common.exception.InvalidRegistrationForPreviousSemesterCourseException import com.wafflestudio.snu4t.common.exception.LectureNotFoundException import com.wafflestudio.snu4t.coursebook.service.CoursebookService -import com.wafflestudio.snu4t.lectures.service.LectureService +import com.wafflestudio.snu4t.lectures.data.Lecture +import com.wafflestudio.snu4t.lectures.repository.LectureRepository import com.wafflestudio.snu4t.vacancynotification.data.VacancyNotification import com.wafflestudio.snu4t.vacancynotification.repository.VacancyNotificationRepository import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import org.springframework.dao.DuplicateKeyException import org.springframework.stereotype.Service interface VacancyNotificationService { + suspend fun getVacancyNotificationLectures(userId: String): List suspend fun addVacancyNotification(userId: String, lectureId: String): VacancyNotification - suspend fun getVacancyNotifications(userId: String): List - suspend fun getVacancyNotification(userId: String, lectureId: String): VacancyNotification - suspend fun deleteVacancyNotification(id: String) + suspend fun deleteVacancyNotification(lectureId: String) } @Service class VacancyNotificationServiceImpl( private val vacancyNotificationRepository: VacancyNotificationRepository, - private val lectureService: LectureService, + private val lectureRepository: LectureRepository, private val coursebookService: CoursebookService ) : VacancyNotificationService { + override suspend fun getVacancyNotificationLectures(userId: String): List = + vacancyNotificationRepository.findAllByUserId(userId).map { it.lectureId } + .let { lectureRepository.findAllById(it) }.toList() + override suspend fun addVacancyNotification(userId: String, lectureId: String): VacancyNotification = coroutineScope { - val deferredLecture = async { lectureService.getByIdOrNull(lectureId) } + val deferredLecture = async { lectureRepository.findById(lectureId) } val deferredLatestCoursebook = async { coursebookService.getLatestCoursebook() } val (lecture, latestCoursebook) = deferredLecture.await() to deferredLatestCoursebook.await() @@ -40,14 +45,8 @@ class VacancyNotificationServiceImpl( trySave(VacancyNotification(userId = userId, lectureId = lectureId, coursebookId = latestCoursebook.id!!)) } - override suspend fun getVacancyNotifications(userId: String): List = - vacancyNotificationRepository.findAllByUserId(userId).toList() - - override suspend fun getVacancyNotification(userId: String, lectureId: String): VacancyNotification = - vacancyNotificationRepository.findFirstByUserIdAndLectureId(userId, lectureId) - - override suspend fun deleteVacancyNotification(id: String) { - vacancyNotificationRepository.deleteById(id) + override suspend fun deleteVacancyNotification(lectureId: String) { + vacancyNotificationRepository.deleteByLectureId(lectureId) } private suspend fun trySave(vacancyNotification: VacancyNotification) = From ed6955e078cd39abc016a19f0d16d10a9e42b33e Mon Sep 17 00:00:00 2001 From: Hank-Choi Date: Thu, 6 Jul 2023 18:29:14 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=9D=B4=EC=A0=84=20=ED=95=99=EA=B8=B0=20?= =?UTF-8?q?=EB=B9=88=EC=9E=90=EB=A6=AC=20=EC=95=8C=EB=A6=BC=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/sugangsnu/job/sync/SugangSnuSyncJobConfig.kt | 3 +++ .../kotlin/vacancynotification/data/VacancyNotification.kt | 3 --- .../service/VacancyNotificationService.kt | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/batch/src/main/kotlin/sugangsnu/job/sync/SugangSnuSyncJobConfig.kt b/batch/src/main/kotlin/sugangsnu/job/sync/SugangSnuSyncJobConfig.kt index 980ae698..6c7d679d 100644 --- a/batch/src/main/kotlin/sugangsnu/job/sync/SugangSnuSyncJobConfig.kt +++ b/batch/src/main/kotlin/sugangsnu/job/sync/SugangSnuSyncJobConfig.kt @@ -4,6 +4,7 @@ import com.wafflestudio.snu4t.coursebook.service.CoursebookService import com.wafflestudio.snu4t.sugangsnu.common.service.SugangSnuNotificationService import com.wafflestudio.snu4t.sugangsnu.common.utils.nextCoursebook import com.wafflestudio.snu4t.sugangsnu.job.sync.service.SugangSnuSyncService +import com.wafflestudio.snu4t.vacancynotification.service.VacancyNotificationService import kotlinx.coroutines.runBlocking import org.springframework.batch.core.Job import org.springframework.batch.core.Step @@ -29,6 +30,7 @@ class SugangSnuSyncJobConfig { sugangSnuSyncService: SugangSnuSyncService, coursebookService: CoursebookService, sugangSnuNotificationService: SugangSnuNotificationService, + vacancyNotificationService: VacancyNotificationService, ): Step = StepBuilder("fetchSugangSnuStep", jobRepository).tasklet( { _, _ -> runBlocking { @@ -38,6 +40,7 @@ class SugangSnuSyncJobConfig { sugangSnuNotificationService.notifyUserLectureChanges(updateResult) } else { val newCoursebook = existingCoursebook.nextCoursebook() + vacancyNotificationService.deleteAll() sugangSnuSyncService.addCoursebook(newCoursebook) sugangSnuNotificationService.notifyCoursebookUpdate(newCoursebook) } diff --git a/core/src/main/kotlin/vacancynotification/data/VacancyNotification.kt b/core/src/main/kotlin/vacancynotification/data/VacancyNotification.kt index c547211d..de80e1f2 100644 --- a/core/src/main/kotlin/vacancynotification/data/VacancyNotification.kt +++ b/core/src/main/kotlin/vacancynotification/data/VacancyNotification.kt @@ -20,7 +20,4 @@ data class VacancyNotification( @Indexed @Field(targetType = FieldType.OBJECT_ID) val lectureId: String, - @Indexed - @Field(targetType = FieldType.OBJECT_ID) - val coursebookId: String, ) diff --git a/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt b/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt index 50bc8152..4ede8ff3 100644 --- a/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt +++ b/core/src/main/kotlin/vacancynotification/service/VacancyNotificationService.kt @@ -19,6 +19,7 @@ interface VacancyNotificationService { suspend fun getVacancyNotificationLectures(userId: String): List suspend fun addVacancyNotification(userId: String, lectureId: String): VacancyNotification suspend fun deleteVacancyNotification(lectureId: String) + suspend fun deleteAll() } @Service @@ -42,13 +43,17 @@ class VacancyNotificationServiceImpl( throw InvalidRegistrationForPreviousSemesterCourseException } - trySave(VacancyNotification(userId = userId, lectureId = lectureId, coursebookId = latestCoursebook.id!!)) + trySave(VacancyNotification(userId = userId, lectureId = lectureId)) } override suspend fun deleteVacancyNotification(lectureId: String) { vacancyNotificationRepository.deleteByLectureId(lectureId) } + override suspend fun deleteAll() { + vacancyNotificationRepository.deleteAll() + } + private suspend fun trySave(vacancyNotification: VacancyNotification) = runCatching { vacancyNotificationRepository.save(vacancyNotification) From bd1fd5f8afd88cb01ab6c481f2c5039d6f9d5ebd Mon Sep 17 00:00:00 2001 From: Hank-Choi Date: Thu, 6 Jul 2023 18:38:37 +0900 Subject: [PATCH 3/4] =?UTF-8?q?docs=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../router/docs/VacancyNotificationDocs.kt | 16 ++++++++++++---- .../dto/VacancyNotificationDto.kt | 15 --------------- 2 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 core/src/main/kotlin/vacancynotification/dto/VacancyNotificationDto.kt diff --git a/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt b/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt index a0cc25c5..c4aa8f30 100644 --- a/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt +++ b/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt @@ -1,9 +1,10 @@ package com.wafflestudio.snu4t.router.docs -import com.wafflestudio.snu4t.vacancynotification.dto.VacancyNotificationDto +import com.wafflestudio.snu4t.lectures.data.Lecture import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.enums.ParameterIn +import io.swagger.v3.oas.annotations.media.ArraySchema import io.swagger.v3.oas.annotations.media.Content import io.swagger.v3.oas.annotations.media.Schema import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -13,6 +14,13 @@ import org.springframework.http.MediaType import org.springframework.web.bind.annotation.RequestMethod @RouterOperations( + RouterOperation( + path = "/v1/vacancy-notifications/lectures", method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE], + operation = Operation( + operationId = "getVacancyNotificationLectures", + responses = [ApiResponse(responseCode = "200", content = [Content(array = ArraySchema(schema = Schema(implementation = Lecture::class)))])] + ), + ), RouterOperation( path = "/v1/vacancy-notifications/lectures/{lectureId}", method = [RequestMethod.POST], produces = [MediaType.APPLICATION_JSON_VALUE], operation = Operation( @@ -20,15 +28,15 @@ import org.springframework.web.bind.annotation.RequestMethod parameters = [ Parameter(`in` = ParameterIn.PATH, name = "lectureId", required = true), ], - responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema(implementation = VacancyNotificationDto::class))])] + responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema())])] ), ), RouterOperation( - path = "/v1/vacancy-notifications/{id}", method = [RequestMethod.DELETE], produces = [MediaType.APPLICATION_JSON_VALUE], + path = "/v1/vacancy-notifications/lectures/{lectureId}", method = [RequestMethod.DELETE], produces = [MediaType.APPLICATION_JSON_VALUE], operation = Operation( operationId = "deleteVacancyNotification", parameters = [ - Parameter(`in` = ParameterIn.PATH, name = "id", required = true), + Parameter(`in` = ParameterIn.PATH, name = "lectureId", required = true), ], responses = [ApiResponse(responseCode = "200", content = [Content(schema = Schema())])] ), diff --git a/core/src/main/kotlin/vacancynotification/dto/VacancyNotificationDto.kt b/core/src/main/kotlin/vacancynotification/dto/VacancyNotificationDto.kt deleted file mode 100644 index 21442ef0..00000000 --- a/core/src/main/kotlin/vacancynotification/dto/VacancyNotificationDto.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.wafflestudio.snu4t.vacancynotification.dto - -import com.wafflestudio.snu4t.vacancynotification.data.VacancyNotification - -data class VacancyNotificationDto( - val id: String, - val userId: String, - val lectureId: String, -) - -fun VacancyNotificationDto(vacancyNotification: VacancyNotification) = VacancyNotificationDto( - id = vacancyNotification.id!!, - userId = vacancyNotification.userId, - lectureId = vacancyNotification.lectureId, -) From fa7893c20b8982c7d3770ffb122e7e78ae195c32 Mon Sep 17 00:00:00 2001 From: Hank-Choi Date: Thu, 6 Jul 2023 19:13:22 +0900 Subject: [PATCH 4/4] =?UTF-8?q?dto=EC=A0=81=EC=9A=A9=20/=20docs=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/VacancyNotifcationHandler.kt | 13 ++++++------ .../router/docs/VacancyNotificationDocs.kt | 21 +++++++++++++++++-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt b/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt index 005fcd2d..140b9e00 100644 --- a/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt +++ b/api/src/main/kotlin/handler/VacancyNotifcationHandler.kt @@ -1,5 +1,6 @@ package com.wafflestudio.snu4t.handler +import com.wafflestudio.snu4t.lectures.dto.LectureDto import com.wafflestudio.snu4t.middleware.SnuttRestApiDefaultMiddleware import com.wafflestudio.snu4t.vacancynotification.service.VacancyNotificationService import org.springframework.stereotype.Component @@ -13,18 +14,18 @@ class VacancyNotifcationHandler( ) : ServiceHandler( handlerMiddleware = snuttRestApiDefaultMiddleware ) { - suspend fun addVacancyNotification(req: ServerRequest): ServerResponse = handle(req) { + suspend fun getVacancyNotificationLectures(req: ServerRequest): ServerResponse = handle(req) { val userId = req.userId - val lectureId = req.pathVariable("lectureId") - vacancyNotificationService.addVacancyNotification(userId, lectureId) - null + vacancyNotificationService.getVacancyNotificationLectures(userId).map { LectureDto(it) } } - suspend fun getVacancyNotificationLectures(req: ServerRequest): ServerResponse = handle(req) { + suspend fun addVacancyNotification(req: ServerRequest): ServerResponse = handle(req) { val userId = req.userId + val lectureId = req.pathVariable("lectureId") - vacancyNotificationService.getVacancyNotificationLectures(userId) + vacancyNotificationService.addVacancyNotification(userId, lectureId) + null } suspend fun deleteVacancyNotification(req: ServerRequest): ServerResponse = handle(req) { diff --git a/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt b/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt index c4aa8f30..7e4ea1f9 100644 --- a/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt +++ b/api/src/main/kotlin/router/docs/VacancyNotificationDocs.kt @@ -1,11 +1,12 @@ package com.wafflestudio.snu4t.router.docs -import com.wafflestudio.snu4t.lectures.data.Lecture +import com.wafflestudio.snu4t.lectures.dto.LectureDto import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.enums.ParameterIn import io.swagger.v3.oas.annotations.media.ArraySchema import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.ExampleObject import io.swagger.v3.oas.annotations.media.Schema import io.swagger.v3.oas.annotations.responses.ApiResponse import org.springdoc.core.annotations.RouterOperation @@ -18,7 +19,23 @@ import org.springframework.web.bind.annotation.RequestMethod path = "/v1/vacancy-notifications/lectures", method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE], operation = Operation( operationId = "getVacancyNotificationLectures", - responses = [ApiResponse(responseCode = "200", content = [Content(array = ArraySchema(schema = Schema(implementation = Lecture::class)))])] + responses = [ + ApiResponse(responseCode = "200", content = [Content(array = ArraySchema(schema = Schema(implementation = LectureDto::class)))]), + ApiResponse( + responseCode = "400", + description = "이전 학기 강의 등록 시", + content = [Content(examples = [ExampleObject(value = """{"errcode" : "40005", "message" : "이전 학기에는 빈자리 알림을 등록할 수 없습니다.", "displayMessage" : "이전 학기에는 빈자리 알림을 등록할 수 없습니다."}""")])] + ), + ApiResponse( + responseCode = "400", + description = "빈자리 알림 중복", + content = [Content(examples = [ExampleObject(value = """{"errcode" : "40900", "message" : "빈자리 알림 중복", "displayMessage" : "빈자리 알림 중복"}""")])] + ), + ApiResponse( + responseCode = "404", + content = [Content(examples = [ExampleObject(value = """{"errcode" : "16387", "message" : "lecture가 없습니다.", "displayMessage" : "lecture가 없습니다."}""")])] + ), + ] ), ), RouterOperation(