From d94418067fbf37cc615c709264475be2a9f32aaa Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 21 Apr 2025 19:08:31 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=A7=80=EA=B8=89=20=EC=A0=95=EC=B1=85=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8,=20=EC=83=9D=EC=84=B1,=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../point/CreatePointRewardPolicyRequest.kt | 37 +++++++++++++ .../point/GetPointRewardPolicyResponse.kt | 15 +++++ .../point/ModifyPointRewardPolicyRequest.kt | 8 +++ .../admin/point/PointPolicyController.kt | 36 ++++++++++++ .../admin/point/PointPolicyRepository.kt | 55 +++++++++++++++++++ .../admin/point/PointPolicyService.kt | 49 +++++++++++++++++ 6 files changed, 200 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/point/CreatePointRewardPolicyRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/point/GetPointRewardPolicyResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/point/ModifyPointRewardPolicyRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyService.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/CreatePointRewardPolicyRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/CreatePointRewardPolicyRequest.kt new file mode 100644 index 0000000..a466a32 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/CreatePointRewardPolicyRequest.kt @@ -0,0 +1,37 @@ +package kr.co.vividnext.sodalive.admin.point + +import kr.co.vividnext.sodalive.point.PointRewardPolicy +import kr.co.vividnext.sodalive.useraction.ActionType +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +data class CreatePointRewardPolicyRequest( + val title: String, + val actionType: ActionType, + val threshold: Int, + val pointAmount: Int, + val startDate: String, + val endDate: String, + val isActive: Boolean +) { + fun toEntity(): PointRewardPolicy { + val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + + return PointRewardPolicy( + title = title, + actionType = actionType, + threshold = threshold, + pointAmount = pointAmount, + startDate = LocalDateTime.parse(startDate, dateTimeFormatter) + .atZone(ZoneId.of("Asia/Seoul")) + .withZoneSameInstant(ZoneId.of("UTC")) + .toLocalDateTime(), + endDate = LocalDateTime.parse(endDate, dateTimeFormatter).withSecond(59) + .atZone(ZoneId.of("Asia/Seoul")) + .withZoneSameInstant(ZoneId.of("UTC")) + .toLocalDateTime(), + isActive = isActive + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/GetPointRewardPolicyResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/GetPointRewardPolicyResponse.kt new file mode 100644 index 0000000..4e8203d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/GetPointRewardPolicyResponse.kt @@ -0,0 +1,15 @@ +package kr.co.vividnext.sodalive.admin.point + +import com.querydsl.core.annotations.QueryProjection +import kr.co.vividnext.sodalive.useraction.ActionType + +data class GetPointRewardPolicyResponse @QueryProjection constructor( + val id: Long, + val title: String, + val actionType: ActionType, + val threshold: Int, + val pointAmount: Int, + val startDate: String, + val endDate: String, + val isActive: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/ModifyPointRewardPolicyRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/ModifyPointRewardPolicyRequest.kt new file mode 100644 index 0000000..3a4bc11 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/ModifyPointRewardPolicyRequest.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.admin.point + +data class ModifyPointRewardPolicyRequest( + val title: String?, + val startDate: String?, + val endDate: String?, + val isActive: Boolean? +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyController.kt new file mode 100644 index 0000000..12d4dc9 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyController.kt @@ -0,0 +1,36 @@ +package kr.co.vividnext.sodalive.admin.point + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.data.domain.Pageable +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/admin/point-policies") +@PreAuthorize("hasRole('ADMIN')") +class PointPolicyController(private val service: PointPolicyService) { + @GetMapping + fun getAll(pageable: Pageable) = ApiResponse.ok( + service.getAll( + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) + + @PostMapping + fun create( + @RequestBody request: CreatePointRewardPolicyRequest + ) = ApiResponse.ok(service.create(request)) + + @PutMapping("/{id}") + fun update( + @PathVariable id: Long, + @RequestBody request: ModifyPointRewardPolicyRequest + ) = ApiResponse.ok(service.update(id, request)) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyRepository.kt new file mode 100644 index 0000000..3c644d5 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyRepository.kt @@ -0,0 +1,55 @@ +package kr.co.vividnext.sodalive.admin.point + +import com.querydsl.core.types.dsl.DateTimePath +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.core.types.dsl.StringTemplate +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.point.PointRewardPolicy +import kr.co.vividnext.sodalive.point.QPointRewardPolicy.pointRewardPolicy +import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDateTime + +interface PointPolicyRepository : JpaRepository, PointPolicyQueryRepository + +interface PointPolicyQueryRepository { + fun getAll(offset: Long, limit: Long): List +} + +class PointPolicyQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : PointPolicyQueryRepository { + override fun getAll(offset: Long, limit: Long): List { + return queryFactory + .select( + QGetPointRewardPolicyResponse( + pointRewardPolicy.id, + pointRewardPolicy.title, + pointRewardPolicy.actionType, + pointRewardPolicy.threshold, + pointRewardPolicy.pointAmount, + getFormattedDate(pointRewardPolicy.startDate), + getFormattedDate(pointRewardPolicy.endDate), + pointRewardPolicy.isActive + ) + ) + .from(pointRewardPolicy) + .orderBy(pointRewardPolicy.isActive.desc(), pointRewardPolicy.startDate.desc()) + .offset(offset) + .limit(limit) + .fetch() + } + + private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { + return Expressions.stringTemplate( + "DATE_FORMAT({0}, {1})", + Expressions.dateTimeTemplate( + LocalDateTime::class.java, + "CONVERT_TZ({0},{1},{2})", + dateTimePath, + "UTC", + "Asia/Seoul" + ), + "%Y-%m-%d %H:%i" + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyService.kt new file mode 100644 index 0000000..d5b8c54 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/point/PointPolicyService.kt @@ -0,0 +1,49 @@ +package kr.co.vividnext.sodalive.admin.point + +import kr.co.vividnext.sodalive.common.SodaException +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +@Service +class PointPolicyService(private val repository: PointPolicyRepository) { + fun getAll(offset: Long, limit: Long): List { + return repository.getAll(offset, limit) + } + + fun create(request: CreatePointRewardPolicyRequest) { + val pointPolicy = request.toEntity() + repository.save(pointPolicy) + } + + fun update(id: Long, request: ModifyPointRewardPolicyRequest) { + val pointPolicy = repository.findByIdOrNull(id) + ?: throw SodaException("잘못된 접근입니다.") + + val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + + if (request.title != null) { + pointPolicy.title = request.title + } + + if (request.startDate != null) { + pointPolicy.startDate = LocalDateTime.parse(request.startDate, dateTimeFormatter) + .atZone(ZoneId.of("Asia/Seoul")) + .withZoneSameInstant(ZoneId.of("UTC")) + .toLocalDateTime() + } + + if (request.endDate != null) { + pointPolicy.endDate = LocalDateTime.parse(request.endDate, dateTimeFormatter).withSecond(59) + .atZone(ZoneId.of("Asia/Seoul")) + .withZoneSameInstant(ZoneId.of("UTC")) + .toLocalDateTime() + } + + if (request.isActive != null) { + pointPolicy.isActive = request.isActive + } + } +}