From 68b864e332ce553f989ecf06cde7cf497be29f51 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 4 Jul 2024 18:15:43 +0900 Subject: [PATCH 1/5] feat: QuestionSheet Scheduler --- .../com/mashup/dojo/config/SchedulerConfig.kt | 11 ++++++ .../com/mashup/dojo/scheduler/Scheduler.kt | 9 +++++ .../mashup/dojo/service/QuestionService.kt | 36 +++++++++++++++++++ .../mashup/dojo/usecase/QuestionUseCase.kt | 8 +++++ 4 files changed, 64 insertions(+) diff --git a/api/src/main/kotlin/com/mashup/dojo/config/SchedulerConfig.kt b/api/src/main/kotlin/com/mashup/dojo/config/SchedulerConfig.kt index 042a8016..75617355 100644 --- a/api/src/main/kotlin/com/mashup/dojo/config/SchedulerConfig.kt +++ b/api/src/main/kotlin/com/mashup/dojo/config/SchedulerConfig.kt @@ -22,4 +22,15 @@ class SchedulerConfig { setWaitForTasksToCompleteOnShutdown(true) } } + + @Bean(name = ["questionSheetSchedulerExecutor"]) + fun questionSheetSchedulerExecutor(): Executor { + return ThreadPoolTaskExecutor().apply { + corePoolSize = 5 + maxPoolSize = 5 + queueCapacity = 20 + setThreadNamePrefix("questionSheetScheduler-") + setWaitForTasksToCompleteOnShutdown(true) + } + } } diff --git a/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt b/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt index 9751bc81..bfd7c2de 100644 --- a/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt +++ b/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt @@ -21,7 +21,16 @@ class Scheduler( log.info { "=== Done Create questionSet at ${LocalDateTime.now()}. ===" } } + @Scheduled(cron = SCHEDULED_SHEET_CRON) + @Async("questionSheetSchedulerExecutor") + fun createQuestionSheet() { + log.info { "=== Start Create questionSheet at ${LocalDateTime.now()}. ===" } + questionUseCase.createQuestionSheet() + log.info { "=== Done Create questionSheet at ${LocalDateTime.now()}. ===" } + } + companion object { private const val SCHEDULED_CRON = "0 0 9,21 * * *" + private const val SCHEDULED_SHEET_CRON = "0 5 9,21 * * *" // 매일 9시 5분과 21시 5분에 실행 } } diff --git a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt index 0aac4bf1..9aadc548 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt @@ -1,12 +1,16 @@ package com.mashup.dojo.service +import com.mashup.dojo.domain.Candidate import com.mashup.dojo.domain.ImageId +import com.mashup.dojo.domain.MemberId import com.mashup.dojo.domain.Question import com.mashup.dojo.domain.QuestionCategory import com.mashup.dojo.domain.QuestionId import com.mashup.dojo.domain.QuestionOrder import com.mashup.dojo.domain.QuestionSet import com.mashup.dojo.domain.QuestionSetId +import com.mashup.dojo.domain.QuestionSheet +import com.mashup.dojo.domain.QuestionSheetId import com.mashup.dojo.domain.QuestionType import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service @@ -30,6 +34,8 @@ interface QuestionService { questionIds: List, publishedAt: LocalDateTime, ): QuestionSet + + fun createQuestionSheetsForAllMembers(excludedQuestionSheet: QuestionSet?): List } @Service @@ -79,6 +85,17 @@ class DefaultQuestionService : QuestionService { return SAMPLE_QUESTION_SET } + override fun createQuestionSheetsForAllMembers(questionSet: QuestionSet?): List { + /** + * TODO: + * - Get QuestionSheet by member for all members + * - Create a QuestionSheet with the candidates and resolver + * - cache put -> QuestionSet and return + * - Temporarily set to create for all members, discuss details later + */ + return LIST_SAMPLE_QUESTION_SHEET + } + companion object { private const val DEFAULT_QUESTION_SIZE: Int = 12 val SAMPLE_QUESTION = @@ -112,5 +129,24 @@ class DefaultQuestionService : QuestionService { ), publishedAt = LocalDateTime.now() ) + + private val SAMPLE_QUESTION_SHEET = + QuestionSheet( + questionSheetId = QuestionSheetId("1"), + questionSetId = SAMPLE_QUESTION_SET.id, + questionId = QuestionId("1"), + resolverId = MemberId("1"), + candidates = + listOf( + Candidate(MemberId("2"), "임준형", 1), + Candidate(MemberId("3"), "한씨", 1), + Candidate(MemberId("4"), "박씨", 1), + Candidate(MemberId("5"), "오씨", 1) + ) + ) + + // TODO: Set to 3 sheets initially. Need to modify for all users later. + val LIST_SAMPLE_QUESTION_SHEET = + listOf(SAMPLE_QUESTION_SHEET, SAMPLE_QUESTION_SHEET, SAMPLE_QUESTION_SHEET) } } diff --git a/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt b/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt index 1a70367f..c5c121e7 100644 --- a/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt +++ b/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt @@ -4,6 +4,7 @@ import com.mashup.dojo.domain.ImageId import com.mashup.dojo.domain.Question import com.mashup.dojo.domain.QuestionId import com.mashup.dojo.domain.QuestionSet +import com.mashup.dojo.domain.QuestionSheet import com.mashup.dojo.domain.QuestionType import com.mashup.dojo.service.QuestionService import org.springframework.stereotype.Component @@ -29,6 +30,8 @@ interface QuestionUseCase { fun createQuestionSet(): QuestionSet fun createCustomQuestionSet(command: CreateQuestionSetCommand): QuestionSet + + fun createQuestionSheet(): List } @Component @@ -60,4 +63,9 @@ class DefaultQuestionUseCase( override fun createCustomQuestionSet(command: QuestionUseCase.CreateQuestionSetCommand): QuestionSet { return questionService.createQuestionSet(command.questionIdList, command.publishedAt) } + + override fun createQuestionSheet(): List { + val currentQuestionSet = questionService.getCurrentQuestionSet() + return questionService.createQuestionSheetsForAllMembers(currentQuestionSet) + } } From 9cc6b4b8656af982270b3f29574c52277bb3e272 Mon Sep 17 00:00:00 2001 From: toychip Date: Sat, 6 Jul 2024 03:32:45 +0900 Subject: [PATCH 2/5] feat: update createQuestionSheet to generate sheets for all members --- .../com/mashup/dojo/DojoExceptionType.kt | 2 + .../kotlin/com/mashup/dojo/domain/Member.kt | 44 +++++++++++++++++++ .../com/mashup/dojo/service/MemberService.kt | 22 ++++++++++ .../mashup/dojo/service/QuestionService.kt | 18 ++++++-- .../mashup/dojo/usecase/QuestionUseCase.kt | 5 ++- 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt b/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt index 492b7896..e79f1fef 100644 --- a/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt +++ b/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt @@ -16,4 +16,6 @@ enum class DojoExceptionType( ACCESS_DENIED("Access denied. Check authentication.", "C007_ACCESS_DENIED", 403), AUTHENTICATION_FAILURE("Authentication failed. Check login.", "C008_AUTHENTICATION_FAILURE", 401), ARGUMENT_NOT_VALID("Method Argument Not Valid. Check argument validation.", "C009_ARGUMENT_NOT_VALID", 400), + INVALID_MEMBER_GENDER("The gender does not exist.", "C011_INVALID_MEMBER_GENDER", 400), + INVALID_MEMBER_PLATFORM("The platform does not exist.", "C010_INVALID_MEMBER_PLATFORM", 400), } diff --git a/service/src/main/kotlin/com/mashup/dojo/domain/Member.kt b/service/src/main/kotlin/com/mashup/dojo/domain/Member.kt index 3630093d..4599e363 100644 --- a/service/src/main/kotlin/com/mashup/dojo/domain/Member.kt +++ b/service/src/main/kotlin/com/mashup/dojo/domain/Member.kt @@ -1,5 +1,7 @@ package com.mashup.dojo.domain +import com.mashup.dojo.DojoException +import com.mashup.dojo.DojoExceptionType import com.mashup.dojo.UUIDGenerator import java.time.LocalDateTime @@ -66,12 +68,46 @@ data class Member( updatedAt = LocalDateTime.now() ) } + + internal fun convertToMember( + id: String, + fullName: String, + secondInitialName: String, + profileImageId: ImageId?, + ordinal: Int, + platform: MemberPlatform, + gender: MemberGender, + point: Int, + createdAt: LocalDateTime, + updatedAt: LocalDateTime, + ): Member { + return Member( + id = MemberId(id), + fullName = fullName, + secondInitialName = secondInitialName, + profileImageId = profileImageId, + ordinal = ordinal, + platform = platform, + gender = gender, + point = point, + createdAt = createdAt, + updatedAt = updatedAt + ) + } } } enum class MemberGender { MALE, FEMALE, + ; + + companion object { + fun findByValue(value: String): MemberGender { + return entries.find { it.name.equals(value, ignoreCase = true) } + ?: throw DojoException.of(DojoExceptionType.INVALID_MEMBER_GENDER) + } + } } enum class MemberPlatform { @@ -81,4 +117,12 @@ enum class MemberPlatform { ANDROID, IOS, DESIGN, + ; + + companion object { + fun findByValue(value: String): MemberPlatform { + return entries.find { it.name.equals(value, ignoreCase = true) } + ?: throw DojoException.of(DojoExceptionType.INVALID_MEMBER_PLATFORM) + } + } } diff --git a/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt b/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt index df6b8a03..d788dc95 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt @@ -20,6 +20,8 @@ interface MemberService { fun create(command: CreateMember): MemberId + fun findAllMember(): List + data class CreateMember( val fullName: String, val profileImageId: ImageId?, @@ -90,6 +92,26 @@ class DefaultMemberService( return MemberId(id) } + override fun findAllMember(): List { + return memberRepository.findAll().stream().map { m -> + val platform = MemberPlatform.findByValue(m.platform) + val gender = MemberGender.findByValue(m.gender) + val imageId = m.profileImageId?.let { profileImageId -> ImageId(profileImageId) } + Member.convertToMember( + m.id, + m.fullName, + m.secondInitialName, + imageId, + m.ordinal, + platform, + gender, + m.point, + m.createdAt, + m.updatedAt + ) + }.toList() + } + private fun mockMember(memberId: MemberId) = Member( memberId, "임준형", "ㅈ", ImageId("123456"), MemberPlatform.SPRING, 14, MemberGender.MALE, 200, LocalDateTime.now(), LocalDateTime.now() diff --git a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt index 9aadc548..7e045247 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt @@ -2,6 +2,7 @@ package com.mashup.dojo.service import com.mashup.dojo.domain.Candidate import com.mashup.dojo.domain.ImageId +import com.mashup.dojo.domain.Member import com.mashup.dojo.domain.MemberId import com.mashup.dojo.domain.Question import com.mashup.dojo.domain.QuestionCategory @@ -35,7 +36,10 @@ interface QuestionService { publishedAt: LocalDateTime, ): QuestionSet - fun createQuestionSheetsForAllMembers(excludedQuestionSheet: QuestionSet?): List + fun createQuestionSheets( + questionSet: QuestionSet?, + members: List, + ): List } @Service @@ -85,11 +89,17 @@ class DefaultQuestionService : QuestionService { return SAMPLE_QUESTION_SET } - override fun createQuestionSheetsForAllMembers(questionSet: QuestionSet?): List { + override fun createQuestionSheets( + questionSet: QuestionSet?, + members: List, + ): List { /** * TODO: - * - Get QuestionSheet by member for all members - * - Create a QuestionSheet with the candidates and resolver + * target : members + * question : QuestionSet + * candidate : member.candidate() + * + * - make friend logic, get Candidate logic * - cache put -> QuestionSet and return * - Temporarily set to create for all members, discuss details later */ diff --git a/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt b/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt index c5c121e7..93a7d1fd 100644 --- a/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt +++ b/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt @@ -6,6 +6,7 @@ import com.mashup.dojo.domain.QuestionId import com.mashup.dojo.domain.QuestionSet import com.mashup.dojo.domain.QuestionSheet import com.mashup.dojo.domain.QuestionType +import com.mashup.dojo.service.MemberService import com.mashup.dojo.service.QuestionService import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @@ -38,6 +39,7 @@ interface QuestionUseCase { @Transactional(readOnly = true) class DefaultQuestionUseCase( private val questionService: QuestionService, + private val memberService: MemberService, ) : QuestionUseCase { override fun create(command: QuestionUseCase.CreateCommand): Question { return questionService.createQuestion( @@ -66,6 +68,7 @@ class DefaultQuestionUseCase( override fun createQuestionSheet(): List { val currentQuestionSet = questionService.getCurrentQuestionSet() - return questionService.createQuestionSheetsForAllMembers(currentQuestionSet) + val allMemberRecords = memberService.findAllMember() + return questionService.createQuestionSheets(currentQuestionSet, allMemberRecords) } } From f573ce04e5777bf8f48a1435b8bacd5981b335af Mon Sep 17 00:00:00 2001 From: toychip Date: Sat, 13 Jul 2024 14:15:41 +0900 Subject: [PATCH 3/5] refactor: findAllMember method for improved readability and efficiency --- .../com/mashup/dojo/service/MemberService.kt | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt b/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt index d788dc95..69a640a1 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/MemberService.kt @@ -93,23 +93,25 @@ class DefaultMemberService( } override fun findAllMember(): List { - return memberRepository.findAll().stream().map { m -> - val platform = MemberPlatform.findByValue(m.platform) - val gender = MemberGender.findByValue(m.gender) - val imageId = m.profileImageId?.let { profileImageId -> ImageId(profileImageId) } - Member.convertToMember( - m.id, - m.fullName, - m.secondInitialName, - imageId, - m.ordinal, - platform, - gender, - m.point, - m.createdAt, - m.updatedAt - ) - }.toList() + return memberRepository.findAll() + .map { m -> + val platform = MemberPlatform.findByValue(m.platform) + val gender = MemberGender.findByValue(m.gender) + val imageId = m.profileImageId?.let { ImageId(it) } + + Member.convertToMember( + id = m.id, + fullName = m.fullName, + secondInitialName = m.secondInitialName, + profileImageId = imageId, + ordinal = m.ordinal, + platform = platform, + gender = gender, + point = m.point, + createdAt = m.createdAt, + updatedAt = m.updatedAt + ) + } } private fun mockMember(memberId: MemberId) = From bde18496b159e9f61591ae9d23bf88632bb8a465 Mon Sep 17 00:00:00 2001 From: toychip Date: Sat, 13 Jul 2024 14:36:01 +0900 Subject: [PATCH 4/5] refactor: scheduler cron configuration properties --- .../main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt | 9 ++------- api/src/main/resources/application.yaml | 4 ++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt b/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt index bfd7c2de..fc9a5211 100644 --- a/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt +++ b/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt @@ -13,7 +13,7 @@ private val log = KotlinLogging.logger {} class Scheduler( private val questionUseCase: QuestionUseCase, ) { - @Scheduled(cron = SCHEDULED_CRON) + @Scheduled(cron = "\${scheduler.cron}") @Async("questionSetSchedulerExecutor") fun createQuestionSet() { log.info { "=== Start Create questionSet at ${LocalDateTime.now()}. ===" } @@ -21,16 +21,11 @@ class Scheduler( log.info { "=== Done Create questionSet at ${LocalDateTime.now()}. ===" } } - @Scheduled(cron = SCHEDULED_SHEET_CRON) + @Scheduled(cron = "\${scheduler.sheet-cron}") @Async("questionSheetSchedulerExecutor") fun createQuestionSheet() { log.info { "=== Start Create questionSheet at ${LocalDateTime.now()}. ===" } questionUseCase.createQuestionSheet() log.info { "=== Done Create questionSheet at ${LocalDateTime.now()}. ===" } } - - companion object { - private const val SCHEDULED_CRON = "0 0 9,21 * * *" - private const val SCHEDULED_SHEET_CRON = "0 5 9,21 * * *" // 매일 9시 5분과 21시 5분에 실행 - } } diff --git a/api/src/main/resources/application.yaml b/api/src/main/resources/application.yaml index a1bb35b1..56a3ff7f 100644 --- a/api/src/main/resources/application.yaml +++ b/api/src/main/resources/application.yaml @@ -3,3 +3,7 @@ spring: name: api profiles: include: entity, common + +scheduler: + cron: "0 0 9,21 * * *" + sheet-cron: "0 5 9,21 * * *" From 638fc67783b16d6359290dcf7eccbb4c8cb9beb2 Mon Sep 17 00:00:00 2001 From: toychip Date: Sat, 13 Jul 2024 14:40:40 +0900 Subject: [PATCH 5/5] refactor: ktlintFormat --- .../src/main/kotlin/com/mashup/dojo/service/QuestionService.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt index ec289057..553b1511 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt @@ -36,7 +36,6 @@ interface QuestionService { publishedAt: LocalDateTime, ): QuestionSet - fun createQuestionSheets( questionSet: QuestionSet?, members: List, @@ -92,7 +91,6 @@ class DefaultQuestionService : QuestionService { return SAMPLE_QUESTION_SET } - override fun createQuestionSheets( questionSet: QuestionSet?, members: List,