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 fc9a5211..34b1e84b 100644 --- a/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt +++ b/api/src/main/kotlin/com/mashup/dojo/scheduler/Scheduler.kt @@ -25,7 +25,9 @@ class Scheduler( @Async("questionSheetSchedulerExecutor") fun createQuestionSheet() { log.info { "=== Start Create questionSheet at ${LocalDateTime.now()}. ===" } - questionUseCase.createQuestionSheet() + + // ToDo 친구 관계 생성 후 주석 해제할 것 + // questionUseCase.createQuestionSheet() log.info { "=== Done Create questionSheet at ${LocalDateTime.now()}. ===" } } } diff --git a/entity/src/main/kotlin/com/mashup/dojo/MemberRelationRepository.kt b/entity/src/main/kotlin/com/mashup/dojo/MemberRelationRepository.kt index 3df77917..653d5d4c 100644 --- a/entity/src/main/kotlin/com/mashup/dojo/MemberRelationRepository.kt +++ b/entity/src/main/kotlin/com/mashup/dojo/MemberRelationRepository.kt @@ -1,6 +1,7 @@ package com.mashup.dojo import com.mashup.dojo.base.BaseTimeEntity +import com.querydsl.core.types.dsl.Expressions import com.querydsl.jpa.impl.JPAQueryFactory import jakarta.persistence.Column import jakarta.persistence.Entity @@ -48,6 +49,16 @@ interface MemberRelationQueryRepository { fromId: String, toId: String, ): Boolean + + fun findRandomOfFriend( + memberId: String, + limit: Long, + ): List + + fun findRandomOfAccompany( + memberId: String, + limit: Long, + ): List } class MemberRelationQueryRepositoryImpl( @@ -92,6 +103,42 @@ class MemberRelationQueryRepositoryImpl( return findMemberRelation != null } + override fun findRandomOfFriend( + memberId: String, + limit: Long, + ): List { + val memberRelation = QMemberRelationEntity.memberRelationEntity + + return jpaQueryFactory + .select(memberRelation.toId) + .from(memberRelation) + .where( + memberRelation.fromId.eq(memberId), + memberRelation.relationType.eq(RelationType.FRIEND) + ) + .orderBy(Expressions.numberTemplate(Double::class.java, "function('RAND')").asc()) + .limit(limit) + .fetch() + } + + override fun findRandomOfAccompany( + memberId: String, + limit: Long, + ): List { + val memberRelation = QMemberRelationEntity.memberRelationEntity + + return jpaQueryFactory + .select(memberRelation.toId) + .from(memberRelation) + .where( + memberRelation.fromId.eq(memberId), + memberRelation.relationType.eq(RelationType.ACCOMPANY) + ) + .orderBy(Expressions.numberTemplate(Double::class.java, "function('RAND')").asc()) + .limit(8) + .fetch() + } + private fun findByFromIdAndRelationType( fromId: String, relationType: RelationType, diff --git a/entity/src/main/kotlin/com/mashup/dojo/QuestionRepository.kt b/entity/src/main/kotlin/com/mashup/dojo/QuestionRepository.kt index e13dd6d3..85fcd8d6 100644 --- a/entity/src/main/kotlin/com/mashup/dojo/QuestionRepository.kt +++ b/entity/src/main/kotlin/com/mashup/dojo/QuestionRepository.kt @@ -1,6 +1,7 @@ package com.mashup.dojo import com.mashup.dojo.base.BaseEntity +import com.querydsl.jpa.impl.JPAQueryFactory import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType @@ -12,7 +13,7 @@ import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.query.Param -interface QuestionRepository : JpaRepository { +interface QuestionRepository : JpaRepository, QuestionRepositoryCustom { @Query("SELECT q FROM QuestionEntity q WHERE q.type = :type AND q.id NOT IN :excludeIds ORDER BY function('RAND')") fun findRandomQuestions( @Param("type") type: QuestionType, @@ -54,3 +55,37 @@ enum class QuestionCategory { HUMOR, OTHER, } + +interface QuestionRepositoryCustom { + fun findFriendQuestionsByIds(questionIds: List): List + + fun findAccompanyQuestionsByIds(questionIds: List): List +} + +class QuestionRepositoryImpl( + private val queryFactory: JPAQueryFactory, +) : QuestionRepositoryCustom { + override fun findFriendQuestionsByIds(questionIds: List): List { + val question = QQuestionEntity.questionEntity + + return queryFactory.select(question.id) + .from(question) + .where( + question.type.eq(QuestionType.FRIEND), + question.id.`in`(questionIds) + ) + .fetch() + } + + override fun findAccompanyQuestionsByIds(questionIds: List): List { + val question = QQuestionEntity.questionEntity + + return queryFactory.select(question.id) + .from(question) + .where( + question.type.eq(QuestionType.ACCOMPANY), + question.id.`in`(questionIds) + ) + .fetch() + } +} diff --git a/entity/src/main/resources/application-entity.yaml b/entity/src/main/resources/application-entity.yaml index f1e78305..c4941061 100644 --- a/entity/src/main/resources/application-entity.yaml +++ b/entity/src/main/resources/application-entity.yaml @@ -11,6 +11,7 @@ spring: hibernate: dialect: org.hibernate.dialect.MySQLDialect show_sql: true + default_batch_fetch_size: 1000 jasypt: encryptor: diff --git a/service/src/main/kotlin/com/mashup/dojo/domain/Question.kt b/service/src/main/kotlin/com/mashup/dojo/domain/Question.kt index 454a2d39..3036c0f3 100644 --- a/service/src/main/kotlin/com/mashup/dojo/domain/Question.kt +++ b/service/src/main/kotlin/com/mashup/dojo/domain/Question.kt @@ -66,4 +66,21 @@ data class QuestionSheet( val questionId: QuestionId, val resolverId: MemberId, val candidates: List, -) +) { + companion object { + fun create( + questionSetId: QuestionSetId, + questionId: QuestionId, + resolverId: MemberId, + candidates: List, + ): QuestionSheet { + return QuestionSheet( + questionSheetId = QuestionSheetId(UUIDGenerator.generate()), + questionSetId = questionSetId, + questionId = questionId, + resolverId = resolverId, + candidates = candidates + ) + } + } +} diff --git a/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt b/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt index 605b77dc..5bc49e6f 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt @@ -8,6 +8,7 @@ import com.mashup.dojo.domain.MemberId import com.mashup.dojo.domain.MemberRelation import com.mashup.dojo.domain.MemberRelationId import com.mashup.dojo.domain.RelationType +import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -32,12 +33,18 @@ interface MemberRelationService { fromId: MemberId, toId: MemberId, ): Boolean + + fun findCandidateOfFriend(memberId: MemberId): List + + fun findCandidateOfAccompany(memberId: MemberId): List } @Service @Transactional(readOnly = true) class DefaultMemberRelationService( private val memberRelationRepository: MemberRelationRepository, + @Value("\${dojo.candidate.size}") + private val defaultCandidateSize: Long, ) : MemberRelationService { override fun getAllRelationShip(fromId: MemberId): List { return memberRelationRepository.findByFromId(fromId.value).map { MemberId(it) } @@ -79,6 +86,24 @@ class DefaultMemberRelationService( ): Boolean { return memberRelationRepository.isFriend(fromId = fromId.value, toId = toId.value) } + + override fun findCandidateOfFriend(memberId: MemberId): List { + return memberRelationRepository.findRandomOfFriend( + memberId = memberId.value, + limit = defaultCandidateSize + ).map { + MemberId(it) + } + } + + override fun findCandidateOfAccompany(memberId: MemberId): List { + return memberRelationRepository.findRandomOfAccompany( + memberId = memberId.value, + limit = defaultCandidateSize + ).map { + MemberId(it) + } + } } private fun MemberRelation.toEntity(): MemberRelationEntity { 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 2d12b003..7f2217b0 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt @@ -10,7 +10,6 @@ import com.mashup.dojo.QuestionSheetEntity import com.mashup.dojo.QuestionSheetRepository import com.mashup.dojo.Status import com.mashup.dojo.domain.ImageId -import com.mashup.dojo.domain.Member import com.mashup.dojo.domain.MemberId import com.mashup.dojo.domain.PublishStatus import com.mashup.dojo.domain.PublishedTime @@ -71,10 +70,14 @@ interface QuestionService { endAt: LocalDateTime, ): QuestionSet - fun createQuestionSheets( + fun createQuestionSheetsForMember( questionSet: QuestionSet, - members: List, + candidatesOfFriend: List, + candidatesOfAccompany: List, + resolver: MemberId, ): List + + fun saveQuestionSheets(allMemberQuestionSheets: List): List } @Service @@ -224,21 +227,49 @@ class DefaultQuestionService( } @Transactional - override fun createQuestionSheets( + override fun createQuestionSheetsForMember( questionSet: QuestionSet, - members: List, + candidatesOfFriend: List, + candidatesOfAccompany: List, + resolver: MemberId, ): List { /** - * TODO: - * target : members - * question : QuestionSet - * candidate : member.candidate() - * - * - make friend logic, get Candidate logic + * ToDo 아래는 추후 캐시에 넣는 작업을 해야합니다. * - cache put -> QuestionSet and return * - Temporarily set to create for all members, discuss details later */ - return LIST_SAMPLE_QUESTION_SHEET + + val questionIds = questionSet.questionIds.map { questionOrder -> questionOrder.questionId.value } + val friendQuestionIds = questionRepository.findFriendQuestionsByIds(questionIds) + val accompanyQuestionIds = questionRepository.findAccompanyQuestionsByIds(questionIds) + + val friendQuestionSheets = + friendQuestionIds.map { friendQuestionId -> + QuestionSheet.create( + questionSetId = questionSet.id, + questionId = QuestionId(friendQuestionId), + resolverId = resolver, + candidates = candidatesOfFriend + ) + } + + val accompanyQuestionSheets = + accompanyQuestionIds.map { friendQuestionId -> + QuestionSheet.create( + questionSetId = questionSet.id, + questionId = QuestionId(friendQuestionId), + resolverId = resolver, + candidates = candidatesOfAccompany + ) + } + + return friendQuestionSheets + accompanyQuestionSheets + } + + override fun saveQuestionSheets(allMemberQuestionSheets: List): List { + val questionSheetEntities = allMemberQuestionSheets.map { it.toEntity() } + val saveQuestionSheetEntities = questionSheetRepository.saveAll(questionSheetEntities) + return saveQuestionSheetEntities.map { it.toQuestionSheetWithCandidatesId() } } override fun getQuestionById(id: QuestionId): Question? { @@ -379,6 +410,16 @@ private fun QuestionSetEntity.toQuestionSet(): QuestionSet { ) } +private fun QuestionSheet.toEntity(): QuestionSheetEntity { + return QuestionSheetEntity( + id = questionSheetId.value, + questionSetId = questionSetId.value, + questionId = questionId.value, + resolverId = resolverId.value, + candidates = candidates.map { it.value }.toList() + ) +} + private fun QuestionSheetEntity.toQuestionSheetWithCandidatesId(): QuestionSheet { return QuestionSheet( questionSheetId = QuestionSheetId(id), 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 13c1cf13..e2457ad8 100644 --- a/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt +++ b/service/src/main/kotlin/com/mashup/dojo/usecase/QuestionUseCase.kt @@ -13,6 +13,7 @@ import com.mashup.dojo.domain.QuestionSheet import com.mashup.dojo.domain.QuestionSheetId import com.mashup.dojo.domain.QuestionType import com.mashup.dojo.service.ImageService +import com.mashup.dojo.service.MemberRelationService import com.mashup.dojo.service.MemberService import com.mashup.dojo.service.PickService import com.mashup.dojo.service.QuestionService @@ -85,6 +86,7 @@ class DefaultQuestionUseCase( private val memberService: MemberService, private val pickService: PickService, private val imageService: ImageService, + private val memberRelationService: MemberRelationService, ) : QuestionUseCase { @Transactional override fun create(command: QuestionUseCase.CreateCommand): QuestionId { @@ -120,7 +122,21 @@ class DefaultQuestionUseCase( override fun createQuestionSheet(): List { val currentQuestionSet = questionService.getNextOperatingQuestionSet() ?: throw DojoException.of(DojoExceptionType.QUESTION_SET_NOT_READY) val allMemberRecords = memberService.findAllMember() - return questionService.createQuestionSheets(currentQuestionSet, allMemberRecords) + // ToDo Default 친구 수가 8명 이하일 경우 오류 발생하므로, 친구가 8명이 아니면 회원가입 불가능 + + val allMemberQuestionSheets = + allMemberRecords.flatMap { member -> + val candidateOfFriend = memberRelationService.findCandidateOfFriend(member.id) + val candidateOfAccompany = memberRelationService.findCandidateOfAccompany(member.id) + questionService.createQuestionSheetsForMember( + questionSet = currentQuestionSet, + candidatesOfFriend = candidateOfFriend, + candidatesOfAccompany = candidateOfAccompany, + resolver = member.id + ) + } + + return questionService.saveQuestionSheets(allMemberQuestionSheets) } override fun getQuestionSheetList(memberId: MemberId): QuestionUseCase.GetQuestionSheetsResult { diff --git a/service/src/main/resources/application.yaml b/service/src/main/resources/application.yaml new file mode 100644 index 00000000..f133ee54 --- /dev/null +++ b/service/src/main/resources/application.yaml @@ -0,0 +1,10 @@ +spring: + application: + name: api + profiles: + include: entity, common + +dojo: + candidate: + size: 8 +