Compare commits
No commits in common. "3c087bc2757ea9e443b030d25a3880edd0f58622" and "8ad13c289e1afa45e866a1275130aadc1016c91d" have entirely different histories.
3c087bc275
...
8ad13c289e
|
@ -1,6 +1,5 @@
|
||||||
package kr.co.vividnext.sodalive.admin.calculate
|
package kr.co.vividnext.sodalive.admin.calculate
|
||||||
|
|
||||||
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
|
||||||
|
@ -52,10 +51,6 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
|
|
||||||
fun getCalculateContentList(startDate: LocalDateTime, endDate: LocalDateTime): List<GetCalculateContentQueryData> {
|
fun getCalculateContentList(startDate: LocalDateTime, endDate: LocalDateTime): List<GetCalculateContentQueryData> {
|
||||||
val orderFormattedDate = getFormattedDate(order.createdAt)
|
val orderFormattedDate = getFormattedDate(order.createdAt)
|
||||||
val pointGroup = CaseBuilder()
|
|
||||||
.`when`(order.point.loe(0)).then(0)
|
|
||||||
.otherwise(1)
|
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(
|
.select(
|
||||||
QGetCalculateContentQueryData(
|
QGetCalculateContentQueryData(
|
||||||
|
@ -67,7 +62,6 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
order.can,
|
order.can,
|
||||||
order.id.count(),
|
order.id.count(),
|
||||||
order.can.sum(),
|
order.can.sum(),
|
||||||
order.point.sum(),
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
creatorSettlementRatio.contentSettlementRatio
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -86,7 +80,6 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
order.type,
|
order.type,
|
||||||
orderFormattedDate,
|
orderFormattedDate,
|
||||||
order.can,
|
order.can,
|
||||||
pointGroup,
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
creatorSettlementRatio.contentSettlementRatio
|
||||||
)
|
)
|
||||||
.orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())
|
.orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())
|
||||||
|
@ -120,10 +113,6 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCumulativeSalesByContent(offset: Long, limit: Long): List<GetCumulativeSalesByContentQueryData> {
|
fun getCumulativeSalesByContent(offset: Long, limit: Long): List<GetCumulativeSalesByContentQueryData> {
|
||||||
val pointGroup = CaseBuilder()
|
|
||||||
.`when`(order.point.loe(0)).then(0)
|
|
||||||
.otherwise(1)
|
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(
|
.select(
|
||||||
QGetCumulativeSalesByContentQueryData(
|
QGetCumulativeSalesByContentQueryData(
|
||||||
|
@ -134,7 +123,6 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
order.can,
|
order.can,
|
||||||
order.id.count(),
|
order.id.count(),
|
||||||
order.can.sum(),
|
order.can.sum(),
|
||||||
order.point.sum(),
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
creatorSettlementRatio.contentSettlementRatio
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -144,14 +132,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
.leftJoin(creatorSettlementRatio)
|
.leftJoin(creatorSettlementRatio)
|
||||||
.on(member.id.eq(creatorSettlementRatio.member.id))
|
.on(member.id.eq(creatorSettlementRatio.member.id))
|
||||||
.where(order.isActive.isTrue)
|
.where(order.isActive.isTrue)
|
||||||
.groupBy(
|
.groupBy(member.id, audioContent.id, order.type, order.can)
|
||||||
member.id,
|
|
||||||
audioContent.id,
|
|
||||||
order.type,
|
|
||||||
order.can,
|
|
||||||
pointGroup,
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
|
||||||
)
|
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.orderBy(member.id.desc(), audioContent.id.desc())
|
.orderBy(member.id.desc(), audioContent.id.desc())
|
||||||
|
|
|
@ -22,15 +22,11 @@ data class GetCalculateContentQueryData @QueryProjection constructor(
|
||||||
val numberOfPeople: Long,
|
val numberOfPeople: Long,
|
||||||
// 합계
|
// 합계
|
||||||
val totalCan: Int,
|
val totalCan: Int,
|
||||||
// 포인트
|
|
||||||
val totalPoint: Int,
|
|
||||||
// 정산비율
|
// 정산비율
|
||||||
val settlementRatio: Int?
|
val settlementRatio: Int?
|
||||||
) {
|
) {
|
||||||
fun toGetCalculateContentResponse(): GetCalculateContentResponse {
|
fun toGetCalculateContentResponse(): GetCalculateContentResponse {
|
||||||
val orderTypeStr = if (totalPoint > 0) {
|
val orderTypeStr = if (orderType == OrderType.RENTAL) {
|
||||||
"포인트"
|
|
||||||
} else if (orderType == OrderType.RENTAL) {
|
|
||||||
"대여"
|
"대여"
|
||||||
} else {
|
} else {
|
||||||
"소장"
|
"소장"
|
||||||
|
|
|
@ -21,15 +21,11 @@ data class GetCumulativeSalesByContentQueryData @QueryProjection constructor(
|
||||||
val numberOfPeople: Long,
|
val numberOfPeople: Long,
|
||||||
// 합계
|
// 합계
|
||||||
val totalCan: Int,
|
val totalCan: Int,
|
||||||
// 포인트
|
|
||||||
val totalPoint: Int,
|
|
||||||
// 정산비율
|
// 정산비율
|
||||||
val settlementRatio: Int?
|
val settlementRatio: Int?
|
||||||
) {
|
) {
|
||||||
fun toCumulativeSalesByContentItem(): CumulativeSalesByContentItem {
|
fun toCumulativeSalesByContentItem(): CumulativeSalesByContentItem {
|
||||||
val orderTypeStr = if (totalPoint > 0) {
|
val orderTypeStr = if (orderType == OrderType.RENTAL) {
|
||||||
"포인트"
|
|
||||||
} else if (orderType == OrderType.RENTAL) {
|
|
||||||
"대여"
|
"대여"
|
||||||
} else {
|
} else {
|
||||||
"소장"
|
"소장"
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
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
|
|
||||||
) {
|
|
||||||
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 = if (endDate.isNotBlank()) {
|
|
||||||
LocalDateTime.parse(endDate, dateTimeFormatter).withSecond(59)
|
|
||||||
.atZone(ZoneId.of("Asia/Seoul"))
|
|
||||||
.withZoneSameInstant(ZoneId.of("UTC"))
|
|
||||||
.toLocalDateTime()
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
},
|
|
||||||
isActive = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.admin.point
|
|
||||||
|
|
||||||
import com.querydsl.core.annotations.QueryProjection
|
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
|
|
||||||
data class GetPointRewardPolicyListResponse(
|
|
||||||
val totalCount: Int,
|
|
||||||
val items: List<GetPointRewardPolicyListItem>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class GetPointRewardPolicyListItem @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
|
|
||||||
)
|
|
|
@ -1,8 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.admin.point
|
|
||||||
|
|
||||||
data class ModifyPointRewardPolicyRequest(
|
|
||||||
val title: String?,
|
|
||||||
val startDate: String?,
|
|
||||||
val endDate: String?,
|
|
||||||
val isActive: Boolean?
|
|
||||||
)
|
|
|
@ -1,36 +0,0 @@
|
||||||
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))
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
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<PointRewardPolicy, Long>, PointPolicyQueryRepository
|
|
||||||
|
|
||||||
interface PointPolicyQueryRepository {
|
|
||||||
fun getTotalCount(): Int
|
|
||||||
fun getAll(offset: Long, limit: Long): List<GetPointRewardPolicyListItem>
|
|
||||||
}
|
|
||||||
|
|
||||||
class PointPolicyQueryRepositoryImpl(
|
|
||||||
private val queryFactory: JPAQueryFactory
|
|
||||||
) : PointPolicyQueryRepository {
|
|
||||||
override fun getTotalCount(): Int {
|
|
||||||
return queryFactory
|
|
||||||
.select(pointRewardPolicy.id)
|
|
||||||
.from(pointRewardPolicy)
|
|
||||||
.fetch()
|
|
||||||
.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAll(offset: Long, limit: Long): List<GetPointRewardPolicyListItem> {
|
|
||||||
return queryFactory
|
|
||||||
.select(
|
|
||||||
QGetPointRewardPolicyListItem(
|
|
||||||
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<LocalDateTime>): StringTemplate {
|
|
||||||
return Expressions.stringTemplate(
|
|
||||||
"COALESCE(DATE_FORMAT(CONVERT_TZ({0}, 'UTC', 'Asia/Seoul'), '%Y-%m-%d %H:%i'), '')",
|
|
||||||
dateTimePath
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
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 org.springframework.transaction.annotation.Transactional
|
|
||||||
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): GetPointRewardPolicyListResponse {
|
|
||||||
val totalCount = repository.getTotalCount()
|
|
||||||
val items = repository.getAll(offset, limit)
|
|
||||||
|
|
||||||
return GetPointRewardPolicyListResponse(totalCount, items)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun create(request: CreatePointRewardPolicyRequest) {
|
|
||||||
val pointPolicy = request.toEntity()
|
|
||||||
repository.save(pointPolicy)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -49,12 +49,10 @@ class AuditionApplicantQueryRepositoryImpl(
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(auditionApplicant.id)
|
.select(auditionApplicant.id)
|
||||||
.from(auditionApplicant)
|
.from(auditionApplicant)
|
||||||
.innerJoin(auditionApplicant.member, member)
|
|
||||||
.innerJoin(auditionApplicant.role, auditionRole)
|
.innerJoin(auditionApplicant.role, auditionRole)
|
||||||
.where(
|
.where(
|
||||||
auditionRole.id.eq(auditionRoleId),
|
auditionRole.id.eq(auditionRoleId),
|
||||||
auditionApplicant.isActive.isTrue,
|
auditionApplicant.isActive.isTrue
|
||||||
member.isActive.isTrue
|
|
||||||
)
|
)
|
||||||
.fetch()
|
.fetch()
|
||||||
.size
|
.size
|
||||||
|
@ -89,8 +87,7 @@ class AuditionApplicantQueryRepositoryImpl(
|
||||||
.leftJoin(auditionVote).on(auditionApplicant.id.eq(auditionVote.applicant.id))
|
.leftJoin(auditionVote).on(auditionApplicant.id.eq(auditionVote.applicant.id))
|
||||||
.where(
|
.where(
|
||||||
auditionRole.id.eq(auditionRoleId),
|
auditionRole.id.eq(auditionRoleId),
|
||||||
auditionApplicant.isActive.isTrue,
|
auditionApplicant.isActive.isTrue
|
||||||
member.isActive.isTrue
|
|
||||||
)
|
)
|
||||||
.groupBy(auditionApplicant.id)
|
.groupBy(auditionApplicant.id)
|
||||||
.orderBy(orderBy)
|
.orderBy(orderBy)
|
||||||
|
|
|
@ -42,7 +42,6 @@ data class AudioContent(
|
||||||
val isGeneratePreview: Boolean = true,
|
val isGeneratePreview: Boolean = true,
|
||||||
var isOnlyRental: Boolean = false,
|
var isOnlyRental: Boolean = false,
|
||||||
var isAdult: Boolean = false,
|
var isAdult: Boolean = false,
|
||||||
var isPointAvailable: Boolean = false,
|
|
||||||
var isCommentAvailable: Boolean = true,
|
var isCommentAvailable: Boolean = true,
|
||||||
var isFullDetailVisible: Boolean = true
|
var isFullDetailVisible: Boolean = true
|
||||||
) : BaseEntity() {
|
) : BaseEntity() {
|
||||||
|
|
|
@ -214,7 +214,6 @@ class AudioContentService(
|
||||||
purchaseOption = purchaseOption,
|
purchaseOption = purchaseOption,
|
||||||
isGeneratePreview = request.isGeneratePreview,
|
isGeneratePreview = request.isGeneratePreview,
|
||||||
isOnlyRental = isOnlyRental,
|
isOnlyRental = isOnlyRental,
|
||||||
isPointAvailable = request.isPointAvailable,
|
|
||||||
isCommentAvailable = request.isCommentAvailable,
|
isCommentAvailable = request.isCommentAvailable,
|
||||||
isFullDetailVisible = isFullDetailVisible
|
isFullDetailVisible = isFullDetailVisible
|
||||||
)
|
)
|
||||||
|
@ -708,8 +707,7 @@ class AudioContentService(
|
||||||
),
|
),
|
||||||
previousContent = previousContent,
|
previousContent = previousContent,
|
||||||
nextContent = nextContent,
|
nextContent = nextContent,
|
||||||
buyerList = buyerList,
|
buyerList = buyerList
|
||||||
isAvailableUsePoint = audioContent.isPointAvailable
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ data class CreateAudioContentRequest(
|
||||||
val isAdult: Boolean = false,
|
val isAdult: Boolean = false,
|
||||||
val isGeneratePreview: Boolean = false,
|
val isGeneratePreview: Boolean = false,
|
||||||
val isOnlyRental: Boolean = false,
|
val isOnlyRental: Boolean = false,
|
||||||
val isPointAvailable: Boolean = false,
|
|
||||||
val isCommentAvailable: Boolean = false,
|
val isCommentAvailable: Boolean = false,
|
||||||
val isFullDetailVisible: Boolean = true,
|
val isFullDetailVisible: Boolean = true,
|
||||||
val previewStartTime: String? = null,
|
val previewStartTime: String? = null,
|
||||||
|
|
|
@ -38,8 +38,7 @@ data class GetAudioContentDetailResponse(
|
||||||
val creator: AudioContentCreator,
|
val creator: AudioContentCreator,
|
||||||
val previousContent: OtherContentResponse?,
|
val previousContent: OtherContentResponse?,
|
||||||
val nextContent: OtherContentResponse?,
|
val nextContent: OtherContentResponse?,
|
||||||
val buyerList: List<ContentBuyer>,
|
val buyerList: List<ContentBuyer>
|
||||||
val isAvailableUsePoint: Boolean
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class OtherContentResponse @QueryProjection constructor(
|
data class OtherContentResponse @QueryProjection constructor(
|
||||||
|
|
|
@ -25,7 +25,6 @@ data class Order(
|
||||||
var isActive: Boolean = true
|
var isActive: Boolean = true
|
||||||
) : BaseEntity() {
|
) : BaseEntity() {
|
||||||
var can: Int = 0
|
var can: Int = 0
|
||||||
var point: Int = 0
|
|
||||||
|
|
||||||
val startDate: LocalDateTime = LocalDateTime.now()
|
val startDate: LocalDateTime = LocalDateTime.now()
|
||||||
var endDate: LocalDateTime? = null
|
var endDate: LocalDateTime? = null
|
||||||
|
|
|
@ -10,7 +10,6 @@ import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
||||||
import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository
|
import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository
|
||||||
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
|
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.point.PointUsageService
|
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
@ -20,7 +19,6 @@ import java.time.LocalDateTime
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
class OrderService(
|
class OrderService(
|
||||||
private val repository: OrderRepository,
|
private val repository: OrderRepository,
|
||||||
private val pointUsageService: PointUsageService,
|
|
||||||
private val canPaymentService: CanPaymentService,
|
private val canPaymentService: CanPaymentService,
|
||||||
private val audioContentRepository: AudioContentRepository,
|
private val audioContentRepository: AudioContentRepository,
|
||||||
private val audioContentCommentQueryRepository: AudioContentCommentRepository,
|
private val audioContentCommentQueryRepository: AudioContentCommentRepository,
|
||||||
|
@ -43,27 +41,13 @@ class OrderService(
|
||||||
orderContent(orderType, content, member)
|
orderContent(orderType, content, member)
|
||||||
}
|
}
|
||||||
|
|
||||||
val usedPoint = if (order.type == OrderType.RENTAL && content.isPointAvailable) {
|
canPaymentService.spendCan(
|
||||||
pointUsageService.usePoint(member.id!!, order.can)
|
memberId = member.id!!,
|
||||||
} else {
|
needCan = order.can,
|
||||||
0
|
canUsage = CanUsage.ORDER_CONTENT,
|
||||||
}
|
order = order,
|
||||||
order.point = usedPoint
|
container = container
|
||||||
|
)
|
||||||
val remainingCan = order.can - (usedPoint / 10)
|
|
||||||
if (order.type == OrderType.RENTAL && content.isPointAvailable && usedPoint > 0) {
|
|
||||||
order.can = remainingCan
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingCan > 0) {
|
|
||||||
canPaymentService.spendCan(
|
|
||||||
memberId = member.id!!,
|
|
||||||
needCan = remainingCan,
|
|
||||||
canUsage = CanUsage.ORDER_CONTENT,
|
|
||||||
order = order,
|
|
||||||
container = container
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun orderContent(orderType: OrderType, content: AudioContent, member: Member): Order {
|
private fun orderContent(orderType: OrderType, content: AudioContent, member: Member): Order {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package kr.co.vividnext.sodalive.creator.admin.calculate
|
package kr.co.vividnext.sodalive.creator.admin.calculate
|
||||||
|
|
||||||
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
|
||||||
|
@ -96,10 +95,6 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
|
||||||
limit: Long
|
limit: Long
|
||||||
): List<GetCalculateContentQueryData> {
|
): List<GetCalculateContentQueryData> {
|
||||||
val orderFormattedDate = getFormattedDate(order.createdAt)
|
val orderFormattedDate = getFormattedDate(order.createdAt)
|
||||||
val pointGroup = CaseBuilder()
|
|
||||||
.`when`(order.point.loe(0)).then(0)
|
|
||||||
.otherwise(1)
|
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(
|
.select(
|
||||||
QGetCalculateContentQueryData(
|
QGetCalculateContentQueryData(
|
||||||
|
@ -111,7 +106,6 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
|
||||||
order.can,
|
order.can,
|
||||||
order.id.count(),
|
order.id.count(),
|
||||||
order.can.sum(),
|
order.can.sum(),
|
||||||
order.point.sum(),
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
creatorSettlementRatio.contentSettlementRatio
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -131,7 +125,6 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
|
||||||
order.type,
|
order.type,
|
||||||
orderFormattedDate,
|
orderFormattedDate,
|
||||||
order.can,
|
order.can,
|
||||||
pointGroup,
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
creatorSettlementRatio.contentSettlementRatio
|
||||||
)
|
)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
|
@ -174,10 +167,6 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
|
||||||
offset: Long,
|
offset: Long,
|
||||||
limit: Long
|
limit: Long
|
||||||
): List<GetCumulativeSalesByContentQueryData> {
|
): List<GetCumulativeSalesByContentQueryData> {
|
||||||
val pointGroup = CaseBuilder()
|
|
||||||
.`when`(order.point.loe(0)).then(0)
|
|
||||||
.otherwise(1)
|
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(
|
.select(
|
||||||
QGetCumulativeSalesByContentQueryData(
|
QGetCumulativeSalesByContentQueryData(
|
||||||
|
@ -188,7 +177,6 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
|
||||||
order.can,
|
order.can,
|
||||||
order.id.count(),
|
order.id.count(),
|
||||||
order.can.sum(),
|
order.can.sum(),
|
||||||
order.point.sum(),
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
creatorSettlementRatio.contentSettlementRatio
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -201,14 +189,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
|
||||||
audioContent.member.id.eq(memberId)
|
audioContent.member.id.eq(memberId)
|
||||||
.and(order.isActive.isTrue)
|
.and(order.isActive.isTrue)
|
||||||
)
|
)
|
||||||
.groupBy(
|
.groupBy(member.id, audioContent.id, order.type, order.can)
|
||||||
member.id,
|
|
||||||
audioContent.id,
|
|
||||||
order.type,
|
|
||||||
order.can,
|
|
||||||
pointGroup,
|
|
||||||
creatorSettlementRatio.contentSettlementRatio
|
|
||||||
)
|
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.orderBy(member.id.desc(), audioContent.id.desc())
|
.orderBy(member.id.desc(), audioContent.id.desc())
|
||||||
|
|
|
@ -84,7 +84,6 @@ class CreatorAdminAudioContentQueryRepositoryImpl(
|
||||||
audioContent.limited,
|
audioContent.limited,
|
||||||
audioContent.remaining,
|
audioContent.remaining,
|
||||||
audioContent.isAdult,
|
audioContent.isAdult,
|
||||||
audioContent.isPointAvailable,
|
|
||||||
audioContent.isCommentAvailable,
|
audioContent.isCommentAvailable,
|
||||||
audioContent.duration,
|
audioContent.duration,
|
||||||
audioContent.content,
|
audioContent.content,
|
||||||
|
|
|
@ -131,10 +131,6 @@ class CreatorAdminContentService(
|
||||||
audioContent.isAdult = request.isAdult
|
audioContent.isAdult = request.isAdult
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.isPointAvailable != null) {
|
|
||||||
audioContent.isPointAvailable = request.isPointAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.isCommentAvailable != null) {
|
if (request.isCommentAvailable != null) {
|
||||||
audioContent.isCommentAvailable = request.isCommentAvailable
|
audioContent.isCommentAvailable = request.isCommentAvailable
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ data class GetCreatorAdminContentListItem @QueryProjection constructor(
|
||||||
val totalContentCount: Int?,
|
val totalContentCount: Int?,
|
||||||
val remainingContentCount: Int?,
|
val remainingContentCount: Int?,
|
||||||
val isAdult: Boolean,
|
val isAdult: Boolean,
|
||||||
val isPointAvailable: Boolean,
|
|
||||||
val isCommentAvailable: Boolean,
|
val isCommentAvailable: Boolean,
|
||||||
val remainingTime: String,
|
val remainingTime: String,
|
||||||
var contentUrl: String,
|
var contentUrl: String,
|
||||||
|
|
|
@ -7,6 +7,5 @@ data class UpdateCreatorAdminContentRequest(
|
||||||
val price: Int?,
|
val price: Int?,
|
||||||
val isAdult: Boolean?,
|
val isAdult: Boolean?,
|
||||||
val isActive: Boolean?,
|
val isActive: Boolean?,
|
||||||
val isPointAvailable: Boolean?,
|
|
||||||
val isCommentAvailable: Boolean?
|
val isCommentAvailable: Boolean?
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,9 +4,6 @@ import com.google.firebase.messaging.AndroidConfig
|
||||||
import com.google.firebase.messaging.ApnsConfig
|
import com.google.firebase.messaging.ApnsConfig
|
||||||
import com.google.firebase.messaging.Aps
|
import com.google.firebase.messaging.Aps
|
||||||
import com.google.firebase.messaging.FirebaseMessaging
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
import com.google.firebase.messaging.FirebaseMessagingException
|
|
||||||
import com.google.firebase.messaging.Message
|
|
||||||
import com.google.firebase.messaging.MessagingErrorCode
|
|
||||||
import com.google.firebase.messaging.MulticastMessage
|
import com.google.firebase.messaging.MulticastMessage
|
||||||
import com.google.firebase.messaging.Notification
|
import com.google.firebase.messaging.Notification
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
@ -29,14 +26,8 @@ class FcmService {
|
||||||
creatorId: Long? = null,
|
creatorId: Long? = null,
|
||||||
auditionId: Long? = null
|
auditionId: Long? = null
|
||||||
) {
|
) {
|
||||||
if (tokens.isEmpty()) return
|
if (tokens.isNotEmpty()) {
|
||||||
logger.info("os: $container")
|
logger.info("os: $container")
|
||||||
|
|
||||||
var targets = tokens
|
|
||||||
val maxAttempts = 3
|
|
||||||
var attempt = 1
|
|
||||||
|
|
||||||
while (attempt <= maxAttempts && targets.isNotEmpty()) {
|
|
||||||
val multicastMessage = MulticastMessage.builder()
|
val multicastMessage = MulticastMessage.builder()
|
||||||
.addAllTokens(tokens)
|
.addAllTokens(tokens)
|
||||||
|
|
||||||
|
@ -91,80 +82,8 @@ class FcmService {
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = FirebaseMessaging.getInstance().sendEachForMulticast(multicastMessage.build())
|
val response = FirebaseMessaging.getInstance().sendEachForMulticast(multicastMessage.build())
|
||||||
val failedTokens = mutableListOf<String>()
|
logger.info("보내기 성공: ${response.successCount}")
|
||||||
|
logger.info("보내기 실패: ${response.failureCount}")
|
||||||
response.responses.forEachIndexed { index, res ->
|
|
||||||
if (!res.isSuccessful) {
|
|
||||||
val exception = res.exception
|
|
||||||
val token = targets[index]
|
|
||||||
|
|
||||||
if (exception?.messagingErrorCode == MessagingErrorCode.UNREGISTERED) {
|
|
||||||
logger.error("[FCM] ❌ UNREGISTERED → $token")
|
|
||||||
// 필요 시 DB에서 삭제
|
|
||||||
} else {
|
|
||||||
logger.error("[FCM] ❌ 실패: $token / ${exception?.messagingErrorCode}")
|
|
||||||
failedTokens.add(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failedTokens.isEmpty()) {
|
|
||||||
logger.info("[FCM] ✅ 전체 전송 성공")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
targets = failedTokens
|
|
||||||
attempt++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targets.isNotEmpty()) {
|
|
||||||
logger.error("[FCM] ❌ 최종 실패 대상 ${targets.size}명 → $targets")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendPointGranted(token: String, point: Int) {
|
|
||||||
val data = mapOf(
|
|
||||||
"type" to "POINT_GRANTED",
|
|
||||||
"point" to point.toString(),
|
|
||||||
"message" to "${point}포인트가 지급되었습니다!"
|
|
||||||
)
|
|
||||||
|
|
||||||
var attempts = 0
|
|
||||||
val maxAttempts = 3
|
|
||||||
|
|
||||||
while (attempts < maxAttempts) {
|
|
||||||
try {
|
|
||||||
val message = Message.builder()
|
|
||||||
.setToken(token)
|
|
||||||
.putAllData(data)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val response = FirebaseMessaging.getInstance().send(message)
|
|
||||||
logger.info("[FCM] ✅ 성공 (attempt ${attempts + 1}): messageId=$response")
|
|
||||||
return // 성공 시 즉시 종료
|
|
||||||
} catch (e: FirebaseMessagingException) {
|
|
||||||
attempts++
|
|
||||||
|
|
||||||
// "registration-token-not-registered" 예외 코드 확인
|
|
||||||
if (e.messagingErrorCode == MessagingErrorCode.UNREGISTERED) {
|
|
||||||
logger.error("[FCM] ❌ 실패: 토큰이 등록되지 않음 (등록 해제됨) → 재시도 안함")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.error("[FCM] ❌ 실패 (attempt $attempts): ${e.errorCode} - ${e.message}")
|
|
||||||
|
|
||||||
if (attempts >= maxAttempts) {
|
|
||||||
logger.error("[FCM] ❌ 최종 실패: 전송 불가")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Firebase 이외의 예외도 잡기
|
|
||||||
attempts++
|
|
||||||
logger.error("[FCM] ❌ 실패 (attempt $attempts): ${e.message}")
|
|
||||||
|
|
||||||
if (attempts >= maxAttempts) {
|
|
||||||
logger.error("[FCM] ❌ 최종 실패: 알 수 없는 오류")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingReq
|
||||||
import kr.co.vividnext.sodalive.member.signUp.SignUpRequestV2
|
import kr.co.vividnext.sodalive.member.signUp.SignUpRequestV2
|
||||||
import kr.co.vividnext.sodalive.member.social.google.GoogleAuthService
|
import kr.co.vividnext.sodalive.member.social.google.GoogleAuthService
|
||||||
import kr.co.vividnext.sodalive.member.social.kakao.KakaoAuthService
|
import kr.co.vividnext.sodalive.member.social.kakao.KakaoAuthService
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
import kr.co.vividnext.sodalive.useraction.UserActionService
|
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
import org.springframework.security.core.userdetails.User
|
import org.springframework.security.core.userdetails.User
|
||||||
|
@ -36,8 +34,7 @@ class MemberController(
|
||||||
private val service: MemberService,
|
private val service: MemberService,
|
||||||
private val kakaoAuthService: KakaoAuthService,
|
private val kakaoAuthService: KakaoAuthService,
|
||||||
private val googleAuthService: GoogleAuthService,
|
private val googleAuthService: GoogleAuthService,
|
||||||
private val trackingService: AdTrackingService,
|
private val trackingService: AdTrackingService
|
||||||
private val userActionService: UserActionService
|
|
||||||
) {
|
) {
|
||||||
@GetMapping("/check/email")
|
@GetMapping("/check/email")
|
||||||
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
||||||
|
@ -63,12 +60,6 @@ class MemberController(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
userActionService.recordAction(
|
|
||||||
memberId = response.memberId,
|
|
||||||
actionType = ActionType.SIGN_UP,
|
|
||||||
pushToken = request.pushToken
|
|
||||||
)
|
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +332,7 @@ class MemberController(
|
||||||
}
|
}
|
||||||
|
|
||||||
val token = authHeader.substring(7)
|
val token = authHeader.substring(7)
|
||||||
val response = googleAuthService.authenticate(token, request.container, request.marketingPid, request.pushToken)
|
val response = googleAuthService.authenticate(token, request.container, request.marketingPid)
|
||||||
|
|
||||||
if (!response.marketingPid.isNullOrBlank()) {
|
if (!response.marketingPid.isNullOrBlank()) {
|
||||||
trackingService.saveTrackingHistory(
|
trackingService.saveTrackingHistory(
|
||||||
|
@ -351,12 +342,6 @@ class MemberController(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
userActionService.recordAction(
|
|
||||||
memberId = response.memberId,
|
|
||||||
actionType = ActionType.SIGN_UP,
|
|
||||||
pushToken = request.pushToken
|
|
||||||
)
|
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +355,7 @@ class MemberController(
|
||||||
}
|
}
|
||||||
|
|
||||||
val token = authHeader.substring(7)
|
val token = authHeader.substring(7)
|
||||||
val response = kakaoAuthService.authenticate(token, request.container, request.marketingPid, request.pushToken)
|
val response = kakaoAuthService.authenticate(token, request.container, request.marketingPid)
|
||||||
|
|
||||||
if (!response.marketingPid.isNullOrBlank()) {
|
if (!response.marketingPid.isNullOrBlank()) {
|
||||||
trackingService.saveTrackingHistory(
|
trackingService.saveTrackingHistory(
|
||||||
|
@ -380,12 +365,6 @@ class MemberController(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
userActionService.recordAction(
|
|
||||||
memberId = response.memberId,
|
|
||||||
actionType = ActionType.SIGN_UP,
|
|
||||||
pushToken = request.pushToken
|
|
||||||
)
|
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,6 @@ import kr.co.vividnext.sodalive.member.stipulation.StipulationRepository
|
||||||
import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag
|
import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag
|
||||||
import kr.co.vividnext.sodalive.member.tag.MemberTagRepository
|
import kr.co.vividnext.sodalive.member.tag.MemberTagRepository
|
||||||
import kr.co.vividnext.sodalive.member.token.MemberTokenRepository
|
import kr.co.vividnext.sodalive.member.token.MemberTokenRepository
|
||||||
import kr.co.vividnext.sodalive.point.MemberPointRepository
|
|
||||||
import kr.co.vividnext.sodalive.utils.generateFileName
|
import kr.co.vividnext.sodalive.utils.generateFileName
|
||||||
import kr.co.vividnext.sodalive.utils.generatePassword
|
import kr.co.vividnext.sodalive.utils.generatePassword
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
@ -78,7 +77,6 @@ class MemberService(
|
||||||
private val memberTagRepository: MemberTagRepository,
|
private val memberTagRepository: MemberTagRepository,
|
||||||
private val liveReservationRepository: LiveReservationRepository,
|
private val liveReservationRepository: LiveReservationRepository,
|
||||||
private val chargeRepository: ChargeRepository,
|
private val chargeRepository: ChargeRepository,
|
||||||
private val memberPointRepository: MemberPointRepository,
|
|
||||||
|
|
||||||
private val orderService: OrderService,
|
private val orderService: OrderService,
|
||||||
private val emailService: SendEmailService,
|
private val emailService: SendEmailService,
|
||||||
|
@ -127,7 +125,6 @@ class MemberService(
|
||||||
gender = Gender.NONE,
|
gender = Gender.NONE,
|
||||||
container = request.container
|
container = request.container
|
||||||
)
|
)
|
||||||
member.pushToken = request.pushToken
|
|
||||||
|
|
||||||
if (!request.marketingPid.isNullOrBlank()) {
|
if (!request.marketingPid.isNullOrBlank()) {
|
||||||
member.activePid = request.marketingPid
|
member.activePid = request.marketingPid
|
||||||
|
@ -264,11 +261,6 @@ class MemberService(
|
||||||
limit = 4
|
limit = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
val totalPoint = memberPointRepository.findByMemberIdAndExpiresAtAfterOrderByExpiresAtAsc(
|
|
||||||
memberId = member.id!!,
|
|
||||||
expiresAt = LocalDateTime.now()
|
|
||||||
).sumOf { it.point }
|
|
||||||
|
|
||||||
return MyPageResponse(
|
return MyPageResponse(
|
||||||
nickname = member.nickname,
|
nickname = member.nickname,
|
||||||
profileUrl = if (member.profileImage != null) {
|
profileUrl = if (member.profileImage != null) {
|
||||||
|
@ -278,7 +270,6 @@ class MemberService(
|
||||||
},
|
},
|
||||||
chargeCan = member.getChargeCan(container = container),
|
chargeCan = member.getChargeCan(container = container),
|
||||||
rewardCan = member.getRewardCan(container = container),
|
rewardCan = member.getRewardCan(container = container),
|
||||||
point = totalPoint,
|
|
||||||
youtubeUrl = member.youtubeUrl,
|
youtubeUrl = member.youtubeUrl,
|
||||||
instagramUrl = member.instagramUrl,
|
instagramUrl = member.instagramUrl,
|
||||||
websiteUrl = member.websiteUrl,
|
websiteUrl = member.websiteUrl,
|
||||||
|
@ -789,12 +780,7 @@ class MemberService(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun findOrRegister(
|
fun findOrRegister(googleUserInfo: GoogleUserInfo, container: String, marketingPid: String?): Member {
|
||||||
googleUserInfo: GoogleUserInfo,
|
|
||||||
container: String,
|
|
||||||
marketingPid: String?,
|
|
||||||
pushToken: String?
|
|
||||||
): Member {
|
|
||||||
val findMember = repository.findByGoogleId(googleUserInfo.sub)
|
val findMember = repository.findByGoogleId(googleUserInfo.sub)
|
||||||
if (findMember != null) {
|
if (findMember != null) {
|
||||||
if (findMember.isActive) {
|
if (findMember.isActive) {
|
||||||
|
@ -824,7 +810,6 @@ class MemberService(
|
||||||
provider = MemberProvider.GOOGLE,
|
provider = MemberProvider.GOOGLE,
|
||||||
container = container
|
container = container
|
||||||
)
|
)
|
||||||
member.pushToken = pushToken
|
|
||||||
|
|
||||||
if (!marketingPid.isNullOrBlank()) {
|
if (!marketingPid.isNullOrBlank()) {
|
||||||
member.activePid = marketingPid
|
member.activePid = marketingPid
|
||||||
|
@ -838,12 +823,7 @@ class MemberService(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun findOrRegister(
|
fun findOrRegister(kakaoUserInfo: KakaoUserInfo, container: String, marketingPid: String?): Member {
|
||||||
kakaoUserInfo: KakaoUserInfo,
|
|
||||||
container: String,
|
|
||||||
marketingPid: String?,
|
|
||||||
pushToken: String?
|
|
||||||
): Member {
|
|
||||||
val findMember = repository.findByKakaoId(kakaoUserInfo.id)
|
val findMember = repository.findByKakaoId(kakaoUserInfo.id)
|
||||||
if (findMember != null) {
|
if (findMember != null) {
|
||||||
if (findMember.isActive) {
|
if (findMember.isActive) {
|
||||||
|
@ -873,7 +853,6 @@ class MemberService(
|
||||||
provider = MemberProvider.KAKAO,
|
provider = MemberProvider.KAKAO,
|
||||||
container = container
|
container = container
|
||||||
)
|
)
|
||||||
member.pushToken = pushToken
|
|
||||||
|
|
||||||
if (!marketingPid.isNullOrBlank()) {
|
if (!marketingPid.isNullOrBlank()) {
|
||||||
member.activePid = marketingPid
|
member.activePid = marketingPid
|
||||||
|
|
|
@ -3,8 +3,6 @@ package kr.co.vividnext.sodalive.member.auth
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
import kr.co.vividnext.sodalive.useraction.UserActionService
|
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
@ -13,10 +11,7 @@ import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/auth")
|
@RequestMapping("/auth")
|
||||||
class AuthController(
|
class AuthController(private val service: AuthService) {
|
||||||
private val service: AuthService,
|
|
||||||
private val userActionService: UserActionService
|
|
||||||
) {
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
fun authVerify(
|
fun authVerify(
|
||||||
@RequestBody request: AuthVerifyRequest,
|
@RequestBody request: AuthVerifyRequest,
|
||||||
|
@ -31,12 +26,6 @@ class AuthController(
|
||||||
throw SodaException("운영정책을 위반하여 이용을 제한합니다.")
|
throw SodaException("운영정책을 위반하여 이용을 제한합니다.")
|
||||||
}
|
}
|
||||||
|
|
||||||
userActionService.recordAction(
|
|
||||||
memberId = member.id!!,
|
|
||||||
actionType = ActionType.USER_AUTHENTICATION,
|
|
||||||
pushToken = member.pushToken
|
|
||||||
)
|
|
||||||
|
|
||||||
ApiResponse.ok(service.authenticate(authenticateData, member.id!!))
|
ApiResponse.ok(service.authenticate(authenticateData, member.id!!))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,4 @@ data class LoginRequest(
|
||||||
val isCreator: Boolean = false
|
val isCreator: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SocialLoginRequest(
|
data class SocialLoginRequest(val container: String, val marketingPid: String? = null)
|
||||||
val container: String,
|
|
||||||
val pushToken: String? = null,
|
|
||||||
val marketingPid: String? = null
|
|
||||||
)
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ data class MyPageResponse(
|
||||||
val profileUrl: String,
|
val profileUrl: String,
|
||||||
val chargeCan: Int,
|
val chargeCan: Int,
|
||||||
val rewardCan: Int,
|
val rewardCan: Int,
|
||||||
val point: Int,
|
|
||||||
val youtubeUrl: String?,
|
val youtubeUrl: String?,
|
||||||
val instagramUrl: String?,
|
val instagramUrl: String?,
|
||||||
val websiteUrl: String? = null,
|
val websiteUrl: String? = null,
|
||||||
|
|
|
@ -16,7 +16,6 @@ data class SignUpRequest(
|
||||||
data class SignUpRequestV2(
|
data class SignUpRequestV2(
|
||||||
val email: String,
|
val email: String,
|
||||||
val password: String,
|
val password: String,
|
||||||
val pushToken: String? = null,
|
|
||||||
val marketingPid: String? = null,
|
val marketingPid: String? = null,
|
||||||
val isAgreeTermsOfService: Boolean,
|
val isAgreeTermsOfService: Boolean,
|
||||||
val isAgreePrivacyPolicy: Boolean,
|
val isAgreePrivacyPolicy: Boolean,
|
||||||
|
|
|
@ -19,15 +19,10 @@ class GoogleAuthService(
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val cloudFrontHost: String
|
private val cloudFrontHost: String
|
||||||
) {
|
) {
|
||||||
fun authenticate(
|
fun authenticate(idToken: String, container: String, marketingPid: String?): SocialLoginResponse {
|
||||||
idToken: String,
|
|
||||||
container: String,
|
|
||||||
marketingPid: String?,
|
|
||||||
pushToken: String?
|
|
||||||
): SocialLoginResponse {
|
|
||||||
val googleUserInfo = googleService.getUserInfo(idToken)
|
val googleUserInfo = googleService.getUserInfo(idToken)
|
||||||
?: throw SodaException("구글 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
?: throw SodaException("구글 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
||||||
val member = memberService.findOrRegister(googleUserInfo, container, marketingPid, pushToken)
|
val member = memberService.findOrRegister(googleUserInfo, container, marketingPid)
|
||||||
val principal = MemberAdapter(member)
|
val principal = MemberAdapter(member)
|
||||||
val authToken = GoogleAuthenticationToken(idToken, principal.authorities)
|
val authToken = GoogleAuthenticationToken(idToken, principal.authorities)
|
||||||
authToken.setPrincipal(principal)
|
authToken.setPrincipal(principal)
|
||||||
|
|
|
@ -19,15 +19,10 @@ class KakaoAuthService(
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val cloudFrontHost: String
|
private val cloudFrontHost: String
|
||||||
) {
|
) {
|
||||||
fun authenticate(
|
fun authenticate(accessToken: String, container: String, marketingPid: String?): SocialLoginResponse {
|
||||||
accessToken: String,
|
|
||||||
container: String,
|
|
||||||
marketingPid: String?,
|
|
||||||
pushToken: String?
|
|
||||||
): SocialLoginResponse {
|
|
||||||
val kakaoUserInfo = kakaoService.getUserInfo(accessToken)
|
val kakaoUserInfo = kakaoService.getUserInfo(accessToken)
|
||||||
?: throw SodaException("카카오 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
?: throw SodaException("카카오 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
||||||
val member = memberService.findOrRegister(kakaoUserInfo, container, marketingPid, pushToken)
|
val member = memberService.findOrRegister(kakaoUserInfo, container, marketingPid)
|
||||||
val principal = MemberAdapter(member)
|
val principal = MemberAdapter(member)
|
||||||
val authToken = KakaoAuthenticationToken(accessToken, principal.authorities)
|
val authToken = KakaoAuthenticationToken(accessToken, principal.authorities)
|
||||||
authToken.setPrincipal(principal)
|
authToken.setPrincipal(principal)
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import javax.persistence.Entity
|
|
||||||
import javax.persistence.EnumType
|
|
||||||
import javax.persistence.Enumerated
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class MemberPoint(
|
|
||||||
val memberId: Long,
|
|
||||||
var point: Int,
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
val actionType: ActionType,
|
|
||||||
val expiresAt: LocalDateTime
|
|
||||||
) : BaseEntity()
|
|
|
@ -1,29 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
|
||||||
import kr.co.vividnext.sodalive.point.QMemberPoint.memberPoint
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
interface MemberPointRepository : JpaRepository<MemberPoint, Long>, MemberPointQueryRepository
|
|
||||||
|
|
||||||
interface MemberPointQueryRepository {
|
|
||||||
fun findByMemberIdAndExpiresAtAfterOrderByExpiresAtAsc(memberId: Long, expiresAt: LocalDateTime): List<MemberPoint>
|
|
||||||
}
|
|
||||||
|
|
||||||
class MemberPointQueryRepositoryImpl(
|
|
||||||
private val queryFactory: JPAQueryFactory
|
|
||||||
) : MemberPointQueryRepository {
|
|
||||||
override fun findByMemberIdAndExpiresAtAfterOrderByExpiresAtAsc(
|
|
||||||
memberId: Long,
|
|
||||||
expiresAt: LocalDateTime
|
|
||||||
): List<MemberPoint> {
|
|
||||||
return queryFactory
|
|
||||||
.selectFrom(memberPoint)
|
|
||||||
.where(
|
|
||||||
memberPoint.memberId.eq(memberId),
|
|
||||||
memberPoint.expiresAt.goe(expiresAt)
|
|
||||||
)
|
|
||||||
.fetch()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
import javax.persistence.Entity
|
|
||||||
import javax.persistence.EnumType
|
|
||||||
import javax.persistence.Enumerated
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class PointGrantLog(
|
|
||||||
val memberId: Long,
|
|
||||||
val point: Int,
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
val actionType: ActionType,
|
|
||||||
val policyId: Long?
|
|
||||||
) : BaseEntity()
|
|
|
@ -1,27 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
|
||||||
import kr.co.vividnext.sodalive.point.QPointGrantLog.pointGrantLog
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
|
|
||||||
interface PointGrantLogRepository : JpaRepository<PointGrantLog, Long>, PointGrantLogQueryRepository
|
|
||||||
|
|
||||||
interface PointGrantLogQueryRepository {
|
|
||||||
fun existsByMemberIdAndPolicyId(memberId: Long, policyId: Long): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
class PointGrantLogQueryRepositoryImpl(
|
|
||||||
private val queryFactory: JPAQueryFactory
|
|
||||||
) : PointGrantLogQueryRepository {
|
|
||||||
override fun existsByMemberIdAndPolicyId(memberId: Long, policyId: Long): Boolean {
|
|
||||||
return queryFactory
|
|
||||||
.select(pointGrantLog.id)
|
|
||||||
.from(pointGrantLog)
|
|
||||||
.where(
|
|
||||||
pointGrantLog.memberId.eq(memberId),
|
|
||||||
pointGrantLog.policyId.eq(policyId)
|
|
||||||
)
|
|
||||||
.fetch()
|
|
||||||
.isNotEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import javax.persistence.Entity
|
|
||||||
import javax.persistence.EnumType
|
|
||||||
import javax.persistence.Enumerated
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class PointRewardPolicy(
|
|
||||||
var title: String,
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
val actionType: ActionType,
|
|
||||||
val threshold: Int,
|
|
||||||
val pointAmount: Int,
|
|
||||||
var startDate: LocalDateTime,
|
|
||||||
var endDate: LocalDateTime? = null,
|
|
||||||
var isActive: Boolean = true
|
|
||||||
) : BaseEntity()
|
|
|
@ -1,36 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
|
||||||
import kr.co.vividnext.sodalive.point.QPointRewardPolicy.pointRewardPolicy
|
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
interface PointRewardPolicyRepository : JpaRepository<PointRewardPolicy, Long>, PointRewardPolicyQueryRepository
|
|
||||||
|
|
||||||
interface PointRewardPolicyQueryRepository {
|
|
||||||
fun findByActionTypeAndIsActiveTrue(actionType: ActionType, nowDateTime: LocalDateTime): PointRewardPolicy?
|
|
||||||
}
|
|
||||||
|
|
||||||
class PointRewardPolicyQueryRepositoryImpl(
|
|
||||||
private val queryFactory: JPAQueryFactory
|
|
||||||
) : PointRewardPolicyQueryRepository {
|
|
||||||
override fun findByActionTypeAndIsActiveTrue(
|
|
||||||
actionType: ActionType,
|
|
||||||
nowDateTime: LocalDateTime
|
|
||||||
): PointRewardPolicy? {
|
|
||||||
return queryFactory
|
|
||||||
.selectFrom(pointRewardPolicy)
|
|
||||||
.where(
|
|
||||||
pointRewardPolicy.isActive.isTrue
|
|
||||||
.and(pointRewardPolicy.actionType.eq(actionType))
|
|
||||||
.and(pointRewardPolicy.startDate.loe(nowDateTime))
|
|
||||||
.and(
|
|
||||||
pointRewardPolicy.endDate.goe(nowDateTime)
|
|
||||||
.or(pointRewardPolicy.endDate.isNull)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.orderBy(pointRewardPolicy.endDate.asc())
|
|
||||||
.fetchFirst()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class PointUsageService(
|
|
||||||
private val memberPointRepository: MemberPointRepository,
|
|
||||||
private val usePointRepository: UsePointRepository
|
|
||||||
) {
|
|
||||||
fun usePoint(memberId: Long, contentPrice: Int): Int {
|
|
||||||
val now = LocalDateTime.now()
|
|
||||||
val maxUsablePoint = contentPrice * 10
|
|
||||||
|
|
||||||
val points = memberPointRepository.findByMemberIdAndExpiresAtAfterOrderByExpiresAtAsc(
|
|
||||||
memberId = memberId,
|
|
||||||
expiresAt = now
|
|
||||||
)
|
|
||||||
|
|
||||||
val totalAvailable = points.sumOf { it.point }
|
|
||||||
val usablePoint = minOf(totalAvailable, maxUsablePoint).floorToNearest10()
|
|
||||||
|
|
||||||
var remaining = usablePoint
|
|
||||||
var used = 0
|
|
||||||
|
|
||||||
for (p in points) {
|
|
||||||
if (remaining <= 0) break
|
|
||||||
val usable = minOf(p.point, remaining)
|
|
||||||
p.point -= usable
|
|
||||||
remaining -= usable
|
|
||||||
used += usable
|
|
||||||
}
|
|
||||||
|
|
||||||
if (used > 0) {
|
|
||||||
memberPointRepository.saveAll(points)
|
|
||||||
usePointRepository.save(UsePoint(memberId = memberId, amount = used))
|
|
||||||
}
|
|
||||||
|
|
||||||
return used
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Int.floorToNearest10(): Int = (this / 10) * 10
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
|
||||||
import javax.persistence.Entity
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class UsePoint(
|
|
||||||
val memberId: Long,
|
|
||||||
val amount: Int
|
|
||||||
) : BaseEntity()
|
|
|
@ -1,5 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.point
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
|
|
||||||
interface UsePointRepository : JpaRepository<UsePoint, Long>
|
|
|
@ -1,6 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.useraction
|
|
||||||
|
|
||||||
enum class ActionType(val displayName: String) {
|
|
||||||
SIGN_UP("회원가입"),
|
|
||||||
USER_AUTHENTICATION("본인인증")
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.useraction
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
|
||||||
import javax.persistence.Entity
|
|
||||||
import javax.persistence.EnumType
|
|
||||||
import javax.persistence.Enumerated
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class UserActionLog(
|
|
||||||
val memberId: Long,
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
val actionType: ActionType
|
|
||||||
) : BaseEntity()
|
|
|
@ -1,39 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.useraction
|
|
||||||
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
|
||||||
import kr.co.vividnext.sodalive.useraction.QUserActionLog.userActionLog
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
interface UserActionLogRepository : JpaRepository<UserActionLog, Long>, UserActionLogQueryRepository
|
|
||||||
|
|
||||||
interface UserActionLogQueryRepository {
|
|
||||||
fun countByMemberIdAndActionTypeAndCreatedAtBetween(
|
|
||||||
memberId: Long,
|
|
||||||
actionType: ActionType,
|
|
||||||
startDate: LocalDateTime,
|
|
||||||
endDate: LocalDateTime
|
|
||||||
): Int
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserActionLogQueryRepositoryImpl(
|
|
||||||
private val queryFactory: JPAQueryFactory
|
|
||||||
) : UserActionLogQueryRepository {
|
|
||||||
override fun countByMemberIdAndActionTypeAndCreatedAtBetween(
|
|
||||||
memberId: Long,
|
|
||||||
actionType: ActionType,
|
|
||||||
startDate: LocalDateTime,
|
|
||||||
endDate: LocalDateTime
|
|
||||||
): Int {
|
|
||||||
return queryFactory
|
|
||||||
.select(userActionLog.id)
|
|
||||||
.from(userActionLog)
|
|
||||||
.where(
|
|
||||||
userActionLog.memberId.eq(memberId)
|
|
||||||
.and(userActionLog.actionType.eq(actionType))
|
|
||||||
.and(userActionLog.createdAt.between(startDate, endDate))
|
|
||||||
)
|
|
||||||
.fetch()
|
|
||||||
.size
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.useraction
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kr.co.vividnext.sodalive.fcm.FcmService
|
|
||||||
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.point.PointRewardPolicyRepository
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class UserActionService(
|
|
||||||
private val repository: UserActionLogRepository,
|
|
||||||
private val policyRepository: PointRewardPolicyRepository,
|
|
||||||
private val grantLogRepository: PointGrantLogRepository,
|
|
||||||
private val memberPointRepository: MemberPointRepository,
|
|
||||||
|
|
||||||
private val fcmService: FcmService
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
|
||||||
|
|
||||||
fun recordAction(memberId: Long, actionType: ActionType, pushToken: String?) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
val now = LocalDateTime.now()
|
|
||||||
repository.save(UserActionLog(memberId, actionType))
|
|
||||||
|
|
||||||
val policy = policyRepository.findByActionTypeAndIsActiveTrue(actionType, now)
|
|
||||||
if (policy != null) {
|
|
||||||
val actionCount = repository.countByMemberIdAndActionTypeAndCreatedAtBetween(
|
|
||||||
memberId = memberId,
|
|
||||||
actionType = actionType,
|
|
||||||
startDate = policy.startDate,
|
|
||||||
endDate = policy.endDate ?: now
|
|
||||||
)
|
|
||||||
if (actionCount < policy.threshold) return@launch
|
|
||||||
|
|
||||||
val alreadyGranted = grantLogRepository.existsByMemberIdAndPolicyId(memberId, policy.id!!)
|
|
||||||
if (alreadyGranted) return@launch
|
|
||||||
|
|
||||||
memberPointRepository.save(
|
|
||||||
MemberPoint(
|
|
||||||
memberId = memberId,
|
|
||||||
point = policy.pointAmount,
|
|
||||||
actionType = actionType,
|
|
||||||
expiresAt = now.plusDays(3)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
grantLogRepository.save(
|
|
||||||
PointGrantLog(
|
|
||||||
memberId = memberId,
|
|
||||||
point = policy.pointAmount,
|
|
||||||
actionType = actionType,
|
|
||||||
policyId = policy.id!!
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (pushToken != null) {
|
|
||||||
fcmService.sendPointGranted(pushToken, policy.pointAmount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue