feat(gift): 관리자 선물함 조회 서비스를 추가한다
This commit is contained in:
+2
@@ -4,4 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository
|
||||
|
||||
interface GiftDeliveryRepository : JpaRepository<GiftDelivery, Long> {
|
||||
fun findByGiftId(giftId: Long): GiftDelivery?
|
||||
|
||||
fun findByGiftIdIn(giftIds: Collection<Long>): List<GiftDelivery>
|
||||
}
|
||||
|
||||
+35
@@ -26,6 +26,41 @@ interface GiftRepository : JpaRepository<Gift, Long> {
|
||||
pageable: Pageable
|
||||
): Page<Gift>
|
||||
|
||||
@Query(
|
||||
value = """
|
||||
select g
|
||||
from Gift g
|
||||
join Member sender on sender.id = g.senderMemberId
|
||||
join Member recipient on recipient.id = g.recipientMemberId
|
||||
where (:status is null or g.status = :status)
|
||||
and (:applicationNo is null or g.applicationNo like concat('%', :applicationNo, '%'))
|
||||
and (
|
||||
:nickname is null
|
||||
or sender.nickname like concat('%', :nickname, '%')
|
||||
or recipient.nickname like concat('%', :nickname, '%')
|
||||
)
|
||||
""",
|
||||
countQuery = """
|
||||
select count(g)
|
||||
from Gift g
|
||||
join Member sender on sender.id = g.senderMemberId
|
||||
join Member recipient on recipient.id = g.recipientMemberId
|
||||
where (:status is null or g.status = :status)
|
||||
and (:applicationNo is null or g.applicationNo like concat('%', :applicationNo, '%'))
|
||||
and (
|
||||
:nickname is null
|
||||
or sender.nickname like concat('%', :nickname, '%')
|
||||
or recipient.nickname like concat('%', :nickname, '%')
|
||||
)
|
||||
"""
|
||||
)
|
||||
fun findAdminGifts(
|
||||
@Param("status") status: GiftStatus?,
|
||||
@Param("applicationNo") applicationNo: String?,
|
||||
@Param("nickname") nickname: String?,
|
||||
pageable: Pageable
|
||||
): Page<Gift>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
select g
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
package kr.co.vividnext.sodalive.v2.gift.application
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.Gift
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategory
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategoryRepository
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDeliveryRepository
|
||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftRepository
|
||||
import kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
data class AdminGiftListResult(
|
||||
val totalCount: Long,
|
||||
val items: List<AdminGiftListItemResult>,
|
||||
val page: Int,
|
||||
val size: Int,
|
||||
val hasNext: Boolean
|
||||
)
|
||||
|
||||
data class AdminGiftListItemResult(
|
||||
val applicationNo: String,
|
||||
val senderNickname: String,
|
||||
val recipientNickname: String,
|
||||
val sizeCode: String,
|
||||
val sizeName: String,
|
||||
val categoryName: String,
|
||||
val classificationNumber: String,
|
||||
val courierCompanyName: String?,
|
||||
val trackingNumber: String?,
|
||||
val status: GiftStatus,
|
||||
val statusName: String,
|
||||
val availableActions: List<String>
|
||||
)
|
||||
|
||||
data class AdminGiftDetailResult(
|
||||
val applicationNo: String,
|
||||
val senderInfo: AdminGiftMemberInfoResult,
|
||||
val recipientInfo: AdminGiftMemberInfoResult,
|
||||
val productInfo: AdminGiftProductInfoResult,
|
||||
val inboundDeliveryInfo: AdminGiftInboundDeliveryInfoResult,
|
||||
val status: GiftStatus,
|
||||
val statusName: String,
|
||||
val availableActions: List<String>
|
||||
)
|
||||
|
||||
data class AdminGiftMemberInfoResult(
|
||||
val nickname: String,
|
||||
val name: String,
|
||||
val phoneNumber: String,
|
||||
val address: String
|
||||
)
|
||||
|
||||
data class AdminGiftProductInfoResult(
|
||||
val sizeCode: String,
|
||||
val sizeName: String,
|
||||
val categoryName: String,
|
||||
val classificationNumber: String
|
||||
)
|
||||
|
||||
data class AdminGiftInboundDeliveryInfoResult(
|
||||
val courierCompanyName: String?,
|
||||
val trackingNumber: String?
|
||||
)
|
||||
|
||||
@Service
|
||||
class GiftAdminQueryService(
|
||||
private val giftRepository: GiftRepository,
|
||||
private val deliveryRepository: GiftDeliveryRepository,
|
||||
private val categoryRepository: GiftCategoryRepository,
|
||||
private val memberRepository: MemberRepository
|
||||
) {
|
||||
@Transactional(readOnly = true)
|
||||
fun getGifts(
|
||||
status: GiftStatus?,
|
||||
applicationNo: String?,
|
||||
nickname: String?,
|
||||
page: Int,
|
||||
size: Int
|
||||
): AdminGiftListResult {
|
||||
val normalizedPage = page.coerceAtLeast(0)
|
||||
val normalizedSize = if (size <= 0) DEFAULT_PAGE_SIZE else size
|
||||
val gifts = giftRepository.findAdminGifts(
|
||||
status = status,
|
||||
applicationNo = applicationNo?.takeIf { it.isNotBlank() },
|
||||
nickname = nickname?.takeIf { it.isNotBlank() },
|
||||
pageable = PageRequest.of(normalizedPage, normalizedSize, Sort.by(Sort.Direction.DESC, "createdAt", "id"))
|
||||
)
|
||||
val context = buildContext(gifts.content)
|
||||
|
||||
return AdminGiftListResult(
|
||||
totalCount = gifts.totalElements,
|
||||
items = gifts.content.map { gift -> gift.toListItem(context) },
|
||||
page = normalizedPage,
|
||||
size = normalizedSize,
|
||||
hasNext = gifts.hasNext()
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun getGiftDetail(applicationNo: String): AdminGiftDetailResult {
|
||||
val gift = giftRepository.findByApplicationNo(applicationNo) ?: throw invalidRequest()
|
||||
val context = buildContext(listOf(gift))
|
||||
val delivery = context.delivery(gift)
|
||||
val sender = context.sender(gift)
|
||||
val recipient = context.recipient(gift)
|
||||
val category = context.category(gift)
|
||||
|
||||
return AdminGiftDetailResult(
|
||||
applicationNo = gift.applicationNo,
|
||||
senderInfo = AdminGiftMemberInfoResult(
|
||||
nickname = sender.nickname,
|
||||
name = delivery.senderName,
|
||||
phoneNumber = delivery.senderPhoneNumber,
|
||||
address = formatAddress(delivery.senderZipCode, delivery.senderAddress, delivery.senderAddressDetail)
|
||||
),
|
||||
recipientInfo = AdminGiftMemberInfoResult(
|
||||
nickname = recipient.nickname,
|
||||
name = delivery.recipientName.orEmpty(),
|
||||
phoneNumber = delivery.recipientPhoneNumber.orEmpty(),
|
||||
address = delivery.recipientAddressResult()
|
||||
),
|
||||
productInfo = AdminGiftProductInfoResult(
|
||||
sizeCode = gift.sizeCode.name,
|
||||
sizeName = gift.sizeCode.displayName,
|
||||
categoryName = gift.categoryNameSnapshot,
|
||||
classificationNumber = category.classificationNumber
|
||||
),
|
||||
inboundDeliveryInfo = AdminGiftInboundDeliveryInfoResult(
|
||||
courierCompanyName = delivery.courierCompanyName,
|
||||
trackingNumber = delivery.trackingNumber
|
||||
),
|
||||
status = gift.status,
|
||||
statusName = gift.status.displayName,
|
||||
availableActions = gift.status.availableActions()
|
||||
)
|
||||
}
|
||||
|
||||
private fun Gift.toListItem(context: GiftAdminQueryContext) = AdminGiftListItemResult(
|
||||
applicationNo = applicationNo,
|
||||
senderNickname = context.sender(this).nickname,
|
||||
recipientNickname = context.recipient(this).nickname,
|
||||
sizeCode = sizeCode.name,
|
||||
sizeName = sizeCode.displayName,
|
||||
categoryName = categoryNameSnapshot,
|
||||
classificationNumber = context.category(this).classificationNumber,
|
||||
courierCompanyName = context.delivery(this).courierCompanyName,
|
||||
trackingNumber = context.delivery(this).trackingNumber,
|
||||
status = status,
|
||||
statusName = status.displayName,
|
||||
availableActions = status.availableActions()
|
||||
)
|
||||
|
||||
private fun GiftStatus.availableActions() = when (this) {
|
||||
GiftStatus.TRACKING_REGISTERED -> listOf("ARRIVE_MAILBOX", "MARK_UNDELIVERABLE")
|
||||
GiftStatus.ARRIVED_AT_MAILBOX -> listOf("COMPLETE_INSPECTION", "MARK_UNDELIVERABLE")
|
||||
GiftStatus.INSPECTION_COMPLETED -> listOf("COMPLETE_DELIVERY", "MARK_UNDELIVERABLE")
|
||||
GiftStatus.RECEIVED, GiftStatus.DELIVERED, GiftStatus.UNDELIVERABLE, GiftStatus.CANCELED -> emptyList()
|
||||
}
|
||||
|
||||
private fun GiftDelivery.recipientAddressResult(): String {
|
||||
val zipCode = recipientZipCode ?: return ""
|
||||
val address = recipientAddress ?: return ""
|
||||
return formatAddress(zipCode, address, recipientAddressDetail)
|
||||
}
|
||||
|
||||
private fun formatAddress(zipCode: String, address: String, detail: String?) =
|
||||
"($zipCode) $address" + if (detail.isNullOrBlank()) "" else ", $detail"
|
||||
|
||||
private fun invalidRequest() = SodaException(messageKey = "common.error.invalid_request")
|
||||
|
||||
private fun buildContext(gifts: List<Gift>): GiftAdminQueryContext {
|
||||
val giftIds = gifts.mapNotNull { it.id }
|
||||
val memberIds = gifts.flatMap { listOf(it.senderMemberId, it.recipientMemberId) }.toSet()
|
||||
val categoryIds = gifts.map { it.categoryId }.toSet()
|
||||
return GiftAdminQueryContext(
|
||||
deliveriesByGiftId = deliveryRepository.findByGiftIdIn(giftIds).associateBy { it.giftId },
|
||||
membersById = memberRepository.findAllById(memberIds).associateBy { it.id ?: throw invalidRequest() },
|
||||
categoriesById = categoryRepository.findAllById(categoryIds).associateBy { it.id ?: throw invalidRequest() }
|
||||
)
|
||||
}
|
||||
|
||||
private inner class GiftAdminQueryContext(
|
||||
private val deliveriesByGiftId: Map<Long, GiftDelivery>,
|
||||
private val membersById: Map<Long, Member>,
|
||||
private val categoriesById: Map<Long, GiftCategory>
|
||||
) {
|
||||
fun delivery(gift: Gift) = deliveriesByGiftId[gift.id] ?: throw invalidRequest()
|
||||
|
||||
fun sender(gift: Gift) = membersById[gift.senderMemberId] ?: throw invalidRequest()
|
||||
|
||||
fun recipient(gift: Gift) = membersById[gift.recipientMemberId] ?: throw invalidRequest()
|
||||
|
||||
fun category(gift: Gift) = categoriesById[gift.categoryId] ?: throw invalidRequest()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DEFAULT_PAGE_SIZE = 20
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user