From be5d0515e8363ed9ad028261563ff03f8f30b6f8 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 6 Sep 2024 10:28:14 +0900 Subject: [PATCH] =?UTF-8?q?[Feat/#331]=20My=20=EA=B5=AC=EB=8F=85=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20API=20=EC=B6=94=EA=B0=80=20(#376)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 구독 관리를 위한 send_time, send_day 컬럼 추가 * feat: workbook 제목 조회 쿼리 추가 * feat: 구독 전송 상태 조회 쿼리 추가 * feat: selectAllWorkbookInActiveSubscriptionStatusQuery progress에 따른 정렬 조건 추가 * feat: workbook 제목 조회 서비스 구현 * refactor: SUBSCRIBE_WORKBOOK_STRATEGY에 따라 workbook 구독 정보 조회할 수 있도록 수정 * feat: DayCode webConfig에 등록 * feat: myPage ViewCategory 추가 * refactor: /subscriptions/workbooks view에 따른 구분 및 /subscriptions/workbooks/main 삭제 --- .../repo/dao/subscription/SubscriptionDao.kt | 26 ++- .../SelectAllSubscriptionSendStatusQuery.kt | 6 + .../record/SubscriptionSendStatusRecord.kt | 9 + .../few/api/repo/dao/workbook/WorkbookDao.kt | 15 ++ .../query/SelectAllWorkbookTitleQuery.kt | 5 + .../workbook/record/WorkbookTitleRecord.kt | 6 + .../SubscriptionDaoExplainGenerateTest.kt | 19 +- .../WorkbookDaoExplainGenerateTest.kt | 14 ++ .../service/SubscriptionWorkbookService.kt | 15 +- .../service/dto/ReadAllWorkbookTitleInDto.kt | 5 + .../BrowseSubscribeWorkbooksUseCase.kt | 168 ++++++++++++----- .../dto/BrowseSubscribeWorkbooksUseCaseIn.kt | 3 + .../dto/BrowseSubscribeWorkbooksUseCaseOut.kt | 50 +++++- .../com/few/api/web/config/WebConfig.kt | 2 + .../web/config/converter/DayCodeConverter.kt | 10 ++ .../subscription/SubscriptionController.kt | 144 ++++----------- ...ainViewBrowseSubscribeWorkbooksResponse.kt | 30 ---- .../response/SubscribeWorkbooksResponse.kt | 44 ++++- .../kotlin/com/few/api/web/support/DayCode.kt | 137 ++++++++++++++ .../com/few/api/web/support/ViewCategory.kt | 1 + .../BrowseSubscribeWorkbooksUseCaseTest.kt | 170 +++++++++++++++--- .../SubscriptionControllerTest.kt | 137 ++++++++++++-- .../V1.00.0.21__add_subscription_time.sql | 2 + .../V1.00.0.22__add_subscription_day.sql | 2 + 24 files changed, 779 insertions(+), 241 deletions(-) create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/query/SelectAllSubscriptionSendStatusQuery.kt create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/record/SubscriptionSendStatusRecord.kt create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/query/SelectAllWorkbookTitleQuery.kt create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/record/WorkbookTitleRecord.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/subscription/service/dto/ReadAllWorkbookTitleInDto.kt create mode 100644 api/src/main/kotlin/com/few/api/web/config/converter/DayCodeConverter.kt delete mode 100644 api/src/main/kotlin/com/few/api/web/controller/subscription/response/MainViewBrowseSubscribeWorkbooksResponse.kt create mode 100644 api/src/main/kotlin/com/few/api/web/support/DayCode.kt create mode 100644 data/db/migration/entity/V1.00.0.21__add_subscription_time.sql create mode 100644 data/db/migration/entity/V1.00.0.22__add_subscription_day.sql diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt index 4513b4b29..fb6825bbf 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/SubscriptionDao.kt @@ -5,6 +5,7 @@ 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 jooq.jooq_dsl.Tables.MAPPING_WORKBOOK_ARTICLE import jooq.jooq_dsl.Tables.SUBSCRIPTION import jooq.jooq_dsl.tables.MappingWorkbookArticle @@ -79,8 +80,10 @@ class SubscriptionDao( 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) + DSL.max(SUBSCRIPTION.PROGRESS).add(1) + .`as`(MemberWorkbookSubscriptionStatusRecord::currentDay.name), + DSL.max(MAPPING_WORKBOOK_ARTICLE.DAY_COL) + .`as`(MemberWorkbookSubscriptionStatusRecord::totalDay.name) ) .from(SUBSCRIPTION) .join(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE) @@ -89,6 +92,7 @@ class SubscriptionDao( .and(SUBSCRIPTION.TARGET_MEMBER_ID.isNull) .and(SUBSCRIPTION.UNSUBS_OPINION.eq(query.unsubOpinion)) .groupBy(SUBSCRIPTION.TARGET_WORKBOOK_ID, SUBSCRIPTION.DELETED_AT) + .orderBy(SUBSCRIPTION.PROGRESS) .query fun selectAllActiveWorkbookSubscriptionStatus(query: SelectAllMemberWorkbookActiveSubscriptionQuery): List { @@ -110,6 +114,7 @@ class SubscriptionDao( .and(SUBSCRIPTION.TARGET_MEMBER_ID.isNull) .and(SUBSCRIPTION.UNSUBS_OPINION.isNull) .groupBy(SUBSCRIPTION.TARGET_WORKBOOK_ID, SUBSCRIPTION.DELETED_AT) + .orderBy(SUBSCRIPTION.PROGRESS) .query fun updateDeletedAtInAllSubscription(command: UpdateDeletedAtInAllSubscriptionCommand) { @@ -185,4 +190,21 @@ class SubscriptionDao( .set(SUBSCRIPTION.UNSUBS_OPINION, command.opinion) .where(SUBSCRIPTION.MEMBER_ID.eq(command.memberId)) .and(SUBSCRIPTION.TARGET_WORKBOOK_ID.eq(command.workbookId)) + + fun selectAllSubscriptionSendStatus(query: SelectAllSubscriptionSendStatusQuery): List { + return selectAllSubscriptionSendStatusQuery(query) + .fetchInto( + SubscriptionSendStatusRecord::class.java + ) + } + + fun selectAllSubscriptionSendStatusQuery(query: SelectAllSubscriptionSendStatusQuery) = + dslContext.select( + SUBSCRIPTION.TARGET_WORKBOOK_ID.`as`(SubscriptionSendStatusRecord::workbookId.name), + SUBSCRIPTION.SEND_TIME.`as`(SubscriptionSendStatusRecord::sendTime.name), + SUBSCRIPTION.SEND_DAY.`as`(SubscriptionSendStatusRecord::sendDay.name) + ) + .from(SUBSCRIPTION) + .where(SUBSCRIPTION.MEMBER_ID.eq(query.memberId)) + .and(SUBSCRIPTION.TARGET_WORKBOOK_ID.`in`(query.workbookIds)) } \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/query/SelectAllSubscriptionSendStatusQuery.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/query/SelectAllSubscriptionSendStatusQuery.kt new file mode 100644 index 000000000..811924ec2 --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/query/SelectAllSubscriptionSendStatusQuery.kt @@ -0,0 +1,6 @@ +package com.few.api.repo.dao.subscription.query + +data class SelectAllSubscriptionSendStatusQuery( + val memberId: Long, + val workbookIds: List, +) \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/record/SubscriptionSendStatusRecord.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/record/SubscriptionSendStatusRecord.kt new file mode 100644 index 000000000..048fa2a9b --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/subscription/record/SubscriptionSendStatusRecord.kt @@ -0,0 +1,9 @@ +package com.few.api.repo.dao.subscription.record + +import java.time.LocalTime + +data class SubscriptionSendStatusRecord( + val workbookId: Long, + val sendTime: LocalTime, + val sendDay: String, +) \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt index 2cc8ea845..561a0e00b 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/WorkbookDao.kt @@ -5,10 +5,12 @@ import com.few.api.repo.config.LocalCacheConfig.Companion.LOCAL_CM import com.few.api.repo.dao.workbook.command.InsertWorkBookCommand import com.few.api.repo.dao.workbook.command.MapWorkBookToArticleCommand import com.few.api.repo.dao.workbook.query.BrowseWorkBookQueryWithSubscriptionCountQuery +import com.few.api.repo.dao.workbook.query.SelectAllWorkbookTitleQuery import com.few.api.repo.dao.workbook.query.SelectWorkBookLastArticleIdQuery import com.few.api.repo.dao.workbook.query.SelectWorkBookRecordQuery import com.few.api.repo.dao.workbook.record.SelectWorkBookRecord import com.few.api.repo.dao.workbook.record.SelectWorkBookRecordWithSubscriptionCount +import com.few.api.repo.dao.workbook.record.WorkbookTitleRecord import com.few.data.common.code.CategoryType import jooq.jooq_dsl.tables.MappingWorkbookArticle import jooq.jooq_dsl.tables.Subscription @@ -144,4 +146,17 @@ class WorkbookDao( .where(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.WORKBOOK_ID.eq(query.workbookId)) .and(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.DELETED_AT.isNull) .groupBy(MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE.WORKBOOK_ID) + + fun selectAllWorkbookTitle(query: SelectAllWorkbookTitleQuery): List { + return selectAllWorkbookTitleQuery(query) + .fetchInto(WorkbookTitleRecord::class.java) + } + + fun selectAllWorkbookTitleQuery(query: SelectAllWorkbookTitleQuery) = + dslContext.select( + Workbook.WORKBOOK.ID.`as`(WorkbookTitleRecord::workbookId.name), + Workbook.WORKBOOK.TITLE.`as`(WorkbookTitleRecord::title.name) + ) + .from(Workbook.WORKBOOK) + .where(Workbook.WORKBOOK.ID.`in`(query.workbookIds)) } \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/query/SelectAllWorkbookTitleQuery.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/query/SelectAllWorkbookTitleQuery.kt new file mode 100644 index 000000000..1bfa772db --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/query/SelectAllWorkbookTitleQuery.kt @@ -0,0 +1,5 @@ +package com.few.api.repo.dao.workbook.query + +data class SelectAllWorkbookTitleQuery( + val workbookIds: List, +) \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/record/WorkbookTitleRecord.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/record/WorkbookTitleRecord.kt new file mode 100644 index 000000000..f6531c5f5 --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/workbook/record/WorkbookTitleRecord.kt @@ -0,0 +1,6 @@ +package com.few.api.repo.dao.workbook.record + +data class WorkbookTitleRecord( + val workbookId: Long, + val title: String, +) \ No newline at end of file diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt index 47339debb..c918e0a49 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/subscription/SubscriptionDaoExplainGenerateTest.kt @@ -2,10 +2,7 @@ package com.few.api.repo.explain.subscription import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.command.* -import com.few.api.repo.dao.subscription.query.CountWorkbookMappedArticlesQuery -import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookActiveSubscriptionQuery -import com.few.api.repo.dao.subscription.query.SelectAllWorkbookSubscriptionStatusNotConsiderDeletedAtQuery -import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookInActiveSubscriptionQuery +import com.few.api.repo.dao.subscription.query.* import com.few.api.repo.explain.ExplainGenerator import com.few.api.repo.explain.InsertUpdateExplainGenerator import com.few.api.repo.explain.ResultGenerator @@ -190,4 +187,18 @@ class SubscriptionDaoExplainGenerateTest : JooqTestSpec() { ResultGenerator.execute(command, explain, "updateLastArticleProgressCommandExplain") } + + @Test + fun selectAllSubscriptionSendStatusQueryExplain() { + val query = subscriptionDao.selectAllSubscriptionSendStatusQuery( + SelectAllSubscriptionSendStatusQuery( + memberId = 1L, + workbookIds = listOf(1L) + ) + ) + + val explain = ExplainGenerator.execute(dslContext, query) + + ResultGenerator.execute(query, explain, "selectAllSubscriptionSendStatusQueryExplain") + } } \ No newline at end of file diff --git a/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt b/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt index f99de4ce0..db176769b 100644 --- a/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt +++ b/api-repo/src/test/kotlin/com/few/api/repo/explain/workbook/WorkbookDaoExplainGenerateTest.kt @@ -4,6 +4,7 @@ import com.few.api.repo.dao.workbook.WorkbookDao import com.few.api.repo.dao.workbook.command.InsertWorkBookCommand import com.few.api.repo.dao.workbook.command.MapWorkBookToArticleCommand import com.few.api.repo.dao.workbook.query.BrowseWorkBookQueryWithSubscriptionCountQuery +import com.few.api.repo.dao.workbook.query.SelectAllWorkbookTitleQuery import com.few.api.repo.dao.workbook.query.SelectWorkBookLastArticleIdQuery import com.few.api.repo.dao.workbook.query.SelectWorkBookRecordQuery import com.few.api.repo.explain.ExplainGenerator @@ -118,4 +119,17 @@ class WorkbookDaoExplainGenerateTest : JooqTestSpec() { ResultGenerator.execute(query, explain, "selectWorkBookLastArticleIdQueryExplain") } + + @Test + fun selectAllWorkbookTitleQueryExplain() { + val query = workbookDao.selectAllWorkbookTitleQuery( + SelectAllWorkbookTitleQuery( + listOf(1L) + ) + ) + + val explain = ExplainGenerator.execute(dslContext, query) + + ResultGenerator.execute(query, explain, "selectAllWorkbookTitleQueryExplain") + } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/service/SubscriptionWorkbookService.kt b/api/src/main/kotlin/com/few/api/domain/subscription/service/SubscriptionWorkbookService.kt index 1362062e5..967446fa9 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/service/SubscriptionWorkbookService.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/service/SubscriptionWorkbookService.kt @@ -1,10 +1,8 @@ package com.few.api.domain.subscription.service -import com.few.api.domain.subscription.service.dto.ReadWorkbookLastArticleIdInDto -import com.few.api.domain.subscription.service.dto.ReadWorkbookLastArticleIdOutDto -import com.few.api.domain.subscription.service.dto.ReadWorkbookTitleInDto -import com.few.api.domain.subscription.service.dto.ReadWorkbookTitleOutDto +import com.few.api.domain.subscription.service.dto.* import com.few.api.repo.dao.workbook.WorkbookDao +import com.few.api.repo.dao.workbook.query.SelectAllWorkbookTitleQuery import com.few.api.repo.dao.workbook.query.SelectWorkBookLastArticleIdQuery import com.few.api.repo.dao.workbook.query.SelectWorkBookRecordQuery import org.springframework.stereotype.Service @@ -24,6 +22,15 @@ class SubscriptionWorkbookService( } } + /** + * key: workbookId + * value: title + */ + fun readAllWorkbookTitle(dto: ReadAllWorkbookTitleInDto): Map { + return workbookDao.selectAllWorkbookTitle(SelectAllWorkbookTitleQuery(dto.workbookIds)) + .associateBy({ it.workbookId }, { it.title }) + } + fun readWorkbookLastArticleId(dto: ReadWorkbookLastArticleIdInDto): ReadWorkbookLastArticleIdOutDto? { return workbookDao.selectWorkBookLastArticleId(SelectWorkBookLastArticleIdQuery(dto.workbookId)) ?.let { diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/service/dto/ReadAllWorkbookTitleInDto.kt b/api/src/main/kotlin/com/few/api/domain/subscription/service/dto/ReadAllWorkbookTitleInDto.kt new file mode 100644 index 000000000..d662bb2c0 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/subscription/service/dto/ReadAllWorkbookTitleInDto.kt @@ -0,0 +1,5 @@ +package com.few.api.domain.subscription.service.dto + +data class ReadAllWorkbookTitleInDto( + val workbookIds: List, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt index deb2cc8d9..70b72404e 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt @@ -2,80 +2,160 @@ 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.SubscriptionWorkbookService +import com.few.api.domain.subscription.service.dto.ReadAllWorkbookTitleInDto 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.domain.subscription.usecase.dto.* import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.query.CountAllWorkbooksSubscriptionQuery import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookActiveSubscriptionQuery import com.few.api.repo.dao.subscription.query.SelectAllMemberWorkbookInActiveSubscriptionQuery +import com.few.api.repo.dao.subscription.query.SelectAllSubscriptionSendStatusQuery +import com.few.api.web.support.ViewCategory import com.few.api.web.support.WorkBookStatus import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional import org.webjars.NotFoundException +import java.lang.IllegalStateException +enum class SUBSCRIBE_WORKBOOK_STRATEGY { + /** + * 로그인 상태에서 메인 화면에 보여질 워크북을 정렬합니다. + * - view의 값이 MAIN_CARD이다. + * */ + MAIN_CARD, + + /** + * 마이페이지에서 보여질 워크북을 정렬합니다. + * - view의 값이 MY_PAGE이다. + * */ + MY_PAGE, +} + +data class ArticleInfo( + // todo fix articleId to id + val articleId: Long, +) + +data class WorkbookInfo( + val id: Long, + val title: String, +) + +// todo refactor to model @Component class BrowseSubscribeWorkbooksUseCase( private val subscriptionDao: SubscriptionDao, private val subscriptionArticleService: SubscriptionArticleService, + private val subscriptionWorkbookService: SubscriptionWorkbookService, private val objectMapper: ObjectMapper, ) { @Transactional fun execute(useCaseIn: BrowseSubscribeWorkbooksUseCaseIn): BrowseSubscribeWorkbooksUseCaseOut { - val inActiveSubscriptionRecords = - subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus( - SelectAllMemberWorkbookInActiveSubscriptionQuery(useCaseIn.memberId) - ) - - val activeSubscriptionRecords = subscriptionDao.selectAllActiveWorkbookSubscriptionStatus( - SelectAllMemberWorkbookActiveSubscriptionQuery(useCaseIn.memberId) - ) - - val subscriptionRecords = inActiveSubscriptionRecords + activeSubscriptionRecords - - /** - * key: workbookId - * value: workbook의 currentDay에 해당하는 articleId - */ - val workbookSubscriptionCurrentArticleIdRecords = subscriptionRecords.associate { record -> - val articleId = subscriptionArticleService.readArticleIdByWorkbookIdAndDay( - ReadArticleIdByWorkbookIdAndDayDto(record.workbookId, record.currentDay) - ) ?: throw NotFoundException("article.notfound.workbookIdAndCurrentDay") + val strategy = when (useCaseIn.view) { + ViewCategory.MAIN_CARD -> SUBSCRIBE_WORKBOOK_STRATEGY.MAIN_CARD + ViewCategory.MY_PAGE -> SUBSCRIBE_WORKBOOK_STRATEGY.MY_PAGE + } - record.workbookId to articleId + val workbookStatusRecords = when (strategy) { + SUBSCRIBE_WORKBOOK_STRATEGY.MAIN_CARD -> { + val activeSubscriptionRecords = subscriptionDao.selectAllActiveWorkbookSubscriptionStatus( + SelectAllMemberWorkbookActiveSubscriptionQuery(useCaseIn.memberId) + ) + val inActiveSubscriptionRecords = + subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus( + SelectAllMemberWorkbookInActiveSubscriptionQuery(useCaseIn.memberId) + ) + activeSubscriptionRecords + inActiveSubscriptionRecords + } + SUBSCRIBE_WORKBOOK_STRATEGY.MY_PAGE -> { + subscriptionDao.selectAllActiveWorkbookSubscriptionStatus( + SelectAllMemberWorkbookActiveSubscriptionQuery(useCaseIn.memberId) + ) + } } - val subscriptionWorkbookIds = subscriptionRecords.map { it.workbookId } - val workbookSubscriptionCountRecords = subscriptionDao.countAllWorkbookSubscription( + val subscriptionWorkbookIds = workbookStatusRecords.map { it.workbookId } + val subscriptionWorkbookCountRecords = subscriptionDao.countAllWorkbookSubscription( CountAllWorkbooksSubscriptionQuery(subscriptionWorkbookIds) ) + val subscriptionWorkbookSendStatusRecords = subscriptionDao.selectAllSubscriptionSendStatus( + SelectAllSubscriptionSendStatusQuery(useCaseIn.memberId, subscriptionWorkbookIds) + ).associateBy { it.workbookId } - subscriptionRecords.map { - /** - * 임시 코드 - * Batch 코드에서 currentDay가 totalDay보다 큰 경우가 발생하여 - * currentDay가 totalDay보다 크면 totalDay로 변경 - * */ - var currentDay = it.currentDay - if (it.currentDay > it.totalDay) { - currentDay = it.totalDay - } - + val workbookDetails = workbookStatusRecords.map { SubscribeWorkbookDetail( workbookId = it.workbookId, isActiveSub = WorkBookStatus.fromStatus(it.isActiveSub), - currentDay = currentDay, + currentDay = it.currentDay, totalDay = it.totalDay, - totalSubscriber = workbookSubscriptionCountRecords[it.workbookId]?.toLong() ?: 0, - articleInfo = objectMapper.writeValueAsString( - mapOf( - "articleId" to workbookSubscriptionCurrentArticleIdRecords[it.workbookId] + totalSubscriber = subscriptionWorkbookCountRecords[it.workbookId]?.toLong() ?: 0, + subscription = subscriptionWorkbookSendStatusRecords[it.workbookId]?.let { record -> + Subscription( + time = record.sendTime, + dateTimeCode = record.sendDay ) - ) + } ?: throw IllegalStateException("${it.workbookId}'s subscription send status is null") ) - }.let { - return BrowseSubscribeWorkbooksUseCaseOut(it) + } + + return when (strategy) { + SUBSCRIBE_WORKBOOK_STRATEGY.MAIN_CARD -> { + val workbookSubscriptionCurrentArticleIdRecords = workbookStatusRecords.associate { record -> + val articleId = subscriptionArticleService.readArticleIdByWorkbookIdAndDay( + ReadArticleIdByWorkbookIdAndDayDto(record.workbookId, record.currentDay) + ) ?: throw NotFoundException("article.notfound.workbookIdAndCurrentDay") + + record.workbookId to articleId + } + + BrowseSubscribeWorkbooksUseCaseOut( + clazz = MainCardSubscribeWorkbookDetail::class.java, + workbooks = workbookDetails.map { + MainCardSubscribeWorkbookDetail( + workbookId = it.workbookId, + isActiveSub = it.isActiveSub, + currentDay = it.currentDay, + totalDay = it.totalDay, + totalSubscriber = it.totalSubscriber, + subscription = it.subscription, + articleInfo = objectMapper.writeValueAsString( + ArticleInfo( + workbookSubscriptionCurrentArticleIdRecords[it.workbookId] + ?: throw IllegalStateException("${it.workbookId}'s articleId is null") + ) + ) + ) + } + ) + } + + SUBSCRIBE_WORKBOOK_STRATEGY.MY_PAGE -> { + val workbookTitleRecords = subscriptionWorkbookService.readAllWorkbookTitle( + ReadAllWorkbookTitleInDto(subscriptionWorkbookIds) + ) + + BrowseSubscribeWorkbooksUseCaseOut( + clazz = MyPageSubscribeWorkbookDetail::class.java, + workbooks = workbookDetails.map { + MyPageSubscribeWorkbookDetail( + workbookId = it.workbookId, + isActiveSub = it.isActiveSub, + currentDay = it.currentDay, + totalDay = it.totalDay, + totalSubscriber = it.totalSubscriber, + subscription = it.subscription, + workbookInfo = objectMapper.writeValueAsString( + WorkbookInfo( + id = it.workbookId, + title = workbookTitleRecords[it.workbookId] + ?: throw IllegalStateException("${it.workbookId}'s title is null") + ) + ) + ) + } + ) + } } } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseIn.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseIn.kt index 7fb389b90..e59b47284 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseIn.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseIn.kt @@ -1,5 +1,8 @@ package com.few.api.domain.subscription.usecase.dto +import com.few.api.web.support.ViewCategory + data class BrowseSubscribeWorkbooksUseCaseIn( val memberId: Long, + val view: ViewCategory, ) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseOut.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseOut.kt index 5ec182507..5138f197e 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseOut.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/dto/BrowseSubscribeWorkbooksUseCaseOut.kt @@ -1,17 +1,65 @@ package com.few.api.domain.subscription.usecase.dto +import com.fasterxml.jackson.annotation.JsonFormat +import com.few.api.web.support.DayCode import com.few.api.web.support.WorkBookStatus +import java.time.LocalTime data class BrowseSubscribeWorkbooksUseCaseOut( val workbooks: List, + val clazz: Class, ) -data class SubscribeWorkbookDetail( +open class SubscribeWorkbookDetail( val workbookId: Long, val isActiveSub: WorkBookStatus, val currentDay: Int, val totalDay: Int, val rank: Long = 0, val totalSubscriber: Long, + val subscription: Subscription, +) + +data class Subscription( + @JsonFormat(pattern = "HH:mm") + val time: LocalTime = LocalTime.of(0, 0), + val dateTimeCode: String = DayCode.MON_TUE_WED_THU_FRI_SAT_SUN.code, +) + +class MainCardSubscribeWorkbookDetail( + workbookId: Long, + isActiveSub: WorkBookStatus, + currentDay: Int, + totalDay: Int, + rank: Long = 0, + totalSubscriber: Long, + subscription: Subscription, val articleInfo: String = "{}", +) : SubscribeWorkbookDetail( + workbookId = workbookId, + isActiveSub = isActiveSub, + currentDay = currentDay, + totalDay = totalDay, + rank = rank, + totalSubscriber = totalSubscriber, + subscription = subscription +) + +class MyPageSubscribeWorkbookDetail( + workbookId: Long, + isActiveSub: WorkBookStatus, + currentDay: Int, + totalDay: Int, + rank: Long = 0, + totalSubscriber: Long, + subscription: Subscription, + val workbookInfo: String = "{}", +) : SubscribeWorkbookDetail( + workbookId = workbookId, + isActiveSub = isActiveSub, + currentDay = currentDay, + totalDay = totalDay, + rank = rank, + totalSubscriber = totalSubscriber, + subscription = subscription ) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/config/WebConfig.kt b/api/src/main/kotlin/com/few/api/web/config/WebConfig.kt index 494650b4a..404982450 100644 --- a/api/src/main/kotlin/com/few/api/web/config/WebConfig.kt +++ b/api/src/main/kotlin/com/few/api/web/config/WebConfig.kt @@ -1,5 +1,6 @@ package com.few.api.web.config +import com.few.api.web.config.converter.DayCodeConverter import com.few.api.web.config.converter.ViewConverter import com.few.api.web.config.converter.WorkBookCategoryConverter import org.springframework.context.annotation.Configuration @@ -28,5 +29,6 @@ class WebConfig : WebMvcConfigurer { override fun addFormatters(registry: FormatterRegistry) { registry.addConverter(WorkBookCategoryConverter()) registry.addConverter(ViewConverter()) + registry.addConverter(DayCodeConverter()) } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/config/converter/DayCodeConverter.kt b/api/src/main/kotlin/com/few/api/web/config/converter/DayCodeConverter.kt new file mode 100644 index 000000000..35645d020 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/web/config/converter/DayCodeConverter.kt @@ -0,0 +1,10 @@ +package com.few.api.web.config.converter + +import com.few.api.web.support.DayCode +import org.springframework.core.convert.converter.Converter + +class DayCodeConverter : Converter { + override fun convert(source: String): DayCode { + return DayCode.fromCode(source) + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/controller/subscription/SubscriptionController.kt b/api/src/main/kotlin/com/few/api/web/controller/subscription/SubscriptionController.kt index 88bf98f5e..583fdf5e0 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/subscription/SubscriptionController.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/subscription/SubscriptionController.kt @@ -7,23 +7,11 @@ import com.few.api.web.support.ApiResponseGenerator import com.few.api.domain.subscription.usecase.SubscribeWorkbookUseCase import com.few.api.domain.subscription.usecase.UnsubscribeAllUseCase import com.few.api.domain.subscription.usecase.UnsubscribeWorkbookUseCase -import com.few.api.domain.subscription.usecase.dto.BrowseSubscribeWorkbooksUseCaseIn -import com.few.api.domain.subscription.usecase.dto.SubscribeWorkbookUseCaseIn -import com.few.api.domain.subscription.usecase.dto.UnsubscribeAllUseCaseIn -import com.few.api.domain.subscription.usecase.dto.UnsubscribeWorkbookUseCaseIn -import com.few.api.domain.workbook.usecase.BrowseWorkbooksUseCase -import com.few.api.domain.workbook.usecase.dto.BrowseWorkbooksUseCaseIn +import com.few.api.domain.subscription.usecase.dto.* import com.few.api.security.authentication.token.TokenUserDetails -import com.few.api.security.filter.token.AccessTokenResolver -import com.few.api.security.token.TokenResolver import com.few.api.web.controller.subscription.request.UnsubscribeAllRequest -import com.few.api.web.controller.subscription.response.MainViewBrowseSubscribeWorkbooksResponse -import com.few.api.web.controller.subscription.response.MainViewSubscribeWorkbookInfo -import com.few.api.web.controller.subscription.response.SubscribeWorkbookInfo -import com.few.api.web.controller.subscription.response.SubscribeWorkbooksResponse +import com.few.api.web.controller.subscription.response.* import com.few.api.web.support.ViewCategory -import com.few.api.web.support.WorkBookCategory -import jakarta.servlet.http.HttpServletRequest import jakarta.validation.Valid import jakarta.validation.constraints.Min import org.springframework.http.HttpStatus @@ -31,6 +19,7 @@ import org.springframework.http.MediaType import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.* +import java.lang.IllegalStateException @Validated @RestController @@ -40,10 +29,6 @@ class SubscriptionController( private val unsubscribeWorkbookUseCase: UnsubscribeWorkbookUseCase, private val unsubscribeAllUseCase: UnsubscribeAllUseCase, private val browseSubscribeWorkbooksUseCase: BrowseSubscribeWorkbooksUseCase, - private val tokenResolver: TokenResolver, - - // 임시 구현용 - private val browseWorkBooksUseCase: BrowseWorkbooksUseCase, ) { @GetMapping("/subscriptions/workbooks") @@ -52,110 +37,45 @@ class SubscriptionController( @RequestParam( value = "view", required = false - ) view: ViewCategory? = ViewCategory.MAIN_CARD, + ) view: ViewCategory?, ): ApiResponse> { val memberId = userDetails.username.toLong() - val useCaseOut = BrowseSubscribeWorkbooksUseCaseIn(memberId).let { - browseSubscribeWorkbooksUseCase.execute(it) - } - - return SubscribeWorkbooksResponse( - workbooks = useCaseOut.workbooks.map { - SubscribeWorkbookInfo( - id = it.workbookId, - currentDay = it.currentDay, - totalDay = it.totalDay, - status = it.isActiveSub.name, - rank = it.rank, - totalSubscriber = it.totalSubscriber, - articleInfo = it.articleInfo - ) - } - ).let { - ApiResponseGenerator.success(it, HttpStatus.OK) - } - } - - @GetMapping("/subscriptions/workbooks/main") - fun mainViewBrowseSubscribeWorkbooks( - servletRequest: HttpServletRequest, - @RequestParam(value = "category", required = false) - category: WorkBookCategory?, - ): ApiResponse> { - val authorization: String? = servletRequest.getHeader("Authorization") - val memberId = authorization?.let { - AccessTokenResolver.resolve(it) - }.let { - tokenResolver.resolveId(it) - } - - if (memberId != null) { - val memberSubscribeWorkbooks = BrowseSubscribeWorkbooksUseCaseIn(memberId).let { + val useCaseOut = + BrowseSubscribeWorkbooksUseCaseIn(memberId, view ?: ViewCategory.MAIN_CARD).let { browseSubscribeWorkbooksUseCase.execute(it) } - val workbooks = - BrowseWorkbooksUseCaseIn( - category ?: WorkBookCategory.All, - ViewCategory.MAIN_CARD, - memberId - ).let { useCaseIn -> - browseWorkBooksUseCase.execute(useCaseIn) - } - return MainViewBrowseSubscribeWorkbooksResponse( - workbooks = workbooks.workbooks.map { - MainViewSubscribeWorkbookInfo( - id = it.id, - mainImageUrl = it.mainImageUrl, - title = it.title, - description = it.description, - category = it.category, - createdAt = it.createdAt, - writerDetails = it.writerDetails, - subscriptionCount = it.subscriptionCount, - status = memberSubscribeWorkbooks.workbooks.find { subscribe -> subscribe.workbookId == it.id }?.isActiveSub?.name, - totalDay = memberSubscribeWorkbooks.workbooks.find { subscribe -> subscribe.workbookId == it.id }?.totalDay, - currentDay = memberSubscribeWorkbooks.workbooks.find { subscribe -> subscribe.workbookId == it.id }?.currentDay, - rank = memberSubscribeWorkbooks.workbooks.find { subscribe -> subscribe.workbookId == it.id }?.rank, - totalSubscriber = memberSubscribeWorkbooks.workbooks.find { subscribe -> subscribe.workbookId == it.id }?.totalSubscriber, - articleInfo = memberSubscribeWorkbooks.workbooks.find { subscribe -> subscribe.workbookId == it.id }?.articleInfo + // todo fix to facade usecase + return SubscribeWorkbooksResponse( + workbooks = when (useCaseOut.clazz) { + MainCardSubscribeWorkbookDetail::class.java -> useCaseOut.workbooks.map { it as MainCardSubscribeWorkbookDetail }.map { + MainCardSubscribeWorkbookInfo( + id = it.workbookId, + status = it.isActiveSub.name, + totalDay = it.totalDay, + currentDay = it.currentDay, + rank = it.rank, + totalSubscriber = it.totalSubscriber, + subscription = it.subscription, + articleInfo = it.articleInfo ) } - ).let { - ApiResponseGenerator.success(it, HttpStatus.OK) - } - } else { - val workbooks = - BrowseWorkbooksUseCaseIn( - category ?: WorkBookCategory.All, - ViewCategory.MAIN_CARD, - memberId - ).let { useCaseIn -> - browseWorkBooksUseCase.execute(useCaseIn) - } - - return MainViewBrowseSubscribeWorkbooksResponse( - workbooks = workbooks.workbooks.map { - MainViewSubscribeWorkbookInfo( - id = it.id, - mainImageUrl = it.mainImageUrl, - title = it.title, - description = it.description, - category = it.category, - createdAt = it.createdAt, - writerDetails = it.writerDetails, - subscriptionCount = it.subscriptionCount, - status = null, - totalDay = null, - currentDay = null, - rank = null, - totalSubscriber = null, - articleInfo = null + MyPageSubscribeWorkbookDetail::class.java -> useCaseOut.workbooks.map { it as MyPageSubscribeWorkbookDetail }.map { + MyPageSubscribeWorkbookInfo( + id = it.workbookId, + status = it.isActiveSub.name, + totalDay = it.totalDay, + currentDay = it.currentDay, + rank = it.rank, + totalSubscriber = it.totalSubscriber, + subscription = it.subscription, + workbookInfo = it.workbookInfo ) } - ).let { - ApiResponseGenerator.success(it, HttpStatus.OK) + else -> throw IllegalStateException("Invalid class type") } + ).let { + ApiResponseGenerator.success(it, HttpStatus.OK) } } diff --git a/api/src/main/kotlin/com/few/api/web/controller/subscription/response/MainViewBrowseSubscribeWorkbooksResponse.kt b/api/src/main/kotlin/com/few/api/web/controller/subscription/response/MainViewBrowseSubscribeWorkbooksResponse.kt deleted file mode 100644 index ccf23008a..000000000 --- a/api/src/main/kotlin/com/few/api/web/controller/subscription/response/MainViewBrowseSubscribeWorkbooksResponse.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.few.api.web.controller.subscription.response - -import com.fasterxml.jackson.annotation.JsonFormat -import com.fasterxml.jackson.annotation.JsonInclude -import com.few.api.domain.workbook.usecase.dto.WriterDetail -import java.net.URL -import java.time.LocalDateTime - -data class MainViewBrowseSubscribeWorkbooksResponse( - val workbooks: List, -) - -@JsonInclude(JsonInclude.Include.NON_NULL) -data class MainViewSubscribeWorkbookInfo( - val id: Long, - val mainImageUrl: URL, - val title: String, - val description: String, - val category: String, - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") - val createdAt: LocalDateTime, - val writerDetails: List, - val subscriptionCount: Long, - val status: String?, // convert from enum - val totalDay: Int?, - val currentDay: Int?, - val rank: Long?, - val totalSubscriber: Long?, - val articleInfo: String?, // convert from Json -) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/controller/subscription/response/SubscribeWorkbooksResponse.kt b/api/src/main/kotlin/com/few/api/web/controller/subscription/response/SubscribeWorkbooksResponse.kt index f7ad75dab..e8c51f615 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/subscription/response/SubscribeWorkbooksResponse.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/subscription/response/SubscribeWorkbooksResponse.kt @@ -1,15 +1,55 @@ package com.few.api.web.controller.subscription.response +import com.few.api.domain.subscription.usecase.dto.Subscription + data class SubscribeWorkbooksResponse( val workbooks: List, ) -data class SubscribeWorkbookInfo( +open class SubscribeWorkbookInfo( val id: Long, val status: String, // convert from enum val totalDay: Int, val currentDay: Int, val rank: Long, val totalSubscriber: Long, - val articleInfo: String, // convert from Json + val subscription: Subscription, +) + +class MainCardSubscribeWorkbookInfo( + id: Long, + status: String, + totalDay: Int, + currentDay: Int, + rank: Long, + totalSubscriber: Long, + subscription: Subscription, + val articleInfo: String, +) : SubscribeWorkbookInfo( + id = id, + status = status, + totalDay = totalDay, + currentDay = currentDay, + rank = rank, + totalSubscriber = totalSubscriber, + subscription = subscription +) + +class MyPageSubscribeWorkbookInfo( + id: Long, + status: String, + totalDay: Int, + currentDay: Int, + rank: Long, + totalSubscriber: Long, + subscription: Subscription, + val workbookInfo: String, +) : SubscribeWorkbookInfo( + id = id, + status = status, + totalDay = totalDay, + currentDay = currentDay, + rank = rank, + totalSubscriber = totalSubscriber, + subscription = subscription ) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/support/DayCode.kt b/api/src/main/kotlin/com/few/api/web/support/DayCode.kt new file mode 100644 index 000000000..2c67f4107 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/web/support/DayCode.kt @@ -0,0 +1,137 @@ +package com.few.api.web.support + +enum class DayCode(val code: String, val days: String) { + MON("0000001", "월"), + TUE("0000010", "화"), + MON_TUE("0000011", "월,화"), + WED("0000100", "수"), + MON_WED("0000101", "월,수"), + TUE_WED("0000110", "화,수"), + MON_TUE_WED("0000111", "월,화,수"), + THU("0001000", "목"), + MON_THU("0001001", "월,목"), + TUE_THU("0001010", "화,목"), + MON_TUE_THU("0001011", "월,화,목"), + WED_THU("0001100", "수,목"), + MON_WED_THU("0001101", "월,수,목"), + TUE_WED_THU("0001110", "화,수,목"), + MON_TUE_WED_THU("0001111", "월,화,수,목"), + FRI("0010000", "금"), + MON_FRI("0010001", "월,금"), + TUE_FRI("0010010", "화,금"), + MON_TUE_FRI("0010011", "월,화,금"), + WED_FRI("0010100", "수,금"), + MON_WED_FRI("0010101", "월,수,금"), + TUE_WED_FRI("0010110", "화,수,금"), + MON_TUE_WED_FRI("0010111", "월,화,수,금"), + THU_FRI("0011000", "목,금"), + MON_THU_FRI("0011001", "월,목,금"), + TUE_THU_FRI("0011010", "화,목,금"), + MON_TUE_THU_FRI("0011011", "월,화,목,금"), + WED_THU_FRI("0011100", "수,목,금"), + MON_WED_THU_FRI("0011101", "월,수,목,금"), + TUE_WED_THU_FRI("0011110", "화,수,목,금"), + MON_TUE_WED_THU_FRI("0011111", "월,화,수,목,금"), + SAT("0100000", "토"), + MON_SAT("0100001", "월,토"), + TUE_SAT("0100010", "화,토"), + MON_TUE_SAT("0100011", "월,화,토"), + WED_SAT("0100100", "수,토"), + MON_WED_SAT("0100101", "월,수,토"), + TUE_WED_SAT("0100110", "화,수,토"), + MON_TUE_WED_SAT("0100111", "월,화,수,토"), + THU_SAT("0101000", "목,토"), + MON_THU_SAT("0101001", "월,목,토"), + TUE_THU_SAT("0101010", "화,목,토"), + MON_TUE_THU_SAT("0101011", "월,화,목,토"), + WED_THU_SAT("0101100", "수,목,토"), + MON_WED_THU_SAT("0101101", "월,수,목,토"), + TUE_WED_THU_SAT("0101110", "화,수,목,토"), + MON_TUE_WED_THU_SAT("0101111", "월,화,수,목,토"), + FRI_SAT("0110000", "금,토"), + MON_FRI_SAT("0110001", "월,금,토"), + TUE_FRI_SAT("0110010", "화,금,토"), + MON_TUE_FRI_SAT("0110011", "월,화,금,토"), + WED_FRI_SAT("0110100", "수,금,토"), + MON_WED_FRI_SAT("0110101", "월,수,금,토"), + TUE_WED_FRI_SAT("0110110", "화,수,금,토"), + MON_TUE_WED_FRI_SAT("0110111", "월,화,수,금,토"), + THU_FRI_SAT("0111000", "목,금,토"), + MON_THU_FRI_SAT("0111001", "월,목,금,토"), + TUE_THU_FRI_SAT("0111010", "화,목,금,토"), + MON_TUE_THU_FRI_SAT("0111011", "월,화,목,금,토"), + WED_THU_FRI_SAT("0111100", "수,목,금,토"), + MON_WED_THU_FRI_SAT("0111101", "월,수,목,금,토"), + TUE_WED_THU_FRI_SAT("0111110", "화,수,목,금,토"), + MON_TUE_WED_THU_FRI_SAT("0111111", "월,화,수,목,금,토"), + SUN("1000000", "일"), + MON_SUN("1000001", "월,일"), + TUE_SUN("1000010", "화,일"), + MON_TUE_SUN("1000011", "월,화,일"), + WED_SUN("1000100", "수,일"), + MON_WED_SUN("1000101", "월,수,일"), + TUE_WED_SUN("1000110", "화,수,일"), + MON_TUE_WED_SUN("1000111", "월,화,수,일"), + THU_SUN("1001000", "목,일"), + MON_THU_SUN("1001001", "월,목,일"), + TUE_THU_SUN("1001010", "화,목,일"), + MON_TUE_THU_SUN("1001011", "월,화,목,일"), + WED_THU_SUN("1001100", "수,목,일"), + MON_WED_THU_SUN("1001101", "월,수,목,일"), + TUE_WED_THU_SUN("1001110", "화,수,목,일"), + MON_TUE_WED_THU_SUN("1001111", "월,화,수,목,일"), + FRI_SUN("1010000", "금,일"), + MON_FRI_SUN("1010001", "월,금,일"), + TUE_FRI_SUN("1010010", "화,금,일"), + MON_TUE_FRI_SUN("1010011", "월,화,금,일"), + WED_FRI_SUN("1010100", "수,금,일"), + MON_WED_FRI_SUN("1010101", "월,수,금,일"), + TUE_WED_FRI_SUN("1010110", "화,수,금,일"), + MON_TUE_WED_FRI_SUN("1010111", "월,화,수,금,일"), + THU_FRI_SUN("1011000", "목,금,일"), + MON_THU_FRI_SUN("1011001", "월,목,금,일"), + TUE_THU_FRI_SUN("1011010", "화,목,금,일"), + MON_TUE_THU_FRI_SUN("1011011", "월,화,목,금,일"), + WED_THU_FRI_SUN("1011100", "수,목,금,일"), + MON_WED_THU_FRI_SUN("1011101", "월,수,목,금,일"), + TUE_WED_THU_FRI_SUN("1011110", "화,수,목,금,일"), + MON_TUE_WED_THU_FRI_SUN("1011111", "월,화,수,목,금,일"), + SAT_SUN("1100000", "토,일"), + MON_SAT_SUN("1100001", "월,토,일"), + TUE_SAT_SUN("1100010", "화,토,일"), + MON_TUE_SAT_SUN("1100011", "월,화,토,일"), + WED_SAT_SUN("1100100", "수,토,일"), + MON_WED_SAT_SUN("1100101", "월,수,토,일"), + TUE_WED_SAT_SUN("1100110", "화,수,토,일"), + MON_TUE_WED_SAT_SUN("1100111", "월,화,수,토,일"), + THU_SAT_SUN("1101000", "목,토,일"), + MON_THU_SAT_SUN("1101001", "월,목,토,일"), + TUE_THU_SAT_SUN("1101010", "화,목,토,일"), + MON_TUE_THU_SAT_SUN("1101011", "월,화,목,토,일"), + WED_THU_SAT_SUN("1101100", "수,목,토,일"), + MON_WED_THU_SAT_SUN("1101101", "월,수,목,토,일"), + TUE_WED_THU_SAT_SUN("1101110", "화,수,목,토,일"), + MON_TUE_WED_THU_SAT_SUN("1101111", "월,화,수,목,토,일"), + FRI_SAT_SUN("1110000", "금,토,일"), + MON_FRI_SAT_SUN("1110001", "월,금,토,일"), + TUE_FRI_SAT_SUN("1110010", "화,금,토,일"), + MON_TUE_FRI_SAT_SUN("1110011", "월,화,금,토,일"), + WED_FRI_SAT_SUN("1110100", "수,금,토,일"), + MON_WED_FRI_SAT_SUN("1110101", "월,수,금,토,일"), + TUE_WED_FRI_SAT_SUN("1110110", "화,수,금,토,일"), + MON_TUE_WED_FRI_SAT_SUN("1110111", "월,화,수,금,토,일"), + THU_FRI_SAT_SUN("1111000", "목,금,토,일"), + MON_THU_FRI_SAT_SUN("1111001", "월,목,금,토,일"), + TUE_THU_FRI_SAT_SUN("1111010", "화,목,금,토,일"), + MON_TUE_THU_FRI_SAT_SUN("1111011", "월,화,목,금,토,일"), + WED_THU_FRI_SAT_SUN("1111100", "수,목,금,토,일"), + MON_WED_THU_FRI_SAT_SUN("1111101", "월,수,목,금,토,일"), + TUE_WED_THU_FRI_SAT_SUN("1111110", "화,수,목,금,토,일"), + MON_TUE_WED_THU_FRI_SAT_SUN("1111111", "월,화,수,목,금,토,일"), + ; + companion object { + fun fromCode(code: String): DayCode { + return entries.first { it.code == code } + } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/support/ViewCategory.kt b/api/src/main/kotlin/com/few/api/web/support/ViewCategory.kt index cce04ffd8..514cff4e4 100644 --- a/api/src/main/kotlin/com/few/api/web/support/ViewCategory.kt +++ b/api/src/main/kotlin/com/few/api/web/support/ViewCategory.kt @@ -2,6 +2,7 @@ package com.few.api.web.support enum class ViewCategory(val viewName: String) { MAIN_CARD("mainCard"), + MY_PAGE("myPage"), ; companion object { diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt index b7bb374df..11f0794ad 100644 --- a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCaseTest.kt @@ -2,9 +2,15 @@ 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.SubscriptionWorkbookService import com.few.api.domain.subscription.usecase.dto.BrowseSubscribeWorkbooksUseCaseIn +import com.few.api.domain.subscription.usecase.dto.MainCardSubscribeWorkbookDetail +import com.few.api.domain.subscription.usecase.dto.MyPageSubscribeWorkbookDetail import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.record.MemberWorkbookSubscriptionStatusRecord +import com.few.api.repo.dao.subscription.record.SubscriptionSendStatusRecord +import com.few.api.web.support.DayCode +import com.few.api.web.support.ViewCategory import com.few.api.web.support.WorkBookStatus import io.github.oshai.kotlinlogging.KotlinLogging import io.kotest.core.spec.style.BehaviorSpec @@ -12,40 +18,37 @@ import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import io.mockk.verify +import java.time.LocalTime class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ val log = KotlinLogging.logger {} lateinit var subscriptionDao: SubscriptionDao lateinit var subscriptionArticleService: SubscriptionArticleService + lateinit var subscriptionWorkbookService: SubscriptionWorkbookService lateinit var objectMapper: ObjectMapper lateinit var useCase: BrowseSubscribeWorkbooksUseCase beforeContainer { subscriptionDao = mockk() subscriptionArticleService = mockk() + subscriptionWorkbookService = mockk() objectMapper = mockk() - useCase = BrowseSubscribeWorkbooksUseCase(subscriptionDao, subscriptionArticleService, objectMapper) + useCase = BrowseSubscribeWorkbooksUseCase( + subscriptionDao, + subscriptionArticleService, + subscriptionWorkbookService, + objectMapper + ) } - given("멤버의 구독 워크북 정보 조회 요청이 온 상황에서") { + given("메인 카드 뷰에서 멤버의 구독 워크북 정보를 조회하는 경우") { val memberId = 1L - val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId = memberId) + val useCaseIn = + BrowseSubscribeWorkbooksUseCaseIn(memberId = memberId, view = ViewCategory.MAIN_CARD) `when`("멤버의 구독 워크북 정보가 존재할 경우") { - val inactiveWorkbookId = 1L - val inactiveWorkbookCurrentDay = 2 - val inactiveWorkbookTotalDay = 3 - every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns listOf( - MemberWorkbookSubscriptionStatusRecord( - workbookId = inactiveWorkbookId, - isActiveSub = false, - currentDay = inactiveWorkbookCurrentDay, - totalDay = inactiveWorkbookTotalDay - ) - ) - - val activeWorkbookId = 2L + val activeWorkbookId = 1L val activeWorkbookCurrentDay = 1 val activeWorkbookTotalDay = 3 every { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } returns listOf( @@ -55,7 +58,18 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ currentDay = activeWorkbookCurrentDay, totalDay = activeWorkbookTotalDay ) + ) + val inactiveWorkbookId = 2L + val inactiveWorkbookCurrentDay = 2 + val inactiveWorkbookTotalDay = 3 + every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns listOf( + MemberWorkbookSubscriptionStatusRecord( + workbookId = inactiveWorkbookId, + isActiveSub = false, + currentDay = inactiveWorkbookCurrentDay, + totalDay = inactiveWorkbookTotalDay + ) ) every { @@ -69,21 +83,26 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ activeWorkbookId to activeWorkbookSubscriptionCount ) - every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$inactiveWorkbookId}" andThen "{\"articleId\":$activeWorkbookId}" + every { subscriptionDao.selectAllSubscriptionSendStatus(any()) } returns listOf( + SubscriptionSendStatusRecord( + workbookId = inactiveWorkbookId, + sendTime = LocalTime.of(8, 0), + sendDay = DayCode.MON_TUE_WED_THU_FRI_SAT_SUN.code + ), + SubscriptionSendStatusRecord( + workbookId = activeWorkbookId, + sendTime = LocalTime.of(8, 0), + sendDay = DayCode.MON_TUE_WED_THU_FRI_SAT_SUN.code + ) + ) + + every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$activeWorkbookId}" andThen "{\"articleId\":$inactiveWorkbookId}" then("멤버의 구독 워크북 정보를 반환한다") { val useCaseOut = useCase.execute(useCaseIn) useCaseOut.workbooks.size shouldBe 2 - val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] - inActiveSubscriptionWorkbook.workbookId shouldBe inactiveWorkbookId - inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE - inActiveSubscriptionWorkbook.currentDay shouldBe inactiveWorkbookCurrentDay - inActiveSubscriptionWorkbook.totalDay shouldBe inactiveWorkbookTotalDay - inActiveSubscriptionWorkbook.totalSubscriber shouldBe inactiveWorkbookSubscriptionCount - inActiveSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$inactiveWorkbookId}" - - val activeSubscriptionWorkbook = useCaseOut.workbooks[1] + val activeSubscriptionWorkbook = useCaseOut.workbooks[0] as MainCardSubscribeWorkbookDetail activeSubscriptionWorkbook.workbookId shouldBe activeWorkbookId activeSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.ACTIVE activeSubscriptionWorkbook.currentDay shouldBe activeWorkbookCurrentDay @@ -91,10 +110,19 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ activeSubscriptionWorkbook.totalSubscriber shouldBe activeWorkbookSubscriptionCount activeSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$activeWorkbookId}" + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[1] as MainCardSubscribeWorkbookDetail + inActiveSubscriptionWorkbook.workbookId shouldBe inactiveWorkbookId + inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE + inActiveSubscriptionWorkbook.currentDay shouldBe inactiveWorkbookCurrentDay + inActiveSubscriptionWorkbook.totalDay shouldBe inactiveWorkbookTotalDay + inActiveSubscriptionWorkbook.totalSubscriber shouldBe inactiveWorkbookSubscriptionCount + inActiveSubscriptionWorkbook.articleInfo shouldBe "{\"articleId\":$inactiveWorkbookId}" + verify(exactly = 1) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } - verify(exactly = 2) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } + verify(exactly = 1) { subscriptionDao.selectAllSubscriptionSendStatus(any()) } + verify(exactly = 2) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } verify(exactly = 2) { objectMapper.writeValueAsString(any()) } } } @@ -123,13 +151,21 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ inactiveWorkbookId to inactiveWorkbookSubscriptionCount ) + every { subscriptionDao.selectAllSubscriptionSendStatus(any()) } returns listOf( + SubscriptionSendStatusRecord( + workbookId = inactiveWorkbookId, + sendTime = LocalTime.of(8, 0), + sendDay = DayCode.MON_TUE_WED_THU_FRI_SAT_SUN.code + ) + ) + every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$inactiveWorkbookId}" then("멤버의 구독 비활성 워크북 정보를 반환한다") { val useCaseOut = useCase.execute(useCaseIn) useCaseOut.workbooks.size shouldBe 1 - val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] as MainCardSubscribeWorkbookDetail inActiveSubscriptionWorkbook.workbookId shouldBe inactiveWorkbookId inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE inActiveSubscriptionWorkbook.currentDay shouldBe inactiveWorkbookCurrentDay @@ -141,6 +177,7 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } verify(exactly = 1) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } + verify(exactly = 1) { subscriptionDao.selectAllSubscriptionSendStatus(any()) } verify(exactly = 1) { objectMapper.writeValueAsString(any()) } } } @@ -169,13 +206,21 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ activeWorkbookId to activeWorkbookSubscriptionCount ) + every { subscriptionDao.selectAllSubscriptionSendStatus(any()) } returns listOf( + SubscriptionSendStatusRecord( + workbookId = activeWorkbookId, + sendTime = LocalTime.of(8, 0), + sendDay = DayCode.MON_TUE_WED_THU_FRI_SAT_SUN.code + ) + ) + every { objectMapper.writeValueAsString(any()) } returns "{\"articleId\":$activeWorkbookId}" then("멤버의 구독 활성 워크북 정보를 반환한다") { val useCaseOut = useCase.execute(useCaseIn) useCaseOut.workbooks.size shouldBe 1 - val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] as MainCardSubscribeWorkbookDetail inActiveSubscriptionWorkbook.workbookId shouldBe activeWorkbookId inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE inActiveSubscriptionWorkbook.currentDay shouldBe activeWorkbookCurrentDay @@ -185,8 +230,75 @@ class BrowseSubscribeWorkbooksUseCaseTest : BehaviorSpec({ verify(exactly = 1) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } + verify(exactly = 1) { subscriptionDao.selectAllSubscriptionSendStatus(any()) } verify(exactly = 1) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } + verify(exactly = 1) { objectMapper.writeValueAsString(any()) } + } + } + } + + given("마이 페이지에서 멤버의 구독 워크북 정보를 조회하는 경우") { + val memberId = 1L + val useCaseIn = + BrowseSubscribeWorkbooksUseCaseIn(memberId = memberId, view = ViewCategory.MY_PAGE) + + `when`("멤버의 구독 활성 워크북 정보이 존재할 경우") { + every { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } returns emptyList() + + val activeWorkbookId = 1L + val activeWorkbookCurrentDay = 2 + val activeWorkbookTotalDay = 3 + every { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } returns listOf( + MemberWorkbookSubscriptionStatusRecord( + workbookId = activeWorkbookId, + isActiveSub = false, + currentDay = activeWorkbookCurrentDay, + totalDay = activeWorkbookTotalDay + ) + ) + + every { + subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) + } returns activeWorkbookId + + val activeWorkbookSubscriptionCount = 1 + every { subscriptionDao.countAllWorkbookSubscription(any()) } returns mapOf( + activeWorkbookId to activeWorkbookSubscriptionCount + ) + + every { subscriptionDao.selectAllSubscriptionSendStatus(any()) } returns listOf( + SubscriptionSendStatusRecord( + workbookId = activeWorkbookId, + sendTime = LocalTime.of(8, 0), + sendDay = DayCode.MON_TUE_WED_THU_FRI_SAT_SUN.code + ) + ) + + every { subscriptionWorkbookService.readAllWorkbookTitle(any()) } returns mapOf( + activeWorkbookId to "title" + ) + + every { objectMapper.writeValueAsString(any()) } returns "{\"id\":$activeWorkbookId, \"title\":\"title\"}" + + then("멤버의 구독 워크북 정보를 반환한다") { + val useCaseOut = useCase.execute(useCaseIn) + useCaseOut.workbooks.size shouldBe 1 + + val inActiveSubscriptionWorkbook = useCaseOut.workbooks[0] as MyPageSubscribeWorkbookDetail + inActiveSubscriptionWorkbook.workbookId shouldBe activeWorkbookId + inActiveSubscriptionWorkbook.isActiveSub shouldBe WorkBookStatus.DONE + inActiveSubscriptionWorkbook.currentDay shouldBe activeWorkbookCurrentDay + inActiveSubscriptionWorkbook.totalDay shouldBe activeWorkbookTotalDay + inActiveSubscriptionWorkbook.totalSubscriber shouldBe activeWorkbookSubscriptionCount + inActiveSubscriptionWorkbook.workbookInfo shouldBe "{\"id\":$activeWorkbookId, \"title\":\"title\"}" + + verify(exactly = 0) { subscriptionDao.selectAllInActiveWorkbookSubscriptionStatus(any()) } + verify(exactly = 1) { subscriptionDao.selectAllActiveWorkbookSubscriptionStatus(any()) } verify(exactly = 1) { subscriptionDao.countAllWorkbookSubscription(any()) } + verify(exactly = 1) { subscriptionDao.selectAllSubscriptionSendStatus(any()) } + verify(exactly = 0) { subscriptionArticleService.readArticleIdByWorkbookIdAndDay(any()) } + verify(exactly = 1) { subscriptionWorkbookService.readAllWorkbookTitle(any()) } verify(exactly = 1) { objectMapper.writeValueAsString(any()) } } } diff --git a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt index bffabac20..fc9bc3a3a 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/subscription/SubscriptionControllerTest.kt @@ -17,8 +17,7 @@ import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn import org.springframework.http.MediaType import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document -import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get -import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.* import org.springframework.restdocs.payload.PayloadDocumentation import org.springframework.security.test.context.support.WithUserDetails import org.springframework.test.web.servlet.result.MockMvcResultMatchers @@ -32,49 +31,53 @@ class SubscriptionControllerTest : ControllerTestSpec() { } @Test - @DisplayName("[GET] /api/v1/subscriptions/workbooks") + @DisplayName("[GET] /api/v1/subscriptions/workbooks?view=mainCard") @WithUserDetails(userDetailsServiceBeanName = "testTokenUserDetailsService") - fun browseSubscribeWorkbooks() { + fun browseSubscribeWorkbooksViewMainCard() { // given - val api = "BrowseSubscribeWorkBooks" + val api = "BrowseSubscribeWorkBooksViewMainCard" val token = "thisisaccesstoken" val view = ViewCategory.MAIN_CARD val uri = UriComponentsBuilder.newInstance() .path("$BASE_URL/subscriptions/workbooks") - .queryParam("view", view) + .queryParam("view", view.viewName) .build() .toUriString() val memberId = 1L - val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId) + val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId, view) val useCaseOut = BrowseSubscribeWorkbooksUseCaseOut( + clazz = MainCardSubscribeWorkbookDetail::class.java, workbooks = listOf( - SubscribeWorkbookDetail( + MainCardSubscribeWorkbookDetail( workbookId = 1L, isActiveSub = WorkBookStatus.ACTIVE, currentDay = 1, totalDay = 3, rank = 0, totalSubscriber = 100, + subscription = Subscription(), articleInfo = "{\"articleId\":1}" ), - SubscribeWorkbookDetail( + MainCardSubscribeWorkbookDetail( workbookId = 2L, isActiveSub = WorkBookStatus.ACTIVE, currentDay = 2, totalDay = 3, rank = 0, totalSubscriber = 1, + subscription = Subscription(), articleInfo = "{\"articleId\":5}" ), - SubscribeWorkbookDetail( + MainCardSubscribeWorkbookDetail( workbookId = 3L, isActiveSub = WorkBookStatus.DONE, currentDay = 3, totalDay = 3, rank = 0, totalSubscriber = 2, - articleInfo = "{}" + subscription = Subscription(), + articleInfo = "{\"articleId\":6}" ) ) ) @@ -91,7 +94,7 @@ class SubscriptionControllerTest : ControllerTestSpec() { api.toIdentifier(), ResourceDocumentation.resource( ResourceSnippetParameters.builder() - .description("구독한 학습지 정보 목록을 조회합니다.") + .description("메인 카드에 표시할 구독한 학습지 정보 목록을 조회합니다.") .summary(api.toIdentifier()) .privateResource(false) .deprecated(false) @@ -123,7 +126,115 @@ class SubscriptionControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.workbooks[].totalSubscriber") .fieldWithNumber("누적 구독자 수"), PayloadDocumentation.fieldWithPath("data.workbooks[].articleInfo") - .fieldWithString("학습지 정보(Json 타입)") + .fieldWithString("아티클 정보(Json 타입)"), + PayloadDocumentation.fieldWithPath("data.workbooks[].subscription") + .fieldWithObject("구독 정보"), + PayloadDocumentation.fieldWithPath("data.workbooks[].subscription.time") + .fieldWithString("구독 시간"), + PayloadDocumentation.fieldWithPath("data.workbooks[].subscription.dateTimeCode") + .fieldWithString("구독 시간 코드") + ) + ) + ) + .build() + ) + ) + ) + } + + @Test + @DisplayName("[GET] /api/v1/subscriptions/workbooks?view=myPage") + @WithUserDetails(userDetailsServiceBeanName = "testTokenUserDetailsService") + fun browseSubscribeWorkbooksViewMyPage() { + // given + val api = "BrowseSubscribeWorkBooksViewMyPage" + val token = "thisisaccesstoken" + val view = ViewCategory.MY_PAGE + val uri = UriComponentsBuilder.newInstance() + .path("$BASE_URL/subscriptions/workbooks") + .queryParam("view", view.viewName) + .build() + .toUriString() + + val memberId = 1L + val useCaseIn = BrowseSubscribeWorkbooksUseCaseIn(memberId, view) + val useCaseOut = BrowseSubscribeWorkbooksUseCaseOut( + clazz = MyPageSubscribeWorkbookDetail::class.java, + workbooks = listOf( + MyPageSubscribeWorkbookDetail( + workbookId = 1L, + isActiveSub = WorkBookStatus.ACTIVE, + currentDay = 1, + totalDay = 3, + rank = 0, + totalSubscriber = 100, + subscription = Subscription(), + workbookInfo = "{\"id\":1, \"title\":\"title1\"}" + ), + MyPageSubscribeWorkbookDetail( + workbookId = 2L, + isActiveSub = WorkBookStatus.ACTIVE, + currentDay = 2, + totalDay = 3, + rank = 0, + totalSubscriber = 1, + subscription = Subscription(), + workbookInfo = "{\"id\":2, \"title\":\"title2\"}" + ) + ) + ) + doReturn(useCaseOut).`when`(browseSubscribeWorkbooksUseCase).execute(useCaseIn) + + // when + mockMvc.perform( + get(uri) + .header("Authorization", "Bearer $token") + .contentType(MediaType.APPLICATION_JSON) + ).andExpect(MockMvcResultMatchers.status().is2xxSuccessful) + .andDo( + document( + api.toIdentifier(), + ResourceDocumentation.resource( + ResourceSnippetParameters.builder() + .description("마이 페이지에 표시할 구독한 학습지 정보 목록을 조회합니다.") + .summary(api.toIdentifier()) + .privateResource(false) + .deprecated(false) + .tag(TAG) + .requestSchema(Schema.schema(api.toRequestSchema())) + .requestHeaders( + ResourceDocumentation.headerWithName("Authorization") + .defaultValue("{{accessToken}}") + .description("Bearer 어세스 토큰") + ) + .responseSchema(Schema.schema(api.toResponseSchema())) + .responseFields( + *Description.describe( + arrayOf( + PayloadDocumentation.fieldWithPath("data") + .fieldWithObject("data"), + PayloadDocumentation.fieldWithPath("data.workbooks") + .fieldWithArray("학습지 목록"), + PayloadDocumentation.fieldWithPath("data.workbooks[].id") + .fieldWithNumber("학습지 Id"), + PayloadDocumentation.fieldWithPath("data.workbooks[].status") + .fieldWithString("구독 상태"), + PayloadDocumentation.fieldWithPath("data.workbooks[].totalDay") + .fieldWithNumber("총 일수"), + PayloadDocumentation.fieldWithPath("data.workbooks[].currentDay") + .fieldWithNumber("현재 일수"), + PayloadDocumentation.fieldWithPath("data.workbooks[].rank") + .fieldWithNumber("순위"), + PayloadDocumentation.fieldWithPath("data.workbooks[].totalSubscriber") + .fieldWithNumber("누적 구독자 수"), + PayloadDocumentation.fieldWithPath("data.workbooks[].workbookInfo") + .fieldWithString("학습지 정보(Json 타입)"), + PayloadDocumentation.fieldWithPath("data.workbooks[].subscription") + .fieldWithObject("구독 정보"), + PayloadDocumentation.fieldWithPath("data.workbooks[].subscription.time") + .fieldWithString("구독 시간"), + PayloadDocumentation.fieldWithPath("data.workbooks[].subscription.dateTimeCode") + .fieldWithString("구독 시간 코드") ) ) ) diff --git a/data/db/migration/entity/V1.00.0.21__add_subscription_time.sql b/data/db/migration/entity/V1.00.0.21__add_subscription_time.sql new file mode 100644 index 000000000..e12fe9d9b --- /dev/null +++ b/data/db/migration/entity/V1.00.0.21__add_subscription_time.sql @@ -0,0 +1,2 @@ +-- 구독 전송 시간을 추가한다. +ALTER TABLE SUBSCRIPTION ADD COLUMN send_time TIME DEFAULT '08:00'; diff --git a/data/db/migration/entity/V1.00.0.22__add_subscription_day.sql b/data/db/migration/entity/V1.00.0.22__add_subscription_day.sql new file mode 100644 index 000000000..46c59b29f --- /dev/null +++ b/data/db/migration/entity/V1.00.0.22__add_subscription_day.sql @@ -0,0 +1,2 @@ +-- 구독 전송 요일 추가 +ALTER TABLE SUBSCRIPTION ADD COLUMN send_day CHAR(10) NOT NULL DEFAULT '1111111';