Compare commits

..

No commits in common. "d1a936d55bfda1369fa14d4f5554315ebcaa002d" and "dc97eaa8355cac19579cc905c2f9060c870d018d" have entirely different histories.

21 changed files with 40 additions and 116 deletions

View File

@ -5,7 +5,6 @@ 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.coupon.CanCouponNumberRepository
import kr.co.vividnext.sodalive.can.coupon.CouponType
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
@ -13,11 +12,6 @@ import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.google.GooglePlayService import kr.co.vividnext.sodalive.google.GooglePlayService
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.member.MemberRepository
import kr.co.vividnext.sodalive.point.MemberPoint
import kr.co.vividnext.sodalive.point.MemberPointRepository
import kr.co.vividnext.sodalive.point.PointGrantLog
import kr.co.vividnext.sodalive.point.PointGrantLogRepository
import kr.co.vividnext.sodalive.useraction.ActionType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -33,7 +27,6 @@ import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import java.time.LocalDateTime
@Service @Service
@Transactional(readOnly = true) @Transactional(readOnly = true)
@ -43,9 +36,6 @@ class ChargeService(
private val memberRepository: MemberRepository, private val memberRepository: MemberRepository,
private val couponNumberRepository: CanCouponNumberRepository, private val couponNumberRepository: CanCouponNumberRepository,
private val grantLogRepository: PointGrantLogRepository,
private val memberPointRepository: MemberPointRepository,
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
private val okHttpClient: OkHttpClient, private val okHttpClient: OkHttpClient,
private val applicationEventPublisher: ApplicationEventPublisher, private val applicationEventPublisher: ApplicationEventPublisher,
@ -67,63 +57,30 @@ class ChargeService(
) { ) {
@Transactional @Transactional
fun chargeByCoupon(couponNumber: String, member: Member): String { fun chargeByCoupon(couponNumber: String, member: Member) {
val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber) val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber)
?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.") ?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.")
if (canCouponNumber.member != null) { if (canCouponNumber.member != null) {
throw SodaException("이미 사용한 쿠폰번호 입니다.") throw SodaException("이미 사용한 쿠폰번호 입니다.")
} }
canCouponNumber.member = member canCouponNumber.member = member
val coupon = canCouponNumber.canCoupon!! val coupon = canCouponNumber.canCoupon!!
val couponCharge = Charge(0, coupon.can, status = ChargeStatus.COUPON)
couponCharge.title = "${coupon.can}"
couponCharge.member = member
when (coupon.couponType) { val payment = Payment(
CouponType.CAN -> { status = PaymentStatus.COMPLETE,
val couponCharge = Charge(0, coupon.can, status = ChargeStatus.COUPON) paymentGateway = PaymentGateway.PG
couponCharge.title = "${coupon.can}" )
couponCharge.member = member payment.method = coupon.couponName
couponCharge.payment = payment
chargeRepository.save(couponCharge)
val payment = Payment( member.charge(0, coupon.can, "pg")
status = PaymentStatus.COMPLETE,
paymentGateway = PaymentGateway.PG
)
payment.method = coupon.couponName
couponCharge.payment = payment
chargeRepository.save(couponCharge)
member.charge(0, coupon.can, "pg")
return "쿠폰 사용이 완료되었습니다.\n${coupon.can}캔이 지급되었습니다."
}
CouponType.POINT -> {
val memberId = member.id!!
val point = coupon.can
val actionType = ActionType.COUPON
grantLogRepository.save(
PointGrantLog(
memberId = memberId,
point = point,
actionType = actionType,
policyId = null,
orderId = null,
couponName = coupon.couponName
)
)
memberPointRepository.save(
MemberPoint(
memberId = memberId,
point = point,
actionType = actionType,
expiresAt = LocalDateTime.now().plusDays(3)
)
)
return "쿠폰 사용이 완료되었습니다.\n${coupon.can}포인트가 지급되었습니다."
}
}
} }
@Transactional @Transactional

View File

@ -3,21 +3,13 @@ package kr.co.vividnext.sodalive.can.coupon
import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.common.BaseEntity
import java.time.LocalDateTime import java.time.LocalDateTime
import javax.persistence.Entity import javax.persistence.Entity
import javax.persistence.EnumType
import javax.persistence.Enumerated
@Entity @Entity
data class CanCoupon( data class CanCoupon(
val couponName: String, val couponName: String,
@Enumerated(EnumType.STRING)
val couponType: CouponType,
val can: Int, val can: Int,
val couponCount: Int, val couponCount: Int,
var validity: LocalDateTime, var validity: LocalDateTime,
var isActive: Boolean, var isActive: Boolean,
var isMultipleUse: Boolean var isMultipleUse: Boolean
) : BaseEntity() ) : BaseEntity()
enum class CouponType {
CAN, POINT
}

View File

@ -109,11 +109,11 @@ class CanCouponController(private val service: CanCouponService) {
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
val completeMessage = service.useCanCoupon( ApiResponse.ok(
couponNumber = request.couponNumber, service.useCanCoupon(
memberId = member.id!! couponNumber = request.couponNumber,
memberId = member.id!!
)
) )
ApiResponse.ok(Unit, completeMessage)
} }
} }

View File

@ -79,7 +79,6 @@ class CanCouponNumberQueryRepositoryImpl(private val queryFactory: JPAQueryFacto
override fun findByCouponNumber(couponNumber: String): CanCouponNumber? { override fun findByCouponNumber(couponNumber: String): CanCouponNumber? {
return queryFactory return queryFactory
.selectFrom(canCouponNumber) .selectFrom(canCouponNumber)
.innerJoin(canCouponNumber.canCoupon, canCoupon)
.where(canCouponNumber.couponNumber.eq(couponNumber)) .where(canCouponNumber.couponNumber.eq(couponNumber))
.fetchFirst() .fetchFirst()
} }

View File

@ -1,6 +1,5 @@
package kr.co.vividnext.sodalive.can.coupon package kr.co.vividnext.sodalive.can.coupon
import com.querydsl.core.types.dsl.CaseBuilder
import com.querydsl.core.types.dsl.DateTimePath import com.querydsl.core.types.dsl.DateTimePath
import com.querydsl.core.types.dsl.Expressions import com.querydsl.core.types.dsl.Expressions
import com.querydsl.core.types.dsl.StringTemplate import com.querydsl.core.types.dsl.StringTemplate
@ -31,9 +30,6 @@ class CanCouponQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) :
QGetCouponListItemResponse( QGetCouponListItemResponse(
canCoupon.id, canCoupon.id,
canCoupon.couponName, canCoupon.couponName,
CaseBuilder()
.`when`(canCoupon.couponType.eq(CouponType.POINT)).then("포인트 쿠폰")
.otherwise("캔 쿠폰"),
canCoupon.can, canCoupon.can,
canCoupon.couponCount, canCoupon.couponCount,
Expressions.ZERO, Expressions.ZERO,

View File

@ -68,12 +68,15 @@ class CanCouponService(
fun getCouponList(offset: Long, limit: Long): GetCouponListResponse { fun getCouponList(offset: Long, limit: Long): GetCouponListResponse {
val totalCount = repository.getCouponTotalCount() val totalCount = repository.getCouponTotalCount()
val items = repository.getCouponList(offset = offset, limit = limit) val items = repository.getCouponList(offset = offset, limit = limit)
.asSequence()
.map { .map {
val useCouponCount = couponNumberRepository.getUseCouponCount(id = it.id) val useCouponCount = couponNumberRepository.getUseCouponCount(id = it.id)
it.useCouponCount = useCouponCount it.useCouponCount = useCouponCount
it it
} }
.toList()
return GetCouponListResponse(totalCount, items) return GetCouponListResponse(totalCount, items)
} }
@ -121,7 +124,7 @@ class CanCouponService(
} }
} }
fun useCanCoupon(couponNumber: String, memberId: Long): String { fun useCanCoupon(couponNumber: String, memberId: Long) {
val member = memberRepository.findByIdOrNull(id = memberId) val member = memberRepository.findByIdOrNull(id = memberId)
?: throw SodaException("로그인 정보를 확인해주세요.") ?: throw SodaException("로그인 정보를 확인해주세요.")
@ -129,7 +132,7 @@ class CanCouponService(
issueService.validateAvailableUseCoupon(couponNumber, memberId) issueService.validateAvailableUseCoupon(couponNumber, memberId)
return chargeService.chargeByCoupon(couponNumber, member) chargeService.chargeByCoupon(couponNumber, member)
} }
private fun insertHyphens(input: String): String { private fun insertHyphens(input: String): String {

View File

@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty
data class GenerateCanCouponRequest( data class GenerateCanCouponRequest(
@JsonProperty("couponName") val couponName: String, @JsonProperty("couponName") val couponName: String,
@JsonProperty("couponType") val couponType: CouponType,
@JsonProperty("can") val can: Int, @JsonProperty("can") val can: Int,
@JsonProperty("validity") val validity: String, @JsonProperty("validity") val validity: String,
@JsonProperty("isMultipleUse") val isMultipleUse: Boolean, @JsonProperty("isMultipleUse") val isMultipleUse: Boolean,

View File

@ -10,7 +10,6 @@ data class GetCouponListResponse(
data class GetCouponListItemResponse @QueryProjection constructor( data class GetCouponListItemResponse @QueryProjection constructor(
val id: Long, val id: Long,
val couponName: String, val couponName: String,
val couponType: String,
val can: Int, val can: Int,
val couponCount: Int, val couponCount: Int,
var useCouponCount: Int, var useCouponCount: Int,

View File

@ -262,8 +262,7 @@ class AudioContentQueryRepositoryImpl(
audioContent.releaseDate.gt(LocalDateTime.now()), audioContent.releaseDate.gt(LocalDateTime.now()),
Expressions.asBoolean(false), Expressions.asBoolean(false),
Expressions.asBoolean(false), Expressions.asBoolean(false),
audioContent.remaining.loe(0), audioContent.remaining.loe(0)
audioContent.isPointAvailable
) )
) )
.from(audioContent) .from(audioContent)
@ -489,8 +488,7 @@ class AudioContentQueryRepositoryImpl(
member.profileImage.prepend("/").prepend(imageHost), member.profileImage.prepend("/").prepend(imageHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(audioContent) .from(audioContent)
@ -559,8 +557,7 @@ class AudioContentQueryRepositoryImpl(
member.profileImage.prepend("/").prepend(imageHost), member.profileImage.prepend("/").prepend(imageHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(audioContent) .from(audioContent)
@ -779,8 +776,7 @@ class AudioContentQueryRepositoryImpl(
member.profileImage.prepend("/").prepend(cloudfrontHost), member.profileImage.prepend("/").prepend(cloudfrontHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(audioContent) .from(audioContent)
@ -900,8 +896,7 @@ class AudioContentQueryRepositoryImpl(
.prepend(imageHost), .prepend(imageHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(audioContent) .from(audioContent)
@ -961,8 +956,7 @@ class AudioContentQueryRepositoryImpl(
member.profileImage.prepend("/").prepend(imageHost), member.profileImage.prepend("/").prepend(imageHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(audioContentCurationItem) .from(audioContentCurationItem)
@ -1024,7 +1018,6 @@ class AudioContentQueryRepositoryImpl(
audioContent.duration, audioContent.duration,
member.id, member.id,
member.nickname, member.nickname,
audioContent.isPointAvailable,
member.profileImage.prepend("/").prepend(imageHost) member.profileImage.prepend("/").prepend(imageHost)
) )
) )

View File

@ -21,6 +21,5 @@ data class GetAudioContentListItem @QueryProjection constructor(
val isScheduledToOpen: Boolean, val isScheduledToOpen: Boolean,
var isRented: Boolean, var isRented: Boolean,
var isOwned: Boolean, var isOwned: Boolean,
var isSoldOut: Boolean, var isSoldOut: Boolean
val isPointAvailable: Boolean
) )

View File

@ -11,6 +11,5 @@ data class GetAudioContentMainItem @QueryProjection constructor(
@JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String, @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String,
@JsonProperty("creatorNickname") val creatorNickname: String, @JsonProperty("creatorNickname") val creatorNickname: String,
@JsonProperty("price") val price: Int, @JsonProperty("price") val price: Int,
@JsonProperty("duration") val duration: String, @JsonProperty("duration") val duration: String
@JsonProperty("isPointAvailable") val isPointAvailable: Boolean
) )

View File

@ -18,6 +18,5 @@ data class GetAudioContentRankingItem @QueryProjection constructor(
@JsonProperty("duration") val duration: String, @JsonProperty("duration") val duration: String,
@JsonProperty("creatorId") val creatorId: Long, @JsonProperty("creatorId") val creatorId: Long,
@JsonProperty("creatorNickname") val creatorNickname: String, @JsonProperty("creatorNickname") val creatorNickname: String,
@JsonProperty("isPointAvailable") val isPointAvailable: Boolean,
@JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String
) )

View File

@ -94,8 +94,7 @@ class AudioContentCurationQueryRepository(private val queryFactory: JPAQueryFact
member.profileImage.prepend("/").prepend(cloudfrontHost), member.profileImage.prepend("/").prepend(cloudfrontHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(audioContent) .from(audioContent)

View File

@ -97,8 +97,7 @@ class ContentMainTabTagCurationRepository(
member.profileImage.prepend("/").prepend(imageHost), member.profileImage.prepend("/").prepend(imageHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(contentHashTagCurationItem) .from(contentHashTagCurationItem)

View File

@ -210,8 +210,7 @@ class OrderQueryRepositoryImpl(
.prepend(coverImageHost), .prepend(coverImageHost),
member.nickname, member.nickname,
audioContent.price, audioContent.price,
audioContent.duration, audioContent.duration
audioContent.isPointAvailable
) )
) )
.from(order) .from(order)

View File

@ -124,8 +124,7 @@ class ContentSeriesContentQueryRepositoryImpl(
audioContent.duration, audioContent.duration,
audioContent.price, audioContent.price,
Expressions.asBoolean(false), Expressions.asBoolean(false),
Expressions.asBoolean(false), Expressions.asBoolean(false)
audioContent.isPointAvailable
) )
) )
.from(seriesContent) .from(seriesContent)

View File

@ -15,6 +15,5 @@ data class GetSeriesContentListItem @QueryProjection constructor(
val duration: String, val duration: String,
val price: Int, val price: Int,
var isRented: Boolean, var isRented: Boolean,
var isOwned: Boolean, var isOwned: Boolean
val isPointAvailable: Boolean
) )

View File

@ -13,6 +13,5 @@ data class PointGrantLog(
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
val actionType: ActionType, val actionType: ActionType,
val policyId: Long?, val policyId: Long?,
val orderId: Long?, val orderId: Long?
val couponName: String? = null
) : BaseEntity() ) : BaseEntity()

View File

@ -63,11 +63,11 @@ class PointGrantLogQueryRepositoryImpl(
QGetPointRewardStatusResponse( QGetPointRewardStatusResponse(
pointGrantLog.point.stringValue().concat(" 포인트"), pointGrantLog.point.stringValue().concat(" 포인트"),
formattedDate, formattedDate,
pointRewardPolicy.title.coalesce(pointGrantLog.couponName) pointRewardPolicy.title
) )
) )
.from(pointGrantLog) .from(pointGrantLog)
.leftJoin(pointRewardPolicy).on(pointGrantLog.policyId.eq(pointRewardPolicy.id)) .innerJoin(pointRewardPolicy).on(pointGrantLog.policyId.eq(pointRewardPolicy.id))
.where(pointGrantLog.memberId.eq(memberId)) .where(pointGrantLog.memberId.eq(memberId))
.orderBy(pointGrantLog.id.desc()) .orderBy(pointGrantLog.id.desc())
.fetch() .fetch()

View File

@ -102,7 +102,6 @@ class RankingRepository(
audioContent.duration, audioContent.duration,
member.id, member.id,
member.nickname, member.nickname,
audioContent.isPointAvailable,
member.profileImage.prepend("/").prepend(imageHost) member.profileImage.prepend("/").prepend(imageHost)
) )
) )
@ -561,7 +560,6 @@ class RankingRepository(
audioContent.duration, audioContent.duration,
member.id, member.id,
member.nickname, member.nickname,
audioContent.isPointAvailable,
member.profileImage.prepend("/").prepend(imageHost) member.profileImage.prepend("/").prepend(imageHost)
) )
) )
@ -728,7 +726,6 @@ class RankingRepository(
audioContent.duration, audioContent.duration,
member.id, member.id,
member.nickname, member.nickname,
audioContent.isPointAvailable,
member.profileImage.prepend("/").prepend(imageHost) member.profileImage.prepend("/").prepend(imageHost)
) )
) )
@ -785,7 +782,6 @@ class RankingRepository(
audioContent.duration, audioContent.duration,
member.id, member.id,
member.nickname, member.nickname,
audioContent.isPointAvailable,
member.profileImage.prepend("/").prepend(imageHost) member.profileImage.prepend("/").prepend(imageHost)
) )
) )

View File

@ -5,6 +5,5 @@ enum class ActionType(val displayName: String) {
USER_AUTHENTICATION("본인인증"), USER_AUTHENTICATION("본인인증"),
CONTENT_COMMENT("콘텐츠 댓글"), CONTENT_COMMENT("콘텐츠 댓글"),
ORDER_CONTENT_COMMENT("구매한 콘텐츠 댓글"), ORDER_CONTENT_COMMENT("구매한 콘텐츠 댓글"),
LIVE_CONTINUOUS_LISTEN_30("라이브 연속 청취 30분"), LIVE_CONTINUOUS_LISTEN_30("라이브 연속 청취 30분")
COUPON("쿠폰")
} }