Skip to content

Commit

Permalink
[Feature/468] 문답 목록 조회 api v2를 구현한다 (#510)
Browse files Browse the repository at this point in the history
* feat: 문답 목록 조회 api v2 구현

* fix: lastStatus 값 반환 수정
  • Loading branch information
miseongk authored Oct 10, 2024
1 parent b2ba7c4 commit 82ce772
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.nexters.bottles.api.bottle.controller

import com.nexters.bottles.api.bottle.facade.BottleFacadeV2
import com.nexters.bottles.api.bottle.facade.dto.PingPongListResponseV2
import com.nexters.bottles.api.bottle.facade.dto.RandomBottleListResponse
import com.nexters.bottles.api.bottle.facade.dto.SentBottleListResponse
import com.nexters.bottles.api.global.interceptor.AuthRequired
Expand Down Expand Up @@ -29,4 +30,11 @@ class BottleControllerV2(
fun getSentBottlesList(@AuthUserId userId: Long): SentBottleListResponse {
return bottleFacadeV2.getSentBottles(userId)
}

@ApiOperation("문답 - 핑퐁중인 보틀 목록 조회하기")
@GetMapping("/ping-pong")
@AuthRequired
fun getPingPongList(@AuthUserId userId: Long): PingPongListResponseV2 {
return bottleFacadeV2.getPingPongBottles(userId)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.nexters.bottles.api.bottle.facade

import com.nexters.bottles.api.bottle.event.dto.BottleMatchEventDto
import com.nexters.bottles.api.bottle.facade.dto.PingPongBottleDtoV2
import com.nexters.bottles.api.bottle.facade.dto.PingPongListResponseV2
import com.nexters.bottles.api.bottle.facade.dto.RandomBottleDto
import com.nexters.bottles.api.bottle.facade.dto.RandomBottleListResponse
import com.nexters.bottles.api.bottle.facade.dto.SentBottleDto
Expand All @@ -9,7 +11,9 @@ import com.nexters.bottles.api.bottle.util.getLastActivatedAtInKorean
import com.nexters.bottles.api.user.component.event.dto.UserApplicationEventDto
import com.nexters.bottles.app.bottle.domain.Bottle
import com.nexters.bottles.app.bottle.domain.enum.BottleStatus
import com.nexters.bottles.app.bottle.service.BottleCachingService
import com.nexters.bottles.app.bottle.service.BottleService
import com.nexters.bottles.app.bottle.service.LetterService
import com.nexters.bottles.app.user.domain.User
import com.nexters.bottles.app.user.service.BlockContactListService
import com.nexters.bottles.app.user.service.UserReportService
Expand All @@ -25,6 +29,8 @@ class BottleFacadeV2(
private val userService: UserService,
private val userReportService: UserReportService,
private val blockContactListService: BlockContactListService,
private val letterService: LetterService,
private val bottleCachingService: BottleCachingService,
private val applicationEventPublisher: ApplicationEventPublisher,
) {

Expand Down Expand Up @@ -142,4 +148,46 @@ class BottleFacadeV2(
)
)
}

fun getPingPongBottles(userId: Long): PingPongListResponseV2 {
val user = userService.findByIdAndNotDeleted(userId)
val pingPongBottles = bottleCachingService.getPingPongBottlesV2(userId)
val reportUserIds = userReportService.getReportRespondentList(userId)
.map { it.respondentUserId }
.toSet()
val blockUserIds = blockContactListService.findAllByUserId(userId).map { it.userId }.toSet() // 내가 차단한 유저
val blockMeUserIds = blockContactListService.findAllByPhoneNumber(
user.phoneNumber ?: throw IllegalStateException("핸드폰 번호를 등록해주세요")
).map { it.userId }.toSet() // 나를 차단한 유저

return PingPongListResponseV2(
pingPongBottles = pingPongBottles.filter { it.findOtherUserId(user.id) !in reportUserIds }
.filter { it.findOtherUserId(user.id) !in blockUserIds }
.filter { it.findOtherUserId(user.id) !in blockMeUserIds }
.map { toPingPongBottleDto(it, user) }
)
}

private fun toPingPongBottleDto(bottle: Bottle, user: User): PingPongBottleDtoV2 {
val otherUser = bottle.findOtherUser(user)
val otherUserLetter = letterService.findLetter(bottle, otherUser)
val lastStatus = letterService.findLastStatus(bottle, user, otherUser)
val lastUpdatedAt = letterService.findLastUpdated(bottle, user, otherUser)

return PingPongBottleDtoV2(
id = bottle.id,
isRead = otherUserLetter.isReadByOtherUser,
userName = otherUser.getMaskedName(),
userId = otherUser.id,
age = otherUser.getKoreanAge(),
mbti = otherUser.userProfile?.profileSelect?.mbti,
keyword = otherUser.userProfile?.profileSelect?.keyword,
userImageUrl = otherUser.userProfile?.blurredImageUrl,
lastActivatedAt = getLastActivatedAtInKorean(
basedAt = lastUpdatedAt,
now = LocalDateTime.now()
),
lastStatus = lastStatus
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.nexters.bottles.api.bottle.facade.dto

import java.time.LocalDateTime

data class PingPongListResponse(
val activeBottles: List<PingPongBottleDto>,
val doneBottles: List<PingPongBottleDto>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.nexters.bottles.api.bottle.facade.dto

import com.nexters.bottles.app.bottle.domain.enum.LetterLastStatus

data class PingPongListResponseV2(
val pingPongBottles: List<PingPongBottleDtoV2>,
)

data class PingPongBottleDtoV2(
val id: Long,
val isRead: Boolean,
val userName: String?,
val userId: Long,
val age: Int,
val mbti: String?,
val keyword: List<String>?,
val userImageUrl: String?,
val lastActivatedAt: String?,
val lastStatus: LetterLastStatus
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nexters.bottles.app.bottle.domain

import com.nexters.bottles.app.bottle.domain.enum.LetterLastStatus
import com.nexters.bottles.app.bottle.repository.converter.LetterQuestionAndAnswerConverter
import com.nexters.bottles.app.common.BaseEntity
import com.nexters.bottles.app.user.domain.User
Expand Down Expand Up @@ -80,6 +81,33 @@ class Letter(
fun notFinishedLastAnswer(): Boolean {
return letters.last().answer == null
}

fun findLastStatusWithOtherLetter(otherLetter: Letter): LetterLastStatus {
return when {
bottle.isStopped() -> LetterLastStatus.CONVERSATION_STOPPED
this.isShareContact != null && otherLetter.isShareContact == null -> LetterLastStatus.CONTACT_SHARED_BY_ME_ONLY
otherLetter.isShareContact != null -> LetterLastStatus.CONTACT_SHARED_BY_OTHER
this.isShareImage != null && otherLetter.isShareImage == null -> LetterLastStatus.PHOTO_SHARED_BY_ME_ONLY
otherLetter.isShareImage != null -> LetterLastStatus.PHOTO_SHARED_BY_OTHER
this.findAnsweredSize() == 0 && otherLetter.findAnsweredSize() == 0 -> LetterLastStatus.NO_ANSWER_FROM_BOTH
this.findAnsweredSize() > otherLetter.findAnsweredSize() -> LetterLastStatus.ANSWER_FROM_ME_ONLY
otherLetter.findAnsweredSize() >= this.findAnsweredSize() -> LetterLastStatus.ANSWER_FROM_OTHER
else -> LetterLastStatus.NO_ANSWER_FROM_BOTH
}
}

fun findAnsweredSize(): Int {
return this.letters.count {
it.answer != null
}
}

fun findLastUpdatedWithOtherLetter(otherLetter: Letter): LocalDateTime {
return when {
this.updatedAt > otherLetter.updatedAt -> this.updatedAt
else -> otherLetter.updatedAt
}
}
}

data class LetterQuestionAndAnswer(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.nexters.bottles.app.bottle.domain.enum

enum class LetterLastStatus {
// 대화는 시작했으나 두 사람 모두 문답을 작성하지 않았을 때
NO_ANSWER_FROM_BOTH,

//상대방이 새로운 문답을 작성했을 때
ANSWER_FROM_OTHER,

// 상대방이 사진을 공유했을 때
PHOTO_SHARED_BY_OTHER,

// 상대방이 연락처를 공유했을 때
CONTACT_SHARED_BY_OTHER,

// 내가 문답을 작성했을 때 (상대방은 작성X)
ANSWER_FROM_ME_ONLY,

// 내가 사진을 공유했을 때 (상대방은 공유X)
PHOTO_SHARED_BY_ME_ONLY,

// 내가 연락처를 공유했을 때 (상대방은 공유X)
CONTACT_SHARED_BY_ME_ONLY,

// 대화가 중단됐을 때
CONVERSATION_STOPPED
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ interface BottleRepository : JpaRepository<Bottle, Long> {
"AND b.targetUser.deleted = false " +
"AND b.sourceUser.deleted = false " +
"AND b.pingPongStatus IN :pingPongStatus " +
"AND b.deleted = false "
"AND b.deleted = false " +
"ORDER BY b.updatedAt desc "
)
fun findAllByNotDeletedUserAndStatusAndDeletedFalse(
fun findAllByNotDeletedUserAndStatusAndDeletedFalseOrderByUpdatedAtDesc(
@Param("user") user: User,
@Param("pingPongStatus") pingPongStatus: Set<PingPongStatus>
): List<Bottle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class BottleCachingService(
return bottleService.getPingPongBottles(userId)
}

@Cacheable(PING_PONG_BOTTLE_LIST, key = "#userId + '-v2'")
fun getPingPongBottlesV2(userId: Long): List<Bottle> {
return bottleService.getPingPongBottles(userId)
}

@Caching(
evict = [
CacheEvict(PING_PONG_BOTTLE_LIST, key = "#sourceUserId"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class BottleService(
@Transactional(readOnly = true)
fun getPingPongBottles(userId: Long): List<Bottle> {
val user = userRepository.findByIdAndDeletedFalse(userId) ?: throw IllegalStateException("회원가입 상태를 문의해주세요")
return bottleRepository.findAllByNotDeletedUserAndStatusAndDeletedFalse(
return bottleRepository.findAllByNotDeletedUserAndStatusAndDeletedFalseOrderByUpdatedAtDesc(
user,
setOf(
PingPongStatus.ACTIVE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.nexters.bottles.app.bottle.service

import com.nexters.bottles.app.bottle.domain.Bottle
import com.nexters.bottles.app.bottle.domain.Letter
import com.nexters.bottles.app.bottle.domain.enum.LetterLastStatus
import com.nexters.bottles.app.bottle.repository.BottleRepository
import com.nexters.bottles.app.bottle.repository.LetterRepository
import com.nexters.bottles.app.user.domain.User
Expand Down Expand Up @@ -60,4 +61,24 @@ class LetterService(
letter.finishIfAllShare()
}
}

@Transactional(readOnly = true)
fun findLastStatus(bottle: Bottle, user: User, otherUser: User): LetterLastStatus {
val myLetter =
letterRepository.findByBottleAndUser(bottle, user) ?: throw IllegalArgumentException("고객센터에 문의해주세요")
val otherLetter =
letterRepository.findByBottleAndUser(bottle, otherUser) ?: throw IllegalArgumentException("고객센터에 문의해주세요")

return myLetter.findLastStatusWithOtherLetter(otherLetter);
}

@Transactional(readOnly = true)
fun findLastUpdated(bottle: Bottle, user: User, otherUser: User): LocalDateTime {
val myLetter =
letterRepository.findByBottleAndUser(bottle, user) ?: throw IllegalArgumentException("고객센터에 문의해주세요")
val otherLetter =
letterRepository.findByBottleAndUser(bottle, otherUser) ?: throw IllegalArgumentException("고객센터에 문의해주세요")

return myLetter.findLastUpdatedWithOtherLetter(otherLetter);
}
}
Loading

0 comments on commit 82ce772

Please sign in to comment.