From 4ca15bff6a4ba8dfa03ea26a701d78cbfb8e19c9 Mon Sep 17 00:00:00 2001 From: ybchar Date: Sun, 28 Dec 2025 22:47:05 +0900 Subject: [PATCH 1/3] refactor&feature: FCM Send & Notification Domain --- .../com/pida/client/aws/s3/AwsS3Client.kt | 6 +- .../notification/FcmClientRepository.kt | 5 ++ .../client/notification/FcmSendRequest.kt | 1 + .../FirebaseCloudMessageSender.kt | 7 +- .../CreateNotificationStoredCommand.kt | 11 +++ .../kotlin/com/pida/notification/FcmSender.kt | 1 + .../com/pida/notification/FcmToUserCommand.kt | 7 ++ .../pida/notification/FirebaseCloudMessage.kt | 1 + .../FirebaseCloudMessageCommand.kt | 8 ++ .../notification/NewFirebaseCloudMessage.kt | 1 + .../pida/notification/NotificationService.kt | 42 +++++++++ .../pida/notification/NotificationStored.kt | 39 ++++++++ .../NotificationStoredAppender.kt | 12 +++ .../NotificationStoredKeyGenerator.kt | 19 ++++ .../notification/NotificationStoredReader.kt | 15 ++++ .../NotificationStoredRepository.kt | 29 ++++++ .../notification/NotificationStoredUpdater.kt | 10 +++ .../com/pida/notification/NotificationType.kt | 16 ++++ .../com/pida/notification/ReadStatus.kt | 16 ++++ .../kotlin/com/pida/support/cursor/Cursor.kt | 24 +++++ .../com/pida/support/cursor/CursorRequest.kt | 10 +++ .../com/pida/support/page/OffsetLimit.kt | 22 +++++ .../pida/support/page/OffsetPageRequest.kt | 11 +++ .../main/kotlin/com/pida/support/page/Page.kt | 20 +++++ .../com/pida/support/page/PageResponse.kt | 15 ++++ .../main/kotlin/com/pida/support/page/Sort.kt | 6 ++ .../kotlin/com/pida/support/page/SortType.kt | 7 ++ .../src/main/kotlin/com/pida/support/tx/Tx.kt | 90 +++++++++++++++++++ .../NotificationStoredCoreRepository.kt | 79 ++++++++++++++++ .../NotificationStoredCustomRepository.kt | 53 +++++++++++ .../notification/NotificationStoredEntity.kt | 54 +++++++++++ .../NotificationStoredJpaRepository.kt | 19 ++++ 32 files changed, 653 insertions(+), 3 deletions(-) create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/CreateNotificationStoredCommand.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationService.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStored.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredAppender.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredKeyGenerator.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredReader.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredRepository.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredUpdater.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationType.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/ReadStatus.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/CursorRequest.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetLimit.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetPageRequest.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/page/PageResponse.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/page/Sort.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/page/SortType.kt create mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/tx/Tx.kt create mode 100644 pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCoreRepository.kt create mode 100644 pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCustomRepository.kt create mode 100644 pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredEntity.kt create mode 100644 pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredJpaRepository.kt diff --git a/pida-clients/aws-client/src/main/kotlin/com/pida/client/aws/s3/AwsS3Client.kt b/pida-clients/aws-client/src/main/kotlin/com/pida/client/aws/s3/AwsS3Client.kt index dbc0059..816b557 100644 --- a/pida-clients/aws-client/src/main/kotlin/com/pida/client/aws/s3/AwsS3Client.kt +++ b/pida-clients/aws-client/src/main/kotlin/com/pida/client/aws/s3/AwsS3Client.kt @@ -2,7 +2,11 @@ package com.pida.client.aws.s3 import org.springframework.stereotype.Component import software.amazon.awssdk.services.s3.S3Client -import software.amazon.awssdk.services.s3.model.* +import software.amazon.awssdk.services.s3.model.GetObjectRequest +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response +import software.amazon.awssdk.services.s3.model.PutObjectRequest +import software.amazon.awssdk.services.s3.model.S3Object import software.amazon.awssdk.services.s3.presigner.S3Presigner import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest diff --git a/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmClientRepository.kt b/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmClientRepository.kt index 04dc721..97ff6da 100644 --- a/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmClientRepository.kt +++ b/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmClientRepository.kt @@ -16,6 +16,7 @@ class FcmClientRepository( it.fcmToken, it.title, it.body, + it.destination, ) }.toList() @@ -24,6 +25,7 @@ class FcmClientRepository( firebaseCloudMessageSender.sendEachForMulticastAll( fcmRequests[0].title, fcmRequests[0].body, + fcmRequests[0].destination, tokens, ) @@ -34,6 +36,7 @@ class FcmClientRepository( fcmToken = pushMessage.fcmToken, title = pushMessage.title, body = pushMessage.body, + destination = pushMessage.destination, tryCount = pushMessage.tryCount + 1, sent = batchResult?.responses?.get(index)?.isSuccessful ?: false, ) @@ -47,6 +50,7 @@ class FcmClientRepository( fcmToken = firebaseCloudMessage.fcmToken, title = firebaseCloudMessage.title, body = firebaseCloudMessage.body, + destination = firebaseCloudMessage.destination, ) val sendResult = firebaseCloudMessageSender.sendAsync(request).get() return FirebaseCloudMessage( @@ -54,6 +58,7 @@ class FcmClientRepository( fcmToken = firebaseCloudMessage.fcmToken, title = firebaseCloudMessage.title, body = firebaseCloudMessage.body, + destination = firebaseCloudMessage.destination, tryCount = firebaseCloudMessage.tryCount + 1, sent = sendResult.isNotBlank(), ) diff --git a/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmSendRequest.kt b/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmSendRequest.kt index 3a6c5c8..085dbf3 100644 --- a/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmSendRequest.kt +++ b/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FcmSendRequest.kt @@ -4,4 +4,5 @@ data class FcmSendRequest( val fcmToken: String, val title: String, val body: String, + val destination: String, ) diff --git a/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FirebaseCloudMessageSender.kt b/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FirebaseCloudMessageSender.kt index e0155ac..8cf57ab 100644 --- a/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FirebaseCloudMessageSender.kt +++ b/pida-clients/notification/src/main/kotlin/com/pida/client/notification/FirebaseCloudMessageSender.kt @@ -35,10 +35,11 @@ class FirebaseCloudMessageSender( ).setApnsConfig( ApnsConfig .builder() + .putCustomData("destination", request.destination) .setAps( Aps .builder() - .setAlert("${request.title}\n${request.body}") + .setAlert(request.body) .setBadge(1) .setSound("default") .build(), @@ -48,6 +49,7 @@ class FirebaseCloudMessageSender( fun sendEachForMulticastAll( title: String, body: String, + destination: String, fcmTokens: List, ): BatchResponse? { val notification = @@ -64,10 +66,11 @@ class FirebaseCloudMessageSender( .setApnsConfig( ApnsConfig .builder() + .putCustomData("destination", destination) .setAps( Aps .builder() - .setAlert("$title\n$body") + .setAlert(body) .setBadge(1) .setSound("default") .build(), diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/CreateNotificationStoredCommand.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/CreateNotificationStoredCommand.kt new file mode 100644 index 0000000..1a73fd9 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/CreateNotificationStoredCommand.kt @@ -0,0 +1,11 @@ +package com.pida.notification + +data class CreateNotificationStoredCommand( + val notificationStoredKey: String, + val userId: Long, + val type: NotificationType, + val parameterValue: String, + val topic: String, + val contents: String, + val readStatus: ReadStatus = ReadStatus.UNREAD, +) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmSender.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmSender.kt index 9640c2c..2bbbd87 100644 --- a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmSender.kt +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmSender.kt @@ -22,6 +22,7 @@ class FcmSender( fcmToken = it.fcmToken, title = it.title, body = it.body, + destination = it.destination, tryCount = 0, sent = false, ) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt new file mode 100644 index 0000000..1bc3dc0 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt @@ -0,0 +1,7 @@ +package com.pida.notification + +data class FcmToUserCommand( + val userKey: String, + val title: String, + val body: String, +) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessage.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessage.kt index 4ddb6b4..4c7578e 100644 --- a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessage.kt +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessage.kt @@ -5,6 +5,7 @@ data class FirebaseCloudMessage( val fcmToken: String, val title: String, val body: String, + val destination: String, val tryCount: Int, val sent: Boolean, ) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt new file mode 100644 index 0000000..565fd2f --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt @@ -0,0 +1,8 @@ +package com.pida.notification + +data class FirebaseCloudMessageCommand( + val fcmToken: String, + val title: String, + val body: String, + val destination: String, +) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NewFirebaseCloudMessage.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NewFirebaseCloudMessage.kt index 0e49830..1021334 100644 --- a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NewFirebaseCloudMessage.kt +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NewFirebaseCloudMessage.kt @@ -4,4 +4,5 @@ data class NewFirebaseCloudMessage( val fcmToken: String, val title: String, val body: String, + val destination: String, ) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationService.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationService.kt new file mode 100644 index 0000000..ccc9315 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationService.kt @@ -0,0 +1,42 @@ +package com.pida.notification + +import com.pida.support.cursor.Cursor +import com.pida.support.cursor.CursorRequest +import org.springframework.stereotype.Service + +@Service +class NotificationService( + private val notificationStoredAppender: NotificationStoredAppender, + private val notificationStoredReader: NotificationStoredReader, + private val notificationStoredUpdater: NotificationStoredUpdater, + private val notificationStoredKeyGenerator: NotificationStoredKeyGenerator, +) { + fun append(notificationStored: NotificationStored.Create): NotificationStored.Info = + notificationStoredAppender.append( + notificationStored.copy( + notificationStoredKey = notificationStoredKeyGenerator.generate(), + ), + ) + + fun appendAll(createAll: List): List = + notificationStoredAppender.appendAll( + createAll.map { notification -> + NotificationStored.Create( + notificationStoredKey = notificationStoredKeyGenerator.generate(), + userId = notification.userId, + type = notification.type, + parameterValue = notification.parameterValue, + topic = notification.topic, + contents = notification.contents, + readStatus = ReadStatus.UNREAD, + ) + }, + ) + + fun findAllNotifications( + userId: Long, + cursorRequest: CursorRequest, + ): Cursor = notificationStoredReader.findAllBy(userId, cursorRequest) + + fun markAsRead(notificationId: Long) = notificationStoredUpdater.markAsRead(notificationId) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStored.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStored.kt new file mode 100644 index 0000000..11d5329 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStored.kt @@ -0,0 +1,39 @@ +package com.pida.notification + +sealed class NotificationStored { + data class Create( + val notificationStoredKey: String, + val userId: Long, + val type: NotificationType, + val parameterValue: String, + val topic: String, + val contents: String, + val readStatus: ReadStatus, + ) + + data class Info( + val id: Long, + val notificationStoredKey: String, + val userId: Long, + val type: NotificationType, + val parameterValue: String, + val topic: String, + val contents: String, + val readStatus: ReadStatus, + val createdAt: String, + ) + + data class Result( + val id: Long, + val userId: Long, + val nickname: String?, + val spotName: String?, + val site: String?, + val type: String, + val parameterValue: String, + val topic: String, + val contents: String, + val readStatus: ReadStatus, + val createdAt: String, + ) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredAppender.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredAppender.kt new file mode 100644 index 0000000..23dc0a2 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredAppender.kt @@ -0,0 +1,12 @@ +package com.pida.notification + +import org.springframework.stereotype.Component + +@Component +class NotificationStoredAppender( + private val notificationStoredRepository: NotificationStoredRepository, +) { + fun append(create: NotificationStored.Create): NotificationStored.Info = notificationStoredRepository.save(create) + + fun appendAll(createAll: List) = notificationStoredRepository.saveAll(createAll) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredKeyGenerator.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredKeyGenerator.kt new file mode 100644 index 0000000..516339a --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredKeyGenerator.kt @@ -0,0 +1,19 @@ +package com.pida.notification + +import org.springframework.stereotype.Component +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.util.UUID + +@Component +class NotificationStoredKeyGenerator { + companion object { + private val FORMAT_YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd") + } + + fun generate(): String = "${generateDate()}_NHK_${generateUUID()}" + + private fun generateUUID(): String = UUID.randomUUID().toString().replace("-", "") + + private fun generateDate(): String = FORMAT_YYYYMMDD.format(LocalDate.now()) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredReader.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredReader.kt new file mode 100644 index 0000000..2add9cd --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredReader.kt @@ -0,0 +1,15 @@ +package com.pida.notification + +import com.pida.support.cursor.Cursor +import com.pida.support.cursor.CursorRequest +import org.springframework.stereotype.Component + +@Component +class NotificationStoredReader( + private val notificationStoredRepository: NotificationStoredRepository, +) { + fun findAllBy( + userId: Long, + cursorRequest: CursorRequest, + ): Cursor = notificationStoredRepository.findAllBy(userId, cursorRequest) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredRepository.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredRepository.kt new file mode 100644 index 0000000..567fd87 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredRepository.kt @@ -0,0 +1,29 @@ +package com.pida.notification + +import com.pida.support.cursor.Cursor +import com.pida.support.cursor.CursorRequest + +interface NotificationStoredRepository { + fun save(notificationStored: NotificationStored.Create): NotificationStored.Info + + fun saveAll(createAll: List): List + + fun findById(notificationStoredId: Long): NotificationStored.Info + + fun findByUserIdAndReadStatus( + userId: Long, + readStatus: ReadStatus, + ): List + + fun findAllBy( + userId: Long, + cursorRequest: CursorRequest, + ): Cursor + + fun countByUserIdAndReadStatus( + userId: Long, + readStatus: ReadStatus?, + ): Long + + fun markAsRead(notificationId: Long) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredUpdater.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredUpdater.kt new file mode 100644 index 0000000..43ba0cb --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationStoredUpdater.kt @@ -0,0 +1,10 @@ +package com.pida.notification + +import org.springframework.stereotype.Component + +@Component +class NotificationStoredUpdater( + private val notificationStoredRepository: NotificationStoredRepository, +) { + fun markAsRead(notificationId: Long) = notificationStoredRepository.markAsRead(notificationId) +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationType.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationType.kt new file mode 100644 index 0000000..48c1775 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/NotificationType.kt @@ -0,0 +1,16 @@ +package com.pida.notification + +enum class NotificationType { + ADMIN_PUSH, // 관리자 푸시 + REGULAR, // 정기 푸시 알림 (화요일 오전 9시) + ; + + companion object { + fun of(type: String): NotificationType = + when (type) { + "ADMIN_PUSH" -> ADMIN_PUSH + "REGULAR" -> REGULAR + else -> throw IllegalArgumentException("Unknown NotificationType: $type") + } + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/ReadStatus.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/ReadStatus.kt new file mode 100644 index 0000000..6520759 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/notification/ReadStatus.kt @@ -0,0 +1,16 @@ +package com.pida.notification + +enum class ReadStatus { + READ, + UNREAD, + ; + + companion object { + fun of(readStatus: String): ReadStatus = + when (readStatus) { + "READ" -> READ + "UNREAD" -> UNREAD + else -> throw IllegalArgumentException("Unknown ReadStatus: $readStatus") + } + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt new file mode 100644 index 0000000..bace725 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt @@ -0,0 +1,24 @@ +package com.pida.support.cursor + +data class Cursor( + val content: List, + val nextCursor: Long?, + val size: Long, +) { + companion object { + const val DEFAULT_CURSOR = 0L + const val DEFAULT_SIZE = 20 + + fun of( + content: List, + nextCursor: Long?, + size: Long, + ): Cursor { + require(size >= 0) { "size ($size) must be greater than or equal to 0" } + require(size >= content.size) { + "totalCount ($size) cannot be smaller than content.size (${content.size})" + } + return Cursor(content, nextCursor, size) + } + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/CursorRequest.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/CursorRequest.kt new file mode 100644 index 0000000..f72f073 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/CursorRequest.kt @@ -0,0 +1,10 @@ +package com.pida.support.cursor + +data class CursorRequest( + val lastId: Long?, + val size: Long, +) { + init { + require(size in 1..100) { "크기는 1 ~ 100 사이여야 합니다." } + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetLimit.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetLimit.kt new file mode 100644 index 0000000..800b74e --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetLimit.kt @@ -0,0 +1,22 @@ +package com.pida.support.page + +data class OffsetLimit( + val offset: Long, + val limit: Long, + val type: SortType? = SortType.NEW, + val sort: Sort? = Sort.DESC, +) { + init { + require(offset >= 0) { "offset은 0보다 커야 합니다. " } + require(limit > 0) { "limit은 0보다 커야 합니다. " } + } + + companion object { + fun of( + offset: Long, + limit: Long, + type: SortType? = SortType.NEW, + sort: Sort? = Sort.DESC, + ): OffsetLimit = OffsetLimit(offset, limit, type, sort) + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetPageRequest.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetPageRequest.kt new file mode 100644 index 0000000..17583f0 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/OffsetPageRequest.kt @@ -0,0 +1,11 @@ +package com.pida.support.page + +data class OffsetPageRequest( + val page: Int, + val size: Int, +) { + init { + require(page >= 0) { "페이지는 0 이상이야 합니다." } + require(size in 1..100) { "크기는 1 ~ 100 사이여야 합니다." } + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt new file mode 100644 index 0000000..d8e0dba --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt @@ -0,0 +1,20 @@ +package com.pida.support.page + +data class Page( + val content: List, + val totalCount: Long, +) { + companion object { + fun of( + content: List, + totalCount: Long, + ): Page { + require(totalCount >= 0) { "totalCount ($totalCount) cannot be negative" } + require(totalCount >= content.size) { + "totalCount ($totalCount) cannot be smaller than content.size (${content.size})" + } + val count = if (content.isEmpty()) 0 else totalCount + return Page(content, count) + } + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/PageResponse.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/PageResponse.kt new file mode 100644 index 0000000..d37dd4f --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/PageResponse.kt @@ -0,0 +1,15 @@ +package com.pida.support.page + +data class PageResponse( + val content: Data? = null, + val totalPages: Long = 0L, + val totalElements: Long = 0L, +) { + companion object { + fun of( + content: Data?, + totalPages: Long, + totalElements: Long, + ): PageResponse = PageResponse(content, totalPages, totalElements) + } +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Sort.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Sort.kt new file mode 100644 index 0000000..5c4ed9c --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Sort.kt @@ -0,0 +1,6 @@ +package com.pida.support.page + +enum class Sort { + DESC, + ASC, +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/SortType.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/SortType.kt new file mode 100644 index 0000000..942b2a8 --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/SortType.kt @@ -0,0 +1,7 @@ +package com.pida.support.page + +enum class SortType( + val displayName: String, +) { + NEW("id"), +} diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/tx/Tx.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/tx/Tx.kt new file mode 100644 index 0000000..458b08d --- /dev/null +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/tx/Tx.kt @@ -0,0 +1,90 @@ +package com.pida.support.tx + +import com.pida.support.annotation.ReadOnlyTransactional +import kotlinx.coroutines.Dispatchers +import org.springframework.stereotype.Component +import org.springframework.transaction.PlatformTransactionManager +import org.springframework.transaction.TransactionDefinition +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional +import org.springframework.transaction.support.TransactionTemplate +import kotlin.coroutines.CoroutineContext + +@Component +class Tx( + _txAdvice: TxAdvice, +) { + init { + txAdvice = _txAdvice + } + + companion object { + private lateinit var txAdvice: TxAdvice + + fun writeable(function: () -> T): T = txAdvice.write(function) + + fun readable(function: () -> T): T = txAdvice.readOnly(function) + + fun requiresNew(function: () -> T): T = txAdvice.requiresNew(function) + + suspend fun coWriteable( + coroutineContext: CoroutineContext = Dispatchers.IO, + function: () -> T, + ): T = txAdvice.coWrite(coroutineContext, function) + + suspend fun coReadable( + coroutineContext: CoroutineContext = Dispatchers.IO, + function: () -> T, + ): T = txAdvice.coReadOnly(coroutineContext, function) + + suspend fun coRequiresNew( + coroutineContext: CoroutineContext = Dispatchers.IO, + function: () -> T, + ): T = txAdvice.coRequiresNew(coroutineContext, function) + } + + @Component + class TxAdvice( + transactionManager: PlatformTransactionManager, + ) { + private val writeTemplate = + TransactionTemplate(transactionManager).apply { + propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED + } + + private val requiresNewTemplate = + TransactionTemplate(transactionManager).apply { + propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW + } + + private val readOnlyTemplate = + TransactionTemplate(transactionManager).apply { + propagationBehavior = TransactionDefinition.PROPAGATION_SUPPORTS + isReadOnly = true + } + + @Transactional + fun write(block: () -> T): T = block() + + @Transactional(propagation = Propagation.REQUIRES_NEW) + fun requiresNew(block: () -> T): T = block() + + @ReadOnlyTransactional + fun readOnly(block: () -> T): T = block() + + suspend fun coWrite( + coroutineContext: CoroutineContext = Dispatchers.IO, + block: () -> T, + ): T = writeTemplate.coExecute(coroutineContext, block) + + suspend fun coRequiresNew( + coroutineContext: CoroutineContext = Dispatchers.IO, + block: () -> T, + ): T = requiresNewTemplate.coExecute(coroutineContext, block) + + suspend fun coReadOnly( + coroutineContext: CoroutineContext = Dispatchers.IO, + block: () -> T, + ): T = readOnlyTemplate.coExecute(coroutineContext, block) + } +} diff --git a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCoreRepository.kt b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCoreRepository.kt new file mode 100644 index 0000000..de0fba9 --- /dev/null +++ b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCoreRepository.kt @@ -0,0 +1,79 @@ +package com.pida.storage.db.core.notification + +import com.pida.notification.NotificationStored +import com.pida.notification.NotificationStoredRepository +import com.pida.notification.ReadStatus +import com.pida.storage.db.core.support.findByIdOrElseThrow +import com.pida.support.cursor.Cursor +import com.pida.support.cursor.CursorRequest +import com.pida.support.tx.Tx +import org.springframework.stereotype.Repository + +@Repository +class NotificationStoredCoreRepository( + private val notificationStoredJpaRepository: NotificationStoredJpaRepository, + private val notificationStoredCustomRepository: NotificationStoredCustomRepository, +) : NotificationStoredRepository { + override fun save(notificationStored: NotificationStored.Create): NotificationStored.Info = + Tx.writeable { + notificationStoredJpaRepository + .save( + NotificationStoredEntity(notificationStored), + ).toNotificationStoredInfo() + } + + override fun saveAll(createAll: List) = + Tx.writeable { + notificationStoredJpaRepository + .saveAll( + createAll.map { NotificationStoredEntity(it) }, + ).map { it.toNotificationStoredInfo() } + } + + override fun findById(notificationStoredId: Long): NotificationStored.Info = + Tx.readable { + notificationStoredJpaRepository + .findByIdOrElseThrow(notificationStoredId) + .toNotificationStoredInfo() + } + + override fun findByUserIdAndReadStatus( + userId: Long, + readStatus: ReadStatus, + ): List = + Tx.readable { + notificationStoredJpaRepository + .findByUserIdAndReadStatus(userId, readStatus) + .map { it.toNotificationStoredInfo() } + } + + override fun findAllBy( + userId: Long, + cursorRequest: CursorRequest, + ): Cursor = + Tx.readable { + notificationStoredCustomRepository.findAllBy(userId, cursorRequest) + } + + override fun countByUserIdAndReadStatus( + userId: Long, + readStatus: ReadStatus?, + ): Long = + Tx.readable { + notificationStoredJpaRepository + .countByUserIdAndReadStatus(userId, readStatus) + } + + override fun markAsRead(notificationId: Long) = + Tx.writeable { + val notificationStored = + notificationStoredJpaRepository + .findByIdOrElseThrow(notificationId) + + if (notificationStored.readStatus == ReadStatus.READ) { + return@writeable + } + + notificationStored.read() + } +} diff --git a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCustomRepository.kt b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCustomRepository.kt new file mode 100644 index 0000000..3245c6f --- /dev/null +++ b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredCustomRepository.kt @@ -0,0 +1,53 @@ +package com.pida.storage.db.core.notification + +import com.pida.notification.NotificationStored +import com.pida.notification.NotificationType +import com.pida.storage.db.core.support.JDSLExtensions +import com.pida.support.cursor.Cursor +import com.pida.support.cursor.CursorRequest +import org.springframework.data.domain.PageRequest +import org.springframework.stereotype.Repository + +@Repository +class NotificationStoredCustomRepository( + private val notificationStoredJpaRepository: NotificationStoredJpaRepository, +) { + companion object { + private val EXCLUDED_TYPES = + listOf( + NotificationType.REGULAR, + ) + } + + fun findAllBy( + userId: Long, + cursorRequest: CursorRequest, + ): Cursor { + val pageable = PageRequest.ofSize(cursorRequest.size.toInt()) + val notifications = + notificationStoredJpaRepository.findPage(JDSLExtensions, pageable) { + select(entity(NotificationStoredEntity::class)) + .from(entity(NotificationStoredEntity::class)) + .whereAnd( + path(NotificationStoredEntity::userId).equal(userId), + path(NotificationStoredEntity::type).notIn(EXCLUDED_TYPES), + cursorRequest.lastId?.let { + path(NotificationStoredEntity::id).lessThan(cursorRequest.lastId) + }, + ).orderBy( + path(NotificationStoredEntity::id).desc(), + ) + } + val content = + notifications.content + .filterNotNull() + .map { it.toNotificationStoredInfo() } + val nextCursor = if (content.size < cursorRequest.size) null else notifications.lastOrNull()?.id + + return Cursor.of( + nextCursor = nextCursor, + size = notifications.totalElements, + content = content, + ) + } +} diff --git a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredEntity.kt b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredEntity.kt new file mode 100644 index 0000000..1ea8b48 --- /dev/null +++ b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredEntity.kt @@ -0,0 +1,54 @@ +package com.pida.storage.db.core.notification + +import com.pida.notification.NotificationStored +import com.pida.notification.NotificationType +import com.pida.notification.ReadStatus +import com.pida.storage.db.core.support.BaseEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.Table + +@Entity +@Table(name = "t_notification_stored") +class NotificationStoredEntity( + val notificationStoredKey: String, + val userId: Long, + @Enumerated(value = EnumType.STRING) + @Column(length = 30) + val type: NotificationType, + val parameterValue: String, + val topic: String, + val contents: String, + @Enumerated(value = EnumType.STRING) + @Column(length = 10) + var readStatus: ReadStatus, +) : BaseEntity() { + constructor(create: NotificationStored.Create) : this( + notificationStoredKey = create.notificationStoredKey, + userId = create.userId, + type = create.type, + parameterValue = create.parameterValue, + topic = create.topic, + contents = create.contents, + readStatus = create.readStatus, + ) + + fun toNotificationStoredInfo(): NotificationStored.Info = + NotificationStored.Info( + id = id!!, + notificationStoredKey = notificationStoredKey, + userId = userId, + type = type, + parameterValue = parameterValue, + topic = topic, + contents = contents, + readStatus = readStatus, + createdAt = createdAt.toString(), + ) + + fun read() { + this.readStatus = ReadStatus.READ + } +} diff --git a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredJpaRepository.kt b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredJpaRepository.kt new file mode 100644 index 0000000..d33d359 --- /dev/null +++ b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/notification/NotificationStoredJpaRepository.kt @@ -0,0 +1,19 @@ +package com.pida.storage.db.core.notification + +import com.linecorp.kotlinjdsl.support.spring.data.jpa.repository.KotlinJdslJpqlExecutor +import com.pida.notification.ReadStatus +import org.springframework.data.jpa.repository.JpaRepository + +interface NotificationStoredJpaRepository : + JpaRepository, + KotlinJdslJpqlExecutor { + fun findByUserIdAndReadStatus( + userId: Long, + readStatus: ReadStatus, + ): List + + fun countByUserIdAndReadStatus( + userId: Long, + readStatus: ReadStatus?, + ): Long +} From dbb4f3ccb60404ec6778741d3c1cb62814be49a0 Mon Sep 17 00:00:00 2001 From: ybchar Date: Tue, 30 Dec 2025 15:18:04 +0900 Subject: [PATCH 2/3] fix: remove txAdvice --- .../kotlin/com/pida/support/tx/TxAdvice.kt | 56 ------------------- .../core/blooming/BloomingCoreRepository.kt | 20 +++---- .../db/core/user/UserCoreRepository.kt | 40 ++++++------- 3 files changed, 26 insertions(+), 90 deletions(-) delete mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/support/tx/TxAdvice.kt diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/tx/TxAdvice.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/tx/TxAdvice.kt deleted file mode 100644 index 631b552..0000000 --- a/pida-core/core-domain/src/main/kotlin/com/pida/support/tx/TxAdvice.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.pida.support.tx - -import com.pida.support.annotation.ReadOnlyTransactional -import kotlinx.coroutines.Dispatchers -import org.springframework.stereotype.Component -import org.springframework.transaction.PlatformTransactionManager -import org.springframework.transaction.TransactionDefinition -import org.springframework.transaction.annotation.Propagation -import org.springframework.transaction.annotation.Transactional -import org.springframework.transaction.support.TransactionTemplate -import kotlin.coroutines.CoroutineContext - -@Component -class TxAdvice( - transactionManager: PlatformTransactionManager, -) { - private val writeTemplate = - TransactionTemplate(transactionManager).apply { - propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED - } - - private val requiresNewTemplate = - TransactionTemplate(transactionManager).apply { - propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW - } - - private val readOnlyTemplate = - TransactionTemplate(transactionManager).apply { - propagationBehavior = TransactionDefinition.PROPAGATION_SUPPORTS - isReadOnly = true - } - - @Transactional - fun write(block: () -> T): T = block() - - @Transactional(propagation = Propagation.REQUIRES_NEW) - fun requiresNew(block: () -> T): T = block() - - @ReadOnlyTransactional - fun readOnly(block: () -> T): T = block() - - suspend fun coWrite( - coroutineContext: CoroutineContext = Dispatchers.IO, - block: () -> T, - ): T = writeTemplate.coExecute(coroutineContext, block) - - suspend fun coRequiresNew( - coroutineContext: CoroutineContext = Dispatchers.IO, - block: () -> T, - ): T = requiresNewTemplate.coExecute(coroutineContext, block) - - suspend fun coReadOnly( - coroutineContext: CoroutineContext = Dispatchers.IO, - block: () -> T, - ): T = readOnlyTemplate.coExecute(coroutineContext, block) -} diff --git a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/blooming/BloomingCoreRepository.kt b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/blooming/BloomingCoreRepository.kt index d749ce2..1e0a36d 100644 --- a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/blooming/BloomingCoreRepository.kt +++ b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/blooming/BloomingCoreRepository.kt @@ -3,20 +3,16 @@ package com.pida.storage.db.core.blooming import com.pida.blooming.Blooming import com.pida.blooming.BloomingRepository import com.pida.blooming.NewBlooming -import com.pida.support.tx.TransactionTemplates -import com.pida.support.tx.TxAdvice -import com.pida.support.tx.coExecute +import com.pida.support.tx.Tx import org.springframework.stereotype.Repository @Repository class BloomingCoreRepository( private val bloomingJpaRepository: BloomingJpaRepository, private val bloomingCustomRepository: BloomingCustomRepository, - private val txAdvice: TxAdvice, - private val tx: TransactionTemplates, ) : BloomingRepository { override fun add(newBlooming: NewBlooming): Blooming = - txAdvice.write { + Tx.writeable { val bloomingEntity = BloomingEntity( userId = newBlooming.userId, @@ -30,27 +26,27 @@ class BloomingCoreRepository( userId: Long, flowerSpotId: Long, ): Blooming? = - txAdvice.readOnly { + Tx.readable { bloomingJpaRepository.findTopByUserIdAndFlowerSpotIdOrderByCreatedAtDesc(userId, flowerSpotId)?.toBlooming() } override suspend fun findAllByUserId(userId: Long): List = - txAdvice.readOnly { + Tx.readable { bloomingJpaRepository.findAllByUserId(userId).map { it.toBlooming() } } override suspend fun findAllByFlowerSpotId(flowerSpotId: Long): List = - txAdvice.readOnly { + Tx.readable { bloomingJpaRepository.findAllByFlowerSpotId(flowerSpotId).map { it.toBlooming() } } override suspend fun findRecentlyBySpotId(spotId: Long): List = - tx.reader.coExecute { + Tx.coReadable { bloomingCustomRepository.recentlyBySpotId(spotId).map { it.toBlooming() } } override fun findRecentBySpotIds(spotIds: List): List = - txAdvice.readOnly { + Tx.readable { bloomingCustomRepository.recentlyBySpotIds(spotIds).map { it.toBlooming() } } @@ -58,7 +54,7 @@ class BloomingCoreRepository( userId: Long, flowerSpotId: Long, ): Blooming? = - txAdvice.readOnly { + Tx.readable { bloomingCustomRepository.findTodayBloomingByUserId(userId, flowerSpotId)?.toBlooming() } } diff --git a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/user/UserCoreRepository.kt b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/user/UserCoreRepository.kt index 8074b3f..0e62395 100644 --- a/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/user/UserCoreRepository.kt +++ b/pida-storage/db-core/src/main/kotlin/com/pida/storage/db/core/user/UserCoreRepository.kt @@ -3,9 +3,7 @@ package com.pida.storage.db.core.user import com.pida.storage.db.core.support.findByIdAndDeletedAtIsNullOrElseThrow import com.pida.support.error.ErrorException import com.pida.support.error.ErrorType -import com.pida.support.tx.TransactionTemplates -import com.pida.support.tx.TxAdvice -import com.pida.support.tx.coExecute +import com.pida.support.tx.Tx import com.pida.user.NewUser import com.pida.user.NewUserKey import com.pida.user.SocialUser @@ -18,19 +16,17 @@ import org.springframework.stereotype.Repository @Repository class UserCoreRepository( private val userJpaRepository: UserJpaRepository, - private val tx: TransactionTemplates, - private val txAdvice: TxAdvice, ) : UserRepository { override fun create( newUser: NewUser, newUserKey: NewUserKey, ): User = - txAdvice.write { + Tx.writeable { userJpaRepository.save(UserEntity(newUser, newUserKey)).toUser() } override fun readUserById(id: Long): User? = - txAdvice.readOnly { + Tx.readable { userJpaRepository.findByIdOrNull(id)?.toUser() } @@ -38,44 +34,44 @@ class UserCoreRepository( loginId: String, password: String, ): User = - txAdvice.readOnly { + Tx.readable { userJpaRepository.findByEmailAndPasswordAndDeletedAtIsNull(loginId, password)?.toUser() ?: throw ErrorException(ErrorType.NOT_FOUND_DATA) } override suspend fun readByUserIdOrNull(id: Long): UserProfile? = - tx.reader.coExecute { + Tx.coReadable { userJpaRepository.findByIdAndDeletedAtIsNull(id)?.toProfile() } override suspend fun readAllByUserIds(userIds: List): List = - tx.reader.coExecute { + Tx.coReadable { userJpaRepository.findAllByIdIn(userIds).map { it.toProfile() } } override suspend fun readByUserKey(userKey: String): UserProfile = - tx.reader.coExecute { + Tx.coReadable { userJpaRepository.findByUserKeyAndDeletedAtIsNull(userKey)?.toProfile() ?: throw ErrorException(ErrorType.NOT_FOUND_DATA) } override suspend fun readByUserId(id: Long): UserProfile = - tx.reader.coExecute { + Tx.coReadable { userJpaRepository.findByIdAndDeletedAtIsNullOrElseThrow(id).toProfile() } override fun readUserByEmail(email: String): SocialUser? = - txAdvice.readOnly { + Tx.readable { userJpaRepository.findByEmailAndDeletedAtIsNull(email)?.toSocialUser() } override suspend fun existsByEmail(email: String): Boolean = - tx.reader.coExecute { + Tx.coReadable { userJpaRepository.existsByEmailAndDeletedAtIsNull(email) } override suspend fun existsByNickname(nickname: String): Boolean = - tx.reader.coExecute { + Tx.coReadable { userJpaRepository.existsByNicknameAndDeletedAtIsNull(nickname) } @@ -83,35 +79,35 @@ class UserCoreRepository( userKey: String, nickname: String, ): UserProfile = - txAdvice.write { + Tx.writeable { val user = userJpaRepository.findByUserKeyAndDeletedAtIsNull(userKey) ?: throw ErrorException(ErrorType.NOT_FOUND_DATA) user.updateNickname(nickname) - return@write user.toProfile() + return@writeable user.toProfile() } override fun updateName( userKey: String, name: String, ): UserProfile = - txAdvice.write { + Tx.writeable { val user = userJpaRepository.findByUserKeyAndDeletedAtIsNull(userKey) ?: throw ErrorException(ErrorType.NOT_FOUND_DATA) user.updateName(name) user.updateNickname(name) - return@write user.toProfile() + return@writeable user.toProfile() } override suspend fun updateEmail( userKey: String, email: String, ): UserProfile = - tx.writer.coExecute { + Tx.coWriteable { val user = userJpaRepository.findByUserKeyAndDeletedAtIsNull(userKey) ?: throw ErrorException(ErrorType.NOT_FOUND_DATA) user.updateEmail(email) - return@coExecute user.toProfile() + return@coWriteable user.toProfile() } override fun delete(userKey: String) = - txAdvice.write { + Tx.writeable { val user = userJpaRepository.findByUserKeyAndDeletedAtIsNull(userKey) ?: throw ErrorException(ErrorType.NOT_FOUND_DATA) user.softDelete() } From 6aae403ef68de0eaaf8b55b25995c30a5fc051a4 Mon Sep 17 00:00:00 2001 From: ybchar Date: Tue, 30 Dec 2025 15:24:42 +0900 Subject: [PATCH 3/3] fix: cursor, page require message --- pida-core/core-api/src/main/resources/application.yml | 2 -- .../main/kotlin/com/pida/notification/FcmToUserCommand.kt | 7 ------- .../com/pida/notification/FirebaseCloudMessageCommand.kt | 8 -------- .../src/main/kotlin/com/pida/support/cursor/Cursor.kt | 4 ++-- .../src/main/kotlin/com/pida/support/page/Page.kt | 4 ++-- 5 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt delete mode 100644 pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt diff --git a/pida-core/core-api/src/main/resources/application.yml b/pida-core/core-api/src/main/resources/application.yml index 502ff85..a000d63 100644 --- a/pida-core/core-api/src/main/resources/application.yml +++ b/pida-core/core-api/src/main/resources/application.yml @@ -2,8 +2,6 @@ spring.application.name: pida-server spring.profiles.active: ${SPRING_PROFILES_ACTIVE} spring: - main: - lazy-initialization: true threads: virtual: enabled: true diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt deleted file mode 100644 index 1bc3dc0..0000000 --- a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FcmToUserCommand.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.pida.notification - -data class FcmToUserCommand( - val userKey: String, - val title: String, - val body: String, -) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt b/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt deleted file mode 100644 index 565fd2f..0000000 --- a/pida-core/core-domain/src/main/kotlin/com/pida/notification/FirebaseCloudMessageCommand.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.pida.notification - -data class FirebaseCloudMessageCommand( - val fcmToken: String, - val title: String, - val body: String, - val destination: String, -) diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt index bace725..fac6b2a 100644 --- a/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/cursor/Cursor.kt @@ -14,9 +14,9 @@ data class Cursor( nextCursor: Long?, size: Long, ): Cursor { - require(size >= 0) { "size ($size) must be greater than or equal to 0" } + require(size >= 0) { "size ($size)는 0 이상이어야 합니다" } require(size >= content.size) { - "totalCount ($size) cannot be smaller than content.size (${content.size})" + "size ($size)는 content.size (${content.size})보다 작을 수 없습니다" } return Cursor(content, nextCursor, size) } diff --git a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt index d8e0dba..c049c8a 100644 --- a/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt +++ b/pida-core/core-domain/src/main/kotlin/com/pida/support/page/Page.kt @@ -9,9 +9,9 @@ data class Page( content: List, totalCount: Long, ): Page { - require(totalCount >= 0) { "totalCount ($totalCount) cannot be negative" } + require(totalCount >= 0) { "totalCount ($totalCount)는 음수일 수 없습니다" } require(totalCount >= content.size) { - "totalCount ($totalCount) cannot be smaller than content.size (${content.size})" + "totalCount ($totalCount)는 content.size (${content.size})보다 작을 수 없습니다" } val count = if (content.isEmpty()) 0 else totalCount return Page(content, count)