From c711fb11ca531b3c2689a0387cc5e5f26396d759 Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:03:46 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat=20(=20#36=20):=20KafkaTopics=EC=97=90?= =?UTF-8?q?=20CANCEL=5FSUBMITTED=5FAPPLICATION=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/infrastructure/kafka/configuration/KafkaTopics.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/configuration/KafkaTopics.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/configuration/KafkaTopics.kt index 824217c..bd08676 100644 --- a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/configuration/KafkaTopics.kt +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/configuration/KafkaTopics.kt @@ -39,4 +39,6 @@ object KafkaTopics { * 원서 서비스에서 이 이벤트를 수신하면 보상 트랜잭션을 수행함 */ const val USER_RECEIPT_CODE_UPDATE_FAILED = "user-receipt-code-update-failed" + + const val CANCEL_SUBMITTED_APPLICATION = "cancel-submitted-application" } From 17475164de773198ecc21c7917a5086553e92e90 Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:03:58 +0900 Subject: [PATCH 2/7] feat ( #36 ): CancelApplicationConsumer --- .../consumer/CancelApplicationConsumer.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/consumer/CancelApplicationConsumer.kt diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/consumer/CancelApplicationConsumer.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/consumer/CancelApplicationConsumer.kt new file mode 100644 index 0000000..9daabb2 --- /dev/null +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/consumer/CancelApplicationConsumer.kt @@ -0,0 +1,24 @@ +package hs.kr.entrydsm.user.infrastructure.kafka.consumer + +import com.fasterxml.jackson.databind.ObjectMapper +import hs.kr.entrydsm.user.domain.user.application.port.`in`.DeleteReceiptCodeUseCase +import hs.kr.entrydsm.user.infrastructure.kafka.configuration.KafkaTopics +import org.springframework.kafka.annotation.KafkaListener +import org.springframework.stereotype.Component + +@Component +class CancelApplicationConsumer( + private val mapper: ObjectMapper, + private val deleteReceiptCodeUseCase: DeleteReceiptCodeUseCase +) { + + @KafkaListener( + topics = [KafkaTopics.CANCEL_SUBMITTED_APPLICATION], + groupId = "update-user", + containerFactory = "kafkaListenerContainerFactory", + ) + fun execute(message: String) { + val receiptCode = mapper.readValue(message, Long::class.java) + deleteReceiptCodeUseCase.deleteReceiptCode(receiptCode) + } +} \ No newline at end of file From 7074f05ad4185430a26593a9b577670ad8abf17c Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:04:05 +0900 Subject: [PATCH 3/7] feat ( #36 ): DeleteReceiptCodeService --- .../service/DeleteReceiptCodeService.kt | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/service/DeleteReceiptCodeService.kt diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/service/DeleteReceiptCodeService.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/service/DeleteReceiptCodeService.kt new file mode 100644 index 0000000..0afa58f --- /dev/null +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/service/DeleteReceiptCodeService.kt @@ -0,0 +1,62 @@ +package hs.kr.entrydsm.user.domain.user.application.service + +import hs.kr.entrydsm.user.domain.user.application.port.`in`.DeleteReceiptCodeUseCase +import hs.kr.entrydsm.user.domain.user.application.port.out.QueryUserPort +import hs.kr.entrydsm.user.domain.user.application.port.out.SaveUserPort +import hs.kr.entrydsm.user.domain.user.exception.UserNotFoundException +import jakarta.transaction.Transactional +import org.springframework.stereotype.Service + +@Service +class DeleteReceiptCodeService( + private val queryUserPort: QueryUserPort, + private val saveUserPort: SaveUserPort, +) : DeleteReceiptCodeUseCase { + + @Transactional + override fun deleteReceiptCode(receiptCode: Long) { + val user = queryUserPort.findByReceiptCode(receiptCode) + ?: throw UserNotFoundException + + val updatedUser = user.copy(receiptCode = null) + saveUserPort.save(updatedUser) + +// registerAfterCommitCallback(receiptCode) +// try { +// val user = queryUserPort.findByReceiptCode(receiptCode) +// +// if (user == null) { +// userEventProducer.sendReceiptCodeDeleteFailed( +// receiptCode = receiptCode, +// reason = "User not found", +// ) +// throw UserNotFoundException +// } +// +// val updatedUser = user.copy(receiptCode = null) +// saveUserPort.save(updatedUser) +// +// registerAfterCommitCallback(receiptCode) +// } catch (e: Exception) { +// if (e !is UserNotFoundException) { +// userEventProducer.sendReceiptCodeDeleteFailed( +// receiptCode = receiptCode, +// reason = e.message ?: "Unknown error", +// ) +// } +// throw e // 예외 다시 던져서 롤백 발생 +// } + } + +// private fun registerAfterCommitCallback( +// receiptCode: Long +// ) { +// val callback = +// object : TransactionSynchronization { +// override fun afterCommit() { +// userEventProducer.sendReceiptCodeDeleteCompleted(receiptCode) +// } +// } +// TransactionSynchronizationManager.registerSynchronization(callback) +// } +} \ No newline at end of file From 7199f9cabee97f08ac1dabdd097b0b11e02bd33e Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:04:10 +0900 Subject: [PATCH 4/7] feat ( #36 ): DeleteReceiptCodeUseCase --- .../user/application/port/in/DeleteReceiptCodeUseCase.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/in/DeleteReceiptCodeUseCase.kt diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/in/DeleteReceiptCodeUseCase.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/in/DeleteReceiptCodeUseCase.kt new file mode 100644 index 0000000..d99c07e --- /dev/null +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/in/DeleteReceiptCodeUseCase.kt @@ -0,0 +1,5 @@ +package hs.kr.entrydsm.user.domain.user.application.port.`in` + +interface DeleteReceiptCodeUseCase { + fun deleteReceiptCode(receiptCode: Long) +} \ No newline at end of file From f79a697858cfe04652a218e18178da0e3459240d Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:04:32 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat=20(=20#36=20):=20QueryUserPort?= =?UTF-8?q?=EC=97=90=20findByReceiptCode=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/domain/user/application/port/out/QueryUserPort.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/out/QueryUserPort.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/out/QueryUserPort.kt index 04bfc35..68496d9 100644 --- a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/out/QueryUserPort.kt +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/application/port/out/QueryUserPort.kt @@ -31,4 +31,6 @@ interface QueryUserPort { * @return 사용자 존재 여부 */ fun existsByPhoneNumber(phoneNumber: String): Boolean + + fun findByReceiptCode(receiptCode: Long): User? } From 46b09914adfbc393b3a8b90e1466406b7ee2e897 Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:04:52 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat=20(=20#36=20):=20UserPersistenceAdapte?= =?UTF-8?q?r=EC=97=90=20findByReceiptCode=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/adapter/out/persistence/UserPersistenceAdapter.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/UserPersistenceAdapter.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/UserPersistenceAdapter.kt index 0a5602b..061089c 100644 --- a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/UserPersistenceAdapter.kt +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/UserPersistenceAdapter.kt @@ -36,6 +36,11 @@ class UserPersistenceAdapter( ?.let { userMapper.toModel(it) } } + override fun findByReceiptCode(receiptCode: Long): User? { + return userRepository.findByReceiptCode(receiptCode) + ?.let { userMapper.toModel(it) } + } + /** * 전화번호로 사용자를 조회합니다. * 전화번호를 암호화하여 데이터베이스에서 조회합니다. From 61ee3aa71d0644a714d1a30b5755faaedc178699 Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 13 Jan 2026 17:04:59 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat=20(=20#36=20):=20UserRepository?= =?UTF-8?q?=EC=97=90=20findByReceiptCode=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/adapter/out/persistence/repository/UserRepository.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/repository/UserRepository.kt b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/repository/UserRepository.kt index 6a0f8d3..978cc55 100644 --- a/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/repository/UserRepository.kt +++ b/casper-user/src/main/kotlin/hs/kr/entrydsm/user/domain/user/adapter/out/persistence/repository/UserRepository.kt @@ -40,4 +40,6 @@ interface UserRepository : JpaRepository { * @return 삭제 대상 사용자 엔티티 목록 */ fun findAllByActiveFalseAndWithdrawalAtBefore(cutoffDate: LocalDateTime): List + + fun findByReceiptCode(receiptCode: Long): UserJpaEntity? }