diff --git a/Api/src/main/java/tify/server/api/answer/controller/AnswerController.java b/Api/src/main/java/tify/server/api/answer/controller/AnswerController.java index c7a9b654..9940da33 100644 --- a/Api/src/main/java/tify/server/api/answer/controller/AnswerController.java +++ b/Api/src/main/java/tify/server/api/answer/controller/AnswerController.java @@ -4,11 +4,13 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springdoc.api.annotations.ParameterObject; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; +import tify.server.api.answer.model.response.NeighborAnswerInfoDTO; import tify.server.api.answer.model.response.RetrieveAnswerCountResponse; import tify.server.api.answer.model.response.RetrieveAnswerDTO; import tify.server.api.answer.service.RetrieveDailyAnswerCountUseCase; @@ -38,4 +40,15 @@ public SliceResponse getAnswers( public RetrieveAnswerCountResponse getAnswerCounts(@PathVariable Long questionId) { return retrieveDailyAnswerCountUseCase.execute(questionId); } + + @Operation( + summary = "특정 유저의 친구들이 해당 데일리 질문에 답변을 남겼는지 조회하고, 남겼다면 답변의 정보를 조회합니다.", + description = "답변을 남기지 않았다면 답변 필드가 null") + @GetMapping("/{userId}/neighbors") + public List getNeighborAnswerInfoList( + @PathVariable Long questionId, + @PathVariable Long userId, + @ParameterObject @PageableDefault(size = 10) Pageable pageable) { + return retrieveDailyAnswerUseCase.executeNeighborAnswerList(questionId, userId, pageable); + } } diff --git a/Api/src/main/java/tify/server/api/answer/model/response/NeighborAnswerInfoDTO.java b/Api/src/main/java/tify/server/api/answer/model/response/NeighborAnswerInfoDTO.java new file mode 100644 index 00000000..cc1360fd --- /dev/null +++ b/Api/src/main/java/tify/server/api/answer/model/response/NeighborAnswerInfoDTO.java @@ -0,0 +1,16 @@ +package tify.server.api.answer.model.response; + + +import lombok.Builder; +import lombok.Getter; +import tify.server.api.answer.model.vo.AnswerInfoVo; +import tify.server.domain.domains.user.dto.model.RetrieveNeighborDTO; + +@Getter +@Builder +public class NeighborAnswerInfoDTO { + + private final RetrieveNeighborDTO neighborInfo; + + private final AnswerInfoVo answerInfo; +} diff --git a/Api/src/main/java/tify/server/api/answer/model/response/RetrieveAnswerDTO.java b/Api/src/main/java/tify/server/api/answer/model/response/RetrieveAnswerDTO.java index cddd8dc4..acbd97a3 100644 --- a/Api/src/main/java/tify/server/api/answer/model/response/RetrieveAnswerDTO.java +++ b/Api/src/main/java/tify/server/api/answer/model/response/RetrieveAnswerDTO.java @@ -4,7 +4,7 @@ import java.util.Objects; import lombok.Builder; import lombok.Getter; -import tify.server.api.answer.vo.AnswerInfoVo; +import tify.server.api.answer.model.vo.AnswerInfoVo; import tify.server.domain.domains.question.dto.model.AnswerVo; @Getter diff --git a/Api/src/main/java/tify/server/api/answer/vo/AnswerInfoVo.java b/Api/src/main/java/tify/server/api/answer/model/vo/AnswerInfoVo.java similarity index 60% rename from Api/src/main/java/tify/server/api/answer/vo/AnswerInfoVo.java rename to Api/src/main/java/tify/server/api/answer/model/vo/AnswerInfoVo.java index 57450964..3029baaa 100644 --- a/Api/src/main/java/tify/server/api/answer/vo/AnswerInfoVo.java +++ b/Api/src/main/java/tify/server/api/answer/model/vo/AnswerInfoVo.java @@ -1,9 +1,11 @@ -package tify.server.api.answer.vo; +package tify.server.api.answer.model.vo; import io.swagger.v3.oas.annotations.media.Schema; +import java.util.Optional; import lombok.Builder; import lombok.Getter; +import tify.server.domain.domains.question.domain.Answer; import tify.server.domain.domains.question.dto.model.AnswerVo; @Getter @@ -30,4 +32,13 @@ public static AnswerInfoVo from(AnswerVo answer) { .content(answer.getAnswer().getContent()) .build(); } + + public static AnswerInfoVo from(Answer answer) { + return AnswerInfoVo.builder() + .id(Optional.ofNullable(answer).map(Answer::getId).orElse(null)) + .questionId(Optional.ofNullable(answer).map(Answer::getQuestionId).orElse(null)) + .userId(Optional.ofNullable(answer).map(Answer::getUserId).orElse(null)) + .content(Optional.ofNullable(answer).map(Answer::getContent).orElse(null)) + .build(); + } } diff --git a/Api/src/main/java/tify/server/api/answer/service/RetrieveDailyAnswerUseCase.java b/Api/src/main/java/tify/server/api/answer/service/RetrieveDailyAnswerUseCase.java index 8aaacfff..655a41fc 100644 --- a/Api/src/main/java/tify/server/api/answer/service/RetrieveDailyAnswerUseCase.java +++ b/Api/src/main/java/tify/server/api/answer/service/RetrieveDailyAnswerUseCase.java @@ -3,35 +3,48 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Transactional; +import tify.server.api.answer.model.response.NeighborAnswerInfoDTO; import tify.server.api.answer.model.response.RetrieveAnswerDTO; +import tify.server.api.answer.model.vo.AnswerInfoVo; import tify.server.api.common.slice.SliceResponse; -import tify.server.api.utils.UserUtils; +import tify.server.api.config.security.SecurityUtils; import tify.server.core.annotation.UseCase; import tify.server.domain.domains.question.adaptor.AnswerAdaptor; import tify.server.domain.domains.question.adaptor.DailyQuestionAdaptor; +import tify.server.domain.domains.question.domain.Answer; import tify.server.domain.domains.question.domain.DailyQuestion; import tify.server.domain.domains.question.dto.condition.AnswerCondition; import tify.server.domain.domains.question.dto.model.AnswerVo; import tify.server.domain.domains.user.adaptor.NeighborAdaptor; +import tify.server.domain.domains.user.adaptor.UserAdaptor; +import tify.server.domain.domains.user.adaptor.UserBlockAdaptor; import tify.server.domain.domains.user.domain.Neighbor; +import tify.server.domain.domains.user.domain.User; +import tify.server.domain.domains.user.domain.UserBlock; +import tify.server.domain.domains.user.dto.condition.NeighborCondition; +import tify.server.domain.domains.user.dto.model.RetrieveNeighborDTO; +@Slf4j @UseCase @RequiredArgsConstructor public class RetrieveDailyAnswerUseCase { + private final UserAdaptor userAdaptor; private final AnswerAdaptor answerAdaptor; + private final UserBlockAdaptor userBlockAdaptor; private final NeighborAdaptor neighborAdaptor; private final DailyQuestionAdaptor dailyQuestionAdaptor; - private final UserUtils userUtils; @Transactional(readOnly = true) public SliceResponse execute(Long questionId, Pageable pageable) { DailyQuestion dailyQuestion = dailyQuestionAdaptor.query(questionId); - Long currentUserId = userUtils.getUserId(); + Long currentUserId = SecurityUtils.getCurrentUserId(); List userIdList = new ArrayList<>( neighborAdaptor.queryAllByFromUserIdAndIsView(currentUserId, true).stream() @@ -44,4 +57,37 @@ public SliceResponse execute(Long questionId, Pageable pageab return SliceResponse.of( answers.map(answerVo -> RetrieveAnswerDTO.of(answerVo, currentUserId))); } + + @Transactional(readOnly = true) + public List executeNeighborAnswerList( + Long questionId, Long userId, Pageable pageable) { + List neighborList = + neighborAdaptor.queryAllByFromUserId(userId).stream() + .map(Neighbor::getToUserId) + .map(userAdaptor::query) + .toList(); + List blockedIdList = + userBlockAdaptor.queryAllByFromUserId(userId).stream() + .map(UserBlock::getToUserId) + .toList(); + List friendIdList = + neighborAdaptor.queryAllByFromUserId(userId).stream() + .map(Neighbor::getToUserId) + .toList(); + NeighborCondition neighborCondition = + new NeighborCondition(userId, blockedIdList, friendIdList); + List neighbors = neighborAdaptor.searchNeighbors(neighborCondition); + return neighbors.stream() + .map( + dto -> { + Optional answer = + answerAdaptor.optionalQueryByQuestionAndUser( + questionId, dto.getNeighborId()); + return NeighborAnswerInfoDTO.builder() + .neighborInfo(dto) + .answerInfo(AnswerInfoVo.from(answer.orElse(null))) + .build(); + }) + .toList(); + } } diff --git a/Domain/src/main/java/tify/server/domain/domains/user/adaptor/NeighborAdaptor.java b/Domain/src/main/java/tify/server/domain/domains/user/adaptor/NeighborAdaptor.java index fa3ee938..886e28be 100644 --- a/Domain/src/main/java/tify/server/domain/domains/user/adaptor/NeighborAdaptor.java +++ b/Domain/src/main/java/tify/server/domain/domains/user/adaptor/NeighborAdaptor.java @@ -54,7 +54,12 @@ public Optional queryByFromUserIdAndToUserId(Long userId, Long neighbo } public List searchNeighbors(NeighborCondition neighborCondition) { - return neighborRepository.searchToPage(neighborCondition); + return neighborRepository.searchNeighbors(neighborCondition); + } + + public Slice searchNeighbors( + NeighborCondition neighborCondition, Pageable pageable) { + return neighborRepository.searchNeighborsToPage(neighborCondition, pageable); } public boolean existsNeighbor(Long userId, Long neighborId) { @@ -70,7 +75,7 @@ public void delete(Neighbor neighbor) { } public List searchBirthdayNeighbors(NeighborCondition neighborCondition) { - return neighborRepository.searchBirthToPage(neighborCondition); + return neighborRepository.searchBirthdayNeighbors(neighborCondition); } public void saveNeighborApplication(NeighborApplication neighborApplication) { diff --git a/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepository.java b/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepository.java index db7bb035..9d5ebf39 100644 --- a/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepository.java +++ b/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepository.java @@ -2,12 +2,17 @@ import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import tify.server.domain.domains.user.dto.condition.NeighborCondition; import tify.server.domain.domains.user.dto.model.RetrieveNeighborDTO; public interface NeighborCustomRepository { - List searchToPage(NeighborCondition neighborCondition); + List searchNeighbors(NeighborCondition neighborCondition); - List searchBirthToPage(NeighborCondition neighborCondition); + List searchBirthdayNeighbors(NeighborCondition neighborCondition); + + Slice searchNeighborsToPage( + NeighborCondition neighborCondition, Pageable pageable); } diff --git a/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepositoryImpl.java b/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepositoryImpl.java index 64f496ce..a51fc3a6 100644 --- a/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepositoryImpl.java +++ b/Domain/src/main/java/tify/server/domain/domains/user/repository/NeighborCustomRepositoryImpl.java @@ -9,6 +9,9 @@ import java.time.ZoneId; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import tify.server.domain.common.util.SliceUtil; import tify.server.domain.domains.user.dto.condition.NeighborCondition; import tify.server.domain.domains.user.dto.model.RetrieveNeighborDTO; @@ -17,7 +20,7 @@ public class NeighborCustomRepositoryImpl implements NeighborCustomRepository { private final JPAQueryFactory queryFactory; @Override - public List searchToPage(NeighborCondition neighborCondition) { + public List searchNeighbors(NeighborCondition neighborCondition) { return queryFactory .select( Projections.constructor( @@ -45,7 +48,7 @@ public List searchToPage(NeighborCondition neighborConditio } @Override - public List searchBirthToPage(NeighborCondition neighborCondition) { + public List searchBirthdayNeighbors(NeighborCondition neighborCondition) { LocalDateTime today = LocalDateTime.now(ZoneId.of("Asia/Seoul")); String monthAndYear = String.format("%02d%02d", today.getMonth().getValue(), today.getDayOfMonth()); @@ -75,4 +78,37 @@ public List searchBirthToPage(NeighborCondition neighborCon .orderBy(neighbor.order.asc()) .fetch(); } + + @Override + public Slice searchNeighborsToPage( + NeighborCondition neighborCondition, Pageable pageable) { + List retrieveNeighborDTOS = + queryFactory + .select( + Projections.constructor( + RetrieveNeighborDTO.class, + neighbor.toUserId, + neighbor.fromUserId, + user.profile.thumbNail, + user.profile.userName, + user.profile.birth, + user.onBoardingStatus.name, + neighbor.order, + neighbor.isView, + neighbor.isNew, + user.updatedAt, + neighbor.viewedAt)) + .from(neighbor) + .join(user) + .on(user.id.eq(neighbor.toUserId)) + .where( + neighbor.fromUserId.eq(neighborCondition.getCurrentUserId()), + neighbor.toUserId.notIn(neighborCondition.getBlockedUserIdList()), + neighbor.toUserId.in(neighborCondition.getFriendIdList())) + .orderBy(neighbor.order.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + return SliceUtil.valueOf(retrieveNeighborDTOS, pageable); + } }