From 3c02659461f7177b524cf2327834bdaea05ba2c8 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Wed, 17 Jul 2024 22:18:47 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20flyway=20V1.00.0.7=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC(ARTICLE=5FVIEW=5FHIS=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 +++++++ api-repo/.gitignore | 2 -- api/.gitignore | 2 -- batch/.gitignore | 2 -- data/.gitignore | 2 -- .../entity/V1.00.0.7__article_view_his_table.sql | 10 ++++++++++ email/.gitignore | 2 -- storage/.gitignore | 2 -- 8 files changed, 17 insertions(+), 12 deletions(-) delete mode 100644 api-repo/.gitignore delete mode 100644 api/.gitignore delete mode 100644 batch/.gitignore delete mode 100644 data/.gitignore create mode 100644 data/db/migration/entity/V1.00.0.7__article_view_his_table.sql delete mode 100644 email/.gitignore delete mode 100644 storage/.gitignore diff --git a/.gitignore b/.gitignore index 965c592e9..58041a262 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,10 @@ Footer *.log *.tmp +# DB schema migration path (except data module) +data/src/main/resources/**/*.sql +api/**/*.sql +api-repo/**/*.sql +batch/**/*.sql +email/**/*.sql +storage/**/*.sql diff --git a/api-repo/.gitignore b/api-repo/.gitignore deleted file mode 100644 index aabab5468..000000000 --- a/api-repo/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DB schema migration path -**/*.sql diff --git a/api/.gitignore b/api/.gitignore deleted file mode 100644 index aabab5468..000000000 --- a/api/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DB schema migration path -**/*.sql diff --git a/batch/.gitignore b/batch/.gitignore deleted file mode 100644 index aabab5468..000000000 --- a/batch/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DB schema migration path -**/*.sql diff --git a/data/.gitignore b/data/.gitignore deleted file mode 100644 index aabab5468..000000000 --- a/data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DB schema migration path -**/*.sql diff --git a/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql b/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql new file mode 100644 index 000000000..a81813597 --- /dev/null +++ b/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql @@ -0,0 +1,10 @@ +-- 아티클 조회수 저장 테이블 +CREATE TABLE ARTICLE_VIEW_HIS +( + id BIGINT NOT NULL AUTO_INCREMENT, + article_mst_id BIGINT NOT NULL, + member_id BIGINT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP NULL DEFAULT NULL, + PRIMARY KEY (id) +); diff --git a/email/.gitignore b/email/.gitignore deleted file mode 100644 index aabab5468..000000000 --- a/email/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DB schema migration path -**/*.sql diff --git a/storage/.gitignore b/storage/.gitignore deleted file mode 100644 index aabab5468..000000000 --- a/storage/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DB schema migration path -**/*.sql From 977de9a216fe7d1784174a9ffe6dd15d746075b3 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Wed, 17 Jul 2024 22:55:17 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20article=20view=20history=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/repo/dao/article/ArticleViewHisDao.kt | 23 +++++++++++++++++++ .../article/command/ArticleViewHisCommand.kt | 6 +++++ .../article/service/ArticleViewHisService.kt | 17 ++++++++++++++ .../service/dto/ArticleViewHisInDto.kt | 6 +++++ .../article/usecase/ReadArticleUseCase.kt | 5 ++++ .../usecase/dto/ReadArticleUseCaseIn.kt | 1 + .../controller/article/ArticleController.kt | 2 +- 7 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/article/command/ArticleViewHisCommand.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt new file mode 100644 index 000000000..c76b48088 --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt @@ -0,0 +1,23 @@ +package com.few.api.repo.dao.article + +import com.few.api.repo.dao.article.command.ArticleViewHisCommand +import jooq.jooq_dsl.tables.ArticleViewHis +import org.jooq.DSLContext +import org.springframework.stereotype.Repository + +@Repository +class ArticleViewHisDao( + private val dslContext: DSLContext, +) { + + fun insertArticleViewHis(query: ArticleViewHisCommand) { + dslContext.insertInto( + ArticleViewHis.ARTICLE_VIEW_HIS, + ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID, + ArticleViewHis.ARTICLE_VIEW_HIS.MEMBER_ID + ).values( + query.articleId, + query.memberId + ).execute() + } +} \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/command/ArticleViewHisCommand.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/command/ArticleViewHisCommand.kt new file mode 100644 index 000000000..e986fe85f --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/command/ArticleViewHisCommand.kt @@ -0,0 +1,6 @@ +package com.few.api.repo.dao.article.command + +data class ArticleViewHisCommand( + val articleId: Long, + val memberId: Long, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt new file mode 100644 index 000000000..0969200c6 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt @@ -0,0 +1,17 @@ +package com.few.api.domain.article.service + +import com.few.api.domain.article.service.dto.ArticleViewHisInDto +import com.few.api.repo.dao.article.ArticleViewHisDao +import com.few.api.repo.dao.article.command.ArticleViewHisCommand +import org.springframework.stereotype.Service + +@Service +class ArticleViewHisService( + private val articleViewHisDao: ArticleViewHisDao, +) { + fun addArticleViewHis(inDto: ArticleViewHisInDto) { + articleViewHisDao.insertArticleViewHis( + ArticleViewHisCommand(inDto.articleId, inDto.memberId) + ) + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt b/api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt new file mode 100644 index 000000000..53d878c31 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt @@ -0,0 +1,6 @@ +package com.few.api.domain.article.service.dto + +data class ArticleViewHisInDto( + val articleId: Long, + val memberId: Long, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index 78d05a117..e1f69fd73 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -1,10 +1,12 @@ package com.few.api.domain.article.usecase +import com.few.api.domain.article.service.ArticleViewHisService import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseOut import com.few.api.domain.article.usecase.dto.WriterDetail import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService +import com.few.api.domain.article.service.dto.ArticleViewHisInDto import com.few.api.domain.article.service.dto.BrowseArticleProblemIdsInDto import com.few.api.domain.article.service.dto.ReadWriterRecordInDto import com.few.api.exception.common.NotFoundException @@ -19,6 +21,7 @@ class ReadArticleUseCase( private val articleDao: ArticleDao, private val readArticleWriterRecordService: ReadArticleWriterRecordService, private val browseArticleProblemsService: BrowseArticleProblemsService, + private val articleViewHisService: ArticleViewHisService, ) { @Transactional(readOnly = true) @@ -35,6 +38,8 @@ class ReadArticleUseCase( browseArticleProblemsService.execute(query) } + articleViewHisService.addArticleViewHis(ArticleViewHisInDto(useCaseIn.articleId, useCaseIn.memberId)) + return ReadArticleUseCaseOut( id = articleRecord.articleId, writer = WriterDetail( diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseIn.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseIn.kt index c380b2987..6eb1b2df6 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseIn.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseIn.kt @@ -2,4 +2,5 @@ package com.few.api.domain.article.usecase.dto data class ReadArticleUseCaseIn( val articleId: Long, + val memberId: Long, ) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt b/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt index c8fe072e1..4a471b76b 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt @@ -27,7 +27,7 @@ class ArticleController( @Min(value = 1, message = "{min.id}") articleId: Long, ): ApiResponse> { - val useCaseOut = ReadArticleUseCaseIn(articleId).let { useCaseIn: ReadArticleUseCaseIn -> + val useCaseOut = ReadArticleUseCaseIn(articleId = articleId, memberId = 1L).let { useCaseIn: ReadArticleUseCaseIn -> readArticleUseCase.execute(useCaseIn) } From 1334ccfc29140b2655d86dd2cb2e06d5a48472de Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Wed, 17 Jul 2024 23:39:06 +0900 Subject: [PATCH 03/19] =?UTF-8?q?feat:=20article=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=8B=9C=20views=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../few/api/repo/dao/article/ArticleViewHisDao.kt | 14 +++++++++++--- .../dao/article/query/ArticleViewHisCountQuery.kt | 5 +++++ .../article/service/ArticleViewHisService.kt | 12 ++++++++++-- ...leViewHisInDto.kt => AddArticleViewHisInDto.kt} | 2 +- .../article/service/dto/ReadArticleViewsInDto.kt | 5 +++++ .../domain/article/usecase/ReadArticleUseCase.kt | 9 ++++++--- .../article/usecase/dto/ReadArticleUseCaseOut.kt | 1 + 7 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/article/query/ArticleViewHisCountQuery.kt rename api/src/main/kotlin/com/few/api/domain/article/service/dto/{ArticleViewHisInDto.kt => AddArticleViewHisInDto.kt} (73%) create mode 100644 api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt index c76b48088..07055c685 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt @@ -1,6 +1,7 @@ package com.few.api.repo.dao.article import com.few.api.repo.dao.article.command.ArticleViewHisCommand +import com.few.api.repo.dao.article.query.ArticleViewHisCountQuery import jooq.jooq_dsl.tables.ArticleViewHis import org.jooq.DSLContext import org.springframework.stereotype.Repository @@ -10,14 +11,21 @@ class ArticleViewHisDao( private val dslContext: DSLContext, ) { - fun insertArticleViewHis(query: ArticleViewHisCommand) { + fun insertArticleViewHis(command: ArticleViewHisCommand) { dslContext.insertInto( ArticleViewHis.ARTICLE_VIEW_HIS, ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID, ArticleViewHis.ARTICLE_VIEW_HIS.MEMBER_ID ).values( - query.articleId, - query.memberId + command.articleId, + command.memberId ).execute() } + + fun selectArticleViews(query: ArticleViewHisCountQuery): Long { + return dslContext.selectCount() + .from(ArticleViewHis.ARTICLE_VIEW_HIS) + .where(ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID.eq(query.articleId)) + .fetchOne(0, Long::class.java) ?: 0L + } } \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/query/ArticleViewHisCountQuery.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/query/ArticleViewHisCountQuery.kt new file mode 100644 index 000000000..50dfec413 --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/query/ArticleViewHisCountQuery.kt @@ -0,0 +1,5 @@ +package com.few.api.repo.dao.article.query + +data class ArticleViewHisCountQuery( + val articleId: Long, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt index 0969200c6..dbd883b3f 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt @@ -1,17 +1,25 @@ package com.few.api.domain.article.service -import com.few.api.domain.article.service.dto.ArticleViewHisInDto +import com.few.api.domain.article.service.dto.AddArticleViewHisInDto +import com.few.api.domain.article.service.dto.ReadArticleViewsInDto import com.few.api.repo.dao.article.ArticleViewHisDao import com.few.api.repo.dao.article.command.ArticleViewHisCommand +import com.few.api.repo.dao.article.query.ArticleViewHisCountQuery import org.springframework.stereotype.Service @Service class ArticleViewHisService( private val articleViewHisDao: ArticleViewHisDao, ) { - fun addArticleViewHis(inDto: ArticleViewHisInDto) { + fun addArticleViewHis(inDto: AddArticleViewHisInDto) { articleViewHisDao.insertArticleViewHis( ArticleViewHisCommand(inDto.articleId, inDto.memberId) ) } + + fun readArticleViews(inDto: ReadArticleViewsInDto): Long { + return articleViewHisDao.selectArticleViews( + ArticleViewHisCountQuery(inDto.articleId) + ) + } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt b/api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt similarity index 73% rename from api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt rename to api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt index 53d878c31..734db65d3 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/service/dto/ArticleViewHisInDto.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt @@ -1,6 +1,6 @@ package com.few.api.domain.article.service.dto -data class ArticleViewHisInDto( +data class AddArticleViewHisInDto( val articleId: Long, val memberId: Long, ) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt b/api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt new file mode 100644 index 000000000..1753e0f7b --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt @@ -0,0 +1,5 @@ +package com.few.api.domain.article.service.dto + +data class ReadArticleViewsInDto( + val articleId: Long, +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index e1f69fd73..b7ada63f4 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -6,8 +6,9 @@ import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseOut import com.few.api.domain.article.usecase.dto.WriterDetail import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService -import com.few.api.domain.article.service.dto.ArticleViewHisInDto +import com.few.api.domain.article.service.dto.AddArticleViewHisInDto import com.few.api.domain.article.service.dto.BrowseArticleProblemIdsInDto +import com.few.api.domain.article.service.dto.ReadArticleViewsInDto import com.few.api.domain.article.service.dto.ReadWriterRecordInDto import com.few.api.exception.common.NotFoundException import com.few.api.repo.dao.article.ArticleDao @@ -38,7 +39,8 @@ class ReadArticleUseCase( browseArticleProblemsService.execute(query) } - articleViewHisService.addArticleViewHis(ArticleViewHisInDto(useCaseIn.articleId, useCaseIn.memberId)) + articleViewHisService.addArticleViewHis(AddArticleViewHisInDto(useCaseIn.articleId, useCaseIn.memberId)) + val views = articleViewHisService.readArticleViews(ReadArticleViewsInDto(useCaseIn.articleId)) return ReadArticleUseCaseOut( id = articleRecord.articleId, @@ -51,7 +53,8 @@ class ReadArticleUseCase( content = articleRecord.content, problemIds = problemIds.problemIds, category = CategoryType.convertToDisplayName(articleRecord.category), - createdAt = articleRecord.createdAt + createdAt = articleRecord.createdAt, + views = views ) } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseOut.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseOut.kt index 10f880674..7425affa2 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseOut.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/dto/ReadArticleUseCaseOut.kt @@ -11,6 +11,7 @@ data class ReadArticleUseCaseOut( val problemIds: List, val category: String, val createdAt: LocalDateTime, + val views: Long, ) data class WriterDetail( From 390b8980b691f44756aa7056e34190acac24de7d Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Wed, 17 Jul 2024 23:46:23 +0900 Subject: [PATCH 04/19] =?UTF-8?q?test:=20article=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20=EA=B4=80=EB=A0=A8=20test=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/usecase/ReadArticleUseCaseTest.kt | 18 +++++++++++++----- .../article/ArticleControllerTest.kt | 10 +++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt index 914fa4812..95674fce7 100644 --- a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt @@ -1,5 +1,6 @@ package com.few.api.domain.article.usecase +import com.few.api.domain.article.service.ArticleViewHisService import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService import com.few.api.domain.article.service.dto.BrowseArticleProblemsOutDto @@ -9,9 +10,7 @@ import com.few.api.repo.dao.article.ArticleDao import com.few.api.repo.dao.article.record.SelectArticleRecord import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify +import io.mockk.* import java.net.URL import java.time.LocalDateTime @@ -22,13 +21,20 @@ class ReadArticleUseCaseTest : BehaviorSpec({ lateinit var readArticleWriterRecordService: ReadArticleWriterRecordService lateinit var browseArticleProblemsService: BrowseArticleProblemsService lateinit var useCase: ReadArticleUseCase - val useCaseIn = ReadArticleUseCaseIn(articleId = 1L) + lateinit var articleViewHisService: ArticleViewHisService + val useCaseIn = ReadArticleUseCaseIn(articleId = 1L, memberId = 1L) beforeContainer { articleDao = mockk() readArticleWriterRecordService = mockk() browseArticleProblemsService = mockk() - useCase = ReadArticleUseCase(articleDao, readArticleWriterRecordService, browseArticleProblemsService) + articleViewHisService = mockk() + useCase = ReadArticleUseCase( + articleDao, + readArticleWriterRecordService, + browseArticleProblemsService, + articleViewHisService + ) } given("아티클 조회 요청이 온 상황에서") { @@ -52,6 +58,8 @@ class ReadArticleUseCaseTest : BehaviorSpec({ every { articleDao.selectArticleRecord(any()) } returns record every { readArticleWriterRecordService.execute(any()) } returns writerSvcOutDto every { browseArticleProblemsService.execute(any()) } returns probSvcOutDto + every { articleViewHisService.addArticleViewHis(any()) } just Runs + every { articleViewHisService.readArticleViews(any()) } returns 1L then("아티클이 정상 조회된다") { useCase.execute(useCaseIn) diff --git a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt index f545f9f13..7d572acdb 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt @@ -69,7 +69,8 @@ class ArticleControllerTest : ControllerTestSpec() { val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/{articleId}").build().toUriString() // set usecase mock val articleId = 1L - `when`(readArticleUseCase.execute(ReadArticleUseCaseIn(articleId))).thenReturn( + val memberId = 1L + `when`(readArticleUseCase.execute(ReadArticleUseCaseIn(articleId, memberId))).thenReturn( ReadArticleUseCaseOut( id = 1L, writer = WriterDetail( @@ -81,7 +82,8 @@ class ArticleControllerTest : ControllerTestSpec() { content = CategoryType.fromCode(0)!!.name, problemIds = listOf(1L, 2L, 3L), category = "경제", - createdAt = LocalDateTime.now() + createdAt = LocalDateTime.now(), + views = 1L ) ) @@ -119,7 +121,9 @@ class ArticleControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.category") .fieldWithString("아티클 카테고리"), PayloadDocumentation.fieldWithPath("data.createdAt") - .fieldWithString("아티클 생성일") + .fieldWithString("아티클 생성일"), + PayloadDocumentation.fieldWithPath("data.views") + .fieldWithString("아티클 조회수") ) ) ).build() From 23046548a0146acdda51fb89ce6e5051111e4e66 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 00:18:23 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20ARTICLE=5FVIEW=5FHIS=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80(article=5Fmst=5Fid=20=EC=BB=AC=EB=9F=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/db/migration/entity/V1.00.0.7__article_view_his_table.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql b/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql index a81813597..1d2d249ce 100644 --- a/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql +++ b/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql @@ -8,3 +8,6 @@ CREATE TABLE ARTICLE_VIEW_HIS deleted_at TIMESTAMP NULL DEFAULT NULL, PRIMARY KEY (id) ); + +-- [인덱스 추가] -- +CREATE INDEX article_view_his_idx1 ON PROBLEM (article_mst_id); From 29ca0fbb11f93253a59a597c8d321cb184bbd07a Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 00:27:36 +0900 Subject: [PATCH 06/19] =?UTF-8?q?fix:=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/db/migration/entity/V1.00.0.7__article_view_his_table.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql b/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql index 1d2d249ce..2ca763a0e 100644 --- a/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql +++ b/data/db/migration/entity/V1.00.0.7__article_view_his_table.sql @@ -10,4 +10,4 @@ CREATE TABLE ARTICLE_VIEW_HIS ); -- [인덱스 추가] -- -CREATE INDEX article_view_his_idx1 ON PROBLEM (article_mst_id); +CREATE INDEX article_view_his_idx1 ON ARTICLE_VIEW_HIS (article_mst_id); From bfaebe62bdc836648e116ec5812121d8ee936e4e Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 14:57:40 +0900 Subject: [PATCH 07/19] =?UTF-8?q?refactor:=20DAO=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt | 2 +- .../com/few/api/domain/article/service/ArticleViewHisService.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt index 07055c685..7da157b70 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt @@ -22,7 +22,7 @@ class ArticleViewHisDao( ).execute() } - fun selectArticleViews(query: ArticleViewHisCountQuery): Long { + fun countArticleViews(query: ArticleViewHisCountQuery): Long { return dslContext.selectCount() .from(ArticleViewHis.ARTICLE_VIEW_HIS) .where(ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID.eq(query.articleId)) diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt index dbd883b3f..91b07133f 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt @@ -18,7 +18,7 @@ class ArticleViewHisService( } fun readArticleViews(inDto: ReadArticleViewsInDto): Long { - return articleViewHisDao.selectArticleViews( + return articleViewHisDao.countArticleViews( ArticleViewHisCountQuery(inDto.articleId) ) } From 362999e4df8111b762f957619dc5fc09ecb1386f Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 15:02:43 +0900 Subject: [PATCH 08/19] =?UTF-8?q?refactor:=20=EC=95=84=ED=8B=B0=ED=81=B4?= =?UTF-8?q?=20=EB=B7=B0=EC=99=80=20=EC=95=84=ED=8B=B0=ED=81=B4=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/service/ArticleViewHisService.kt | 25 ------------------- .../service/dto/AddArticleViewHisInDto.kt | 6 ----- .../service/dto/ReadArticleViewsInDto.kt | 5 ---- .../article/usecase/ReadArticleUseCase.kt | 12 ++++----- .../article/usecase/ReadArticleUseCaseTest.kt | 12 ++++----- 5 files changed, 12 insertions(+), 48 deletions(-) delete mode 100644 api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt delete mode 100644 api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt delete mode 100644 api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt b/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt deleted file mode 100644 index 91b07133f..000000000 --- a/api/src/main/kotlin/com/few/api/domain/article/service/ArticleViewHisService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.few.api.domain.article.service - -import com.few.api.domain.article.service.dto.AddArticleViewHisInDto -import com.few.api.domain.article.service.dto.ReadArticleViewsInDto -import com.few.api.repo.dao.article.ArticleViewHisDao -import com.few.api.repo.dao.article.command.ArticleViewHisCommand -import com.few.api.repo.dao.article.query.ArticleViewHisCountQuery -import org.springframework.stereotype.Service - -@Service -class ArticleViewHisService( - private val articleViewHisDao: ArticleViewHisDao, -) { - fun addArticleViewHis(inDto: AddArticleViewHisInDto) { - articleViewHisDao.insertArticleViewHis( - ArticleViewHisCommand(inDto.articleId, inDto.memberId) - ) - } - - fun readArticleViews(inDto: ReadArticleViewsInDto): Long { - return articleViewHisDao.countArticleViews( - ArticleViewHisCountQuery(inDto.articleId) - ) - } -} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt b/api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt deleted file mode 100644 index 734db65d3..000000000 --- a/api/src/main/kotlin/com/few/api/domain/article/service/dto/AddArticleViewHisInDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.few.api.domain.article.service.dto - -data class AddArticleViewHisInDto( - val articleId: Long, - val memberId: Long, -) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt b/api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt deleted file mode 100644 index 1753e0f7b..000000000 --- a/api/src/main/kotlin/com/few/api/domain/article/service/dto/ReadArticleViewsInDto.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.few.api.domain.article.service.dto - -data class ReadArticleViewsInDto( - val articleId: Long, -) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index b7ada63f4..ef529b035 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -1,17 +1,17 @@ package com.few.api.domain.article.usecase -import com.few.api.domain.article.service.ArticleViewHisService import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseOut import com.few.api.domain.article.usecase.dto.WriterDetail import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService -import com.few.api.domain.article.service.dto.AddArticleViewHisInDto import com.few.api.domain.article.service.dto.BrowseArticleProblemIdsInDto -import com.few.api.domain.article.service.dto.ReadArticleViewsInDto import com.few.api.domain.article.service.dto.ReadWriterRecordInDto import com.few.api.exception.common.NotFoundException import com.few.api.repo.dao.article.ArticleDao +import com.few.api.repo.dao.article.ArticleViewHisDao +import com.few.api.repo.dao.article.command.ArticleViewHisCommand +import com.few.api.repo.dao.article.query.ArticleViewHisCountQuery import com.few.api.repo.dao.article.query.SelectArticleRecordQuery import com.few.data.common.code.CategoryType import org.springframework.stereotype.Component @@ -22,7 +22,7 @@ class ReadArticleUseCase( private val articleDao: ArticleDao, private val readArticleWriterRecordService: ReadArticleWriterRecordService, private val browseArticleProblemsService: BrowseArticleProblemsService, - private val articleViewHisService: ArticleViewHisService, + private val articleViewHisDao: ArticleViewHisDao, ) { @Transactional(readOnly = true) @@ -39,8 +39,8 @@ class ReadArticleUseCase( browseArticleProblemsService.execute(query) } - articleViewHisService.addArticleViewHis(AddArticleViewHisInDto(useCaseIn.articleId, useCaseIn.memberId)) - val views = articleViewHisService.readArticleViews(ReadArticleViewsInDto(useCaseIn.articleId)) + articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(useCaseIn.articleId, useCaseIn.memberId)) + val views = articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) return ReadArticleUseCaseOut( id = articleRecord.articleId, diff --git a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt index 95674fce7..4568236de 100644 --- a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt @@ -1,12 +1,12 @@ package com.few.api.domain.article.usecase -import com.few.api.domain.article.service.ArticleViewHisService import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService import com.few.api.domain.article.service.dto.BrowseArticleProblemsOutDto import com.few.api.domain.article.service.dto.ReadWriterOutDto import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.repo.dao.article.ArticleDao +import com.few.api.repo.dao.article.ArticleViewHisDao import com.few.api.repo.dao.article.record.SelectArticleRecord import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec @@ -21,19 +21,19 @@ class ReadArticleUseCaseTest : BehaviorSpec({ lateinit var readArticleWriterRecordService: ReadArticleWriterRecordService lateinit var browseArticleProblemsService: BrowseArticleProblemsService lateinit var useCase: ReadArticleUseCase - lateinit var articleViewHisService: ArticleViewHisService + lateinit var articleViewHisDao: ArticleViewHisDao val useCaseIn = ReadArticleUseCaseIn(articleId = 1L, memberId = 1L) beforeContainer { articleDao = mockk() readArticleWriterRecordService = mockk() browseArticleProblemsService = mockk() - articleViewHisService = mockk() + articleViewHisDao = mockk() useCase = ReadArticleUseCase( articleDao, readArticleWriterRecordService, browseArticleProblemsService, - articleViewHisService + articleViewHisDao ) } @@ -58,8 +58,8 @@ class ReadArticleUseCaseTest : BehaviorSpec({ every { articleDao.selectArticleRecord(any()) } returns record every { readArticleWriterRecordService.execute(any()) } returns writerSvcOutDto every { browseArticleProblemsService.execute(any()) } returns probSvcOutDto - every { articleViewHisService.addArticleViewHis(any()) } just Runs - every { articleViewHisService.readArticleViews(any()) } returns 1L + every { articleViewHisDao.insertArticleViewHis(any()) } just Runs + every { articleViewHisDao.countArticleViews(any()) } returns 1L then("아티클이 정상 조회된다") { useCase.execute(useCaseIn) From 9c171406e280fd5c1a41e6406572f09cc45164dc Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 20:15:02 +0900 Subject: [PATCH 09/19] =?UTF-8?q?refactor:=20DAO=20=EB=B9=88=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95=20stereotype=20=EB=B3=80=EA=B2=BD(@Component=20->=20@?= =?UTF-8?q?Repository)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/few/api/repo/dao/problem/ProblemDao.kt | 4 ++-- .../kotlin/com/few/api/repo/dao/problem/SubmitHistoryDao.kt | 4 ++-- .../com/few/api/repo/dao/subscription/SubscriptionDao.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/ProblemDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/ProblemDao.kt index 8d6a0b1ea..63d2f4d90 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/ProblemDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/ProblemDao.kt @@ -12,9 +12,9 @@ import jooq.jooq_dsl.tables.Problem import org.jooq.DSLContext import org.jooq.JSON import org.jooq.impl.DSL -import org.springframework.stereotype.Component +import org.springframework.stereotype.Repository -@Component +@Repository class ProblemDao( private val dslContext: DSLContext, private val contentsJsonMapper: ContentsJsonMapper, diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/SubmitHistoryDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/SubmitHistoryDao.kt index 2bcde4740..7336a95b1 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/SubmitHistoryDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/problem/SubmitHistoryDao.kt @@ -3,9 +3,9 @@ package com.few.api.repo.dao.problem import com.few.api.repo.dao.problem.command.InsertSubmitHistoryCommand import jooq.jooq_dsl.Tables.SUBMIT_HISTORY import org.jooq.DSLContext -import org.springframework.stereotype.Component +import org.springframework.stereotype.Repository -@Component +@Repository class SubmitHistoryDao( private val dslContext: DSLContext, ) { 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 ad6faecba..c0c942e73 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 @@ -10,10 +10,10 @@ import com.few.api.repo.dao.subscription.record.CountAllSubscriptionStatusRecord import jooq.jooq_dsl.Tables.MAPPING_WORKBOOK_ARTICLE import jooq.jooq_dsl.Tables.SUBSCRIPTION import org.jooq.DSLContext -import org.springframework.stereotype.Component +import org.springframework.stereotype.Repository import java.time.LocalDateTime -@Component +@Repository class SubscriptionDao( private val dslContext: DSLContext, ) { From abde0c32745f754b71fe97c86f6e56bd5658d2cd Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 20:18:21 +0900 Subject: [PATCH 10/19] =?UTF-8?q?refactor:=20=EC=A1=B0=ED=9A=8C=EC=88=98?= =?UTF-8?q?=20=EA=B2=B0=EC=A0=95=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20DAO=20->=20UC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt | 4 ++-- .../com/few/api/domain/article/usecase/ReadArticleUseCase.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt index 7da157b70..77bbab5b7 100644 --- a/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/article/ArticleViewHisDao.kt @@ -22,10 +22,10 @@ class ArticleViewHisDao( ).execute() } - fun countArticleViews(query: ArticleViewHisCountQuery): Long { + fun countArticleViews(query: ArticleViewHisCountQuery): Long? { return dslContext.selectCount() .from(ArticleViewHis.ARTICLE_VIEW_HIS) .where(ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID.eq(query.articleId)) - .fetchOne(0, Long::class.java) ?: 0L + .fetchOne(0, Long::class.java) } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index ef529b035..2e9f84c93 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -40,7 +40,7 @@ class ReadArticleUseCase( } articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(useCaseIn.articleId, useCaseIn.memberId)) - val views = articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) + val views = articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) ?: 0L return ReadArticleUseCaseOut( id = articleRecord.articleId, From 89a188f36b7ae9731653943b4feeea8a23f84c01 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 20:24:12 +0900 Subject: [PATCH 11/19] =?UTF-8?q?chore:=20member=20Id=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20TODO=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/few/api/web/controller/article/ArticleController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt b/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt index 4a471b76b..0eeff9c42 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/article/ArticleController.kt @@ -27,7 +27,7 @@ class ArticleController( @Min(value = 1, message = "{min.id}") articleId: Long, ): ApiResponse> { - val useCaseOut = ReadArticleUseCaseIn(articleId = articleId, memberId = 1L).let { useCaseIn: ReadArticleUseCaseIn -> + val useCaseOut = ReadArticleUseCaseIn(articleId = articleId, memberId = 0L).let { useCaseIn: ReadArticleUseCaseIn -> //TODO: membberId검토 readArticleUseCase.execute(useCaseIn) } From a8342ba1b4d39d39e493f7e5130d9cbb445e6511 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Thu, 18 Jul 2024 21:36:58 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20=EC=95=84=ED=8B=B0=ED=81=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=A1=9D=20Insert=EA=B3=BC?= =?UTF-8?q?=EC=A0=95=EC=9D=84=20=EB=8B=A4=EB=A5=B8=20=EC=93=B0=EB=A0=88?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20=EC=88=98=ED=96=89=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/DatabaseAccessThreadPoolConfig.kt | 38 +++++++++++++++++++ .../article/usecase/ReadArticleUseCase.kt | 28 +++++++++++--- .../resources/application-client-local.yml | 8 ++++ .../main/resources/application-client-prd.yml | 8 ++++ 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 api/src/main/kotlin/com/few/api/config/DatabaseAccessThreadPoolConfig.kt diff --git a/api/src/main/kotlin/com/few/api/config/DatabaseAccessThreadPoolConfig.kt b/api/src/main/kotlin/com/few/api/config/DatabaseAccessThreadPoolConfig.kt new file mode 100644 index 000000000..69cb08dcf --- /dev/null +++ b/api/src/main/kotlin/com/few/api/config/DatabaseAccessThreadPoolConfig.kt @@ -0,0 +1,38 @@ +package com.few.api.config + +import com.few.api.config.properties.ThreadPoolProperties +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor + +@Configuration +class DatabaseAccessThreadPoolConfig { + private val log = KotlinLogging.logger {} + + companion object { + const val DATABASE_ACCESS_POOL = "database-task-" + } + + @Bean + @ConfigurationProperties(prefix = "database.thread-pool") + fun databaseAccessThreadPoolProperties(): ThreadPoolProperties { + return ThreadPoolProperties() + } + + @Bean(DATABASE_ACCESS_POOL) + fun databaseAccessThreadPool() = ThreadPoolTaskExecutor().apply { + val properties = databaseAccessThreadPoolProperties() + corePoolSize = properties.getCorePoolSize() + maxPoolSize = properties.getMaxPoolSize() + queueCapacity = properties.getQueueCapacity() + setWaitForTasksToCompleteOnShutdown(properties.getWaitForTasksToCompleteOnShutdown()) + setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds()) + setThreadNamePrefix("databaseAccessThreadPool-") + setRejectedExecutionHandler { r, _ -> + log.warn { "Database Access Task Rejected: $r" } + } + initialize() + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index 2e9f84c93..5fef209a1 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -1,5 +1,6 @@ package com.few.api.domain.article.usecase +import com.few.api.config.DatabaseAccessThreadPoolConfig.Companion.DATABASE_ACCESS_POOL import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseOut import com.few.api.domain.article.usecase.dto.WriterDetail @@ -14,6 +15,8 @@ import com.few.api.repo.dao.article.command.ArticleViewHisCommand import com.few.api.repo.dao.article.query.ArticleViewHisCountQuery import com.few.api.repo.dao.article.query.SelectArticleRecordQuery import com.few.data.common.code.CategoryType +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @@ -25,6 +28,8 @@ class ReadArticleUseCase( private val articleViewHisDao: ArticleViewHisDao, ) { + private val log = KotlinLogging.logger {} + @Transactional(readOnly = true) fun execute(useCaseIn: ReadArticleUseCaseIn): ReadArticleUseCaseOut { val articleRecord = SelectArticleRecordQuery(useCaseIn.articleId).let { query: SelectArticleRecordQuery -> @@ -35,12 +40,14 @@ class ReadArticleUseCase( readArticleWriterRecordService.execute(query) ?: throw NotFoundException("writer.notfound.id") } - val problemIds = BrowseArticleProblemIdsInDto(articleRecord.articleId).let { query: BrowseArticleProblemIdsInDto -> - browseArticleProblemsService.execute(query) - } + val problemIds = + BrowseArticleProblemIdsInDto(articleRecord.articleId).let { query: BrowseArticleProblemIdsInDto -> + browseArticleProblemsService.execute(query) + } + + val views = (articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) ?: 0L) + 1L - articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(useCaseIn.articleId, useCaseIn.memberId)) - val views = articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) ?: 0L + insertArticleViewHisAsync(useCaseIn.articleId, useCaseIn.memberId) return ReadArticleUseCaseOut( id = articleRecord.articleId, @@ -57,4 +64,15 @@ class ReadArticleUseCase( views = views ) } + + @Async(value = DATABASE_ACCESS_POOL) + @Transactional + fun insertArticleViewHisAsync(articleId: Long, memberId: Long) { + try { + articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(articleId, memberId)) + log.debug { "Successfully inserted article view history for articleId: $articleId and memberId: $memberId" } + } catch (e: Exception) { + log.error { "Failed to insert article view history for articleId: $articleId and memberId: $memberId" } + } + } } \ No newline at end of file diff --git a/api/src/main/resources/application-client-local.yml b/api/src/main/resources/application-client-local.yml index 6d13c3012..332cf3815 100644 --- a/api/src/main/resources/application-client-local.yml +++ b/api/src/main/resources/application-client-local.yml @@ -13,3 +13,11 @@ discord: queue-capacity: 30 wait-for-tasks-to-complete-on-shutdown: true await-termination-seconds: 60 + +database: + thread-pool: + core-pool-size: 10 + max-pool-size: 30 + queue-capacity: 70 + wait-for-tasks-to-complete-on-shutdown: true + await-termination-seconds: 60 diff --git a/api/src/main/resources/application-client-prd.yml b/api/src/main/resources/application-client-prd.yml index 2b009b3ce..871d97be1 100644 --- a/api/src/main/resources/application-client-prd.yml +++ b/api/src/main/resources/application-client-prd.yml @@ -13,3 +13,11 @@ discord: queue-capacity: ${DISCORD_THREAD_POOL_QUEUE_CAPACITY:30} wait-for-tasks-to-complete-on-shutdown: ${DISCORD_THREAD_POOL_WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN:true} await-termination-seconds: ${DISCORD_THREAD_POOL_AWAIT_TERMINATION_SECONDS:60} + +database: + thread-pool: + core-pool-size: 10 + max-pool-size: 30 + queue-capacity: 70 + wait-for-tasks-to-complete-on-shutdown: true + await-termination-seconds: 60 From 21c0ad4f4d7d194a5528d2d61ad7c3b810e7c039 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Fri, 19 Jul 2024 01:03:29 +0900 Subject: [PATCH 13/19] fix: add ArticleViewHisAsyncEvent --- .../article/event/ArticleViewHisAsyncEvent.kt | 27 +++++++++++++++++++ .../article/usecase/ReadArticleUseCase.kt | 21 +++------------ 2 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt diff --git a/api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt b/api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt new file mode 100644 index 000000000..a21652ec1 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt @@ -0,0 +1,27 @@ +package com.few.api.domain.article.event + +import com.few.api.config.DatabaseAccessThreadPoolConfig.Companion.DATABASE_ACCESS_POOL +import com.few.api.repo.dao.article.ArticleViewHisDao +import com.few.api.repo.dao.article.command.ArticleViewHisCommand +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.scheduling.annotation.Async +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +@Component +class ArticleViewHisAsyncEvent( + private val articleViewHisDao: ArticleViewHisDao, +) { + private val log = KotlinLogging.logger {} + + @Async(value = DATABASE_ACCESS_POOL) + @Transactional + fun addArticleViewHis(articleId: Long, memberId: Long) { + try { + articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(articleId, memberId)) + log.debug { "Successfully inserted article view history for articleId: $articleId and memberId: $memberId" } + } catch (e: Exception) { + log.error { "Failed to insert article view history for articleId: $articleId and memberId: $memberId" } + } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index 5fef209a1..380bff935 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -1,6 +1,6 @@ package com.few.api.domain.article.usecase -import com.few.api.config.DatabaseAccessThreadPoolConfig.Companion.DATABASE_ACCESS_POOL +import com.few.api.domain.article.event.ArticleViewHisAsyncEvent import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseOut import com.few.api.domain.article.usecase.dto.WriterDetail @@ -11,12 +11,9 @@ import com.few.api.domain.article.service.dto.ReadWriterRecordInDto import com.few.api.exception.common.NotFoundException import com.few.api.repo.dao.article.ArticleDao import com.few.api.repo.dao.article.ArticleViewHisDao -import com.few.api.repo.dao.article.command.ArticleViewHisCommand import com.few.api.repo.dao.article.query.ArticleViewHisCountQuery import com.few.api.repo.dao.article.query.SelectArticleRecordQuery import com.few.data.common.code.CategoryType -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @@ -26,10 +23,9 @@ class ReadArticleUseCase( private val readArticleWriterRecordService: ReadArticleWriterRecordService, private val browseArticleProblemsService: BrowseArticleProblemsService, private val articleViewHisDao: ArticleViewHisDao, + private val articleViewHisAsyncEvent: ArticleViewHisAsyncEvent, ) { - private val log = KotlinLogging.logger {} - @Transactional(readOnly = true) fun execute(useCaseIn: ReadArticleUseCaseIn): ReadArticleUseCaseOut { val articleRecord = SelectArticleRecordQuery(useCaseIn.articleId).let { query: SelectArticleRecordQuery -> @@ -47,7 +43,7 @@ class ReadArticleUseCase( val views = (articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) ?: 0L) + 1L - insertArticleViewHisAsync(useCaseIn.articleId, useCaseIn.memberId) + articleViewHisAsyncEvent.addArticleViewHis(useCaseIn.articleId, useCaseIn.memberId) return ReadArticleUseCaseOut( id = articleRecord.articleId, @@ -64,15 +60,4 @@ class ReadArticleUseCase( views = views ) } - - @Async(value = DATABASE_ACCESS_POOL) - @Transactional - fun insertArticleViewHisAsync(articleId: Long, memberId: Long) { - try { - articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(articleId, memberId)) - log.debug { "Successfully inserted article view history for articleId: $articleId and memberId: $memberId" } - } catch (e: Exception) { - log.error { "Failed to insert article view history for articleId: $articleId and memberId: $memberId" } - } - } } \ No newline at end of file From 4ce6ac99603511bee6d77808afaccf479d3f642f Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Fri, 19 Jul 2024 01:09:52 +0900 Subject: [PATCH 14/19] =?UTF-8?q?test:=20ReadArticleUseCaseTest=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/usecase/ReadArticleUseCaseTest.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt index 4568236de..8a6c7ddef 100644 --- a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt @@ -1,5 +1,6 @@ package com.few.api.domain.article.usecase +import com.few.api.domain.article.event.ArticleViewHisAsyncEvent import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService import com.few.api.domain.article.service.dto.BrowseArticleProblemsOutDto @@ -8,6 +9,7 @@ import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.repo.dao.article.ArticleDao import com.few.api.repo.dao.article.ArticleViewHisDao import com.few.api.repo.dao.article.record.SelectArticleRecord +import io.github.oshai.kotlinlogging.KotlinLogging import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.mockk.* @@ -16,12 +18,14 @@ import java.net.URL import java.time.LocalDateTime class ReadArticleUseCaseTest : BehaviorSpec({ + val log = KotlinLogging.logger {} lateinit var articleDao: ArticleDao lateinit var readArticleWriterRecordService: ReadArticleWriterRecordService lateinit var browseArticleProblemsService: BrowseArticleProblemsService lateinit var useCase: ReadArticleUseCase lateinit var articleViewHisDao: ArticleViewHisDao + lateinit var articleViewHisAsyncEvent: ArticleViewHisAsyncEvent val useCaseIn = ReadArticleUseCaseIn(articleId = 1L, memberId = 1L) beforeContainer { @@ -29,11 +33,13 @@ class ReadArticleUseCaseTest : BehaviorSpec({ readArticleWriterRecordService = mockk() browseArticleProblemsService = mockk() articleViewHisDao = mockk() + articleViewHisAsyncEvent = mockk() useCase = ReadArticleUseCase( articleDao, readArticleWriterRecordService, browseArticleProblemsService, - articleViewHisDao + articleViewHisDao, + articleViewHisAsyncEvent ) } @@ -58,8 +64,10 @@ class ReadArticleUseCaseTest : BehaviorSpec({ every { articleDao.selectArticleRecord(any()) } returns record every { readArticleWriterRecordService.execute(any()) } returns writerSvcOutDto every { browseArticleProblemsService.execute(any()) } returns probSvcOutDto - every { articleViewHisDao.insertArticleViewHis(any()) } just Runs every { articleViewHisDao.countArticleViews(any()) } returns 1L + every { articleViewHisAsyncEvent.addArticleViewHis(any(), any()) } answers { + log.debug { "Inserting article view history asynchronously" } + } then("아티클이 정상 조회된다") { useCase.execute(useCaseIn) @@ -67,6 +75,8 @@ class ReadArticleUseCaseTest : BehaviorSpec({ verify(exactly = 1) { articleDao.selectArticleRecord(any()) } verify(exactly = 1) { readArticleWriterRecordService.execute(any()) } verify(exactly = 1) { browseArticleProblemsService.execute(any()) } + verify(exactly = 1) { articleViewHisDao.countArticleViews(any()) } + verify(exactly = 1) { articleViewHisAsyncEvent.addArticleViewHis(any(), any()) } } } From 51671e4033829754b46eddef37c9ebc78feab5a2 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Fri, 19 Jul 2024 01:11:21 +0900 Subject: [PATCH 15/19] =?UTF-8?q?refactor:=20SubscribeWorkbookUseCaseTest?= =?UTF-8?q?=20=EC=97=90=EC=84=9C=20print=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F?= =?UTF-8?q?=20log=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../subscription/usecase/SubscribeWorkbookUseCaseTest.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt index cb781f828..0d2049086 100644 --- a/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCaseTest.kt @@ -6,6 +6,7 @@ import com.few.api.domain.subscription.service.dto.MemberIdOutDto import com.few.api.domain.subscription.usecase.dto.SubscribeWorkbookUseCaseIn import com.few.api.repo.dao.subscription.SubscriptionDao import com.few.api.repo.dao.subscription.record.WorkbookSubscriptionStatus +import io.github.oshai.kotlinlogging.KotlinLogging import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.BehaviorSpec import io.mockk.every @@ -16,6 +17,7 @@ import io.mockk.Runs import org.springframework.context.ApplicationEventPublisher class SubscribeWorkbookUseCaseTest : BehaviorSpec({ + val log = KotlinLogging.logger {} lateinit var subscriptionDao: SubscriptionDao lateinit var memberService: MemberService @@ -41,7 +43,7 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ every { subscriptionDao.selectTopWorkbookSubscriptionStatus(any()) } returns null every { subscriptionDao.insertWorkbookSubscription(any()) } just Runs every { applicationEventPublisher.publishEvent(event) } answers { - println("Mocking applicationEventPublisher.publishEvent(any()) was called") + log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } then("신규 구독을 추가한다") { @@ -68,7 +70,7 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ every { subscriptionDao.countWorkbookMappedArticles(any()) } returns lastDay every { subscriptionDao.reSubscribeWorkbookSubscription(any()) } just Runs every { applicationEventPublisher.publishEvent(event) } answers { - println("Mocking applicationEventPublisher.publishEvent(any()) was called") + log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } then("재구독한다") { @@ -95,7 +97,7 @@ class SubscribeWorkbookUseCaseTest : BehaviorSpec({ every { subscriptionDao.countWorkbookMappedArticles(any()) } returns lastDay every { subscriptionDao.reSubscribeWorkbookSubscription(any()) } just Runs every { applicationEventPublisher.publishEvent(event) } answers { - println("Mocking applicationEventPublisher.publishEvent(any()) was called") + log.debug { "Mocking applicationEventPublisher.publishEvent(any()) was called" } } then("예외가 발생한다") { From a76ed19e19d2fc4af79324e9bd3d1441f014f6f9 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Fri, 19 Jul 2024 01:13:20 +0900 Subject: [PATCH 16/19] refactor: rename ArticleViewHisAsyncHandler --- .../ArticleViewHisAsyncHandler.kt} | 4 ++-- .../api/domain/article/usecase/ReadArticleUseCase.kt | 6 +++--- .../domain/article/usecase/ReadArticleUseCaseTest.kt | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) rename api/src/main/kotlin/com/few/api/domain/article/{event/ArticleViewHisAsyncEvent.kt => handler/ArticleViewHisAsyncHandler.kt} (93%) diff --git a/api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt b/api/src/main/kotlin/com/few/api/domain/article/handler/ArticleViewHisAsyncHandler.kt similarity index 93% rename from api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt rename to api/src/main/kotlin/com/few/api/domain/article/handler/ArticleViewHisAsyncHandler.kt index a21652ec1..c8a22ac3f 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/event/ArticleViewHisAsyncEvent.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/handler/ArticleViewHisAsyncHandler.kt @@ -1,4 +1,4 @@ -package com.few.api.domain.article.event +package com.few.api.domain.article.handler import com.few.api.config.DatabaseAccessThreadPoolConfig.Companion.DATABASE_ACCESS_POOL import com.few.api.repo.dao.article.ArticleViewHisDao @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @Component -class ArticleViewHisAsyncEvent( +class ArticleViewHisAsyncHandler( private val articleViewHisDao: ArticleViewHisDao, ) { private val log = KotlinLogging.logger {} diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index 380bff935..fc80437e4 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -1,6 +1,6 @@ package com.few.api.domain.article.usecase -import com.few.api.domain.article.event.ArticleViewHisAsyncEvent +import com.few.api.domain.article.handler.ArticleViewHisAsyncHandler import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseIn import com.few.api.domain.article.usecase.dto.ReadArticleUseCaseOut import com.few.api.domain.article.usecase.dto.WriterDetail @@ -23,7 +23,7 @@ class ReadArticleUseCase( private val readArticleWriterRecordService: ReadArticleWriterRecordService, private val browseArticleProblemsService: BrowseArticleProblemsService, private val articleViewHisDao: ArticleViewHisDao, - private val articleViewHisAsyncEvent: ArticleViewHisAsyncEvent, + private val articleViewHisAsyncHandler: ArticleViewHisAsyncHandler, ) { @Transactional(readOnly = true) @@ -43,7 +43,7 @@ class ReadArticleUseCase( val views = (articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) ?: 0L) + 1L - articleViewHisAsyncEvent.addArticleViewHis(useCaseIn.articleId, useCaseIn.memberId) + articleViewHisAsyncHandler.addArticleViewHis(useCaseIn.articleId, useCaseIn.memberId) return ReadArticleUseCaseOut( id = articleRecord.articleId, diff --git a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt index 8a6c7ddef..f333538f4 100644 --- a/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt +++ b/api/src/test/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCaseTest.kt @@ -1,6 +1,6 @@ package com.few.api.domain.article.usecase -import com.few.api.domain.article.event.ArticleViewHisAsyncEvent +import com.few.api.domain.article.handler.ArticleViewHisAsyncHandler import com.few.api.domain.article.service.BrowseArticleProblemsService import com.few.api.domain.article.service.ReadArticleWriterRecordService import com.few.api.domain.article.service.dto.BrowseArticleProblemsOutDto @@ -25,7 +25,7 @@ class ReadArticleUseCaseTest : BehaviorSpec({ lateinit var browseArticleProblemsService: BrowseArticleProblemsService lateinit var useCase: ReadArticleUseCase lateinit var articleViewHisDao: ArticleViewHisDao - lateinit var articleViewHisAsyncEvent: ArticleViewHisAsyncEvent + lateinit var articleViewHisAsyncHandler: ArticleViewHisAsyncHandler val useCaseIn = ReadArticleUseCaseIn(articleId = 1L, memberId = 1L) beforeContainer { @@ -33,13 +33,13 @@ class ReadArticleUseCaseTest : BehaviorSpec({ readArticleWriterRecordService = mockk() browseArticleProblemsService = mockk() articleViewHisDao = mockk() - articleViewHisAsyncEvent = mockk() + articleViewHisAsyncHandler = mockk() useCase = ReadArticleUseCase( articleDao, readArticleWriterRecordService, browseArticleProblemsService, articleViewHisDao, - articleViewHisAsyncEvent + articleViewHisAsyncHandler ) } @@ -65,7 +65,7 @@ class ReadArticleUseCaseTest : BehaviorSpec({ every { readArticleWriterRecordService.execute(any()) } returns writerSvcOutDto every { browseArticleProblemsService.execute(any()) } returns probSvcOutDto every { articleViewHisDao.countArticleViews(any()) } returns 1L - every { articleViewHisAsyncEvent.addArticleViewHis(any(), any()) } answers { + every { articleViewHisAsyncHandler.addArticleViewHis(any(), any()) } answers { log.debug { "Inserting article view history asynchronously" } } @@ -76,7 +76,7 @@ class ReadArticleUseCaseTest : BehaviorSpec({ verify(exactly = 1) { readArticleWriterRecordService.execute(any()) } verify(exactly = 1) { browseArticleProblemsService.execute(any()) } verify(exactly = 1) { articleViewHisDao.countArticleViews(any()) } - verify(exactly = 1) { articleViewHisAsyncEvent.addArticleViewHis(any(), any()) } + verify(exactly = 1) { articleViewHisAsyncHandler.addArticleViewHis(any(), any()) } } } From dc76e36ed0009abddd2f3d901da75c9e5ba6a6e6 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Fri, 19 Jul 2024 01:15:58 +0900 Subject: [PATCH 17/19] =?UTF-8?q?test:=20application-test.yaml=EC=97=90=20?= =?UTF-8?q?database=20async=20thread=20pool=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/test/resources/application-test.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api/src/test/resources/application-test.yml b/api/src/test/resources/application-test.yml index 684314968..387ac0443 100644 --- a/api/src/test/resources/application-test.yml +++ b/api/src/test/resources/application-test.yml @@ -64,3 +64,11 @@ discord: queue-capacity: 30 wait-for-tasks-to-complete-on-shutdown: true await-termination-seconds: 60 + +database: + thread-pool: + core-pool-size: 10 + max-pool-size: 30 + queue-capacity: 70 + wait-for-tasks-to-complete-on-shutdown: true + await-termination-seconds: 60 From 7bac2fcd1cf78a21d197511d2d5a2c5216c8fb0d Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 19 Jul 2024 14:18:21 +0900 Subject: [PATCH 18/19] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=86=B5=EA=B3=BC=ED=95=98=EC=A7=80=20=EB=AA=BB=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/web/controller/article/ArticleControllerTest.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt index 7d572acdb..21aeb68f1 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt @@ -69,7 +69,7 @@ class ArticleControllerTest : ControllerTestSpec() { val uri = UriComponentsBuilder.newInstance().path("$BASE_URL/{articleId}").build().toUriString() // set usecase mock val articleId = 1L - val memberId = 1L + val memberId = 0L `when`(readArticleUseCase.execute(ReadArticleUseCaseIn(articleId, memberId))).thenReturn( ReadArticleUseCaseOut( id = 1L, @@ -121,9 +121,10 @@ class ArticleControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.category") .fieldWithString("아티클 카테고리"), PayloadDocumentation.fieldWithPath("data.createdAt") - .fieldWithString("아티클 생성일"), - PayloadDocumentation.fieldWithPath("data.views") - .fieldWithString("아티클 조회수") + .fieldWithString("아티클 생성일") + // todo: add +// PayloadDocumentation.fieldWithPath("data.views") +// .fieldWithNumber("아티클 조회수") ) ) ).build() From 3348881e7001c800ed1a0bf3f60277ff45921238 Mon Sep 17 00:00:00 2001 From: Jihun-Hwang Date: Fri, 19 Jul 2024 14:45:43 +0900 Subject: [PATCH 19/19] =?UTF-8?q?feat:=20=EC=95=84=ED=8B=B0=ED=81=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EC=9D=91=EB=8B=B5=20=EB=B0=94=EB=94=94=20vie?= =?UTF-8?q?ws=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controller/article/response/ReadArticleResponse.kt | 4 +++- .../api/web/controller/article/ArticleControllerTest.kt | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/api/src/main/kotlin/com/few/api/web/controller/article/response/ReadArticleResponse.kt b/api/src/main/kotlin/com/few/api/web/controller/article/response/ReadArticleResponse.kt index 95ea9cfc5..3d6dd1e2c 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/article/response/ReadArticleResponse.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/article/response/ReadArticleResponse.kt @@ -12,6 +12,7 @@ data class ReadArticleResponse( val problemIds: List, val category: String, val createdAt: LocalDateTime, + val views: Long, ) { constructor( useCaseOut: ReadArticleUseCaseOut, @@ -26,7 +27,8 @@ data class ReadArticleResponse( content = useCaseOut.content, problemIds = useCaseOut.problemIds, category = useCaseOut.category, - createdAt = useCaseOut.createdAt + createdAt = useCaseOut.createdAt, + views = useCaseOut.views ) } diff --git a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt index 21aeb68f1..3c1059b58 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/article/ArticleControllerTest.kt @@ -121,10 +121,9 @@ class ArticleControllerTest : ControllerTestSpec() { PayloadDocumentation.fieldWithPath("data.category") .fieldWithString("아티클 카테고리"), PayloadDocumentation.fieldWithPath("data.createdAt") - .fieldWithString("아티클 생성일") - // todo: add -// PayloadDocumentation.fieldWithPath("data.views") -// .fieldWithNumber("아티클 조회수") + .fieldWithString("아티클 생성일"), + PayloadDocumentation.fieldWithPath("data.views") + .fieldWithNumber("아티클 조회수") ) ) ).build()