From d6db862c9d6259ef3894c8954d27f9ec3e043283 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 19 May 2025 21:38:24 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20-=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=EC=9D=98=20=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=B4?= =?UTF-8?q?=EC=83=81=EB=82=B4=EC=97=AD,=20=EC=82=AC=EC=9A=A9=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../point/GetPointRewardStatusResponse.kt | 9 ++++ .../sodalive/point/GetPointStatusResponse.kt | 3 ++ .../point/GetPointUseStatusResponse.kt | 9 ++++ .../sodalive/point/PointController.kt | 49 +++++++++++++++++++ .../sodalive/point/PointGrantLogRepository.kt | 30 ++++++++++++ .../vividnext/sodalive/point/PointService.kt | 29 +++++++++++ .../sodalive/point/UsePointRepository.kt | 44 ++++++++++++++++- 7 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointRewardStatusResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointStatusResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointUseStatusResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/point/PointController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/point/PointService.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointRewardStatusResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointRewardStatusResponse.kt new file mode 100644 index 0000000..050f7a7 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointRewardStatusResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.point + +import com.querydsl.core.annotations.QueryProjection + +data class GetPointRewardStatusResponse @QueryProjection constructor( + val rewardPoint: String, + val date: String, + val method: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointStatusResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointStatusResponse.kt new file mode 100644 index 0000000..3f60f3c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointStatusResponse.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.point + +data class GetPointStatusResponse(val point: Int) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointUseStatusResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointUseStatusResponse.kt new file mode 100644 index 0000000..bffec5f --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/GetPointUseStatusResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.point + +import com.querydsl.core.annotations.QueryProjection + +data class GetPointUseStatusResponse @QueryProjection constructor( + val title: String, + val date: String, + val point: Int +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/PointController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/PointController.kt new file mode 100644 index 0000000..04d20c0 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/PointController.kt @@ -0,0 +1,49 @@ +package kr.co.vividnext.sodalive.point + +import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.Member +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/point") +class PointController(private val service: PointService) { + @GetMapping("/status") + fun getPointStatus( + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) { + throw SodaException("로그인 정보를 확인해주세요.") + } + + ApiResponse.ok(service.getPointStatus(member)) + } + + @GetMapping("/status/use") + fun getPointUseStatus( + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + @RequestParam("timezone") timezone: String + ) = run { + if (member == null) { + throw SodaException("로그인 정보를 확인해주세요.") + } + + ApiResponse.ok(service.getPointUseStatus(member, timezone)) + } + + @GetMapping("/status/reward") + fun getPointRewardStatus( + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + @RequestParam("timezone") timezone: String + ) = run { + if (member == null) { + throw SodaException("로그인 정보를 확인해주세요.") + } + + ApiResponse.ok(service.getPointRewardStatus(member, timezone)) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/PointGrantLogRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/PointGrantLogRepository.kt index 52a57c4..3ec8c3a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/point/PointGrantLogRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/PointGrantLogRepository.kt @@ -1,7 +1,9 @@ package kr.co.vividnext.sodalive.point +import com.querydsl.core.types.dsl.Expressions import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.point.QPointGrantLog.pointGrantLog +import kr.co.vividnext.sodalive.point.QPointRewardPolicy.pointRewardPolicy import org.springframework.data.jpa.repository.JpaRepository import java.time.LocalDateTime @@ -14,6 +16,8 @@ interface PointGrantLogQueryRepository { startDate: LocalDateTime, orderId: Long? = null ): Int + + fun getPointRewardStatusByMemberId(memberId: Long, timezone: String): List } class PointGrantLogQueryRepositoryImpl( @@ -40,4 +44,30 @@ class PointGrantLogQueryRepositoryImpl( .fetch() .size } + + override fun getPointRewardStatusByMemberId(memberId: Long, timezone: String): List { + val formattedDate = Expressions.stringTemplate( + "DATE_FORMAT({0}, {1})", + Expressions.dateTimeTemplate( + LocalDateTime::class.java, + "CONVERT_TZ({0},{1},{2})", + pointGrantLog.createdAt, + "UTC", + timezone + ), + "%Y.%m.%d | %H:%i:%s" + ) + + return queryFactory + .select( + QGetPointRewardStatusResponse( + pointGrantLog.point.stringValue().concat(" 포인트"), + formattedDate, + pointRewardPolicy.title + ) + ) + .from(pointGrantLog) + .innerJoin(pointRewardPolicy).on(pointGrantLog.policyId.eq(pointRewardPolicy.id)) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/PointService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/PointService.kt new file mode 100644 index 0000000..92ec123 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/PointService.kt @@ -0,0 +1,29 @@ +package kr.co.vividnext.sodalive.point + +import kr.co.vividnext.sodalive.member.Member +import org.springframework.stereotype.Service +import java.time.LocalDateTime + +@Service +class PointService( + private val pointGrantLogRepository: PointGrantLogRepository, + private val memberPointRepository: MemberPointRepository, + private val usePointRepository: UsePointRepository +) { + fun getPointStatus(member: Member): GetPointStatusResponse { + return GetPointStatusResponse( + point = memberPointRepository.findByMemberIdAndExpiresAtAfterOrderByExpiresAtAsc( + memberId = member.id!!, + expiresAt = LocalDateTime.now() + ).sumOf { it.point } + ) + } + + fun getPointUseStatus(member: Member, timezone: String): List { + return usePointRepository.getPointUseStatusByMemberId(member.id!!, timezone) + } + + fun getPointRewardStatus(member: Member, timezone: String): List { + return pointGrantLogRepository.getPointRewardStatusByMemberId(member.id!!, timezone) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/point/UsePointRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/point/UsePointRepository.kt index 49a5fcc..076b4d4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/point/UsePointRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/point/UsePointRepository.kt @@ -1,5 +1,47 @@ package kr.co.vividnext.sodalive.point +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent +import kr.co.vividnext.sodalive.content.order.QOrder.order +import kr.co.vividnext.sodalive.point.QUsePoint.usePoint import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDateTime -interface UsePointRepository : JpaRepository +interface UsePointRepository : JpaRepository, UsePointQueryRepository + +interface UsePointQueryRepository { + fun getPointUseStatusByMemberId(memberId: Long, timezone: String): List +} + +class UsePointQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : UsePointQueryRepository { + override fun getPointUseStatusByMemberId(memberId: Long, timezone: String): List { + val formattedDate = Expressions.stringTemplate( + "DATE_FORMAT({0}, {1})", + Expressions.dateTimeTemplate( + LocalDateTime::class.java, + "CONVERT_TZ({0},{1},{2})", + usePoint.createdAt, + "UTC", + timezone + ), + "%Y.%m.%d | %H:%i:%s" + ) + + return queryFactory + .select( + QGetPointUseStatusResponse( + audioContent.title.prepend("[콘텐츠 대여] "), + formattedDate, + usePoint.amount + ) + ) + .from(usePoint) + .innerJoin(order).on(usePoint.orderId.eq(order.id)) + .innerJoin(audioContent).on(order.audioContent.id.eq(audioContent.id)) + .where(usePoint.memberId.eq(memberId)) + .fetch() + } +}