Skip to content

Commit

Permalink
[Feat/#241] 메인 워크북 API 구현 - 회원 워크북 API (#262)
Browse files Browse the repository at this point in the history
* feat: selectAllWorkbookSubscriptionStatus, countAllWorkbookSubscription 구현

* feat: BrowseSubscribeWorkbooksUseCase 구현

* feat: browseSubscribeWorkbooks 구현

* refactor: articleInfo에 aritcleId가 포함 되도록 수정

* refactor: 코드 변경 사항 BrowseSubscribeWorkbooksUseCaseTest에 반영

* fix: memberId가 테스트 요청과 일치하지 않아 생긴 문제 해결
  • Loading branch information
belljun3395 authored Aug 1, 2024
1 parent 7c1ec42 commit 75ffe72
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.few.api.repo.dao.article
import com.few.api.repo.config.LocalCacheConfig.Companion.LOCAL_CM
import com.few.api.repo.config.LocalCacheConfig.Companion.SELECT_ARTICLE_RECORD_CACHE
import com.few.api.repo.dao.article.command.InsertFullArticleRecordCommand
import com.few.api.repo.dao.article.query.SelectArticleIdByWorkbookIdAndDayQuery
import com.few.api.repo.dao.article.query.SelectArticleRecordQuery
import com.few.api.repo.dao.article.query.SelectWorkBookArticleRecordQuery
import com.few.api.repo.dao.article.query.SelectWorkbookMappedArticleRecordsQuery
Expand Down Expand Up @@ -100,19 +101,32 @@ class ArticleDao(
return mstId.getValue(ArticleMst.ARTICLE_MST.ID)
}

fun insertArticleMstCommand(command: InsertFullArticleRecordCommand) =
dslContext.insertInto(ArticleMst.ARTICLE_MST)
.set(ArticleMst.ARTICLE_MST.MEMBER_ID, command.writerId)
.set(ArticleMst.ARTICLE_MST.MAIN_IMAGE_URL, command.mainImageURL.toString())
.set(ArticleMst.ARTICLE_MST.TITLE, command.title)
.set(ArticleMst.ARTICLE_MST.CATEGORY_CD, command.category)

fun insertArticleIfoCommand(
mstId: Long,
command: InsertFullArticleRecordCommand,
) = dslContext.insertInto(ArticleIfo.ARTICLE_IFO)
.set(ArticleIfo.ARTICLE_IFO.ARTICLE_MST_ID, mstId)
.set(ArticleIfo.ARTICLE_IFO.CONTENT, command.content)

fun insertArticleMstCommand(command: InsertFullArticleRecordCommand) =
dslContext.insertInto(ArticleMst.ARTICLE_MST)
.set(ArticleMst.ARTICLE_MST.MEMBER_ID, command.writerId)
.set(ArticleMst.ARTICLE_MST.MAIN_IMAGE_URL, command.mainImageURL.toString())
.set(ArticleMst.ARTICLE_MST.TITLE, command.title)
.set(ArticleMst.ARTICLE_MST.CATEGORY_CD, command.category)
fun selectArticleIdByWorkbookIdAndDay(query: SelectArticleIdByWorkbookIdAndDayQuery): Long? {
return selectArticleIdByWorkbookIdAndDayQuery(query)
.fetchOneInto(Long::class.java)
}

fun selectArticleIdByWorkbookIdAndDayQuery(query: SelectArticleIdByWorkbookIdAndDayQuery) =
dslContext.select(
MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.ARTICLE_ID
).from(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE)
.where(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.WORKBOOK_ID.eq(query.workbookId))
.and(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.DAY_COL.eq(query.day))
.query

fun selectArticleContents(articleIds: Set<Long>): List<SelectArticleContentsRecord> =
selectArticleContentsQuery(articleIds)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.repo.dao.article.query

data class SelectArticleIdByWorkbookIdAndDayQuery(
val workbookId: Long,
val day: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ package com.few.api.repo.dao.subscription
import com.few.api.repo.dao.subscription.command.InsertWorkbookSubscriptionCommand
import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInAllSubscriptionCommand
import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInWorkbookSubscriptionCommand
import com.few.api.repo.dao.subscription.query.CountAllWorkbooksSubscription
import com.few.api.repo.dao.subscription.query.SelectAllWorkbookSubscriptionStatusNotConsiderDeletedAtQuery
import com.few.api.repo.dao.subscription.record.WorkbookSubscriptionStatus
import com.few.api.repo.dao.subscription.query.CountWorkbookMappedArticlesQuery
import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery
import com.few.api.repo.dao.subscription.record.CountAllSubscriptionStatusRecord
import com.few.api.repo.dao.subscription.record.MemberWorkbookSubscriptionStatusRecord
import jooq.jooq_dsl.Tables.MAPPING_WORKBOOK_ARTICLE
import jooq.jooq_dsl.Tables.SUBSCRIPTION
import jooq.jooq_dsl.tables.MappingWorkbookArticle
import org.jooq.DSLContext
import org.jooq.impl.DSL
import org.springframework.stereotype.Repository
import java.time.LocalDateTime

Expand Down Expand Up @@ -64,6 +69,26 @@ class SubscriptionDao(
.orderBy(SUBSCRIPTION.CREATED_AT.desc())
.limit(1)

fun selectAllWorkbookSubscriptionStatus(query: SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery): List<MemberWorkbookSubscriptionStatusRecord> {
return selectAllWorkbookSubscriptionStatusQuery(query)
.fetchInto(MemberWorkbookSubscriptionStatusRecord::class.java)
}

fun selectAllWorkbookSubscriptionStatusQuery(query: SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery) =
dslContext.select(
SUBSCRIPTION.TARGET_WORKBOOK_ID.`as`(MemberWorkbookSubscriptionStatusRecord::workbookId.name),
SUBSCRIPTION.DELETED_AT.isNull.`as`(MemberWorkbookSubscriptionStatusRecord::isActiveSub.name),
DSL.max(SUBSCRIPTION.PROGRESS).add(1).`as`(MemberWorkbookSubscriptionStatusRecord::currentDay.name),
DSL.max(MAPPING_WORKBOOK_ARTICLE.DAY_COL).`as`(MemberWorkbookSubscriptionStatusRecord::totalDay.name)
)
.from(SUBSCRIPTION)
.leftJoin(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE)
.on(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.WORKBOOK_ID))
.where(SUBSCRIPTION.MEMBER_ID.eq(query.memberId))
.and(SUBSCRIPTION.TARGET_MEMBER_ID.isNull)
.groupBy(SUBSCRIPTION.TARGET_WORKBOOK_ID, SUBSCRIPTION.DELETED_AT)
.query

fun updateDeletedAtInAllSubscription(command: UpdateDeletedAtInAllSubscriptionCommand) {
updateDeletedAtInAllSubscriptionCommand(command)
.execute()
Expand Down Expand Up @@ -95,4 +120,22 @@ class SubscriptionDao(
.fetchOne(0, Int::class.java)!!
return CountAllSubscriptionStatusRecord(total.toLong(), active.toLong())
}

/**
* key: workbookId
* value: workbook 구독 전체 기록 수
*/
fun countAllWorkbookSubscription(query: CountAllWorkbooksSubscription): Map<Long, Int> {
return countAllWorkbookSubscriptionQuery()
.fetch()
.intoMap(SUBSCRIPTION.TARGET_WORKBOOK_ID, DSL.count(SUBSCRIPTION.TARGET_WORKBOOK_ID))
}

fun countAllWorkbookSubscriptionQuery() = dslContext.select(
SUBSCRIPTION.TARGET_WORKBOOK_ID,
DSL.count(SUBSCRIPTION.TARGET_WORKBOOK_ID)
)
.from(SUBSCRIPTION)
.groupBy(SUBSCRIPTION.TARGET_WORKBOOK_ID)
.query
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.few.api.repo.dao.subscription.query

data class CountAllWorkbooksSubscription(
val workbookIds: List<Long>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.few.api.repo.dao.subscription.query

/**
* DeleteAt을 고려하지 않고 멤버의 모든 워크북 구독 상태를 조회하는 쿼리
*/
data class SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery(
val memberId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.few.api.repo.dao.subscription.record

data class MemberWorkbookSubscriptionStatusRecord(
val workbookId: Long,
val isActiveSub: Boolean,
val currentDay: Int,
val totalDay: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.few.api.repo.explain.article

import com.few.api.repo.dao.article.ArticleDao
import com.few.api.repo.dao.article.command.InsertFullArticleRecordCommand
import com.few.api.repo.dao.article.query.SelectArticleIdByWorkbookIdAndDayQuery
import com.few.api.repo.dao.article.query.SelectArticleRecordQuery
import com.few.api.repo.dao.article.query.SelectWorkBookArticleRecordQuery
import com.few.api.repo.dao.article.query.SelectWorkbookMappedArticleRecordsQuery
Expand Down Expand Up @@ -122,4 +123,15 @@ class ArticleDaoExplainGenerateTest : JooqTestSpec() {

ResultGenerator.execute(command, explain, "insertArticleIfoCommandExplain")
}

@Test
fun selectArticleIdByWorkbookIdAndDayQueryExplain() {
val query = SelectArticleIdByWorkbookIdAndDayQuery(1L, 1).let {
articleDao.selectArticleIdByWorkbookIdAndDayQuery(it)
}

val explain = dslContext.explain(query).toString()

ResultGenerator.execute(query, explain, "selectArticleIdByWorkbookIdAndDayQueryExplain")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.few.api.repo.dao.subscription.command.InsertWorkbookSubscriptionComma
import com.few.api.repo.dao.subscription.command.UpdateDeletedAtInAllSubscriptionCommand
import com.few.api.repo.dao.subscription.query.CountWorkbookMappedArticlesQuery
import com.few.api.repo.dao.subscription.query.SelectAllWorkbookSubscriptionStatusNotConsiderDeletedAtQuery
import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery
import com.few.api.repo.explain.InsertUpdateExplainGenerator
import com.few.api.repo.explain.ResultGenerator
import com.few.api.repo.jooq.JooqTestSpec
Expand Down Expand Up @@ -59,6 +60,17 @@ class SubscriptionDaoExplainGenerateTest : JooqTestSpec() {
ResultGenerator.execute(query, explain, "selectTopWorkbookSubscriptionStatusQueryExplain")
}

@Test
fun selectAllTopWorkbookSubscriptionStatusQueryExplain() {
val query = SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery(memberId = 1L).let {
subscriptionDao.selectAllWorkbookSubscriptionStatusQuery(it)
}

val explain = dslContext.explain(query).toString()

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

@Test
fun countWorkbookMappedArticlesQueryExplain() {
val query = CountWorkbookMappedArticlesQuery(
Expand Down Expand Up @@ -99,4 +111,13 @@ class SubscriptionDaoExplainGenerateTest : JooqTestSpec() {

ResultGenerator.execute(command, explain, "updateDeletedAtInAllSubscriptionCommandExplain")
}

@Test
fun countAllWorkbookSubscriptionQueryExplain() {
val query = subscriptionDao.countAllWorkbookSubscriptionQuery()

val explain = dslContext.explain(query).toString()

ResultGenerator.execute(query, explain, "countAllWorkbookSubscriptionQueryExplain")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.few.api.domain.subscription.service

import com.few.api.domain.subscription.service.dto.ReadArticleIdByWorkbookIdAndDayDto
import com.few.api.repo.dao.article.ArticleDao
import com.few.api.repo.dao.article.query.SelectArticleIdByWorkbookIdAndDayQuery
import org.springframework.stereotype.Service

@Service
class SubscriptionArticleService(
private val articleDao: ArticleDao,
) {
fun readArticleIdByWorkbookIdAndDay(dto: ReadArticleIdByWorkbookIdAndDayDto): Long? {
SelectArticleIdByWorkbookIdAndDayQuery(dto.workbookId, dto.day).let { query ->
return articleDao.selectArticleIdByWorkbookIdAndDay(query)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.domain.subscription.service.dto

data class ReadArticleIdByWorkbookIdAndDayDto(
val workbookId: Long,
val day: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.few.api.domain.subscription.usecase

import com.fasterxml.jackson.databind.ObjectMapper
import com.few.api.domain.subscription.service.SubscriptionArticleService
import com.few.api.domain.subscription.service.dto.ReadArticleIdByWorkbookIdAndDayDto
import com.few.api.domain.subscription.usecase.dto.BrowseSubscribeWorkbooksUseCaseIn
import com.few.api.domain.subscription.usecase.dto.BrowseSubscribeWorkbooksUseCaseOut
import com.few.api.domain.subscription.usecase.dto.SubscribeWorkbookDetail
import com.few.api.repo.dao.subscription.SubscriptionDao
import com.few.api.repo.dao.subscription.query.CountAllWorkbooksSubscription
import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery
import com.few.api.web.support.WorkBookStatus
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
class BrowseSubscribeWorkbooksUseCase(
private val subscriptionDao: SubscriptionDao,
private val subscriptionArticleService: SubscriptionArticleService,
private val objectMapper: ObjectMapper,
) {
@Transactional
fun execute(useCaseIn: BrowseSubscribeWorkbooksUseCaseIn): BrowseSubscribeWorkbooksUseCaseOut {
val subscriptionRecords =
SelectAllMemberWorkbookSubscriptionStatusNotConsiderDeletedAtQuery(useCaseIn.memberId).let {
subscriptionDao.selectAllWorkbookSubscriptionStatus(it)
}

/**
* key: workbookId
* value: workbook의 currentDay에 해당하는 articleId
*/
val workbookSubscriptionCurrentArticleIdRecords = subscriptionRecords.associate { it ->
val articleId = ReadArticleIdByWorkbookIdAndDayDto(it.workbookId, it.currentDay).let {
subscriptionArticleService.readArticleIdByWorkbookIdAndDay(it)
} ?: throw IllegalArgumentException("articleId is null")
it.workbookId to articleId
}

val subscriptionWorkbookIds = subscriptionRecords.map { it.workbookId }
val workbookSubscriptionCountRecords =
CountAllWorkbooksSubscription(subscriptionWorkbookIds).let {
subscriptionDao.countAllWorkbookSubscription(it)
}

subscriptionRecords.map {
/**
* 임시 코드
* Batch 코드에서 currentDay가 totalDay보다 큰 경우가 발생하여
* currentDay가 totalDay보다 크면 totalDay로 변경
* */
var currentDay = it.currentDay
if (it.currentDay > it.totalDay) {
currentDay = it.totalDay
}

SubscribeWorkbookDetail(
workbookId = it.workbookId,
isActiveSub = WorkBookStatus.fromStatus(it.isActiveSub),
currentDay = currentDay,
totalDay = it.totalDay,
totalSubscriber = workbookSubscriptionCountRecords[it.workbookId]?.toLong() ?: 0,
articleInfo = objectMapper.writeValueAsString(
mapOf(
"articleId" to workbookSubscriptionCurrentArticleIdRecords[it.workbookId]
)
)
)
}.let {
return BrowseSubscribeWorkbooksUseCaseOut(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.few.api.domain.subscription.usecase.dto

data class BrowseSubscribeWorkbooksUseCaseIn(
val memberId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.few.api.domain.subscription.usecase.dto

import com.few.api.web.support.WorkBookStatus

data class BrowseSubscribeWorkbooksUseCaseOut(
val workbooks: List<SubscribeWorkbookDetail>,
)

data class SubscribeWorkbookDetail(
val workbookId: Long,
val isActiveSub: WorkBookStatus,
val currentDay: Int,
val totalDay: Int,
val rank: Long = 0,
val totalSubscriber: Long,
val articleInfo: String = "{}",
)
Loading

0 comments on commit 75ffe72

Please sign in to comment.