parent
f304242eb4
commit
38cf9e453d
|
@ -44,6 +44,7 @@ dependencies {
|
|||
kapt("org.springframework.boot:spring-boot-configuration-processor")
|
||||
|
||||
// aws
|
||||
implementation("com.amazonaws:aws-java-sdk-sqs:1.12.380")
|
||||
implementation("com.amazonaws:aws-java-sdk-ses:1.12.380")
|
||||
implementation("com.amazonaws:aws-java-sdk-s3:1.12.380")
|
||||
implementation("com.amazonaws:aws-java-sdk-cloudfront:1.12.380")
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package kr.co.vividnext.sodalive.aws.sqs
|
||||
|
||||
import org.springframework.context.event.EventListener
|
||||
import org.springframework.scheduling.annotation.Async
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
enum class SqsEventType {
|
||||
GENERATE_COUPON
|
||||
}
|
||||
|
||||
class SqsEvent(
|
||||
val type: SqsEventType,
|
||||
val message: String
|
||||
)
|
||||
|
||||
@Component
|
||||
class SqsEventListener(private val sqsService: SqsService) {
|
||||
@Async
|
||||
@EventListener
|
||||
fun sendMessage(sqsEvent: SqsEvent) {
|
||||
when (sqsEvent.type) {
|
||||
SqsEventType.GENERATE_COUPON -> sqsService.sendGenerateCouponMessage(sqsEvent.message)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package kr.co.vividnext.sodalive.aws.sqs
|
||||
|
||||
import com.amazonaws.services.sqs.AmazonSQS
|
||||
import com.amazonaws.services.sqs.model.SendMessageRequest
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class SqsService(
|
||||
private val amazonSQS: AmazonSQS,
|
||||
|
||||
@Value("\${cloud.aws.sqs.generate-coupon-url}")
|
||||
private val generateCouponQueueUrl: String
|
||||
) {
|
||||
fun sendGenerateCouponMessage(message: String) {
|
||||
val request = SendMessageRequest()
|
||||
.withQueueUrl(generateCouponQueueUrl)
|
||||
.withMessageBody(message)
|
||||
|
||||
amazonSQS.sendMessage(request)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||
import java.time.LocalDateTime
|
||||
import javax.persistence.Entity
|
||||
|
||||
@Entity
|
||||
data class CanCoupon(
|
||||
val couponName: String,
|
||||
val can: Int,
|
||||
val couponCount: Int,
|
||||
val validity: LocalDateTime,
|
||||
val isActive: Boolean,
|
||||
val isMultipleUse: Boolean
|
||||
) : BaseEntity()
|
|
@ -0,0 +1,39 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/can/coupon")
|
||||
class CanCouponController(private val service: CanCouponService) {
|
||||
@PostMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
fun generateCoupon(
|
||||
@RequestBody request: GenerateCanCouponRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.generateCoupon(request))
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
fun getCouponList(
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||
pageable: Pageable
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.getCouponList(offset = pageable.offset, limit = pageable.pageSize.toLong()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.FetchType
|
||||
import javax.persistence.JoinColumn
|
||||
import javax.persistence.ManyToOne
|
||||
import javax.persistence.OneToOne
|
||||
|
||||
@Entity
|
||||
data class CanCouponNumber(
|
||||
val couponNumber: String
|
||||
) : BaseEntity() {
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "can_coupon_id", nullable = false)
|
||||
var canCoupon: CanCoupon? = null
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "member_id", nullable = true)
|
||||
var member: Member? = null
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.can.coupon.QCanCouponNumber.canCouponNumber
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
|
||||
interface CanCouponNumberRepository : JpaRepository<CanCouponNumber, Long>, CanCouponNumberQueryRepository
|
||||
|
||||
interface CanCouponNumberQueryRepository {
|
||||
fun getUseCouponCount(id: Long): Int
|
||||
}
|
||||
|
||||
class CanCouponNumberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanCouponNumberQueryRepository {
|
||||
override fun getUseCouponCount(id: Long): Int {
|
||||
return queryFactory
|
||||
.select(canCouponNumber.id)
|
||||
.from(canCouponNumber)
|
||||
.where(canCouponNumber.member.isNotNull)
|
||||
.fetch()
|
||||
.size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.can.coupon.QCanCoupon.canCoupon
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
|
||||
interface CanCouponRepository : JpaRepository<CanCoupon, Long>, CanCouponQueryRepository
|
||||
|
||||
interface CanCouponQueryRepository {
|
||||
fun getCouponList(offset: Long, limit: Long): List<CanCoupon>
|
||||
}
|
||||
|
||||
class CanCouponQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanCouponQueryRepository {
|
||||
override fun getCouponList(offset: Long, limit: Long): List<CanCoupon> {
|
||||
return queryFactory
|
||||
.selectFrom(canCoupon)
|
||||
.orderBy(canCoupon.id.desc())
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.fetch()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import kr.co.vividnext.sodalive.aws.sqs.SqsEvent
|
||||
import kr.co.vividnext.sodalive.aws.sqs.SqsEventType
|
||||
import org.springframework.context.ApplicationEventPublisher
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Service
|
||||
class CanCouponService(
|
||||
private val repository: CanCouponRepository,
|
||||
private val couponNumberRepository: CanCouponNumberRepository,
|
||||
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val applicationEventPublisher: ApplicationEventPublisher
|
||||
) {
|
||||
fun generateCoupon(request: GenerateCanCouponRequest) {
|
||||
val message = objectMapper.writeValueAsString(request)
|
||||
applicationEventPublisher.publishEvent(SqsEvent(type = SqsEventType.GENERATE_COUPON, message = message))
|
||||
}
|
||||
|
||||
fun getCouponList(offset: Long, limit: Long): List<GetCouponListResponse> {
|
||||
return repository.getCouponList(offset = offset, limit = limit)
|
||||
.asSequence()
|
||||
.map {
|
||||
val useCouponCount = couponNumberRepository.getUseCouponCount(id = it.id!!)
|
||||
GetCouponListResponse(
|
||||
id = it.id!!,
|
||||
couponName = it.couponName,
|
||||
can = "${it.can}캔",
|
||||
couponCount = it.couponCount,
|
||||
useCouponCount = useCouponCount,
|
||||
validity = it.validity.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
|
||||
isMultipleUse = it.isMultipleUse,
|
||||
isActive = it.isActive
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import java.time.LocalDateTime
|
||||
|
||||
data class GenerateCanCouponRequest(
|
||||
@JsonProperty("couponName") val couponName: String,
|
||||
@JsonProperty("can") val can: Int,
|
||||
@JsonProperty("validity") val validity: LocalDateTime,
|
||||
@JsonProperty("isMultipleUse") val isMultipleUse: Boolean,
|
||||
@JsonProperty("couponNumberCount") val couponNumberCount: Int
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
package kr.co.vividnext.sodalive.can.coupon
|
||||
|
||||
data class GetCouponListResponse(
|
||||
val id: Long,
|
||||
val couponName: String,
|
||||
val can: String,
|
||||
val couponCount: Int,
|
||||
val useCouponCount: Int,
|
||||
val validity: String,
|
||||
val isMultipleUse: Boolean,
|
||||
val isActive: Boolean
|
||||
)
|
|
@ -0,0 +1,30 @@
|
|||
package kr.co.vividnext.sodalive.configs
|
||||
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider
|
||||
import com.amazonaws.auth.BasicAWSCredentials
|
||||
import com.amazonaws.services.sqs.AmazonSQS
|
||||
import com.amazonaws.services.sqs.AmazonSQSClientBuilder
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
class AmazonSQSConfig(
|
||||
@Value("\${cloud.aws.credentials.access-key}")
|
||||
private val accessKey: String,
|
||||
@Value("\${cloud.aws.credentials.secret-key}")
|
||||
private val secretKey: String,
|
||||
@Value("\${cloud.aws.region.static}")
|
||||
private val region: String
|
||||
) {
|
||||
@Bean
|
||||
fun amazonSQS(): AmazonSQS {
|
||||
val basicAWSCredentials = BasicAWSCredentials(accessKey, secretKey)
|
||||
val awsStaticCredentialsProvider = AWSStaticCredentialsProvider(basicAWSCredentials)
|
||||
|
||||
return AmazonSQSClientBuilder.standard()
|
||||
.withCredentials(awsStaticCredentialsProvider)
|
||||
.withRegion(region)
|
||||
.build()
|
||||
}
|
||||
}
|
|
@ -37,6 +37,8 @@ cloud:
|
|||
keyPairId: ${CONTENT_CLOUD_FRONT_KEY_PAIR_ID}
|
||||
cloudFront:
|
||||
host: ${CLOUD_FRONT_HOST}
|
||||
sqs:
|
||||
generateCouponUrl: ${AMAZON_SQS_GENERATE_CAN_COUPON_QUEUE_URL}
|
||||
region:
|
||||
static: ap-northeast-2
|
||||
stack:
|
||||
|
|
Loading…
Reference in New Issue