feat(gift): 배송 개인정보 정리 스케줄과 동시 변경 보호를 추가한다
This commit is contained in:
+18
-1
@@ -1,9 +1,26 @@
|
||||
package kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import javax.persistence.EntityManager
|
||||
import javax.persistence.LockModeType
|
||||
|
||||
interface GiftDeliveryRepository : JpaRepository<GiftDelivery, Long> {
|
||||
interface GiftDeliveryRepository : JpaRepository<GiftDelivery, Long>, GiftDeliveryLockRepository {
|
||||
fun findByGiftId(giftId: Long): GiftDelivery?
|
||||
|
||||
fun findByGiftIdIn(giftIds: Collection<Long>): List<GiftDelivery>
|
||||
}
|
||||
|
||||
interface GiftDeliveryLockRepository {
|
||||
fun findByGiftIdForUpdate(giftId: Long): GiftDelivery?
|
||||
}
|
||||
|
||||
@Repository
|
||||
class GiftDeliveryLockRepositoryImpl(private val entityManager: EntityManager) : GiftDeliveryLockRepository {
|
||||
override fun findByGiftIdForUpdate(giftId: Long): GiftDelivery? = entityManager
|
||||
.createQuery("select d from GiftDelivery d where d.giftId = :giftId", GiftDelivery::class.java)
|
||||
.setParameter("giftId", giftId)
|
||||
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
|
||||
.resultList.singleOrNull()
|
||||
?.also { entityManager.refresh(it, LockModeType.PESSIMISTIC_WRITE) }
|
||||
}
|
||||
|
||||
+35
-7
@@ -7,16 +7,14 @@ import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Lock
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.data.repository.query.Param
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.time.LocalDateTime
|
||||
import javax.persistence.EntityManager
|
||||
import javax.persistence.LockModeType
|
||||
|
||||
interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
interface GiftRepository : JpaRepository<Gift, Long>, GiftLockRepository {
|
||||
fun findByApplicationNo(applicationNo: String): Gift?
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select g from Gift g where g.applicationNo = :applicationNo")
|
||||
fun findByApplicationNoForUpdate(@Param("applicationNo") applicationNo: String): Gift?
|
||||
|
||||
fun findBySenderMemberId(senderMemberId: Long, pageable: Pageable): Page<Gift>
|
||||
|
||||
fun findByRecipientMemberIdAndStatusIn(
|
||||
@@ -67,6 +65,7 @@ interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
pageable: Pageable
|
||||
): Page<Gift>
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query(
|
||||
"""
|
||||
select g
|
||||
@@ -78,9 +77,9 @@ interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
where d.giftId = g.id
|
||||
and d.trackingDeadlineAt < :now
|
||||
)
|
||||
order by g.id
|
||||
"""
|
||||
)
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
fun findReceivedGiftsWithTrackingDeadlineBefore(@Param("now") now: LocalDateTime): List<Gift>
|
||||
|
||||
@Query(
|
||||
@@ -105,6 +104,22 @@ interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
@Param("now") now: LocalDateTime
|
||||
): List<Gift>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
select g
|
||||
from Gift g
|
||||
where g.status = kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus.DELIVERED
|
||||
and exists (
|
||||
select d.id
|
||||
from GiftDelivery d
|
||||
where d.giftId = g.id
|
||||
and d.deliveredAt is not null
|
||||
and d.deliveredAt <= :cutoff
|
||||
)
|
||||
"""
|
||||
)
|
||||
fun findDeliveredGiftsWithDeliveryBeforeOrAt(@Param("cutoff") cutoff: LocalDateTime): List<Gift>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
select g
|
||||
@@ -120,7 +135,6 @@ interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
)
|
||||
"""
|
||||
)
|
||||
@Lock(LockModeType.PESSIMISTIC_WRITE)
|
||||
fun findReceivedGiftsWithTrackingDeadlineReminderDue(
|
||||
@Param("now") now: LocalDateTime,
|
||||
@Param("reminderUntil") reminderUntil: LocalDateTime
|
||||
@@ -151,3 +165,17 @@ interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
@Param("reminderUntil") reminderUntil: LocalDateTime
|
||||
): List<Gift>
|
||||
}
|
||||
|
||||
interface GiftLockRepository {
|
||||
fun findByApplicationNoForUpdate(applicationNo: String): Gift?
|
||||
}
|
||||
|
||||
@Repository
|
||||
class GiftLockRepositoryImpl(private val entityManager: EntityManager) : GiftLockRepository {
|
||||
override fun findByApplicationNoForUpdate(applicationNo: String): Gift? = entityManager
|
||||
.createQuery("select g from Gift g where g.applicationNo = :applicationNo", Gift::class.java)
|
||||
.setParameter("applicationNo", applicationNo)
|
||||
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
|
||||
.resultList.singleOrNull()
|
||||
?.also { entityManager.refresh(it, LockModeType.PESSIMISTIC_WRITE) }
|
||||
}
|
||||
|
||||
+151
-28
@@ -5,10 +5,13 @@ import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDeliveryRepo
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftRepository
|
||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftPushService
|
||||
import kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus
|
||||
import org.redisson.api.RLock
|
||||
import org.redisson.api.RedissonClient
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionSynchronization
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
import java.time.LocalDateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -39,63 +42,183 @@ class GiftScheduler(
|
||||
@Transactional
|
||||
fun cancelExpiredTrackingRegistrationGifts(now: LocalDateTime): Int {
|
||||
val expiredGifts = giftRepository.findReceivedGiftsWithTrackingDeadlineBefore(now)
|
||||
expiredGifts.forEach { gift ->
|
||||
return expiredGifts.sortedBy { it.id }.count { candidate ->
|
||||
val gift = requireNotNull(giftRepository.findByApplicationNoForUpdate(candidate.applicationNo))
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)))
|
||||
if (gift.status != GiftStatus.RECEIVED || !delivery.trackingDeadlineAt.isBefore(now)) {
|
||||
return@count false
|
||||
}
|
||||
gift.status = GiftStatus.CANCELED
|
||||
requireNotNull(giftDeliveryRepository.findByGiftId(requireNotNull(gift.id))).canceledAt = now
|
||||
delivery.canceledAt = now
|
||||
canPaymentService.refundGiftCan(gift.senderMemberId, gift.canUsageId)
|
||||
giftPushService.sendAutoCanceledToSender(gift.applicationNo, gift.senderMemberId)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 10 * * * *", zone = "Asia/Seoul")
|
||||
@Transactional
|
||||
fun runExpiredRecipientAddressGiftExpiration() {
|
||||
val lock = redissonClient.getLock(EXPIRED_RECIPIENT_ADDRESS_GIFTS_LOCK_KEY)
|
||||
if (!lock.tryLock(0, -1, TimeUnit.SECONDS)) {
|
||||
return
|
||||
}
|
||||
|
||||
return expiredGifts.size
|
||||
try {
|
||||
markExpiredRecipientAddressGiftsUndeliverable(LocalDateTime.now())
|
||||
} finally {
|
||||
unlockAfterTransaction(lock)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun markExpiredRecipientAddressGiftsUndeliverable(now: LocalDateTime): Int {
|
||||
val expiredGifts = giftRepository.findGiftsWithRecipientAddressDeadlineBefore(now)
|
||||
expiredGifts.forEach { gift ->
|
||||
return expiredGifts.sortedBy { it.id }.count { candidate ->
|
||||
val gift = requireNotNull(giftRepository.findByApplicationNoForUpdate(candidate.applicationNo))
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)))
|
||||
if (gift.status !in RECIPIENT_ADDRESS_STATUSES || delivery.recipientAddress != null ||
|
||||
delivery.recipientAddressDeadlineAt?.isBefore(now) != true
|
||||
) {
|
||||
return@count false
|
||||
}
|
||||
gift.status = GiftStatus.UNDELIVERABLE
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)))
|
||||
delivery.undeliverableAt = now
|
||||
delivery.undeliverableReason = EXPIRED_RECIPIENT_ADDRESS_REASON
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 20 * * * *", zone = "Asia/Seoul")
|
||||
@Transactional
|
||||
fun runDeliveredGiftPersonalDataErasure() {
|
||||
val lock = redissonClient.getLock(DELIVERED_GIFT_PERSONAL_DATA_ERASURE_LOCK_KEY)
|
||||
if (!lock.tryLock(0, -1, TimeUnit.SECONDS)) {
|
||||
return
|
||||
}
|
||||
|
||||
return expiredGifts.size
|
||||
try {
|
||||
eraseDeliveredGiftPersonalData(LocalDateTime.now())
|
||||
} finally {
|
||||
unlockAfterTransaction(lock)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun eraseDeliveredGiftPersonalData(now: LocalDateTime): Int {
|
||||
val cutoff = now.minusDays(7)
|
||||
val expiredGifts = giftRepository.findDeliveredGiftsWithDeliveryBeforeOrAt(cutoff)
|
||||
|
||||
return expiredGifts.count { gift ->
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)))
|
||||
if (
|
||||
listOf(
|
||||
delivery.senderName,
|
||||
delivery.senderPhoneNumber,
|
||||
delivery.senderZipCode,
|
||||
delivery.senderAddress,
|
||||
delivery.senderAddressDetail,
|
||||
delivery.recipientName,
|
||||
delivery.recipientPhoneNumber,
|
||||
delivery.recipientZipCode,
|
||||
delivery.recipientAddress,
|
||||
delivery.recipientAddressDetail,
|
||||
delivery.courierCompanyName,
|
||||
delivery.trackingNumber
|
||||
).all { it == null }
|
||||
) {
|
||||
false
|
||||
} else {
|
||||
delivery.senderName = null
|
||||
delivery.senderPhoneNumber = null
|
||||
delivery.senderZipCode = null
|
||||
delivery.senderAddress = null
|
||||
delivery.senderAddressDetail = null
|
||||
delivery.recipientName = null
|
||||
delivery.recipientPhoneNumber = null
|
||||
delivery.recipientZipCode = null
|
||||
delivery.recipientAddress = null
|
||||
delivery.recipientAddressDetail = null
|
||||
delivery.courierCompanyName = null
|
||||
delivery.trackingNumber = null
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 40 * * * *", zone = "Asia/Seoul")
|
||||
@Transactional
|
||||
fun runDeadlineReminderGifts() {
|
||||
val lock = redissonClient.getLock(DEADLINE_REMINDER_GIFTS_LOCK_KEY)
|
||||
if (!lock.tryLock(0, -1, TimeUnit.SECONDS)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
sendDeadlineReminderGifts(LocalDateTime.now())
|
||||
} finally {
|
||||
unlockAfterTransaction(lock)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun sendDeadlineReminderGifts(now: LocalDateTime): Int {
|
||||
val reminderUntil = now.plusHours(DEADLINE_REMINDER_HOURS)
|
||||
val trackingReminderCount = sendTrackingDeadlineReminders(now, reminderUntil)
|
||||
val recipientAddressReminderCount = sendRecipientAddressDeadlineReminders(now, reminderUntil)
|
||||
|
||||
return trackingReminderCount + recipientAddressReminderCount
|
||||
val gifts = giftRepository.findReceivedGiftsWithTrackingDeadlineReminderDue(now, reminderUntil) +
|
||||
giftRepository.findGiftsWithRecipientAddressDeadlineReminderDue(now, reminderUntil)
|
||||
return gifts.distinctBy { it.id }.sortedBy { it.id }.count { candidate ->
|
||||
val gift = requireNotNull(giftRepository.findByApplicationNoForUpdate(candidate.applicationNo))
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)))
|
||||
val recipientDeadline = delivery.recipientAddressDeadlineAt
|
||||
when {
|
||||
gift.status == GiftStatus.RECEIVED && delivery.trackingDeadlineAt.isAfter(now) &&
|
||||
!delivery.trackingDeadlineAt.isAfter(reminderUntil) && delivery.trackingDeadlineReminderSentAt == null -> {
|
||||
delivery.trackingDeadlineReminderSentAt = now
|
||||
giftPushService.sendTrackingDeadlineReminder(gift.applicationNo, gift.senderMemberId)
|
||||
true
|
||||
}
|
||||
gift.status in RECIPIENT_ADDRESS_STATUSES && delivery.recipientAddress == null &&
|
||||
recipientDeadline != null && recipientDeadline.isAfter(now) && !recipientDeadline.isAfter(reminderUntil) &&
|
||||
delivery.recipientAddressDeadlineReminderSentAt == null -> {
|
||||
delivery.recipientAddressDeadlineReminderSentAt = now
|
||||
giftPushService.sendRecipientAddressDeadlineReminder(gift.applicationNo, gift.recipientMemberId)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTrackingDeadlineReminders(now: LocalDateTime, reminderUntil: LocalDateTime): Int {
|
||||
val gifts = giftRepository.findReceivedGiftsWithTrackingDeadlineReminderDue(now, reminderUntil)
|
||||
gifts.forEach { gift ->
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)))
|
||||
delivery.trackingDeadlineReminderSentAt = now
|
||||
giftPushService.sendTrackingDeadlineReminder(gift.applicationNo, gift.senderMemberId)
|
||||
private fun unlockAfterTransaction(lock: RLock) {
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
if (lock.isHeldByCurrentThread) {
|
||||
lock.unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return gifts.size
|
||||
}
|
||||
|
||||
private fun sendRecipientAddressDeadlineReminders(now: LocalDateTime, reminderUntil: LocalDateTime): Int {
|
||||
val gifts = giftRepository.findGiftsWithRecipientAddressDeadlineReminderDue(now, reminderUntil)
|
||||
gifts.forEach { gift ->
|
||||
val delivery = requireNotNull(giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)))
|
||||
delivery.recipientAddressDeadlineReminderSentAt = now
|
||||
giftPushService.sendRecipientAddressDeadlineReminder(gift.applicationNo, gift.recipientMemberId)
|
||||
}
|
||||
|
||||
return gifts.size
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
object : TransactionSynchronization {
|
||||
override fun afterCompletion(status: Int) {
|
||||
if (lock.isHeldByCurrentThread) {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val RECIPIENT_ADDRESS_STATUSES = setOf(
|
||||
GiftStatus.TRACKING_REGISTERED,
|
||||
GiftStatus.ARRIVED_AT_MAILBOX,
|
||||
GiftStatus.INSPECTION_COMPLETED
|
||||
)
|
||||
const val DEADLINE_REMINDER_HOURS = 24L
|
||||
const val EXPIRED_TRACKING_REGISTRATION_GIFTS_LOCK_KEY = "lock:gift-expired-tracking-registration-cancellation"
|
||||
const val EXPIRED_RECIPIENT_ADDRESS_GIFTS_LOCK_KEY = "lock:gift-expired-recipient-address-undeliverable"
|
||||
const val DELIVERED_GIFT_PERSONAL_DATA_ERASURE_LOCK_KEY = "lock:gift-delivered-personal-data-erasure"
|
||||
const val DEADLINE_REMINDER_GIFTS_LOCK_KEY = "lock:gift-deadline-reminders"
|
||||
const val EXPIRED_RECIPIENT_ADDRESS_REASON = "배송지 미입력 기한 초과"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ class GiftAdminService(
|
||||
fun cancelGift(applicationNo: String, now: LocalDateTime = LocalDateTime.now()): GiftOperationStatusResult {
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.status != GiftStatus.RECEIVED) throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
val canceledAt = now.truncatedTo(ChronoUnit.SECONDS)
|
||||
|
||||
gift.status = GiftStatus.CANCELED
|
||||
@@ -169,9 +169,9 @@ class GiftAdminService(
|
||||
|
||||
@Transactional
|
||||
fun arriveMailbox(applicationNo: String, now: LocalDateTime = LocalDateTime.now()): GiftOperationStatusResult {
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.status != GiftStatus.TRACKING_REGISTERED) throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
|
||||
gift.status = GiftStatus.ARRIVED_AT_MAILBOX
|
||||
delivery.arrivedAtMailboxAt = now
|
||||
@@ -183,9 +183,9 @@ class GiftAdminService(
|
||||
|
||||
@Transactional
|
||||
fun completeInspection(applicationNo: String, now: LocalDateTime = LocalDateTime.now()): GiftOperationStatusResult {
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.status != GiftStatus.ARRIVED_AT_MAILBOX) throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
|
||||
gift.status = GiftStatus.INSPECTION_COMPLETED
|
||||
delivery.inspectionCompletedAt = now
|
||||
@@ -200,11 +200,11 @@ class GiftAdminService(
|
||||
reason: String,
|
||||
now: LocalDateTime = LocalDateTime.now()
|
||||
): GiftOperationStatusResult {
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.status !in UNDELIVERABLE_ALLOWED_STATUSES || reason.isBlank() || reason.length > 255) {
|
||||
throw invalidRequest()
|
||||
}
|
||||
val delivery = giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
|
||||
gift.status = GiftStatus.UNDELIVERABLE
|
||||
delivery.undeliverableAt = now
|
||||
@@ -217,9 +217,9 @@ class GiftAdminService(
|
||||
|
||||
@Transactional
|
||||
fun completeDelivery(applicationNo: String, now: LocalDateTime = LocalDateTime.now()): GiftOperationStatusResult {
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.status != GiftStatus.INSPECTION_COMPLETED) throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
val delivery = giftDeliveryRepository.findByGiftIdForUpdate(requireNotNull(gift.id)) ?: throw invalidRequest()
|
||||
if (!delivery.hasCompleteRecipientInformation()) throw invalidRequest()
|
||||
|
||||
gift.status = GiftStatus.DELIVERED
|
||||
|
||||
@@ -212,7 +212,7 @@ class GiftCommandService(
|
||||
|
||||
val canceledAt = now()
|
||||
gift.status = GiftStatus.CANCELED
|
||||
deliveryRepository.findByGiftId(gift.id ?: throw invalidRequest())?.canceledAt = canceledAt
|
||||
deliveryRepository.findByGiftIdForUpdate(gift.id ?: throw invalidRequest())?.canceledAt = canceledAt
|
||||
canPaymentService.refundGiftCan(senderMemberId, gift.canUsageId)
|
||||
|
||||
return GiftCancellationResult(
|
||||
@@ -234,7 +234,7 @@ class GiftCommandService(
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.senderMemberId != senderMemberId || gift.status != GiftStatus.RECEIVED) throw invalidRequest()
|
||||
validate(command)
|
||||
val delivery = deliveryRepository.findByGiftId(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||
val delivery = deliveryRepository.findByGiftIdForUpdate(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||
val trackingRegisteredAt = now()
|
||||
val recipientAddressDeadlineAt = trackingRegisteredAt.plusDays(7)
|
||||
|
||||
@@ -263,12 +263,12 @@ class GiftCommandService(
|
||||
command: GiftRecipientAddressRegistrationCommand
|
||||
): GiftRecipientAddressRegistrationResult {
|
||||
val recipientMemberId = member.id ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.recipientMemberId != recipientMemberId || gift.status !in RECIPIENT_ADDRESS_REGISTRABLE_STATUSES) {
|
||||
throw invalidRequest()
|
||||
}
|
||||
validate(command)
|
||||
val delivery = deliveryRepository.findByGiftId(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||
val delivery = deliveryRepository.findByGiftIdForUpdate(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||
val registeredAt = now()
|
||||
|
||||
delivery.recipientName = command.name
|
||||
@@ -292,11 +292,11 @@ class GiftCommandService(
|
||||
@Transactional
|
||||
fun confirmDelivery(member: Member, applicationNo: String): GiftDeliveryConfirmationResult {
|
||||
val recipientMemberId = member.id ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest()
|
||||
if (gift.recipientMemberId != recipientMemberId || gift.status != GiftStatus.INSPECTION_COMPLETED) {
|
||||
throw invalidRequest()
|
||||
}
|
||||
val delivery = deliveryRepository.findByGiftId(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||
val delivery = deliveryRepository.findByGiftIdForUpdate(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||
val deliveredAt = now()
|
||||
|
||||
gift.status = GiftStatus.DELIVERED
|
||||
|
||||
Reference in New Issue
Block a user