Skip to content

Commit

Permalink
Merge pull request #84 from mash-up-kr/xonmin/add-endat-Qset
Browse files Browse the repository at this point in the history
add endTime in Qset
  • Loading branch information
xonmin authored Aug 10, 2024
2 parents 718cc23 + fb568a1 commit 1d79435
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 29 deletions.
2 changes: 1 addition & 1 deletion api/src/main/kotlin/com/mashup/dojo/AdminController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class AdminController(
request.questionIds
val customQuestionSet =
questionUseCase.createCustomQuestionSet(
QuestionUseCase.CreateQuestionSetCommand(request.questionIds, request.publishedAt)
QuestionUseCase.CreateQuestionSetCommand(request.questionIds, request.publishedAt, request.endAt)
)

return DojoApiResponse.success(customQuestionSet.id)
Expand Down
2 changes: 2 additions & 0 deletions api/src/main/kotlin/com/mashup/dojo/dto/QuestionDto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ data class QuestionSetCustomCreateRequest(
val questionIds: List<QuestionId>,
@Schema(description = "질문지 세트를 발행할 시각")
val publishedAt: LocalDateTime,
@Schema(description = "질문지 세트를 종료할 시각")
val endAt: LocalDateTime,
)

@Schema(description = "질문지 (투표 용지) 한 다스 조회 응답")
Expand Down
23 changes: 20 additions & 3 deletions entity/src/main/kotlin/com/mashup/dojo/QuestionSetRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@ import jakarta.persistence.AttributeConverter
import jakarta.persistence.Column
import jakarta.persistence.Convert
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDateTime

interface QuestionSetRepository : JpaRepository<QuestionSetEntity, String> {
// publishedYn : True && publishedAt > now -> 현재 운영중인 QuestionSet
fun findFirstByPublishedYnTrueAndPublishedAtAfterOrderByPublishedAt(compareTime: LocalDateTime = LocalDateTime.now()): QuestionSetEntity?
fun findByPublishedAtAfterAndEndAtBefore(
publishedCompareTime: LocalDateTime = LocalDateTime.now(),
endTimeCompareTime: LocalDateTime = LocalDateTime.now(),
): QuestionSetEntity?

// publishedYn : True && publishedAt < now -> 발행 직전(예정) QuestionSet
fun findFirstByPublishedYnTrueAndPublishedAtBeforeOrderByPublishedAt(compareTime: LocalDateTime = LocalDateTime.now()): QuestionSetEntity?
fun findByStatusAndPublishedAtAfter(
status: Status,
compareTime: LocalDateTime = LocalDateTime.now(),
): QuestionSetEntity?

fun findTopByOrderByPublishedAtDesc(): QuestionSetEntity?
}
Expand All @@ -29,11 +37,20 @@ class QuestionSetEntity(
@Column(name = "question_ids", nullable = false)
val questionIds: List<String>,
@Column(name = "published_yn", nullable = false)
val publishedYn: Boolean = false,
@Enumerated(EnumType.STRING)
val status: Status,
@Column(name = "published_at", nullable = false)
val publishedAt: LocalDateTime,
@Column(name = "end_at", nullable = false)
val endAt: LocalDateTime,
) : BaseEntity()

enum class Status {
TERMINATED, // 종료
ACTIVE, // 운영중
UPCOMING, // 예정
}

class QuestionIdConverter : AttributeConverter<List<String>, String> {
override fun convertToDatabaseColumn(attribute: List<String>): String {
return attribute.joinToString(DELIMITER)
Expand Down
19 changes: 17 additions & 2 deletions service/src/main/kotlin/com/mashup/dojo/domain/QuestionSet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.mashup.dojo.domain

import com.mashup.dojo.UUIDGenerator
import java.time.LocalDateTime
import java.time.LocalTime

@JvmInline
value class QuestionSetId(val value: String)
Expand All @@ -16,20 +17,34 @@ data class QuestionSet(
// 1 based-order
val questionIds: List<QuestionOrder>,
// 발행 여부
val publishedYn: Boolean = false,
val status: PublishStatus = PublishStatus.UPCOMING,
// 질문 발행일
val publishedAt: LocalDateTime,
val endAt: LocalDateTime,
) {
companion object {
fun create(
questionOrders: List<QuestionOrder>,
publishedAt: LocalDateTime,
endAt: LocalDateTime,
): QuestionSet {
return QuestionSet(
id = QuestionSetId(UUIDGenerator.generate()),
questionIds = questionOrders,
publishedAt = publishedAt
publishedAt = publishedAt,
endAt = endAt
)
}
}
}

enum class PublishStatus {
TERMINATED, // 종료
ACTIVE, // 운영중
UPCOMING, // 예정
}

object PublishedTime {
val OPEN_TIME_1: LocalTime = LocalTime.of(8, 0, 0)
val OPEN_TIME_2: LocalTime = LocalTime.of(11, 0, 0)
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ class DefaultPickService(
): List<Pick> {
return pickRepository.findAllByPickedId(pickedMemberId.value)
.map { it.toPick() }
// return listOf(DEFAULT_PICK)
}

override fun getSolvedPickList(
Expand Down
79 changes: 61 additions & 18 deletions service/src/main/kotlin/com/mashup/dojo/service/QuestionService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import com.mashup.dojo.QuestionSetEntity
import com.mashup.dojo.QuestionSetRepository
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
import com.mashup.dojo.domain.Question
import com.mashup.dojo.domain.QuestionCategory
import com.mashup.dojo.domain.QuestionId
Expand All @@ -26,7 +29,9 @@ import org.springframework.data.domain.Pageable
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import kotlin.math.floor

private val log = KotlinLogging.logger {}
Expand Down Expand Up @@ -58,14 +63,12 @@ interface QuestionService {
emojiImageId: ImageId,
): QuestionId

fun createQuestionSet(
excludedQuestionSet: QuestionSet?,
publishedAt: LocalDateTime,
): QuestionSetId
fun createQuestionSet(excludedQuestionSet: QuestionSet?): QuestionSetId

fun createQuestionSet(
questionIds: List<QuestionId>,
publishedAt: LocalDateTime,
endAt: LocalDateTime,
): QuestionSet

fun createQuestionSheets(
Expand Down Expand Up @@ -107,7 +110,7 @@ class DefaultQuestionService(

// 현재 운영중인 QuestionSet
override fun getOperatingQuestionSet(): QuestionSet? {
return questionSetRepository.findFirstByPublishedYnTrueAndPublishedAtAfterOrderByPublishedAt()
return questionSetRepository.findByPublishedAtAfterAndEndAtBefore(LocalDateTime.now(), LocalDateTime.now())
?.toQuestionSet() ?: run {
log.error { "Published And Operating QuestionSet Entity not found" }
null
Expand All @@ -116,7 +119,7 @@ class DefaultQuestionService(

// 발행 출격 준비 완료 QuestionSet
override fun getNextOperatingQuestionSet(): QuestionSet? {
return questionSetRepository.findFirstByPublishedYnTrueAndPublishedAtBeforeOrderByPublishedAt()
return questionSetRepository.findByStatusAndPublishedAtAfter(Status.UPCOMING, LocalDateTime.now())
?.toQuestionSet() ?: run {
log.error { "Published And Prepared for sortie QuestionSet Entity not found" }
null
Expand All @@ -140,10 +143,7 @@ class DefaultQuestionService(
}

@Transactional
override fun createQuestionSet(
excludedQuestionSet: QuestionSet?,
publishedAt: LocalDateTime,
): QuestionSetId {
override fun createQuestionSet(excludedQuestionSet: QuestionSet?): QuestionSetId {
// 비율에 따라 questionType 선정
val friendQuestionSize = floor(questionSetSize * friendQuestionRatio).toInt()
val excludedQuestionIds: List<String> = excludedQuestionSet?.questionIds?.map { it.questionId.value } ?: emptyList()
Expand Down Expand Up @@ -172,13 +172,34 @@ class DefaultQuestionService(
QuestionOrder(questionId = question.id, order = index)
}

// 마지막 QSet 의 발행 시각 가져오기
val latestQSet = getLatestPublishedQuestionSet()

val publishedTime =
latestQSet?.endAt ?: run {
val now = LocalTime.now()
val today = LocalDate.now()

when {
now.isBefore(PublishedTime.OPEN_TIME_1) -> today.atTime(PublishedTime.OPEN_TIME_1)
now.isBefore(PublishedTime.OPEN_TIME_2) -> today.atTime(PublishedTime.OPEN_TIME_2)
else -> today.plusDays(1).atTime(PublishedTime.OPEN_TIME_1)
}
}

val endTime =
if (publishedTime.toLocalTime() == PublishedTime.OPEN_TIME_1) {
publishedTime.toLocalDate().atTime(PublishedTime.OPEN_TIME_2)
} else { // publishedTime.toLocalTime() == PublishedTime.OPEN_TIME_2
publishedTime.toLocalDate().plusDays(1).atTime(PublishedTime.OPEN_TIME_1)
}

// 우선 만들어지는 시점이 다음 투표 이전에 만들어질 QSet 을 만든다고 가정, 따라서 해당 QSet 은 바로 다음 발행될 QSet
// todo: QSet에서 publishedAt 이 가장 큰 녀석 가져온 후 해당 publishedAt 보다 큰 startTime 을 가진 PickTime 정보 가져옴
// (fix publishedAt) 현재 PickTime Entity 는 LocalTime 으로 저장되고 있음. LocalDateTime 이어야 위 가정이 유효
val questionSetEntity =
QuestionSet.create(
questionOrders = questionOrders,
publishedAt = publishedAt
publishedAt = publishedTime,
endAt = endTime
).toEntity()

val id = questionSetRepository.save(questionSetEntity).id
Expand All @@ -190,12 +211,13 @@ class DefaultQuestionService(
override fun createQuestionSet(
questionIds: List<QuestionId>,
publishedAt: LocalDateTime,
endAt: LocalDateTime,
): QuestionSet {
require(questionIds.size == questionSetSize) { "questions size for QuestionSet must be $questionSetSize" }
require(publishedAt >= LocalDateTime.now()) { "publishedAt must be in the future" }

val questionOrders = questionIds.mapIndexed { idx, qId -> QuestionOrder(qId, idx + 1) }
val questionSet = QuestionSet.create(questionOrders, publishedAt)
val questionSet = QuestionSet.create(questionOrders, publishedAt, endAt)

questionSetRepository.save(questionSet.toEntity())
return questionSet
Expand Down Expand Up @@ -235,7 +257,7 @@ class DefaultQuestionService(
emojiImageId = ImageId("345678")
)

val SAMPLE_QUESTION_SET =
private val SAMPLE_QUESTION_SET =
QuestionSet(
id = QuestionSetId("1"),
questionIds =
Expand All @@ -253,7 +275,8 @@ class DefaultQuestionService(
QuestionOrder(QuestionId("11"), 11),
QuestionOrder(QuestionId("12"), 12)
),
publishedAt = LocalDateTime.now()
publishedAt = LocalDateTime.now(),
endAt = LocalDateTime.now().plusHours(12)
)

private val SAMPLE_QUESTION_SHEET =
Expand Down Expand Up @@ -332,7 +355,9 @@ private fun QuestionSet.toEntity(): QuestionSetEntity {
return QuestionSetEntity(
id = id.value,
questionIds = questionIds,
publishedAt = publishedAt
status = status.toDomainPublishStatus(),
publishedAt = publishedAt,
endAt = endAt
)
}

Expand All @@ -348,7 +373,9 @@ private fun QuestionSetEntity.toQuestionSet(): QuestionSet {
return QuestionSet(
id = QuestionSetId(id),
questionIds = questionOrders,
publishedAt = publishedAt
status = status.toDomainPublishStatus(),
publishedAt = publishedAt,
endAt = endAt
)
}

Expand All @@ -361,3 +388,19 @@ private fun QuestionSheetEntity.toQuestionSheetWithCandidatesId(): QuestionSheet
candidates = candidates.map { MemberId(it) }.toList()
)
}

private fun Status.toDomainPublishStatus(): PublishStatus {
return when (this) {
Status.TERMINATED -> PublishStatus.TERMINATED
Status.ACTIVE -> PublishStatus.ACTIVE
Status.UPCOMING -> PublishStatus.UPCOMING
}
}

private fun PublishStatus.toDomainPublishStatus(): Status {
return when (this) {
PublishStatus.TERMINATED -> Status.TERMINATED
PublishStatus.ACTIVE -> Status.ACTIVE
PublishStatus.UPCOMING -> Status.UPCOMING
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface QuestionUseCase {
data class CreateQuestionSetCommand(
val questionIdList: List<QuestionId>,
val publishedAt: LocalDateTime,
val endAt: LocalDateTime,
)

data class GetQuestionSheetsResult(
Expand Down Expand Up @@ -105,15 +106,14 @@ class DefaultQuestionUseCase(
@Transactional
override fun createQuestionSet(): QuestionSetId {
// 가장 마지막에 만들어진 QSet 정보는 제외
val currentQuestionSet = questionService.getLatestPublishedQuestionSet()
val nextPickTime = pickService.getNextPickTime()
val latestQSet = questionService.getLatestPublishedQuestionSet()

return questionService.createQuestionSet(excludedQuestionSet = currentQuestionSet, nextPickTime)
return questionService.createQuestionSet(excludedQuestionSet = latestQSet)
}

@Transactional
override fun createCustomQuestionSet(command: QuestionUseCase.CreateQuestionSetCommand): QuestionSet {
return questionService.createQuestionSet(command.questionIdList, command.publishedAt)
return questionService.createQuestionSet(command.questionIdList, command.publishedAt, command.endAt)
}

@Transactional
Expand Down

0 comments on commit 1d79435

Please sign in to comment.