Skip to content

Commit

Permalink
Merge pull request #39 from Team-Plu/fix/#38
Browse files Browse the repository at this point in the history
[FIX] Answer, Question 관련 오류 수정
  • Loading branch information
sookyungg authored Apr 15, 2024
2 parents 8f708e5 + 4ce1b80 commit 3710675
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import com.th.plu.api.controller.answer.dto.response.AnswerInfoResponse
import com.th.plu.api.controller.answer.dto.response.EveryAnswerInfoResponse
import com.th.plu.api.controller.answer.dto.toAnswerWriting
import com.th.plu.api.controller.answer.dto.toWritingAnswerResponse
import com.th.plu.common.dto.response.ApiResponse
import com.th.plu.api.service.answer.AnswerService
import com.th.plu.common.dto.response.ApiResponse
import com.th.plu.domain.domain.answer.dto.EveryAnswerRetrieveResponses
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
Expand Down Expand Up @@ -62,7 +62,7 @@ class AnswerController(
@GetMapping("/v1/answers/info")
fun findEveryAnswerInfo(
): ApiResponse<EveryAnswerInfoResponse> {
return ApiResponse.success(answerService.findEveryAnswerInfo())
return ApiResponse.success(answerService.findAllAnswerInfo())
}

@Auth
Expand All @@ -72,7 +72,7 @@ class AnswerController(
@RequestParam(defaultValue = Long.MAX_VALUE.toString()) lastAnswerId: Long,
@RequestParam(defaultValue = "10") pageSize: Long,
): ApiResponse<EveryAnswerRetrieveResponses> {
return ApiResponse.success(answerService.findEveryAnswersWithCursor(lastAnswerId, pageSize))
return ApiResponse.success(answerService.findAllAnswersWithCursor(lastAnswerId, pageSize))
}

@Auth
Expand All @@ -81,6 +81,6 @@ class AnswerController(
fun getAnswersAboutLikeTopN(
@RequestParam(defaultValue = "10") getCount: Long,
): ApiResponse<EveryAnswerRetrieveResponses> {
return ApiResponse.success(answerService.findEveryAnswersLikeTopN(getCount))
return ApiResponse.success(answerService.findAllAnswersLikeTopN(getCount))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.th.plu.api.controller.answer.dto.response.EveryAnswerInfoResponse
import com.th.plu.api.service.like.LikeValidator
import com.th.plu.common.exception.code.ErrorCode
import com.th.plu.common.exception.model.ConflictException
import com.th.plu.common.exception.model.IllegalArgumentException
import com.th.plu.domain.domain.answer.AnswerRegister
import com.th.plu.domain.domain.answer.AnswerWriting
import com.th.plu.domain.domain.answer.WritingAnswerResult
Expand All @@ -15,7 +16,7 @@ import com.th.plu.domain.domain.like.Like
import com.th.plu.domain.domain.like.explorer.LikeExplorer
import com.th.plu.domain.domain.like.repository.LikeRepository
import com.th.plu.domain.domain.member.explorer.MemberExplorer
import com.th.plu.domain.domain.question.QuestionExplorer
import com.th.plu.domain.domain.question.explorer.QuestionExplorer
import com.th.plu.domain.isUniqueError
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.stereotype.Service
Expand Down Expand Up @@ -63,21 +64,23 @@ class AnswerService(
}

@Transactional(readOnly = true)
fun findEveryAnswersWithCursor(lastAnswerId: Long, pageSize: Long): EveryAnswerRetrieveResponses {
fun findAllAnswersWithCursor(lastAnswerId: Long, pageSize: Long): EveryAnswerRetrieveResponses {
val todayQuestionId = questionExplorer.findTodayQuestion().id
val answers = answerRepository.findEveryAnswersWithCursorAndPageSize(todayQuestionId, lastAnswerId, pageSize)
return EveryAnswerRetrieveResponses(answers)
}

@Transactional(readOnly = true)
fun findEveryAnswerInfo(): EveryAnswerInfoResponse {
fun findAllAnswerInfo(): EveryAnswerInfoResponse {
val todayQuestion = questionExplorer.findTodayQuestion()
val answerCount = answerRepository.findPublicAnswersCountByQuestionId(todayQuestion.id)
?: throw IllegalArgumentException(ErrorCode.Illegal_ARGUMENT_ANSWER_COUNT_EXCEPTION, "answerCount는 비어 있을 수 없습니다.")

return EveryAnswerInfoResponse.of(todayQuestion, answerCount)
}

fun findEveryAnswersLikeTopN(getCount: Long): EveryAnswerRetrieveResponses {
@Transactional(readOnly = true)
fun findAllAnswersLikeTopN(getCount: Long): EveryAnswerRetrieveResponses {
val todayQuestion = questionExplorer.findTodayQuestion()
val answers = answerRepository.findPublicAnswersLikeTopN(todayQuestion.id, getCount)

Expand All @@ -92,16 +95,7 @@ class AnswerService(

return try {
answerRegister.registerAnswer(memberEntity, questionEntity, answerWriting.body, answerWriting.open).let {
WritingAnswerResult(
questionId = questionEntity.id,
questionTitle = questionEntity.title,
questionContent = questionEntity.content,
questionExposedAt = questionEntity.exposedAt,
questionElementType = questionEntity.elementType,
questionAnswered = true,
answerId = it.id,
answerBody = it.content,
reactionLikeCount = 0 // 최초 생성시는 0
WritingAnswerResult(questionId = questionEntity.id, questionTitle = questionEntity.title, questionContent = questionEntity.content, questionExposedAt = questionEntity.exposedAt, questionElementType = questionEntity.elementType, questionAnswered = true, answerId = it.id, answerBody = it.content, reactionLikeCount = 0 // 최초 생성시는 0
)
}
} catch (e: DataIntegrityViolationException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.th.plu.api.service.question

import com.th.plu.domain.domain.answer.AnswerExplorer
import com.th.plu.domain.domain.question.QuestionExplorer
import com.th.plu.domain.domain.answer.explorer.AnswerExplorer
import com.th.plu.domain.domain.question.QuestionResultDto
import com.th.plu.domain.domain.question.explorer.QuestionExplorer
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
Expand All @@ -17,7 +17,7 @@ class QuestionService(
fun getQuestionToday(memberId: Long): QuestionResultDto {
val today = LocalDateTime.now()

return questionExplorer.findQuestion(today).let { todayQuestion ->
return questionExplorer.findQuestionByDateTime(today).let { todayQuestion ->
val answered = answerExplorer.hasAnswered(memberId, todayQuestion.id)

QuestionResultDto(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,13 @@ enum class ErrorCode(val code: String, val message: String) {
NOT_FOUND_ANSWER_EXCEPTION("N003", "존재하지 않는 답변입니다."),
NOT_FOUND_QUESTION_EXCEPTION("N004", "존재하지 않는 질문입니다."),
NOT_FOUND_LIKE_EXCEPTION("N005", "존재하지 않는 공감 정보입니다."),
NOT_FOUND_ARTICLE_CONTENT_EXCEPTION("N003", "아티클의 컨텐츠가 존재하지 않습니다."),
NOT_FOUND_CHALLENGE_EXCEPTION("N004", "존재하지 않는 챌린지입니다."),
NOT_FOUND_ARTICLE_EXCEPTION("N005", "삭제되었거나 존재하지 않는 아티클입니다."),
NOT_FOUND_ARTICLE_IN_WEEK_AND_DAY_EXCEPTION("N006", "해당 주차 일차에 해당하는 아티클이 존재하지 않습니다."),
NOT_FOUND_ENDPOINT_EXCEPTION("N007", "존재하지 않는 엔드포인트입니다."),
NOT_FOUND_ONBOARDING_EXCEPTION("N008", "존재하지 않는 엔드포인트입니다."),
NOT_FOUND_ENDPOINT_EXCEPTION("N006", "존재하지 않는 엔드포인트입니다."),
NOT_FOUND_ONBOARDING_EXCEPTION("N007", "존재하지 않는 엔드포인트입니다."),

// Conflict Exception
CONFLICT_EXCEPTION("C001", "이미 존재합니다."),
CONFLICT_MEMBER_EXCEPTION("C002", "이미 해당 계정으로 회원가입하셨습니다.\n로그인 해주세요."),
CONFLICT_LIKE_EXCEPTION("C003", "이미 해당 답변에 대한 공감이 되어있습니다."),
CONFLICT_BOOKMARK_EXCEPTION("C003", "요청과 동일한 북마크 상태 입니다."),
CONFLICT_NICKNAME_EXCEPTION("C004", "이미 사용 중인 닉네임 입니다."),
CONFLICT_ANSWER_EXCEPTION("C005", "이미 존재한 답변이 있습니다."),

Expand All @@ -49,4 +44,5 @@ enum class ErrorCode(val code: String, val message: String) {

// Illegal Argument Exception
Illegal_ARGUMENT_NICKNAME_EXCEPTION("IA001", "잘못된 닉네임 형식입니다."),
Illegal_ARGUMENT_ANSWER_COUNT_EXCEPTION("IA002", "answerCount는 비어있을 수 없습니다."),
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ class Answer(
private var _id: Long? = null,

@ManyToOne(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
@JoinColumn(name = "member_id", nullable = false)
private var member: Member,
@JoinColumn(name = "member_id", nullable = false) var member: Member,

@ManyToOne(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
@JoinColumn(name = "question_id", nullable = false)
Expand All @@ -53,6 +52,10 @@ class Answer(
private set

val questionId: Long = question.id

fun getLikeCount(): Int {
return likes.size
}
}

fun newAnswerInstance(member: Member, question: Question, content: String, isPublic: Boolean) = Answer(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import org.springframework.stereotype.Component

@Component
class AnswerExplorer(
private val answerRepository: AnswerRepository
private val answerRepository: AnswerRepository
) {
fun findAnswerById(id: Long): Answer {
return answerRepository.findAnswerById(id)
?: throw NotFoundException(ErrorCode.NOT_FOUND_ANSWER_EXCEPTION, "존재하지 않는 답변(ID: $id) 입니다")
?: throw NotFoundException(ErrorCode.NOT_FOUND_ANSWER_EXCEPTION, "존재하지 않는 답변(ID: $id) 입니다")
}

fun hasAnswered(memberId: Long, questionId: Long): Boolean {
return answerRepository.existsByMemberIdAndQuestionId(memberId, questionId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface AnswerRepositoryCustom {

fun findEveryAnswersWithCursorAndPageSize(questionId: Long, lastAnswerId: Long, pageSize: Long): List<EveryAnswerRetrieveResponse>

fun findPublicAnswersCountByQuestionId(questionId: Long): Long
fun findPublicAnswersCountByQuestionId(questionId: Long): Long?

fun findPublicAnswersLikeTopN(questionId: Long, getCount: Long): List<EveryAnswerRetrieveResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,48 @@ import org.springframework.stereotype.Repository
class AnswerRepositoryImpl(private val queryFactory: JPAQueryFactory) : AnswerRepositoryCustom {
override fun findAnswerById(id: Long): Answer? {
return queryFactory
.selectFrom(answer)
.where(answer.id.eq(id))
.fetchOne()
.selectFrom(answer)
.where(answer._id.eq(id))
.fetchOne()
}

override fun findEveryAnswersWithCursorAndPageSize(questionId: Long, lastAnswerId: Long, pageSize: Long): List<EveryAnswerRetrieveResponse> {
return queryFactory
.select(QEveryAnswerRetrieveResponse(answer.id, like.answer.id.count(), answer.content))
.from(answer)
.leftJoin(like).on(like.answer.id.eq(answer.id))
.where(
answer.isPublic.eq(true),
answer.question.id.eq(questionId),
answer.id.lt(lastAnswerId),
)
.groupBy(answer.id)
.orderBy(answer.id.desc())
.limit(pageSize)
.fetch()
.select(QEveryAnswerRetrieveResponse(answer._id, like.answer._id.count(), answer.content))
.from(answer)
.leftJoin(like).on(like.answer._id.eq(answer._id))
.where(
answer.isPublic.eq(true),
answer.question._id.eq(questionId),
answer._id.lt(lastAnswerId),
)
.groupBy(answer._id)
.orderBy(answer._id.desc())
.limit(pageSize)
.fetch()
}

override fun findPublicAnswersCountByQuestionId(questionId: Long): Long {
override fun findPublicAnswersCountByQuestionId(questionId: Long): Long? {
return queryFactory
.select(answer.id.count())
.from(answer)
.where(answer.question.id.eq(questionId))
.fetchOne()!!
.select(answer._id.count())
.from(answer)
.where(answer.question._id.eq(questionId))
.fetchOne()
}

override fun findPublicAnswersLikeTopN(questionId: Long, getCount: Long): List<EveryAnswerRetrieveResponse> {
return queryFactory
.select(QEveryAnswerRetrieveResponse(answer.id, like.answer.id.count(), answer.content))
.from(answer)
.leftJoin(like).on(like.answer.id.eq(answer.id))
.where(
answer.isPublic.eq(true),
answer.question.id.eq(questionId)
)
.groupBy(answer.id)
.orderBy(like.answer.id.count().desc())
.limit(getCount)
.fetch()
.select(QEveryAnswerRetrieveResponse(answer._id, like.answer._id.count(), answer.content))
.from(answer)
.leftJoin(like).on(like.answer._id.eq(answer._id))
.where(
answer.isPublic.eq(true),
answer.question._id.eq(questionId)
)
.groupBy(answer._id)
.orderBy(like.answer._id.count().desc())
.limit(getCount)
.fetch()
}

override fun existsByMemberIdAndQuestionId(memberId: Long, questionId: Long): Boolean {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,22 +1,56 @@
package com.th.plu.domain.domain.answer.explorer
package com.th.plu.domain.domain.question.explorer

import com.th.plu.common.exception.code.ErrorCode
import com.th.plu.common.exception.model.InternalServerException
import com.th.plu.common.exception.model.NotFoundException
import com.th.plu.domain.domain.question.Question
import com.th.plu.domain.domain.question.repository.QuestionRepository
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.YearMonth

@Component
class QuestionExplorer(
private val questionRepository: QuestionRepository
private val questionRepository: QuestionRepository,
) {
fun findQuestionById(id: Long): Question {
return questionRepository.findQuestionById(id)
?: throw NotFoundException(ErrorCode.NOT_FOUND_QUESTION_EXCEPTION, "존재하지 않는 질문(ID: $id) 입니다")
fun findQuestion(id: Long): Question =
questionRepository.findById(id).orElseThrow {
NotFoundException(ErrorCode.NOT_FOUND_QUESTION_EXCEPTION, "존재 하지 않는 질문 $id 입니다")
}

fun findQuestionByDateTime(dateTime: LocalDateTime): Question {
// 입력된 dateTime이 밤 10시 이후인지 확인
val isAfterTenPM = dateTime.hour >= 22

// 밤 10시 이후인 경우, startOfQuestionPeriod를 그 날의 밤 10시로 설정
// 그렇지 않으면, startOfQuestionPeriod를 전날의 밤 10시로 설정
val startOfQuestionPeriod = if (isAfterTenPM) {
dateTime.toLocalDate().atTime(22, 0)
} else {
dateTime.toLocalDate().minusDays(1).atTime(22, 0)
}

// endOfQuestionPeriod는 startOfQuestionPeriod에서 하루를 더한 후, 밤 9시 59분 59초로 설정
val endOfQuestionPeriod = startOfQuestionPeriod.plusDays(1).withHour(21).withMinute(59).withSecond(59)

// 해당 기간 내의 첫 번째 질문을 조회
return questionRepository.findByExposedAtOrNull(startOfQuestionPeriod, endOfQuestionPeriod)
?: throw InternalServerException(
ErrorCode.DATA_NOT_READY_EXCEPTION,
"($dateTime) 날짜의 질문데이터가 준비되지 않았습니다."
)
}

fun findMyQuestionsMonthly(memberId: Long, yearMonth: YearMonth): List<Question> =
questionRepository.findAllByExposedMonthIn(memberId, yearMonth)

fun findAnsweredYearMonth(memberId: Long): Set<YearMonth> =
questionRepository.findAllExposedAtInAnsweredMonth(memberId)
.map { YearMonth.of(it.year, it.monthValue) }
.toSet() // application 에서 중복 처리중, 500 넘는 warn log 발생시 월별 1건 조회하도록 쿼리 개선 필요!

fun findTodayQuestion(): Question {
return questionRepository.findTodayQuestion()
?: throw NotFoundException(ErrorCode.NOT_FOUND_QUESTION_EXCEPTION, "오늘의 질문이 존재하지 않습니다")
return findQuestionByDateTime(LocalDateTime.now())
}

}
Loading

0 comments on commit 3710675

Please sign in to comment.