From 38c50a4f8a42078e22910cfc942f7532cd696b29 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 11 Oct 2025 02:31:15 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix(admin/charge):=20=ED=86=B5=ED=99=94?= =?UTF-8?q?=EB=B3=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EC=99=80=20=ED=95=A9?= =?UTF-8?q?=EA=B3=84=20=ED=96=89=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=ED=95=A9=EA=B3=84=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 로직은 통화 구분 없이 전체 합계를 계산·표시하여 통화가 혼재된 데이터에서 오해의 소지가 있었음. - 관리 화면 요구사항: 통화(currency)별 합계를 명확히 제공. --- .../AdminChargeStatusQueryRepository.kt | 6 ++-- .../admin/charge/AdminChargeStatusService.kt | 33 +++++++++++-------- .../admin/charge/GetChargeStatusQueryDto.kt | 3 +- .../admin/charge/GetChargeStatusResponse.kt | 3 +- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt index f1ec63c..6c9519d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt @@ -26,6 +26,7 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory ), "%Y-%m-%d" ) + val currency = Expressions.stringTemplate("RIGHT({0}, 3)", payment.locale) return queryFactory .select( @@ -33,7 +34,8 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory formattedDate, payment.price.sum(), payment.id.count(), - payment.paymentGateway + payment.paymentGateway, + currency ) ) .from(payment) @@ -45,7 +47,7 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory .and(charge.status.eq(ChargeStatus.CHARGE)) .and(payment.status.eq(PaymentStatus.COMPLETE)) ) - .groupBy(formattedDate, payment.paymentGateway) + .groupBy(formattedDate, payment.paymentGateway, currency) .orderBy(formattedDate.desc()) .fetch() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt index f1b5e7e..67f4891 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt @@ -20,35 +20,40 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() - var totalChargeAmount = 0.toBigDecimal() - var totalChargeCount = 0L + val totalsByCurrency = mutableMapOf>() val chargeStatusList = repository.getChargeStatus(startDate, endDate) .map { val chargeAmount = it.amount val chargeCount = it.chargeCount + val currency = it.currency - totalChargeAmount += chargeAmount - totalChargeCount += chargeCount + val prev = totalsByCurrency[currency] ?: (0.toBigDecimal() to 0L) + totalsByCurrency[currency] = (prev.first + chargeAmount) to (prev.second + chargeCount) GetChargeStatusResponse( date = it.date, chargeAmount = chargeAmount, chargeCount = chargeCount, - pg = it.paymentGateWay.name + pg = it.paymentGateWay.name, + currency = currency ) } .toMutableList() - chargeStatusList.add( - 0, - GetChargeStatusResponse( - date = "합계", - chargeAmount = totalChargeAmount, - chargeCount = totalChargeCount, - pg = "" - ) - ) + val summaryRows = totalsByCurrency.entries + .sortedBy { it.key } + .map { (currency, total) -> + GetChargeStatusResponse( + date = "합계", + chargeAmount = total.first, + chargeCount = total.second, + pg = "", + currency = currency + ) + } + + chargeStatusList.addAll(0, summaryRows) return chargeStatusList.toList() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt index 22bdd4c..c173892 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt @@ -8,5 +8,6 @@ data class GetChargeStatusQueryDto @QueryProjection constructor( val date: String, val amount: BigDecimal, val chargeCount: Long, - val paymentGateWay: PaymentGateway + val paymentGateWay: PaymentGateway, + val currency: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt index 75dffcb..3d914a9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt @@ -6,5 +6,6 @@ data class GetChargeStatusResponse( val date: String, val chargeAmount: BigDecimal, val chargeCount: Long, - val pg: String + val pg: String, + val currency: String ) From 778f0c3ba2a80eaab5efc67382ddd08d430a699e Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 11 Oct 2025 03:12:10 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix(admin/charge):=20=ED=86=B5=ED=99=94?= =?UTF-8?q?=EB=B3=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EC=99=80=20=ED=95=A9?= =?UTF-8?q?=EA=B3=84=20=ED=96=89=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=ED=95=A9=EA=B3=84=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 로직은 통화 구분 없이 전체 합계를 계산·표시하여 통화가 혼재된 데이터에서 오해의 소지가 있었음. - 관리 화면 요구사항: 통화(currency)별 합계를 명확히 제공. --- .../charge/AdminChargeStatusController.kt | 5 ++-- .../AdminChargeStatusQueryRepository.kt | 25 ++++++++++++------- .../admin/charge/AdminChargeStatusService.kt | 5 ++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusController.kt index be859be..7047d4f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusController.kt @@ -21,6 +21,7 @@ class AdminChargeStatusController(private val service: AdminChargeStatusService) @GetMapping("/detail") fun getChargeStatusDetail( @RequestParam startDateStr: String, - @RequestParam paymentGateway: PaymentGateway - ) = ApiResponse.ok(service.getChargeStatusDetail(startDateStr, paymentGateway)) + @RequestParam paymentGateway: PaymentGateway, + @RequestParam(value = "currency", required = false) currency: String? = null + ) = ApiResponse.ok(service.getChargeStatusDetail(startDateStr, paymentGateway, currency)) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt index 6c9519d..2e94f84 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.admin.charge +import com.querydsl.core.BooleanBuilder import com.querydsl.core.types.dsl.Expressions import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.can.QCan.can1 @@ -55,7 +56,8 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory fun getChargeStatusDetail( startDate: LocalDateTime, endDate: LocalDateTime, - paymentGateway: PaymentGateway + paymentGateway: PaymentGateway, + currency: String? = null ): List { val formattedDate = Expressions.stringTemplate( "DATE_FORMAT({0}, {1})", @@ -68,6 +70,17 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory ), "%Y-%m-%d %H:%i:%s" ) + val currencyExpr = Expressions.stringTemplate("RIGHT({0}, 3)", payment.locale) + val whereBuilder = BooleanBuilder() + whereBuilder.and(charge.createdAt.goe(startDate)) + .and(charge.createdAt.loe(endDate)) + .and(charge.status.eq(ChargeStatus.CHARGE)) + .and(payment.status.eq(PaymentStatus.COMPLETE)) + .and(payment.paymentGateway.eq(paymentGateway)) + + if (currency != null) { + whereBuilder.and(currencyExpr.eq(currency)) + } return queryFactory .select( @@ -76,7 +89,7 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory member.nickname, payment.method.coalesce(""), payment.price, - payment.locale.coalesce(""), + currencyExpr.coalesce(""), formattedDate ) ) @@ -84,13 +97,7 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory .innerJoin(charge.member, member) .innerJoin(charge.payment, payment) .leftJoin(charge.can, can1) - .where( - charge.createdAt.goe(startDate) - .and(charge.createdAt.loe(endDate)) - .and(charge.status.eq(ChargeStatus.CHARGE)) - .and(payment.status.eq(PaymentStatus.COMPLETE)) - .and(payment.paymentGateway.eq(paymentGateway)) - ) + .where(whereBuilder) .orderBy(formattedDate.desc()) .fetch() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt index 67f4891..0b9a0c2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt @@ -60,7 +60,8 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) fun getChargeStatusDetail( startDateStr: String, - paymentGateway: PaymentGateway + paymentGateway: PaymentGateway, + currency: String? = null ): List { val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val startDate = LocalDate.parse(startDateStr, dateTimeFormatter).atTime(0, 0, 0) @@ -73,7 +74,7 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() - return repository.getChargeStatusDetail(startDate, endDate, paymentGateway) + return repository.getChargeStatusDetail(startDate, endDate, paymentGateway, currency) .map { GetChargeStatusDetailResponse( memberId = it.memberId, From 25d1d813f193fe1737e82dd59b660572bd1f8240 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 11 Oct 2025 03:37:46 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor(admin-charge):=20HQL=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20RIGHT=20=E2=86=92=20substring/length=20=EC=B9=98?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/charge/AdminChargeStatusQueryRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt index 2e94f84..e3809e9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusQueryRepository.kt @@ -27,7 +27,7 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory ), "%Y-%m-%d" ) - val currency = Expressions.stringTemplate("RIGHT({0}, 3)", payment.locale) + val currency = Expressions.stringTemplate("substring({0}, length({0}) - 2, 3)", payment.locale) return queryFactory .select( @@ -70,7 +70,7 @@ class AdminChargeStatusQueryRepository(private val queryFactory: JPAQueryFactory ), "%Y-%m-%d %H:%i:%s" ) - val currencyExpr = Expressions.stringTemplate("RIGHT({0}, 3)", payment.locale) + val currencyExpr = Expressions.stringTemplate("substring({0}, length({0}) - 2, 3)", payment.locale) val whereBuilder = BooleanBuilder() whereBuilder.and(charge.createdAt.goe(startDate)) .and(charge.createdAt.loe(endDate)) From 976eeaa44377efe7aa0f45084a324ae453586da2 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 11 Oct 2025 03:54:47 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor(admin-charge):=20GetChargeStatusDe?= =?UTF-8?q?tailResponse=EC=9D=98=20amount=20=ED=83=80=EC=9E=85=EC=9D=84=20?= =?UTF-8?q?Int=EC=97=90=EC=84=9C=20BigDecimal=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 충전 금액 계산을 좀 더 명확하게 하기 위해서 변경 --- .../sodalive/admin/charge/AdminChargeStatusService.kt | 2 +- .../sodalive/admin/charge/GetChargeStatusDetailResponse.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt index 0b9a0c2..ceead40 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt @@ -80,7 +80,7 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) memberId = it.memberId, nickname = it.nickname, method = it.method, - amount = it.amount.toInt(), + amount = it.amount, locale = it.locale, datetime = it.datetime ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailResponse.kt index 6e03036..8a6baee 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailResponse.kt @@ -1,10 +1,12 @@ package kr.co.vividnext.sodalive.admin.charge +import java.math.BigDecimal + data class GetChargeStatusDetailResponse( val memberId: Long, val nickname: String, val method: String, - val amount: Int, + val amount: BigDecimal, val locale: String, val datetime: String )