Skip to content

Commit

Permalink
[RELEASE] v24.09.15.01 (#405)
Browse files Browse the repository at this point in the history
  • Loading branch information
belljun3395 authored Sep 15, 2024
2 parents 03c176e + 604e254 commit 4eb8774
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package com.few.api.repo.dao.subscription

import com.few.api.repo.dao.subscription.command.*
import com.few.api.repo.dao.subscription.query.*
import com.few.api.repo.dao.subscription.record.WorkbookSubscriptionStatus
import com.few.api.repo.dao.subscription.record.CountAllSubscriptionStatusRecord
import com.few.api.repo.dao.subscription.record.MemberWorkbookSubscriptionStatusRecord
import com.few.api.repo.dao.subscription.record.SubscriptionSendStatusRecord
import com.few.api.repo.dao.subscription.record.*
import jooq.jooq_dsl.Tables.MAPPING_WORKBOOK_ARTICLE
import jooq.jooq_dsl.Tables.SUBSCRIPTION
import jooq.jooq_dsl.tables.MappingWorkbookArticle
import jooq.jooq_dsl.tables.Subscription
import org.jooq.DSLContext
import org.jooq.impl.DSL
import org.springframework.stereotype.Repository
Expand Down Expand Up @@ -54,6 +52,7 @@ class SubscriptionDao(
dslContext.update(SUBSCRIPTION)
.set(SUBSCRIPTION.DELETED_AT, null as LocalDateTime?)
.set(SUBSCRIPTION.UNSUBS_OPINION, null as String?)
.set(SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId))
.and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId))

Expand All @@ -65,6 +64,7 @@ class SubscriptionDao(
fun updateDeletedAtInWorkbookSubscriptionCommand(command: UpdateDeletedAtInWorkbookSubscriptionCommand) =
dslContext.update(SUBSCRIPTION)
.set(SUBSCRIPTION.DELETED_AT, LocalDateTime.now())
.set(Subscription.SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.set(SUBSCRIPTION.UNSUBS_OPINION, command.opinion)
.where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId))
.and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId))
Expand Down Expand Up @@ -140,6 +140,7 @@ class SubscriptionDao(
fun updateDeletedAtInAllSubscriptionCommand(command: UpdateDeletedAtInAllSubscriptionCommand) =
dslContext.update(SUBSCRIPTION)
.set(SUBSCRIPTION.DELETED_AT, LocalDateTime.now())
.set(SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.set(SUBSCRIPTION.UNSUBS_OPINION, command.opinion) // TODO: opinion row 마다 중복 해결
.where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId))

Expand Down Expand Up @@ -191,6 +192,8 @@ class SubscriptionDao(
command: UpdateArticleProgressCommand,
) = dslContext.update(SUBSCRIPTION)
.set(SUBSCRIPTION.PROGRESS, SUBSCRIPTION.PROGRESS.add(1))
.set(SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.set(SUBSCRIPTION.SEND_AT, LocalDateTime.now())
.where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId))
.and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId))

Expand All @@ -202,6 +205,8 @@ class SubscriptionDao(
fun updateLastArticleProgressCommand(command: UpdateLastArticleProgressCommand) =
dslContext.update(SUBSCRIPTION)
.set(SUBSCRIPTION.DELETED_AT, LocalDateTime.now())
.set(SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.set(SUBSCRIPTION.SEND_AT, LocalDateTime.now())
.set(SUBSCRIPTION.UNSUBS_OPINION, command.opinion)
.where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId))
.and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId))
Expand Down Expand Up @@ -258,4 +263,23 @@ class SubscriptionDao(
.set(SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId))
.and(SUBSCRIPTION.TARGET_WORKBOOK_ID.`in`(command.workbookIds))

fun selectSubscriptionTimeRecord(
query: SelectSubscriptionQuery,
): SubscriptionTimeRecord? {
return selectSubscriptionTimeRecordQuery(query)
.fetchOneInto(SubscriptionTimeRecord::class.java)
}

fun selectSubscriptionTimeRecordQuery(query: SelectSubscriptionQuery) =
dslContext.select(
SUBSCRIPTION.MEMBER_ID.`as`(SubscriptionTimeRecord::memberId.name),
SUBSCRIPTION.TARGET_WORKBOOK_ID.`as`(SubscriptionTimeRecord::workbookId.name),
SUBSCRIPTION.CREATED_AT.`as`(SubscriptionTimeRecord::createdAt.name),
SUBSCRIPTION.MODIFIED_AT.`as`(SubscriptionTimeRecord::modifiedAt.name),
SUBSCRIPTION.SEND_AT.`as`(SubscriptionTimeRecord::sendAt.name)
)
.from(SUBSCRIPTION)
.where(SUBSCRIPTION.MEMBER_ID.eq(query.memberId))
.and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(query.workbookId))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.repo.dao.subscription.query

data class SelectSubscriptionQuery(
val memberId: Long,
val workbookId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.few.api.repo.dao.subscription.record

import java.time.LocalDateTime

data class SubscriptionTimeRecord(
val memberId: Long,
val workbookId: Long,
val createdAt: LocalDateTime,
val modifiedAt: LocalDateTime,
val sendAt: LocalDateTime?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,18 @@ class SubscriptionDaoExplainGenerateTest : JooqTestSpec() {

ResultGenerator.execute(query, explain, "selectAllSubscriptionSendStatusQueryExplain")
}

@Test
fun selectSubscriptionTimeRecordQueryExplain() {
val query = subscriptionDao.selectSubscriptionTimeRecordQuery(
SelectSubscriptionQuery(
memberId = 1L,
workbookId = 1L
)
)

val explain = ExplainGenerator.execute(dslContext, query)

ResultGenerator.execute(query, explain, "selectSubscriptionTimeRecordQueryExplain")
}
}
49 changes: 38 additions & 11 deletions api/src/main/kotlin/com/few/api/domain/common/lock/LockAspect.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,33 @@ class LockAspect(
getLockFor(joinPoint).run {
when (this.identifier) {
LockIdentifier.SUBSCRIPTION_MEMBER_ID_WORKBOOK_ID -> {
val useCaseIn = joinPoint.args[0] as SubscribeWorkbookUseCaseIn
getSubscriptionMemberIdAndWorkBookIdLock(useCaseIn)
getSubscriptionMemberIdAndWorkBookIdLockCase(joinPoint)
}
}
}
}

private fun getSubscriptionMemberIdAndWorkBookIdLockCase(joinPoint: JoinPoint) {
if (joinPoint.args[0] is SubscribeWorkbookUseCaseIn) {
val useCaseIn = joinPoint.args[0] as SubscribeWorkbookUseCaseIn
getSubscriptionMemberIdAndWorkBookIdLock(useCaseIn)
} else {
val memberId = joinPoint.args[0] as Long
val workbookId = joinPoint.args[1] as Long
getSubscriptionMemberIdAndWorkBookIdLock(memberId, workbookId)
}
}

private fun getSubscriptionMemberIdAndWorkBookIdLock(useCaseIn: SubscribeWorkbookUseCaseIn) {
subscriptionDao.getLock(useCaseIn.memberId, useCaseIn.workbookId).run {
getSubscriptionMemberIdAndWorkBookIdLock(useCaseIn.memberId, useCaseIn.workbookId)
}

private fun getSubscriptionMemberIdAndWorkBookIdLock(memberId: Long, workbookId: Long) {
subscriptionDao.getLock(memberId, workbookId).run {
if (!this) {
throw IllegalStateException("Already in progress for ${useCaseIn.memberId}'s subscription to ${useCaseIn.workbookId}")
throw IllegalStateException("Already in progress for $memberId's subscription to $workbookId")
}
log.debug { "Lock acquired for ${useCaseIn.memberId}'s subscription to ${useCaseIn.workbookId}" }
log.debug { "Lock acquired for $memberId's subscription to $workbookId" }
}
}

Expand All @@ -48,8 +62,7 @@ class LockAspect(
getLockFor(joinPoint).run {
when (this.identifier) {
LockIdentifier.SUBSCRIPTION_MEMBER_ID_WORKBOOK_ID -> {
val useCaseIn = joinPoint.args[0] as SubscribeWorkbookUseCaseIn
releaseSubscriptionMemberIdAndWorkBookIdLock(useCaseIn)
releaseSubscriptionMemberIdAndWorkBookIdLockCase(joinPoint)
}
}
}
Expand All @@ -60,8 +73,7 @@ class LockAspect(
getLockFor(joinPoint).run {
when (this.identifier) {
LockIdentifier.SUBSCRIPTION_MEMBER_ID_WORKBOOK_ID -> {
val useCaseIn = joinPoint.args[0] as SubscribeWorkbookUseCaseIn
releaseSubscriptionMemberIdAndWorkBookIdLock(useCaseIn)
releaseSubscriptionMemberIdAndWorkBookIdLockCase(joinPoint)
}
}
}
Expand All @@ -70,8 +82,23 @@ class LockAspect(
private fun getLockFor(joinPoint: JoinPoint) =
(joinPoint.signature as MethodSignature).method.getAnnotation(LockFor::class.java)

private fun releaseSubscriptionMemberIdAndWorkBookIdLockCase(joinPoint: JoinPoint) {
if (joinPoint.args[0] is SubscribeWorkbookUseCaseIn) {
val useCaseIn = joinPoint.args[0] as SubscribeWorkbookUseCaseIn
releaseSubscriptionMemberIdAndWorkBookIdLock(useCaseIn)
} else {
val memberId = joinPoint.args[0] as Long
val workbookId = joinPoint.args[1] as Long
releaseSubscriptionMemberIdAndWorkBookIdLock(memberId, workbookId)
}
}

private fun releaseSubscriptionMemberIdAndWorkBookIdLock(useCaseIn: SubscribeWorkbookUseCaseIn) {
subscriptionDao.releaseLock(useCaseIn.memberId, useCaseIn.workbookId)
log.debug { "Lock released for ${useCaseIn.memberId}'s subscription to ${useCaseIn.workbookId}" }
releaseSubscriptionMemberIdAndWorkBookIdLock(useCaseIn.memberId, useCaseIn.workbookId)
}

private fun releaseSubscriptionMemberIdAndWorkBookIdLock(memberId: Long, workbookId: Long) {
subscriptionDao.releaseLock(memberId, workbookId)
log.debug { "Lock released for $memberId's subscription to $workbookId" }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ class DeleteMemberUseCase(

memberSubscriptionService.deleteSubscription(
DeleteSubscriptionDto(
memberId = useCaseIn.memberId,
opinion = "cancel"
memberId = useCaseIn.memberId
)
)
return DeleteMemberUseCaseOut(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package com.few.api.domain.subscription.event
import com.few.api.domain.subscription.event.dto.WorkbookSubscriptionEvent
import com.few.api.domain.subscription.handler.SendWorkbookArticleAsyncHandler
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener

Expand All @@ -14,7 +12,6 @@ class WorkbookSubscriptionAfterCompletionEventListener(
) {

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun handleEvent(event: WorkbookSubscriptionEvent) {
sendWorkbookArticleAsyncHandler.sendWorkbookArticle(
event.memberId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.few.api.domain.subscription.handler

import com.few.api.config.DatabaseAccessThreadPoolConfig.Companion.DATABASE_ACCESS_POOL
import com.few.api.domain.common.lock.LockFor
import com.few.api.domain.common.lock.LockIdentifier
import com.few.api.domain.subscription.service.SubscriptionArticleService
import com.few.api.domain.subscription.service.SubscriptionMemberService
import com.few.api.domain.subscription.service.SubscriptionEmailService
Expand All @@ -10,11 +12,14 @@ import com.few.api.exception.common.NotFoundException
import com.few.api.repo.dao.subscription.SubscriptionDao
import com.few.api.repo.dao.subscription.command.UpdateArticleProgressCommand
import com.few.api.repo.dao.subscription.command.UpdateLastArticleProgressCommand
import com.few.api.repo.dao.subscription.query.SelectSubscriptionQuery
import com.few.data.common.code.CategoryType
import com.few.email.service.article.dto.Content
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate

@Component
Expand All @@ -28,8 +33,22 @@ class SendWorkbookArticleAsyncHandler(
private val log = KotlinLogging.logger {}

@Async(value = DATABASE_ACCESS_POOL)
@LockFor(identifier = LockIdentifier.SUBSCRIPTION_MEMBER_ID_WORKBOOK_ID)
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun sendWorkbookArticle(memberId: Long, workbookId: Long, articleDayCol: Byte) {
val date = LocalDate.now()

subscriptionDao.selectSubscriptionTimeRecord(
SelectSubscriptionQuery(
memberId = memberId,
workbookId = workbookId
)
)?.let {
if (it.sendAt?.isAfter(date.atStartOfDay()) == true) {
return
}
}

val memberEmail = memberService.readMemberEmail(ReadMemberEmailInDto(memberId))?.email
?: throw NotFoundException("member.notfound.id")
val article = articleService.readArticleIdByWorkbookIdAndDay(
Expand Down Expand Up @@ -70,18 +89,18 @@ class SendWorkbookArticleAsyncHandler(
)
)?.lastArticleId ?: throw NotFoundException("workbook.notfound.id")

if (article.id == lastDayArticleId) {
if (article.id != lastDayArticleId) {
subscriptionDao.updateArticleProgress(
UpdateArticleProgressCommand(
workbookId,
memberId
memberId,
workbookId
)
)
} else {
subscriptionDao.updateLastArticleProgress(
UpdateLastArticleProgressCommand(
workbookId,
memberId
memberId,
workbookId
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class WorkBookSubscriberWriter(
updateQueries.add(
dslContext.update(Subscription.SUBSCRIPTION)
.set(Subscription.SUBSCRIPTION.PROGRESS, updateTargetMemberRecord.updatedProgress)
.set(Subscription.SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.set(Subscription.SUBSCRIPTION.SEND_AT, LocalDateTime.now())
.where(Subscription.SUBSCRIPTION.MEMBER_ID.eq(updateTargetMemberRecord.memberId))
.and(Subscription.SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(updateTargetMemberRecord.targetWorkBookId))
)
Expand All @@ -101,6 +103,8 @@ class WorkBookSubscriberWriter(
receiveLastDayQueries.add(
dslContext.update(Subscription.SUBSCRIPTION)
.set(Subscription.SUBSCRIPTION.DELETED_AT, LocalDateTime.now())
.set(Subscription.SUBSCRIPTION.MODIFIED_AT, LocalDateTime.now())
.set(Subscription.SUBSCRIPTION.SEND_AT, LocalDateTime.now())
.set(Subscription.SUBSCRIPTION.UNSUBS_OPINION, "receive.all")
.where(Subscription.SUBSCRIPTION.MEMBER_ID.eq(receiveLastDayMember.memberId))
.and(Subscription.SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(receiveLastDayMember.targetWorkBookId))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- 구독 발송 시간 컬럼 추가
ALTER TABLE SUBSCRIPTION ADD COLUMN send_at TIMESTAMP;

0 comments on commit 4eb8774

Please sign in to comment.