From bda9c2444c6fc5591ab279205ce5ecd9bac0dc0f Mon Sep 17 00:00:00 2001 From: birdie Date: Sun, 10 Aug 2025 17:57:18 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20=EC=9A=B0=EC=82=B0=20=EB=8C=80?= =?UTF-8?q?=EC=97=AC=20=EC=BD=94=EB=93=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/config/event/EventConfiguration.kt | 18 ++++++++ .../be/rent/event/RentEventHandler.kt | 45 +++++++++++++++++++ .../be/rent/event/UmbrellaRentedEvent.kt | 13 ++++++ .../upbrella/be/rent/service/RentService.kt | 44 +++++++++--------- .../upbrella/be/slack/SlackAlarmService.kt | 35 ++++++++------- .../dto/service/input/SlackAlarmInput.kt | 13 ++++++ .../upbrella/be/umbrella/entity/Umbrella.kt | 14 +++++- .../kotlin/upbrella/be/util/event/Event.kt | 5 +++ .../kotlin/upbrella/be/util/event/Events.kt | 16 +++++++ 9 files changed, 161 insertions(+), 42 deletions(-) create mode 100644 src/main/kotlin/upbrella/be/config/event/EventConfiguration.kt create mode 100644 src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt create mode 100644 src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt create mode 100644 src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt create mode 100644 src/main/kotlin/upbrella/be/util/event/Event.kt create mode 100644 src/main/kotlin/upbrella/be/util/event/Events.kt diff --git a/src/main/kotlin/upbrella/be/config/event/EventConfiguration.kt b/src/main/kotlin/upbrella/be/config/event/EventConfiguration.kt new file mode 100644 index 00000000..2e2a2501 --- /dev/null +++ b/src/main/kotlin/upbrella/be/config/event/EventConfiguration.kt @@ -0,0 +1,18 @@ +package upbrella.be.config.event + +import org.springframework.beans.factory.InitializingBean +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import upbrella.be.util.event.Events + +@Configuration +class EventsConfiguration( + private val applicationContext: ApplicationContext +) { + + @Bean + fun eventsInitializer(): InitializingBean { + return InitializingBean { Events.setPublisher(applicationContext) } + } +} diff --git a/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt b/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt new file mode 100644 index 00000000..ff9ec433 --- /dev/null +++ b/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt @@ -0,0 +1,45 @@ +package upbrella.be.rent.event + +import org.springframework.context.event.EventListener +import org.springframework.stereotype.Service +import upbrella.be.rent.entity.ConditionReport +import upbrella.be.rent.service.ConditionReportService +import upbrella.be.slack.SlackAlarmService +import upbrella.be.slack.dto.service.input.NotifyConditionReportInput +import upbrella.be.slack.dto.service.input.NotifyRentInput + +@Service +class RentEventHandler( + private val slackAlarmService: SlackAlarmService, + private val conditionReportService: ConditionReportService, +) { + + @EventListener(UmbrellaRentedEvent::class) + fun handleUmbrellaRentedEvent(event: UmbrellaRentedEvent) { + slackAlarmService.notifyRent( + NotifyRentInput( + userId = event.userId, + userName = event.userName, + rentStoreName = event.rentStoreName + ) + ) + + event.conditionReportContent + ?.takeIf { it.isNotBlank() } + ?.let { content -> + ConditionReport( + history = event.history, + content = content + ).also { conditionReport -> + conditionReportService.saveConditionReport(conditionReport) + slackAlarmService.notifyConditionReport( + NotifyConditionReportInput( + umbrellaId = event.umbrellaId, + rentStoreName = event.rentStoreName, + content = content + ) + ) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt b/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt new file mode 100644 index 00000000..e5a5a5a9 --- /dev/null +++ b/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt @@ -0,0 +1,13 @@ +package upbrella.be.rent.event + +import upbrella.be.rent.entity.History +import upbrella.be.util.event.Event + +class UmbrellaRentedEvent( + val userId: Long, + val userName: String, + val rentStoreName: String, + val conditionReportContent: String? = null, + val umbrellaId: Long, + val history: History, +) : Event() diff --git a/src/main/kotlin/upbrella/be/rent/service/RentService.kt b/src/main/kotlin/upbrella/be/rent/service/RentService.kt index d38873bc..1236b9ed 100644 --- a/src/main/kotlin/upbrella/be/rent/service/RentService.kt +++ b/src/main/kotlin/upbrella/be/rent/service/RentService.kt @@ -7,16 +7,18 @@ import upbrella.be.rent.dto.request.HistoryFilterRequest import upbrella.be.rent.dto.request.RentUmbrellaByUserRequest import upbrella.be.rent.dto.request.ReturnUmbrellaByUserRequest import upbrella.be.rent.dto.response.* -import upbrella.be.rent.entity.ConditionReport import upbrella.be.rent.entity.History import upbrella.be.rent.entity.ImprovementReport -import upbrella.be.rent.exception.* +import upbrella.be.rent.event.UmbrellaRentedEvent +import upbrella.be.rent.exception.CannotBeRentedException +import upbrella.be.rent.exception.ExistingUmbrellaForRentException +import upbrella.be.rent.exception.NonExistingHistoryException +import upbrella.be.rent.exception.NonExistingUmbrellaForRentException import upbrella.be.rent.repository.RentRepository import upbrella.be.slack.SlackAlarmService import upbrella.be.store.entity.StoreMeta import upbrella.be.store.repository.StoreMetaReader import upbrella.be.umbrella.entity.Umbrella -import upbrella.be.umbrella.exception.MissingUmbrellaException import upbrella.be.umbrella.exception.NonExistingBorrowedHistoryException import upbrella.be.umbrella.service.UmbrellaService import upbrella.be.user.dto.response.AllHistoryResponse @@ -25,6 +27,7 @@ import upbrella.be.user.dto.response.SingleHistoryResponse import upbrella.be.user.entity.User import upbrella.be.user.repository.UserReader import upbrella.be.user.service.BlackListService +import upbrella.be.util.event.Events import java.time.LocalDateTime import java.time.temporal.ChronoUnit @@ -35,8 +38,6 @@ class RentService( private val slackAlarmService: SlackAlarmService, private val improvementReportService: ImprovementReportService, private val rentRepository: RentRepository, - private val conditionReportService: ConditionReportService, - private val lockerService: LockerService, private val blackListService: BlackListService, private val userReader: UserReader, ) { @@ -66,30 +67,25 @@ class RentService( throw ExistingUmbrellaForRentException("[ERROR] 해당 유저가 대여 중인 우산이 있습니다.") } val umbrella = umbrellaService.findUmbrellaById(rentUmbrellaByUserRequest.umbrellaId) - if (umbrella.storeMeta.id != rentUmbrellaByUserRequest.storeId) { - throw UmbrellaStoreMissMatchException("[ERROR] 해당 우산은 해당 매장에 존재하지 않습니다.") - } - if (umbrella.missed) { - throw MissingUmbrellaException("[ERROR] 해당 우산은 분실되었습니다.") - } - if (!umbrella.rentable) { - throw NotAvailableUmbrellaException("[ERROR] 해당 우산은 대여중입니다.") - } - umbrella.rentUmbrella() + + umbrella.rentUmbrella(rentUmbrellaByUserRequest.storeId) + val rentalStore = storeMetaReader.findById(rentUmbrellaByUserRequest.storeId) val history = rentRepository.save( History.ofCreatedByNewRent(umbrella, userToRent, rentalStore) ) - slackAlarmService.notifyRent(userToRent, history) - rentUmbrellaByUserRequest.conditionReport - ?.takeIf { it.isNotBlank() } - ?.let { content -> - ConditionReport(history = history, content = content).also { conditionReport -> - conditionReportService.saveConditionReport(conditionReport) - slackAlarmService.notifyConditionReport(conditionReport) - } - } + // umbrellaRentedEvent + Events.raise( + UmbrellaRentedEvent( + userId = userToRent.id, + userName = userToRent.name, + rentStoreName = rentalStore.name, + conditionReportContent = rentUmbrellaByUserRequest.conditionReport, + umbrellaId = umbrella.id!!, + history = history, + ) + ) } @Transactional diff --git a/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt b/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt index b9466e8d..151401b0 100644 --- a/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt +++ b/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt @@ -5,9 +5,10 @@ import org.springframework.http.HttpMethod.POST import org.springframework.stereotype.Service import org.springframework.web.client.RestTemplate import upbrella.be.config.SlackBotConfig -import upbrella.be.rent.entity.ConditionReport import upbrella.be.rent.entity.History import upbrella.be.rent.entity.ImprovementReport +import upbrella.be.slack.dto.service.input.NotifyConditionReportInput +import upbrella.be.slack.dto.service.input.NotifyRentInput import upbrella.be.user.entity.User @Service @@ -16,12 +17,12 @@ class SlackAlarmService( private val restTemplate: RestTemplate ) { - fun notifyRent(user: User, history: History) { + fun notifyRent(input: NotifyRentInput) { val message = buildString { append("*우산 대여 알림: 입금을 확인해주세요.*\n\n") - append("사용자 ID : ${user.id}\n") - append("예금주 이름 : ${user.name}\n") - append("대여 지점 이름 : ${history.rentStoreMeta.name}\n") + append("사용자 ID : ${input.userId}\n") + append("예금주 이름 : ${input.userName}\n") + append("대여 지점 이름 : ${input.rentStoreName}\n") } send(message) } @@ -39,20 +40,12 @@ class SlackAlarmService( send(message) } - private fun send(message: String) { - val request = mutableMapOf( - "text" to message - ) - val entity = HttpEntity(request) - restTemplate.exchange(slackBotConfig.webHookUrl, POST, entity, String::class.java) - } - - fun notifyConditionReport(conditionReport: ConditionReport) { + fun notifyConditionReport(input: NotifyConditionReportInput) { val message = buildString { append("*우산 상태 신고 접수*\n\n") - append("우산 ID : ${conditionReport.history.umbrella.id}\n") - append("대여 지점 이름 : ${conditionReport.history.rentStoreMeta.name}\n") - append("신고 내용 : ${conditionReport.content}\n") + append("우산 ID : ${input.umbrellaId}\n") + append("대여 지점 이름 : ${input.rentStoreName}\n") + append("신고 내용 : ${input.content}\n") } send(message) } @@ -69,4 +62,12 @@ class SlackAlarmService( } send(message) } + + private fun send(message: String) { + val request = mutableMapOf( + "text" to message + ) + val entity = HttpEntity(request) + restTemplate.exchange(slackBotConfig.webHookUrl, POST, entity, String::class.java) + } } diff --git a/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt b/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt new file mode 100644 index 00000000..e1922758 --- /dev/null +++ b/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt @@ -0,0 +1,13 @@ +package upbrella.be.slack.dto.service.input + +data class NotifyRentInput( + val userId: Long, + val userName: String, + val rentStoreName: String, +) + +data class NotifyConditionReportInput( + val umbrellaId: Long, + val rentStoreName: String, + val content: String, +) diff --git a/src/main/kotlin/upbrella/be/umbrella/entity/Umbrella.kt b/src/main/kotlin/upbrella/be/umbrella/entity/Umbrella.kt index ca906573..8a46ad8c 100644 --- a/src/main/kotlin/upbrella/be/umbrella/entity/Umbrella.kt +++ b/src/main/kotlin/upbrella/be/umbrella/entity/Umbrella.kt @@ -1,8 +1,11 @@ package upbrella.be.umbrella.entity +import upbrella.be.rent.exception.NotAvailableUmbrellaException +import upbrella.be.rent.exception.UmbrellaStoreMissMatchException import upbrella.be.store.entity.StoreMeta import upbrella.be.umbrella.dto.request.UmbrellaCreateRequest import upbrella.be.umbrella.dto.request.UmbrellaModifyRequest +import upbrella.be.umbrella.exception.MissingUmbrellaException import java.time.LocalDateTime import javax.persistence.* @@ -49,7 +52,16 @@ class Umbrella( this.missed = request.missed } - fun rentUmbrella() { + fun rentUmbrella(storeIdForRent: Long) { + if (this.storeMeta.id != storeIdForRent) { + throw UmbrellaStoreMissMatchException("[ERROR] 해당 우산은 해당 매장에 존재하지 않습니다.") + } + if (this.missed) { + throw MissingUmbrellaException("[ERROR] 해당 우산은 분실되었습니다.") + } + if (!this.rentable) { + throw NotAvailableUmbrellaException("[ERROR] 해당 우산은 대여중입니다.") + } this.rentable = false } diff --git a/src/main/kotlin/upbrella/be/util/event/Event.kt b/src/main/kotlin/upbrella/be/util/event/Event.kt new file mode 100644 index 00000000..4c5255bb --- /dev/null +++ b/src/main/kotlin/upbrella/be/util/event/Event.kt @@ -0,0 +1,5 @@ +package upbrella.be.util.event + +abstract class Event( + val timestamp: Long = System.currentTimeMillis(), +) diff --git a/src/main/kotlin/upbrella/be/util/event/Events.kt b/src/main/kotlin/upbrella/be/util/event/Events.kt new file mode 100644 index 00000000..975e4994 --- /dev/null +++ b/src/main/kotlin/upbrella/be/util/event/Events.kt @@ -0,0 +1,16 @@ +package upbrella.be.util.event + +import org.springframework.context.ApplicationEventPublisher + +object Events { + private var publisher: ApplicationEventPublisher? = null + + fun setPublisher(publisher: ApplicationEventPublisher) { + this.publisher = publisher + } + + fun raise(event: Any) { + publisher?.publishEvent(event) + ?: throw IllegalStateException("ApplicationEventPublisher가 설정되지 않았습니다.") + } +} From c358993beb0fe8d2c5c0c294b032486c7bb3e619 Mon Sep 17 00:00:00 2001 From: birdie Date: Tue, 12 Aug 2025 20:17:16 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20condition=20report,=20history?= =?UTF-8?q?=20=EC=A7=81=EC=A0=91=20=EC=B0=B8=EC=A1=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/ConditionReportResponse.kt | 15 +--------- .../be/rent/entity/ConditionReport.kt | 4 +-- .../be/rent/event/RentEventHandler.kt | 2 +- .../be/rent/event/UmbrellaRentedEvent.kt | 3 +- .../CustomConditionReportRepository.kt | 29 +++++++++++++++++++ .../be/rent/service/ConditionReportService.kt | 11 +++---- .../upbrella/be/rent/service/RentService.kt | 2 +- .../upbrella/be/config/event/NoOpPublisher.kt | 13 +++++++++ .../service/ConditionReportServiceTest.kt | 20 +++++++++---- .../be/rent/service/RentServiceTest.kt | 11 +++++-- 10 files changed, 73 insertions(+), 37 deletions(-) create mode 100644 src/main/kotlin/upbrella/be/rent/repository/CustomConditionReportRepository.kt create mode 100644 src/test/kotlin/upbrella/be/config/event/NoOpPublisher.kt diff --git a/src/main/kotlin/upbrella/be/rent/dto/response/ConditionReportResponse.kt b/src/main/kotlin/upbrella/be/rent/dto/response/ConditionReportResponse.kt index c67a0bbb..af629056 100644 --- a/src/main/kotlin/upbrella/be/rent/dto/response/ConditionReportResponse.kt +++ b/src/main/kotlin/upbrella/be/rent/dto/response/ConditionReportResponse.kt @@ -1,21 +1,8 @@ package upbrella.be.rent.dto.response -import upbrella.be.rent.entity.ConditionReport - data class ConditionReportResponse( val id: Long, val umbrellaUuid: Long, val content: String?, val etc: String? -) { - companion object { - fun fromConditionReport(conditionReport: ConditionReport): ConditionReportResponse { - return ConditionReportResponse( - id = conditionReport.history.id!!, - umbrellaUuid = conditionReport.history.umbrella.uuid, - content = conditionReport.content, - etc = conditionReport.etc - ) - } - } -} +) diff --git a/src/main/kotlin/upbrella/be/rent/entity/ConditionReport.kt b/src/main/kotlin/upbrella/be/rent/entity/ConditionReport.kt index aa8dba1b..bce0eef0 100644 --- a/src/main/kotlin/upbrella/be/rent/entity/ConditionReport.kt +++ b/src/main/kotlin/upbrella/be/rent/entity/ConditionReport.kt @@ -4,9 +4,7 @@ import javax.persistence.* @Entity class ConditionReport( - @OneToOne - @JoinColumn(name = "history_id") - val history: History, + val historyId: Long, val content: String? = null, val etc: String? = null, @Id diff --git a/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt b/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt index ff9ec433..505b4b4d 100644 --- a/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt +++ b/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt @@ -28,7 +28,7 @@ class RentEventHandler( ?.takeIf { it.isNotBlank() } ?.let { content -> ConditionReport( - history = event.history, + historyId = event.historyId, content = content ).also { conditionReport -> conditionReportService.saveConditionReport(conditionReport) diff --git a/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt b/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt index e5a5a5a9..3d3d4d26 100644 --- a/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt +++ b/src/main/kotlin/upbrella/be/rent/event/UmbrellaRentedEvent.kt @@ -1,6 +1,5 @@ package upbrella.be.rent.event -import upbrella.be.rent.entity.History import upbrella.be.util.event.Event class UmbrellaRentedEvent( @@ -9,5 +8,5 @@ class UmbrellaRentedEvent( val rentStoreName: String, val conditionReportContent: String? = null, val umbrellaId: Long, - val history: History, + val historyId: Long, ) : Event() diff --git a/src/main/kotlin/upbrella/be/rent/repository/CustomConditionReportRepository.kt b/src/main/kotlin/upbrella/be/rent/repository/CustomConditionReportRepository.kt new file mode 100644 index 00000000..9da517fc --- /dev/null +++ b/src/main/kotlin/upbrella/be/rent/repository/CustomConditionReportRepository.kt @@ -0,0 +1,29 @@ +package upbrella.be.rent.repository + +import com.querydsl.core.types.Projections +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository +import upbrella.be.rent.dto.response.ConditionReportResponse +import upbrella.be.rent.entity.QConditionReport.conditionReport +import upbrella.be.rent.entity.QHistory.history + +@Repository +class CustomConditionReportRepository( + private val queryFactory: JPAQueryFactory +) { + fun findAllConditionReport(): List { + return queryFactory + .select( + Projections.constructor( + ConditionReportResponse::class.java, + conditionReport.id, + history.umbrella.uuid, + conditionReport.content, + conditionReport.etc + ) + ) + .from(conditionReport) + .join(history) + .fetch() + } +} \ No newline at end of file diff --git a/src/main/kotlin/upbrella/be/rent/service/ConditionReportService.kt b/src/main/kotlin/upbrella/be/rent/service/ConditionReportService.kt index c55d1567..c0bb7f7d 100644 --- a/src/main/kotlin/upbrella/be/rent/service/ConditionReportService.kt +++ b/src/main/kotlin/upbrella/be/rent/service/ConditionReportService.kt @@ -2,23 +2,20 @@ package upbrella.be.rent.service import org.springframework.stereotype.Service import upbrella.be.rent.dto.response.ConditionReportPageResponse -import upbrella.be.rent.dto.response.ConditionReportResponse import upbrella.be.rent.entity.ConditionReport import upbrella.be.rent.repository.ConditionReportRepository +import upbrella.be.rent.repository.CustomConditionReportRepository @Service class ConditionReportService( - private val conditionReportRepository: ConditionReportRepository + private val conditionReportRepository: ConditionReportRepository, + private val customConditionReportRepository: CustomConditionReportRepository, ) { fun findAll(): ConditionReportPageResponse = - ConditionReportPageResponse.of(findAllConditionReport()) + ConditionReportPageResponse.of(customConditionReportRepository.findAllConditionReport()) fun saveConditionReport(conditionReport: ConditionReport) { conditionReportRepository.save(conditionReport) } - - private fun findAllConditionReport(): List = - conditionReportRepository.findAll() - .map { ConditionReportResponse.fromConditionReport(it) } } diff --git a/src/main/kotlin/upbrella/be/rent/service/RentService.kt b/src/main/kotlin/upbrella/be/rent/service/RentService.kt index 1236b9ed..d26f5a54 100644 --- a/src/main/kotlin/upbrella/be/rent/service/RentService.kt +++ b/src/main/kotlin/upbrella/be/rent/service/RentService.kt @@ -83,7 +83,7 @@ class RentService( rentStoreName = rentalStore.name, conditionReportContent = rentUmbrellaByUserRequest.conditionReport, umbrellaId = umbrella.id!!, - history = history, + historyId = history.id!!, ) ) } diff --git a/src/test/kotlin/upbrella/be/config/event/NoOpPublisher.kt b/src/test/kotlin/upbrella/be/config/event/NoOpPublisher.kt new file mode 100644 index 00000000..9468da9e --- /dev/null +++ b/src/test/kotlin/upbrella/be/config/event/NoOpPublisher.kt @@ -0,0 +1,13 @@ +package upbrella.be.config.event + +import org.springframework.context.ApplicationEvent +import org.springframework.context.ApplicationEventPublisher + +/** + * 테스트 전용 No-Operation Publisher + * 실제로 아무 이벤트도 발행하지 않음 + */ +object NoOpPublisher : ApplicationEventPublisher { + override fun publishEvent(event: Any) { /* no-op */ } + override fun publishEvent(event: ApplicationEvent) { /* no-op */ } +} diff --git a/src/test/kotlin/upbrella/be/rent/service/ConditionReportServiceTest.kt b/src/test/kotlin/upbrella/be/rent/service/ConditionReportServiceTest.kt index abf5520a..63a720ea 100644 --- a/src/test/kotlin/upbrella/be/rent/service/ConditionReportServiceTest.kt +++ b/src/test/kotlin/upbrella/be/rent/service/ConditionReportServiceTest.kt @@ -18,6 +18,7 @@ import upbrella.be.rent.dto.response.ConditionReportResponse import upbrella.be.rent.entity.ConditionReport import upbrella.be.rent.entity.History import upbrella.be.rent.repository.ConditionReportRepository +import upbrella.be.rent.repository.CustomConditionReportRepository import upbrella.be.store.entity.StoreMeta import upbrella.be.umbrella.entity.Umbrella import upbrella.be.user.entity.User @@ -29,6 +30,9 @@ class ConditionReportServiceTest { @Mock private lateinit var conditionReportRepository: ConditionReportRepository + @Mock + private lateinit var customConditionReportRepository: CustomConditionReportRepository + @InjectMocks private lateinit var conditionReportService: ConditionReportService @@ -76,7 +80,7 @@ class ConditionReportServiceTest { conditionReport = ConditionReport( id = 1L, content = "content", - history = history, + historyId = history.id!!, etc = "etc", ) } @@ -100,8 +104,15 @@ class ConditionReportServiceTest { ) ) - given(conditionReportRepository.findAll()) - .willReturn(listOf(conditionReport)) + given(customConditionReportRepository.findAllConditionReport()) + .willReturn(listOf( + ConditionReportResponse( + id = 33L, + umbrellaUuid = 99L, + content = "content", + etc = "etc" + ) + )) // when val allConditionReports = conditionReportService.findAll() @@ -115,9 +126,6 @@ class ConditionReportServiceTest { }, { assertThat(allConditionReports.conditionReports.size).isEqualTo(1) - }, - { - then(conditionReportRepository).should(times(1)).findAll() } ) } diff --git a/src/test/kotlin/upbrella/be/rent/service/RentServiceTest.kt b/src/test/kotlin/upbrella/be/rent/service/RentServiceTest.kt index a6160974..5ed5e607 100644 --- a/src/test/kotlin/upbrella/be/rent/service/RentServiceTest.kt +++ b/src/test/kotlin/upbrella/be/rent/service/RentServiceTest.kt @@ -13,10 +13,14 @@ import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.mock +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.ApplicationEventPublisher import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import upbrella.be.config.FixtureBuilderFactory import upbrella.be.config.FixtureFactory +import upbrella.be.config.event.NoOpPublisher import upbrella.be.rent.dto.request.HistoryFilterRequest import upbrella.be.rent.dto.request.RentUmbrellaByUserRequest import upbrella.be.rent.dto.response.HistoryInfoDto @@ -42,6 +46,7 @@ import upbrella.be.user.exception.NonExistingMemberException import upbrella.be.user.repository.UserReader import upbrella.be.user.service.BlackListService import upbrella.be.util.AesEncryptor +import upbrella.be.util.event.Events import java.time.LocalDateTime import java.util.* @@ -143,9 +148,11 @@ class RentServiceTest { conditionReport = ConditionReport( id = 1L, content = "상태 양호", - history = history, + historyId = history.id!!, etc = "etc", ) + + Events.setPublisher(NoOpPublisher) } @Nested @@ -159,8 +166,6 @@ class RentServiceTest { given(storeMetaReader.findById(25L)).willReturn(foundStoreMeta) given(umbrellaService.findUmbrellaById(99L)).willReturn(foundUmbrella) given(rentRepository.save(any(History::class.java) ?: history)).willReturn(history) - doNothing().`when`(conditionReportService) - .saveConditionReport(any(ConditionReport::class.java) ?: conditionReport) // when rentService.addRental(rentUmbrellaByUserRequest, userToRent) From 6f7d9dddddea46d09cc9c74e921bd1b8acdfc4a5 Mon Sep 17 00:00:00 2001 From: birdie Date: Tue, 12 Aug 2025 20:51:07 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20return=20event=20=EB=B0=9C?= =?UTF-8?q?=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/ImprovementReportResponse.kt | 15 +------ .../be/rent/entity/ImprovementReport.kt | 4 +- .../be/rent/event/RentEventHandler.kt | 43 +++++++++++++++++++ .../be/rent/event/UmbrellaReturnedEvent.kt | 15 +++++++ .../CustomImprovementReportRepository.kt | 30 +++++++++++++ .../rent/service/ImprovementReportService.kt | 11 ++--- .../upbrella/be/rent/service/RentService.kt | 12 ------ .../upbrella/be/slack/SlackAlarmService.kt | 33 +++++++------- .../dto/service/input/SlackAlarmInput.kt | 18 ++++++++ .../be/slack/service/SlackAlarmServiceTest.kt | 12 +++++- 10 files changed, 139 insertions(+), 54 deletions(-) create mode 100644 src/main/kotlin/upbrella/be/rent/event/UmbrellaReturnedEvent.kt create mode 100644 src/main/kotlin/upbrella/be/rent/repository/CustomImprovementReportRepository.kt diff --git a/src/main/kotlin/upbrella/be/rent/dto/response/ImprovementReportResponse.kt b/src/main/kotlin/upbrella/be/rent/dto/response/ImprovementReportResponse.kt index d77258b9..4b753265 100644 --- a/src/main/kotlin/upbrella/be/rent/dto/response/ImprovementReportResponse.kt +++ b/src/main/kotlin/upbrella/be/rent/dto/response/ImprovementReportResponse.kt @@ -1,21 +1,8 @@ package upbrella.be.rent.dto.response -import upbrella.be.rent.entity.ImprovementReport - data class ImprovementReportResponse( val id: Long = 0, val umbrellaUuid: Long = 0, val content: String? = "", val etc: String? = "" -) { - companion object { - fun fromImprovementReport(improvementReport: ImprovementReport): ImprovementReportResponse { - return ImprovementReportResponse( - id = improvementReport.history.id ?: 0, - umbrellaUuid = improvementReport.history.umbrella.uuid, - content = improvementReport.content, - etc = improvementReport.etc - ) - } - } -} +) diff --git a/src/main/kotlin/upbrella/be/rent/entity/ImprovementReport.kt b/src/main/kotlin/upbrella/be/rent/entity/ImprovementReport.kt index cd6ca6f1..114cab0b 100644 --- a/src/main/kotlin/upbrella/be/rent/entity/ImprovementReport.kt +++ b/src/main/kotlin/upbrella/be/rent/entity/ImprovementReport.kt @@ -4,9 +4,7 @@ import javax.persistence.* @Entity class ImprovementReport( - @OneToOne - @JoinColumn(name = "history_id") - val history: History, + val historyId: Long, val content: String? = null, val etc: String? = null, @Id diff --git a/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt b/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt index 505b4b4d..b56c2ae3 100644 --- a/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt +++ b/src/main/kotlin/upbrella/be/rent/event/RentEventHandler.kt @@ -3,15 +3,22 @@ package upbrella.be.rent.event import org.springframework.context.event.EventListener import org.springframework.stereotype.Service import upbrella.be.rent.entity.ConditionReport +import upbrella.be.rent.entity.ImprovementReport import upbrella.be.rent.service.ConditionReportService +import upbrella.be.rent.service.ImprovementReportService +import upbrella.be.rent.service.RentService import upbrella.be.slack.SlackAlarmService import upbrella.be.slack.dto.service.input.NotifyConditionReportInput +import upbrella.be.slack.dto.service.input.NotifyImprovementReportInput import upbrella.be.slack.dto.service.input.NotifyRentInput +import upbrella.be.slack.dto.service.input.NotifyReturnInput @Service class RentEventHandler( private val slackAlarmService: SlackAlarmService, private val conditionReportService: ConditionReportService, + private val rentService: RentService, + private val improvementReportService: ImprovementReportService, ) { @EventListener(UmbrellaRentedEvent::class) @@ -42,4 +49,40 @@ class RentEventHandler( } } } + + @EventListener(UmbrellaReturnedEvent::class) + fun handleUmbrellaReturnedEvent(event: UmbrellaReturnedEvent) { + + val unrefundedRentCount = rentService.countUnrefundedRent() + slackAlarmService.notifyReturn( + NotifyReturnInput( + userId = event.rentUserId, + rentStoreName = event.rentStoreName, + rentedAt = event.rentedAt.toString(), + returnStoreName = event.returnStoreName, + returnedAt = event.returnedAt.toString(), + unrefundedCount = unrefundedRentCount + ) + ) + + event.improvementReportContent?.takeIf { it.isNotBlank() } + ?.let { content -> + ImprovementReport( + historyId = event.historyId, + content = content + ).also { improvementReport -> + improvementReportService.save(improvementReport) + slackAlarmService.notifyImprovementReport( + NotifyImprovementReportInput( + umbrellaId = event.returnedUmbrellaId, + rentStoreName = event.rentStoreName, + rentedAt = event.rentedAt.toString(), + returnStoreName = event.returnStoreName, + returnedAt = event.returnedAt.toString(), + content = content + ) + ) + } + } + } } \ No newline at end of file diff --git a/src/main/kotlin/upbrella/be/rent/event/UmbrellaReturnedEvent.kt b/src/main/kotlin/upbrella/be/rent/event/UmbrellaReturnedEvent.kt new file mode 100644 index 00000000..e3aaaded --- /dev/null +++ b/src/main/kotlin/upbrella/be/rent/event/UmbrellaReturnedEvent.kt @@ -0,0 +1,15 @@ +package upbrella.be.rent.event + +import upbrella.be.util.event.Event +import java.time.LocalDateTime + +class UmbrellaReturnedEvent( + val rentUserId: Long, + val rentStoreName: String, + val rentedAt: LocalDateTime, + val returnStoreName: String, + val returnedAt: LocalDateTime, + val historyId: Long, + val improvementReportContent: String? = null, + val returnedUmbrellaId: Long, +) : Event() diff --git a/src/main/kotlin/upbrella/be/rent/repository/CustomImprovementReportRepository.kt b/src/main/kotlin/upbrella/be/rent/repository/CustomImprovementReportRepository.kt new file mode 100644 index 00000000..09bed156 --- /dev/null +++ b/src/main/kotlin/upbrella/be/rent/repository/CustomImprovementReportRepository.kt @@ -0,0 +1,30 @@ +package upbrella.be.rent.repository + +import com.querydsl.core.types.Projections +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository +import upbrella.be.rent.dto.response.ImprovementReportResponse +import upbrella.be.rent.entity.QHistory.history +import upbrella.be.rent.entity.QImprovementReport.improvementReport + +@Repository +class CustomImprovementReportRepository( + private val queryFactory: JPAQueryFactory, +) { + fun findAllImprovementReport(): List { + return queryFactory + .select( + Projections.constructor( + ImprovementReportResponse::class.java, + improvementReport.id, + history.umbrella.uuid, + improvementReport.content, + improvementReport.etc + ) + ) + .from(improvementReport) + .join(history) + .fetchJoin() + .fetch() + } +} diff --git a/src/main/kotlin/upbrella/be/rent/service/ImprovementReportService.kt b/src/main/kotlin/upbrella/be/rent/service/ImprovementReportService.kt index e991ec4d..5e24b7e5 100644 --- a/src/main/kotlin/upbrella/be/rent/service/ImprovementReportService.kt +++ b/src/main/kotlin/upbrella/be/rent/service/ImprovementReportService.kt @@ -2,24 +2,21 @@ package upbrella.be.rent.service import org.springframework.stereotype.Service import upbrella.be.rent.dto.response.ImprovementReportPageResponse -import upbrella.be.rent.dto.response.ImprovementReportResponse import upbrella.be.rent.entity.ImprovementReport +import upbrella.be.rent.repository.CustomImprovementReportRepository import upbrella.be.rent.repository.ImprovementReportRepository @Service class ImprovementReportService( - private val improvementReportRepository: ImprovementReportRepository + private val improvementReportRepository: ImprovementReportRepository, + private val customImprovementReportRepository: CustomImprovementReportRepository ) { fun findAll(): ImprovementReportPageResponse = - ImprovementReportPageResponse.of(findAllImprovementReport()) + ImprovementReportPageResponse.of(customImprovementReportRepository.findAllImprovementReport()) fun save(improvementReport: ImprovementReport) { improvementReportRepository.save(improvementReport) } - - private fun findAllImprovementReport(): List = - improvementReportRepository.findAll() - .map { ImprovementReportResponse.fromImprovementReport(it) } } diff --git a/src/main/kotlin/upbrella/be/rent/service/RentService.kt b/src/main/kotlin/upbrella/be/rent/service/RentService.kt index d26f5a54..fc4e7570 100644 --- a/src/main/kotlin/upbrella/be/rent/service/RentService.kt +++ b/src/main/kotlin/upbrella/be/rent/service/RentService.kt @@ -8,7 +8,6 @@ import upbrella.be.rent.dto.request.RentUmbrellaByUserRequest import upbrella.be.rent.dto.request.ReturnUmbrellaByUserRequest import upbrella.be.rent.dto.response.* import upbrella.be.rent.entity.History -import upbrella.be.rent.entity.ImprovementReport import upbrella.be.rent.event.UmbrellaRentedEvent import upbrella.be.rent.exception.CannotBeRentedException import upbrella.be.rent.exception.ExistingUmbrellaForRentException @@ -103,18 +102,7 @@ class RentService( val returnedUmbrella: Umbrella = history.umbrella returnedUmbrella.returnUmbrella(returnStore) - val unrefundedRentCount = countUnrefundedRent() - - slackAlarmService.notifyReturn(userToReturn, history, unrefundedRentCount) rentRepository.save(history) - - request.improvementReportContent?.takeIf { it.isNotBlank() } - ?.let { content -> - ImprovementReport(history = history, content = content).also { improvementReport -> - improvementReportService.save(improvementReport) - slackAlarmService.notifyImprovementReport(improvementReport) - } - } } @Transactional diff --git a/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt b/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt index 151401b0..3a95dd33 100644 --- a/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt +++ b/src/main/kotlin/upbrella/be/slack/SlackAlarmService.kt @@ -5,11 +5,10 @@ import org.springframework.http.HttpMethod.POST import org.springframework.stereotype.Service import org.springframework.web.client.RestTemplate import upbrella.be.config.SlackBotConfig -import upbrella.be.rent.entity.History -import upbrella.be.rent.entity.ImprovementReport import upbrella.be.slack.dto.service.input.NotifyConditionReportInput +import upbrella.be.slack.dto.service.input.NotifyImprovementReportInput import upbrella.be.slack.dto.service.input.NotifyRentInput -import upbrella.be.user.entity.User +import upbrella.be.slack.dto.service.input.NotifyReturnInput @Service class SlackAlarmService( @@ -27,15 +26,15 @@ class SlackAlarmService( send(message) } - fun notifyReturn(user: User, history: History, unrefundedCount: Long) { + fun notifyReturn(input: NotifyReturnInput) { val message = buildString { append("*우산 반납 알림: 보증금을 환급해주세요.*\n\n") - append("사용자 ID : ${user.id}\n") - append("대여 지점 이름 : ${history.rentStoreMeta.name}\n") - append("대여 시각: ${history.rentedAt}\n") - append("반납 지점 이름 : ${history.returnStoreMeta?.name}\n") - append("반납 시각: ${history.returnedAt}\n") - append("*잔여 환급 대기 건수* : $unrefundedCount") + append("사용자 ID : ${input.userId}\n") + append("대여 지점 이름 : ${input.rentStoreName}\n") + append("대여 시각: ${input.rentedAt}\n") + append("반납 지점 이름 : ${input.returnStoreName}\n") + append("반납 시각: ${input.returnedAt}\n") + append("*잔여 환급 대기 건수* : ${input.unrefundedCount}") } send(message) } @@ -50,15 +49,15 @@ class SlackAlarmService( send(message) } - fun notifyImprovementReport(improvementReport: ImprovementReport) { + fun notifyImprovementReport(input: NotifyImprovementReportInput) { val message = buildString { append("*우산 개선 사항 신고가 접수되었습니다.*\n\n") - append("우산 ID : ${improvementReport.history.umbrella.id}\n") - append("대여 지점 이름 : ${improvementReport.history.rentStoreMeta.name}\n") - append("대여 시각: ${improvementReport.history.rentedAt}\n") - append("반납 지점 이름 : ${improvementReport.history.returnStoreMeta?.name}\n") - append("반납 시각: ${improvementReport.history.returnedAt}\n") - append("신고 내용 : ${improvementReport.content}\n") + append("우산 ID : ${input.umbrellaId}\n") + append("대여 지점 이름 : ${input.rentStoreName}\n") + append("대여 시각: ${input.rentedAt}\n") + append("반납 지점 이름 : ${input.returnStoreName}\n") + append("반납 시각: ${input.returnedAt}\n") + append("신고 내용 : ${input.content}\n") } send(message) } diff --git a/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt b/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt index e1922758..4f66fd88 100644 --- a/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt +++ b/src/main/kotlin/upbrella/be/slack/dto/service/input/SlackAlarmInput.kt @@ -11,3 +11,21 @@ data class NotifyConditionReportInput( val rentStoreName: String, val content: String, ) + +data class NotifyReturnInput( + val userId: Long, + val rentStoreName: String, + val rentedAt: String, + val returnStoreName: String, + val returnedAt: String?, + val unrefundedCount: Long, +) + +data class NotifyImprovementReportInput( + val umbrellaId: Long, + val rentStoreName: String, + val rentedAt: String, + val returnStoreName: String, + val returnedAt: String?, + val content: String, +) diff --git a/src/test/kotlin/upbrella/be/slack/service/SlackAlarmServiceTest.kt b/src/test/kotlin/upbrella/be/slack/service/SlackAlarmServiceTest.kt index bcd70bc2..a13b4d66 100644 --- a/src/test/kotlin/upbrella/be/slack/service/SlackAlarmServiceTest.kt +++ b/src/test/kotlin/upbrella/be/slack/service/SlackAlarmServiceTest.kt @@ -19,6 +19,7 @@ import upbrella.be.config.SlackBotConfig import upbrella.be.rent.dto.request.RentUmbrellaByUserRequest import upbrella.be.rent.entity.History import upbrella.be.slack.SlackAlarmService +import upbrella.be.slack.dto.service.input.NotifyReturnInput import upbrella.be.store.entity.StoreMeta import upbrella.be.umbrella.entity.Umbrella import upbrella.be.user.entity.User @@ -108,7 +109,16 @@ class SlackAlarmServiceTest { ).willReturn(null) // when - slackAlarmService.notifyReturn(userToRent, history, 1L) + slackAlarmService.notifyReturn( + NotifyReturnInput( + userId = userToRent.id!!, + rentStoreName = foundStoreMeta.name, + rentedAt = history.rentedAt.toString(), + returnStoreName = foundStoreMeta.name, + returnedAt = history.returnedAt.toString(), + unrefundedCount = 1L + ) + ) val requestEntityCaptor = ArgumentCaptor.forClass(HttpEntity::class.java)