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..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,7 +21,11 @@ class Scheduler( log.info { "=== Done Create questionSet at ${LocalDateTime.now()}. ===" } } - companion object { - private const val SCHEDULED_CRON = "0 0 9,21 * * *" + @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()}. ===" } } } 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 * * *" 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..69a640a1 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,28 @@ class DefaultMemberService( return MemberId(id) } + override fun findAllMember(): List { + 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) = 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 40965ad7..553b1511 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,17 @@ 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 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 @@ -31,6 +36,11 @@ interface QuestionService { publishedAt: LocalDateTime, ): QuestionSet + fun createQuestionSheets( + questionSet: QuestionSet?, + members: List, + ): List + fun getQuestionById(id: QuestionId): Question? } @@ -81,6 +91,23 @@ class DefaultQuestionService : QuestionService { return SAMPLE_QUESTION_SET } + override fun createQuestionSheets( + questionSet: QuestionSet?, + members: List, + ): List { + /** + * TODO: + * 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 + */ + return LIST_SAMPLE_QUESTION_SHEET + } + override fun getQuestionById(id: QuestionId): Question? { // TODO("Not yet implemented") return SAMPLE_QUESTION @@ -119,5 +146,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..93a7d1fd 100644 --- a/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt +++ b/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt @@ -4,7 +4,9 @@ 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.MemberService import com.mashup.dojo.service.QuestionService import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @@ -29,12 +31,15 @@ interface QuestionUseCase { fun createQuestionSet(): QuestionSet fun createCustomQuestionSet(command: CreateQuestionSetCommand): QuestionSet + + fun createQuestionSheet(): List } @Component @Transactional(readOnly = true) class DefaultQuestionUseCase( private val questionService: QuestionService, + private val memberService: MemberService, ) : QuestionUseCase { override fun create(command: QuestionUseCase.CreateCommand): Question { return questionService.createQuestion( @@ -60,4 +65,10 @@ class DefaultQuestionUseCase( override fun createCustomQuestionSet(command: QuestionUseCase.CreateQuestionSetCommand): QuestionSet { return questionService.createQuestionSet(command.questionIdList, command.publishedAt) } + + override fun createQuestionSheet(): List { + val currentQuestionSet = questionService.getCurrentQuestionSet() + val allMemberRecords = memberService.findAllMember() + return questionService.createQuestionSheets(currentQuestionSet, allMemberRecords) + } }