Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RELEASE] v24.09.15.01 #405

Merged
merged 3 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Loading