Skip to content

Commit

Permalink
Merge pull request #23 from this-is-spear/5-전화번호를-입력해-쿠폰을-나눠줄-수-있게-하고…
Browse files Browse the repository at this point in the history
…-싶음-3

�선물 받은 쿠폰 코드 등록 유효기간 정책을 설정한다.
  • Loading branch information
this-is-spear authored May 23, 2024
2 parents b9e3fce + 695ee05 commit 4dc9bc8
Show file tree
Hide file tree
Showing 18 changed files with 167 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.example.estdelivery.coupon.application

import com.example.estdelivery.coupon.application.port.`in`.FindAvailableGiftCouponUseCase
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftCouponResponse
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftCouponResponses
import com.example.estdelivery.coupon.application.port.out.LoadMemberStatePort
import com.example.estdelivery.coupon.domain.coupon.Coupon
import com.example.estdelivery.coupon.domain.coupon.GiftCoupon
import com.example.estdelivery.coupon.domain.coupon.Coupon.FixDiscountCoupon
import com.example.estdelivery.coupon.domain.coupon.Coupon.RateDiscountCoupon
import com.example.estdelivery.coupon.domain.member.Member

class FindAvailableGiftCouponService(
Expand All @@ -16,12 +19,22 @@ class FindAvailableGiftCouponService(
* 3. 선물 가능한 쿠폰을 고른다.
* 4. 선물 가능한 쿠폰을 반환한다.
*/
override fun findAvailableGiftCoupon(memberId: Long): List<GiftCoupon> {
override fun findAvailableGiftCoupon(memberId: Long): GiftCouponResponses {
val member = findMember(memberId)
val myCouponBook = member.showMyCouponBook()
return myCouponBook
.filter { isGiftAvailable(it) }
.map { GiftCoupon(it) }
.map {
GiftCouponResponse(
id = it.id!!,
name = it.name,
discountAmount = if (it is FixDiscountCoupon) it.discountAmount
else (it as RateDiscountCoupon).discountRate,
discountType = it.couponType
)
}.let {
GiftCouponResponses(it)
}
}

private fun isGiftAvailable(coupon: Coupon): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.estdelivery.coupon.application

import com.example.estdelivery.coupon.application.port.`in`.GiftCouponByMessageUseCase
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftMessageResponse
import com.example.estdelivery.coupon.application.port.out.CreateGiftCouponMessageStatePort
import com.example.estdelivery.coupon.application.port.out.LoadMemberStatePort
import com.example.estdelivery.coupon.application.port.out.UpdateMemberStatePort
Expand All @@ -10,6 +11,7 @@ import com.example.estdelivery.coupon.domain.coupon.Coupon
import com.example.estdelivery.coupon.domain.coupon.GiftCouponCode
import com.example.estdelivery.coupon.domain.coupon.GiftMessage
import com.example.estdelivery.coupon.domain.member.Member
import java.net.URL

class GiftCouponByMessageService(
loadMemberStatePort: LoadMemberStatePort,
Expand Down Expand Up @@ -37,7 +39,7 @@ class GiftCouponByMessageService(
* @param couponId 선물할 쿠폰 식별자
* @param giftMessage 선물 메시지
*/
override fun sendGiftAvailableCoupon(memberId: Long, couponId: Long, giftMessage: String): GiftMessage {
override fun sendGiftAvailableCoupon(memberId: Long, couponId: Long, giftMessage: String): GiftMessageResponse {
return transactionArea.run {
val sender = findMember(memberId)
val coupon = sender.showMyCouponBook().find { it.id == couponId }
Expand All @@ -47,7 +49,14 @@ class GiftCouponByMessageService(
val couponCode: GiftCouponCode = getGiftCouponCode()
sender.useCoupon(coupon)
updateMembersCoupon(sender)
createGiftMessage(sender, coupon, giftMessage, couponCode)
createGiftMessage(sender, coupon, giftMessage, couponCode).let {
GiftMessageResponse(
senderName = it.sender.name,
description = it.giftMessage,
enrollEndDate = it.giftCoupon.enrollEndDate,
enrollHref = URL("http", "localhost", 8080, "/gift-coupons/enroll/${it.giftCouponCode.code}")
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.example.estdelivery.coupon.application.port.`in`

import com.example.estdelivery.coupon.domain.coupon.GiftCoupon
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftCouponResponses

interface FindAvailableGiftCouponUseCase {
/**
* 회원은 가게에 발행된 쿠폰 중 선물 가능한 쿠폰을 조회한다.
* @param memberId
*/
fun findAvailableGiftCoupon(memberId: Long): List<GiftCoupon>
fun findAvailableGiftCoupon(memberId: Long): GiftCouponResponses
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.example.estdelivery.coupon.application.port.`in`

import com.example.estdelivery.coupon.domain.coupon.GiftMessage
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftMessageResponse

interface GiftCouponByMessageUseCase {
fun sendGiftAvailableCoupon(memberId: Long, couponId: Long, giftMessage: String): GiftMessage
fun sendGiftAvailableCoupon(memberId: Long, couponId: Long, giftMessage: String): GiftMessageResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ package com.example.estdelivery.coupon.application.port.`in`.web
import com.example.estdelivery.coupon.application.port.`in`.EnrollCouponByMessageUseCase
import com.example.estdelivery.coupon.application.port.`in`.FindAvailableGiftCouponUseCase
import com.example.estdelivery.coupon.application.port.`in`.GiftCouponByMessageUseCase
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftCouponResponse
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftCouponResponses
import com.example.estdelivery.coupon.application.port.`in`.web.dto.GiftMessageResponse
import com.example.estdelivery.coupon.domain.coupon.Coupon.FixDiscountCoupon
import com.example.estdelivery.coupon.domain.coupon.Coupon.RateDiscountCoupon
import com.example.estdelivery.coupon.domain.coupon.GiftCouponCode
import java.net.URL
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
Expand All @@ -30,35 +26,15 @@ class GiftCouponMessageController(
@GetMapping
fun findAvailableGiftCoupons(@RequestHeader(value = MEMBER_ID) memberId: Long): GiftCouponResponses =
findAvailableGiftCouponUseCase.findAvailableGiftCoupon(memberId)
.map {
GiftCouponResponse(
id = it.coupon.id!!,
name = it.coupon.name,
discountAmount = if (it.coupon is FixDiscountCoupon) it.coupon.discountAmount
else (it.coupon as RateDiscountCoupon).discountRate,
discountType = it.coupon.couponType
)
}.let {
GiftCouponResponses(it)
}


@PostMapping("/send/{couponId}")
fun sendGiftAvailableCoupon(
@RequestHeader(value = MEMBER_ID) memberId: Long,
@RequestParam message: String,
@PathVariable couponId: Long,
): GiftMessageResponse =
giftCouponByMessageUseCase.sendGiftAvailableCoupon(
memberId,
couponId,
message,
).let {
GiftMessageResponse(
senderName = it.sender.name,
description = it.giftMessage,
enrollHref = URL("http", "localhost", 8080, "/gift-coupons/enroll/${it.giftCouponCode.code}")
)
}
giftCouponByMessageUseCase.sendGiftAvailableCoupon(memberId, couponId, message)

@GetMapping("/enroll/{code}")
fun enrollGiftCoupon(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.example.estdelivery.coupon.application.port.`in`.web.dto

import java.net.URL
import java.time.LocalDate

data class GiftMessageResponse(
val senderName: String,
val description: String,
val enrollEndDate: LocalDate,
val enrollHref: URL,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,44 @@ import com.example.estdelivery.coupon.application.port.out.ValidateGiftCouponCod
import com.example.estdelivery.coupon.application.port.out.adapter.persistence.entity.GiftMessageEntity
import com.example.estdelivery.coupon.application.port.out.adapter.persistence.mapper.fromCoupon
import com.example.estdelivery.coupon.application.port.out.adapter.persistence.mapper.toCoupon
import com.example.estdelivery.coupon.application.port.out.adapter.persistence.repository.EnrollDateRepository
import com.example.estdelivery.coupon.application.port.out.adapter.persistence.repository.GiftMessageRepository
import com.example.estdelivery.coupon.domain.coupon.Coupon
import com.example.estdelivery.coupon.domain.coupon.GiftCoupon
import com.example.estdelivery.coupon.domain.coupon.GiftCouponCode
import com.example.estdelivery.coupon.domain.coupon.GiftMessage
import com.example.estdelivery.coupon.domain.member.Member
import jakarta.transaction.Transactional
import java.time.LocalDate
import org.springframework.stereotype.Component

@Component
class GiftCouponMessageAdapter(
private val giftMessageRepository: GiftMessageRepository,
private val enrollDateRepository: EnrollDateRepository,
) : CreateGiftCouponMessageStatePort, ValidateGiftCouponCodeStatePort, UseGiftCouponCodeStatePort,
LoadGiftCouponStatePort {
override fun create(sender: Member, coupon: Coupon, message: String, giftCouponCode: GiftCouponCode): GiftMessage {
val enrollTerm = enrollDateRepository.findLatestPolicy()
?: throw IllegalStateException("EnrollTerm not found")

val enrollDate = LocalDate.now()

val giftMessageEntity = giftMessageRepository.save(
GiftMessageEntity(
sender = sender.id,
coupon = fromCoupon(coupon),
message = message,
enrollDate = enrollDate,
enrollEndDate = enrollDate.plus(enrollTerm.term, enrollTerm.unit),
enrollCode = giftCouponCode.code
)
)
return GiftMessage(
sender = sender,
giftMessage = giftMessageEntity.message,
giftCouponCode = GiftCouponCode(giftMessageEntity.enrollCode),
giftCoupon = GiftCoupon(coupon)
giftCoupon = GiftCoupon(coupon, giftMessageEntity.enrollEndDate, giftMessageEntity.isUsed)
)
}

Expand All @@ -51,7 +61,7 @@ class GiftCouponMessageAdapter(

override fun findGiftCoupon(giftCouponCode: GiftCouponCode): GiftCoupon {
return giftMessageRepository.findByEnrollCode(giftCouponCode.code)
?.let { GiftCoupon(toCoupon(it.coupon), it.isUsed) }
?.let { GiftCoupon(toCoupon(it.coupon), it.enrollEndDate, it.isUsed) }
?: throw IllegalArgumentException("GiftCouponCode not found")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.estdelivery.coupon.application.port.out.adapter.persistence.entity

import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit

@Entity
@Table(name = "enroll_term")
class EnrollTermEntity(
@Column(updatable = false)
val term: Long,
@Enumerated(value = EnumType.STRING)
val unit: ChronoUnit,
@Column(updatable = false)
val createDate: LocalDateTime = LocalDateTime.now(),
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class GiftMessageEntity(
val coupon: CouponEntity,
@Column(nullable = false)
val enrollDate: LocalDate = LocalDate.now(),
@Column(nullable = false)
val enrollEndDate: LocalDate,
var isUsed: Boolean = false,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.estdelivery.coupon.application.port.out.adapter.persistence.repository

import com.example.estdelivery.coupon.application.port.out.adapter.persistence.entity.EnrollTermEntity
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository

interface EnrollDateJpaRepository : JpaRepository<EnrollTermEntity, Long> {
@Query("SELECT e FROM EnrollTermEntity e WHERE e.id = (SELECT MAX(e2.id) FROM EnrollTermEntity e2)")
fun findLatestPolicy(): EnrollTermEntity?
}

@Repository
class EnrollDateRepository(
private val repository: EnrollDateJpaRepository
) {
fun findLatestPolicy(): EnrollTermEntity? {
return repository.findLatestPolicy()
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.example.estdelivery.coupon.domain.coupon

import java.time.LocalDate

/**
* 선물하는 쿠폰을 선정할 때 여러 제약이 생길 수 있다.
* 제약 변경에 대응하기 위해 컴포지션을 활용한다.
*/
data class GiftCoupon(
val coupon: Coupon,
val enrollEndDate: LocalDate,
val isUsed: Boolean = false,
) {
init {
require(!coupon.isPublished()) { "발행한 쿠폰은 선물할 수 없습니다." }
require(enrollEndDate.isAfter(LocalDate.now())) { "유효기간이 지난 쿠폰은 선물할 수 없습니다." }
require(!isUsed) { "이미 사용된 쿠폰은 선물할 수 없습니다." }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import com.example.estdelivery.coupon.domain.coupon.GiftCouponCode
import com.example.estdelivery.coupon.domain.fixture.나눠준_비율_할인_쿠폰
import com.example.estdelivery.coupon.domain.fixture.일건창
import com.example.estdelivery.coupon.domain.member.Member
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.collections.shouldContain
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import java.time.LocalDate

class EnrollCouponByMessageServiceTest : FreeSpec({
val loadMemberStatePort = mockk<LoadMemberStatePort>()
Expand All @@ -40,7 +40,7 @@ class EnrollCouponByMessageServiceTest : FreeSpec({
val member = 일건창()
val giftCouponCode = GiftCouponCode.create()
val coupon = 나눠준_비율_할인_쿠폰
val giftCoupon = GiftCoupon(coupon)
val giftCoupon = GiftCoupon(coupon, LocalDate.now().plusDays(1))
val updatedMember = slot<Member>()

// when
Expand All @@ -54,21 +54,4 @@ class EnrollCouponByMessageServiceTest : FreeSpec({
// then
updatedMember.captured.showMyCouponBook() shouldContain coupon
}

"메시지로 받은 쿠폰 코드는 사용된적이 없으면 예외가 발생한다." {
// given
val member = 일건창()
val giftCouponCode = GiftCouponCode.create()
val coupon = 나눠준_비율_할인_쿠폰
val giftCoupon = GiftCoupon(coupon, true)

// when
every { loadMemberStatePort.findMember(member.id) } returns member
every { loadGiftCouponStatePort.findGiftCoupon(giftCouponCode) } returns giftCoupon

// then
shouldThrow<IllegalArgumentException> {
enrollCouponByMessageService.enroll(member.id, giftCouponCode)
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ class FindAvailableGiftCouponServiceTest : FreeSpec({
val availableGiftCoupons = findAvailableGiftCouponService.findAvailableGiftCoupon(memberId)

// then
availableGiftCoupons.map { it.coupon } shouldBe 일건창.showMyCouponBook().filter { !it.isPublished() }
availableGiftCoupons.coupons.map { it.id } shouldBe 일건창.showMyCouponBook().filter { !it.isPublished() }.map { it.id }
}
})
Loading

0 comments on commit 4dc9bc8

Please sign in to comment.