Compare commits
No commits in common. "9ff6ec1888eee6b6e9f18f85b796c8cdc239e3d2" and "d2950106ec151a85ae539d8c3eda6757b078ee7e" have entirely different histories.
9ff6ec1888
...
d2950106ec
|
@ -44,7 +44,6 @@ dependencies {
|
||||||
kapt("org.springframework.boot:spring-boot-configuration-processor")
|
kapt("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
|
||||||
// aws
|
// 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-ses:1.12.380")
|
||||||
implementation("com.amazonaws:aws-java-sdk-s3:1.12.380")
|
implementation("com.amazonaws:aws-java-sdk-s3:1.12.380")
|
||||||
implementation("com.amazonaws:aws-java-sdk-cloudfront:1.12.380")
|
implementation("com.amazonaws:aws-java-sdk-cloudfront:1.12.380")
|
||||||
|
@ -59,8 +58,6 @@ dependencies {
|
||||||
// firebase admin sdk
|
// firebase admin sdk
|
||||||
implementation("com.google.firebase:firebase-admin:9.2.0")
|
implementation("com.google.firebase:firebase-admin:9.2.0")
|
||||||
|
|
||||||
implementation("org.apache.poi:poi-ooxml:5.2.3")
|
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
runtimeOnly("com.h2database:h2")
|
runtimeOnly("com.h2database:h2")
|
||||||
runtimeOnly("com.mysql:mysql-connector-j")
|
runtimeOnly("com.mysql:mysql-connector-j")
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -111,10 +111,6 @@ class CanService(private val repository: CanRepository) {
|
||||||
"제휴보상"
|
"제휴보상"
|
||||||
}
|
}
|
||||||
|
|
||||||
ChargeStatus.COUPON -> {
|
|
||||||
it.payment!!.method ?: "쿠폰충전"
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
"환불"
|
"환불"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import kr.co.bootpay.Bootpay
|
import kr.co.bootpay.Bootpay
|
||||||
import kr.co.vividnext.sodalive.can.CanRepository
|
import kr.co.vividnext.sodalive.can.CanRepository
|
||||||
import kr.co.vividnext.sodalive.can.charge.event.ChargeSpringEvent
|
import kr.co.vividnext.sodalive.can.charge.event.ChargeSpringEvent
|
||||||
import kr.co.vividnext.sodalive.can.coupon.CanCouponNumberRepository
|
|
||||||
import kr.co.vividnext.sodalive.can.payment.Payment
|
import kr.co.vividnext.sodalive.can.payment.Payment
|
||||||
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
|
||||||
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
|
import kr.co.vividnext.sodalive.can.payment.PaymentStatus
|
||||||
|
@ -30,8 +29,6 @@ class ChargeService(
|
||||||
private val chargeRepository: ChargeRepository,
|
private val chargeRepository: ChargeRepository,
|
||||||
private val canRepository: CanRepository,
|
private val canRepository: CanRepository,
|
||||||
private val memberRepository: MemberRepository,
|
private val memberRepository: MemberRepository,
|
||||||
private val couponNumberRepository: CanCouponNumberRepository,
|
|
||||||
|
|
||||||
private val objectMapper: ObjectMapper,
|
private val objectMapper: ObjectMapper,
|
||||||
private val okHttpClient: OkHttpClient,
|
private val okHttpClient: OkHttpClient,
|
||||||
private val applicationEventPublisher: ApplicationEventPublisher,
|
private val applicationEventPublisher: ApplicationEventPublisher,
|
||||||
|
@ -46,33 +43,6 @@ class ChargeService(
|
||||||
private val appleInAppVerifyUrl: String
|
private val appleInAppVerifyUrl: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun chargeByCoupon(couponNumber: String, member: Member) {
|
|
||||||
val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber)
|
|
||||||
?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.")
|
|
||||||
|
|
||||||
if (canCouponNumber.member != null) {
|
|
||||||
throw SodaException("이미 사용한 쿠폰번호 입니다.")
|
|
||||||
}
|
|
||||||
|
|
||||||
canCouponNumber.member = member
|
|
||||||
|
|
||||||
val coupon = canCouponNumber.canCoupon!!
|
|
||||||
val couponCharge = Charge(0, coupon.can, status = ChargeStatus.COUPON)
|
|
||||||
couponCharge.title = "${coupon.can} 캔"
|
|
||||||
couponCharge.member = member
|
|
||||||
|
|
||||||
val payment = Payment(
|
|
||||||
status = PaymentStatus.COMPLETE,
|
|
||||||
paymentGateway = PaymentGateway.PG
|
|
||||||
)
|
|
||||||
payment.method = coupon.couponName
|
|
||||||
couponCharge.payment = payment
|
|
||||||
chargeRepository.save(couponCharge)
|
|
||||||
|
|
||||||
member.charge(0, coupon.can, "pg")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun charge(member: Member, request: ChargeRequest): ChargeResponse {
|
fun charge(member: Member, request: ChargeRequest): ChargeResponse {
|
||||||
val can = canRepository.findByIdOrNull(request.canId)
|
val can = canRepository.findByIdOrNull(request.canId)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package kr.co.vividnext.sodalive.can.charge
|
package kr.co.vividnext.sodalive.can.charge
|
||||||
|
|
||||||
enum class ChargeStatus {
|
enum class ChargeStatus {
|
||||||
CHARGE, REFUND_CHARGE, EVENT, COUPON, CANCEL,
|
CHARGE, REFUND_CHARGE, EVENT, CANCEL,
|
||||||
|
|
||||||
// 관리자 지급
|
// 관리자 지급
|
||||||
ADMIN,
|
ADMIN,
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
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()
|
|
|
@ -1,107 +0,0 @@
|
||||||
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.core.io.InputStreamResource
|
|
||||||
import org.springframework.data.domain.Pageable
|
|
||||||
import org.springframework.http.HttpHeaders
|
|
||||||
import org.springframework.http.MediaType
|
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
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.RequestParam
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
import java.net.URLEncoder
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
@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()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/number-list")
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun getCouponNumberList(
|
|
||||||
@RequestParam couponId: Long,
|
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
|
||||||
pageable: Pageable
|
|
||||||
) = run {
|
|
||||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
|
||||||
|
|
||||||
ApiResponse.ok(
|
|
||||||
service.getCouponNumberList(
|
|
||||||
couponId = couponId,
|
|
||||||
offset = pageable.offset,
|
|
||||||
limit = pageable.pageSize.toLong()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/number-list/download")
|
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
fun downloadCouponNumberList(
|
|
||||||
@RequestParam couponId: Long,
|
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
|
||||||
) = run {
|
|
||||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
|
||||||
|
|
||||||
val fileName = "쿠폰번호리스트.xlsx"
|
|
||||||
val encodedFileName = URLEncoder.encode(
|
|
||||||
fileName,
|
|
||||||
StandardCharsets.UTF_8.toString()
|
|
||||||
).replace("+", "%20")
|
|
||||||
val contentDisposition = "attachment; filename*=UTF-8''$encodedFileName"
|
|
||||||
val headers = HttpHeaders().apply {
|
|
||||||
add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
|
|
||||||
}
|
|
||||||
|
|
||||||
val response = service.downloadCouponNumberList(couponId)
|
|
||||||
ResponseEntity.ok()
|
|
||||||
.headers(headers)
|
|
||||||
.contentType(
|
|
||||||
MediaType
|
|
||||||
.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
|
||||||
)
|
|
||||||
.body(InputStreamResource(response))
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/use")
|
|
||||||
fun useCanCoupon(
|
|
||||||
@RequestBody request: UseCanCouponRequest,
|
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
|
||||||
) = run {
|
|
||||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
|
||||||
|
|
||||||
ApiResponse.ok(
|
|
||||||
service.useCanCoupon(
|
|
||||||
couponNumber = request.couponNumber,
|
|
||||||
memberId = member.id!!
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.can.coupon
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class CanCouponIssueService(private val couponNumberRepository: CanCouponNumberRepository) {
|
|
||||||
fun validateAvailableUseCoupon(couponNumber: String, memberId: Long) {
|
|
||||||
val canCouponNumber = checkCanCouponNumber(couponNumber)
|
|
||||||
|
|
||||||
if (!isMultipleUse(canCouponNumber)) {
|
|
||||||
val canCouponNumberList = couponNumberRepository.findByMemberId(memberId = memberId)
|
|
||||||
if (canCouponNumberList.isNotEmpty()) {
|
|
||||||
throw SodaException("해당 쿠폰은 1회만 충전이 가능합니다.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkCanCouponNumber(couponNumber: String): CanCouponNumber {
|
|
||||||
val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber)
|
|
||||||
?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.")
|
|
||||||
|
|
||||||
if (canCouponNumber.member != null) {
|
|
||||||
throw SodaException("이미 사용한 쿠폰번호 입니다.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return canCouponNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isMultipleUse(canCouponNumber: CanCouponNumber) = canCouponNumber.canCoupon!!.isMultipleUse
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
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
|
|
||||||
|
|
||||||
fun getCouponNumberTotalCount(couponId: Long): Int
|
|
||||||
|
|
||||||
fun getCouponNumberList(couponId: Long, offset: Long, limit: Long): List<GetCouponNumberListItemResponse>
|
|
||||||
|
|
||||||
fun getAllCouponNumberList(couponId: Long): List<GetCouponNumberListItemResponse>
|
|
||||||
|
|
||||||
fun findByCouponNumber(couponNumber: String): CanCouponNumber?
|
|
||||||
|
|
||||||
fun findByMemberId(memberId: Long): List<Long>
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCouponNumberTotalCount(couponId: Long): Int {
|
|
||||||
return queryFactory
|
|
||||||
.select(canCouponNumber.id)
|
|
||||||
.from(canCouponNumber)
|
|
||||||
.where(canCouponNumber.canCoupon.id.eq(couponId))
|
|
||||||
.fetch()
|
|
||||||
.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCouponNumberList(couponId: Long, offset: Long, limit: Long): List<GetCouponNumberListItemResponse> {
|
|
||||||
return queryFactory
|
|
||||||
.select(
|
|
||||||
QGetCouponNumberListItemResponse(
|
|
||||||
canCouponNumber.id,
|
|
||||||
canCouponNumber.couponNumber,
|
|
||||||
canCouponNumber.member.isNotNull
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.from(canCouponNumber)
|
|
||||||
.where(canCouponNumber.canCoupon.id.eq(couponId))
|
|
||||||
.orderBy(canCouponNumber.id.asc())
|
|
||||||
.offset(offset)
|
|
||||||
.limit(limit)
|
|
||||||
.fetch()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAllCouponNumberList(couponId: Long): List<GetCouponNumberListItemResponse> {
|
|
||||||
return queryFactory
|
|
||||||
.select(
|
|
||||||
QGetCouponNumberListItemResponse(
|
|
||||||
canCouponNumber.id,
|
|
||||||
canCouponNumber.couponNumber,
|
|
||||||
canCouponNumber.member.isNotNull
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.from(canCouponNumber)
|
|
||||||
.where(canCouponNumber.canCoupon.id.eq(couponId))
|
|
||||||
.orderBy(canCouponNumber.id.asc())
|
|
||||||
.fetch()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findByCouponNumber(couponNumber: String): CanCouponNumber? {
|
|
||||||
return queryFactory
|
|
||||||
.selectFrom(canCouponNumber)
|
|
||||||
.where(canCouponNumber.couponNumber.eq(couponNumber))
|
|
||||||
.fetchFirst()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findByMemberId(memberId: Long): List<Long> {
|
|
||||||
return queryFactory
|
|
||||||
.select(canCouponNumber.id)
|
|
||||||
.from(canCouponNumber)
|
|
||||||
.where(canCouponNumber.member.id.eq(memberId))
|
|
||||||
.fetch()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
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 getCouponTotalCount(): Int
|
|
||||||
fun getCouponList(offset: Long, limit: Long): List<CanCoupon>
|
|
||||||
}
|
|
||||||
|
|
||||||
class CanCouponQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanCouponQueryRepository {
|
|
||||||
override fun getCouponTotalCount(): Int {
|
|
||||||
return queryFactory
|
|
||||||
.select(canCoupon.id)
|
|
||||||
.from(canCoupon)
|
|
||||||
.fetch()
|
|
||||||
.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCouponList(offset: Long, limit: Long): List<CanCoupon> {
|
|
||||||
return queryFactory
|
|
||||||
.selectFrom(canCoupon)
|
|
||||||
.orderBy(canCoupon.id.desc())
|
|
||||||
.offset(offset)
|
|
||||||
.limit(limit)
|
|
||||||
.fetch()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
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 kr.co.vividnext.sodalive.can.charge.ChargeService
|
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
|
||||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class CanCouponService(
|
|
||||||
private val issueService: CanCouponIssueService,
|
|
||||||
private val chargeService: ChargeService,
|
|
||||||
|
|
||||||
private val repository: CanCouponRepository,
|
|
||||||
private val couponNumberRepository: CanCouponNumberRepository,
|
|
||||||
|
|
||||||
private val memberRepository: MemberRepository,
|
|
||||||
|
|
||||||
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): GetCouponListResponse {
|
|
||||||
val totalCount = repository.getCouponTotalCount()
|
|
||||||
|
|
||||||
val items = repository.getCouponList(offset = offset, limit = limit)
|
|
||||||
.asSequence()
|
|
||||||
.map {
|
|
||||||
val useCouponCount = couponNumberRepository.getUseCouponCount(id = it.id!!)
|
|
||||||
GetCouponListItemResponse(
|
|
||||||
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()
|
|
||||||
|
|
||||||
return GetCouponListResponse(totalCount, items)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCouponNumberList(couponId: Long, offset: Long, limit: Long): GetCouponNumberListResponse {
|
|
||||||
val totalCount = couponNumberRepository.getCouponNumberTotalCount(couponId = couponId)
|
|
||||||
val items = couponNumberRepository.getCouponNumberList(couponId = couponId, offset = offset, limit = limit)
|
|
||||||
return GetCouponNumberListResponse(totalCount, items)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadCouponNumberList(couponId: Long): ByteArrayInputStream {
|
|
||||||
val header = listOf("순번", "쿠폰번호", "사용여부")
|
|
||||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
|
||||||
val couponNumberList = couponNumberRepository.getAllCouponNumberList(couponId)
|
|
||||||
|
|
||||||
val workbook = XSSFWorkbook()
|
|
||||||
try {
|
|
||||||
val sheet = workbook.createSheet()
|
|
||||||
val row = sheet.createRow(0)
|
|
||||||
header.forEachIndexed { index, string ->
|
|
||||||
val cell = row.createCell(index)
|
|
||||||
cell.setCellValue(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
couponNumberList.forEachIndexed { index, item ->
|
|
||||||
val couponNumberRow = sheet.createRow(index + 1)
|
|
||||||
couponNumberRow.createCell(0).setCellValue(item.couponNumberId.toString())
|
|
||||||
couponNumberRow.createCell(1).setCellValue(insertHyphens(item.couponNumber))
|
|
||||||
couponNumberRow.createCell(2).setCellValue(
|
|
||||||
if (item.isUsed) {
|
|
||||||
"O"
|
|
||||||
} else {
|
|
||||||
"X"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
workbook.write(byteArrayOutputStream)
|
|
||||||
return ByteArrayInputStream(byteArrayOutputStream.toByteArray())
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw SodaException("다운로드를 하지 못했습니다.\n다시 시도해 주세요.")
|
|
||||||
} finally {
|
|
||||||
workbook.close()
|
|
||||||
byteArrayOutputStream.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useCanCoupon(couponNumber: String, memberId: Long) {
|
|
||||||
val member = memberRepository.findByIdOrNull(id = memberId)
|
|
||||||
?: throw SodaException("로그인 정보를 확인해주세요.")
|
|
||||||
|
|
||||||
if (member.auth == null) throw SodaException("쿠폰은 본인인증을 하셔야 사용이 가능합니다.")
|
|
||||||
|
|
||||||
issueService.validateAvailableUseCoupon(couponNumber, memberId)
|
|
||||||
|
|
||||||
chargeService.chargeByCoupon(couponNumber, member)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun insertHyphens(input: String): String {
|
|
||||||
return input.chunked(4).joinToString("-")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.can.coupon
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
|
|
||||||
data class GenerateCanCouponRequest(
|
|
||||||
@JsonProperty("couponName") val couponName: String,
|
|
||||||
@JsonProperty("can") val can: Int,
|
|
||||||
@JsonProperty("validity") val validity: String,
|
|
||||||
@JsonProperty("isMultipleUse") val isMultipleUse: Boolean,
|
|
||||||
@JsonProperty("couponNumberCount") val couponNumberCount: Int
|
|
||||||
)
|
|
|
@ -1,17 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.can.coupon
|
|
||||||
|
|
||||||
data class GetCouponListResponse(
|
|
||||||
val totalCount: Int,
|
|
||||||
val items: List<GetCouponListItemResponse>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class GetCouponListItemResponse(
|
|
||||||
val id: Long,
|
|
||||||
val couponName: String,
|
|
||||||
val can: Int,
|
|
||||||
val couponCount: Int,
|
|
||||||
val useCouponCount: Int,
|
|
||||||
val validity: String,
|
|
||||||
val isMultipleUse: Boolean,
|
|
||||||
val isActive: Boolean
|
|
||||||
)
|
|
|
@ -1,14 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.can.coupon
|
|
||||||
|
|
||||||
import com.querydsl.core.annotations.QueryProjection
|
|
||||||
|
|
||||||
data class GetCouponNumberListResponse(
|
|
||||||
val totalCount: Int,
|
|
||||||
val items: List<GetCouponNumberListItemResponse>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class GetCouponNumberListItemResponse @QueryProjection constructor(
|
|
||||||
val couponNumberId: Long,
|
|
||||||
val couponNumber: String,
|
|
||||||
val isUsed: Boolean
|
|
||||||
)
|
|
|
@ -1,3 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.can.coupon
|
|
||||||
|
|
||||||
data class UseCanCouponRequest(val couponNumber: String)
|
|
|
@ -1,30 +0,0 @@
|
||||||
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,8 +37,6 @@ cloud:
|
||||||
keyPairId: ${CONTENT_CLOUD_FRONT_KEY_PAIR_ID}
|
keyPairId: ${CONTENT_CLOUD_FRONT_KEY_PAIR_ID}
|
||||||
cloudFront:
|
cloudFront:
|
||||||
host: ${CLOUD_FRONT_HOST}
|
host: ${CLOUD_FRONT_HOST}
|
||||||
sqs:
|
|
||||||
generateCouponUrl: ${AMAZON_SQS_GENERATE_CAN_COUPON_QUEUE_URL}
|
|
||||||
region:
|
region:
|
||||||
static: ap-northeast-2
|
static: ap-northeast-2
|
||||||
stack:
|
stack:
|
||||||
|
|
Loading…
Reference in New Issue