diff --git a/src/docs/asciidoc/find-friend-votes-v2.adoc b/src/docs/asciidoc/find-friend-votes-v2.adoc index 32940e36..a201d4e7 100644 --- a/src/docs/asciidoc/find-friend-votes-v2.adoc +++ b/src/docs/asciidoc/find-friend-votes-v2.adoc @@ -1,62 +1,23 @@ :reproducible: -== 친구 투표 전체 조회 (명세) +== 친구 투표 전체 조회 v2 === 요청 -[http] +include::{snippets}/api/v2/vote/friend/http-request.adoc[] ----- +=== 요청 파라미터 -GET /v2/vote/friend?page={}&type={} HTTP/1.1 -Authorization: Bearer your-access-token -Content-Type: application-json +include::{snippets}/api/v2/vote/friend/request-parameters.adoc[] ----- +=== 응답 + +include::{snippets}/api/v2/vote/friend/http-response.adoc[] -*업데이트 예정* -- "type": "send" | null |=== |`+type+`| 조회할 쪽지 종류 (null -> 모든쪽지, send-> 보낸쪽지) |=== -=== 응답 - -[http, json] ----- - -{ - "status" : 200, - "message" : "투표 조회에 성공했습니다.", - "data" : { - "totalCount" : 1, - "friendVotes" : [ { - "id" : 1, - "senderId" : 1, - "senderName" : "name1", - "senderGender" : "MALE", - "senderYelloId" : "MALE", - "senderProfileImage": "imageUrl", - "receiverId" : 2, - "receiverName" : "name2", - "receiverGender" : "MALE", - "receiverYelloId" : "MALE", - "receiverProfileImage" : "test image", - "vote" : { - "nameHead" : "나는", - "nameFoot" : "와", - "keywordHead" : "멋진", - "keyword" : "test", - "keywordFoot" : "에서 놀고싶어" - }, - "isHintUsed" : false, - "createdAt" : "0초 전" - "isUserSenderVote" : true - } ] - } -} - ----- *필드 타입* @@ -86,13 +47,28 @@ Content-Type: application-json * "keyword": String * "keywordFoot": String + +=== Excpetion + +- 잘못된 type을 queryString에 보내는 경우 + +[http,json] +---- +{ + "status": 403, + "message": "[VoteForbiddenException] 잘못된 투표 유형입니다." +} +---- + === NOTE - 모든 종류의 쪽지를 조회할 때 `/api/v1/vote/friend?page=0` 으로 요청해주세요 -* `type=` 을 명시하지 마세요 +* type을 명시하지 마세요 - 내가 보낸 쪽지를 조회할 때 `/api/v1/vote/friend?page=0&type=send` 으로 요청해주세요 - `senderGender` 필드가 다른 API와 일관되지 못한점 미안해요 ㅠ === CHANGELOG + +- 2924.01.30 API 릴리즈 - 2024.01.26 필드 명세 업데이트 - 2024.01.09 `type` 명세 업데이트 \ No newline at end of file diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index bab5eaca..0eb3544a 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -57,7 +57,7 @@ * link:find-friend-votes.html[친구 투표 전체 조회하기, 2024-01-09] -* 🆕️ link:find-friend-votes-v2.html[친구 투표 전체 조회하기 v2 (명세), 2024-01-26] +* ⬆️ link:find-friend-votes-v2.html[친구 투표 전체 조회하기 v2, 2024-01-30] * link:get-unread-vote.html[읽지 않은 쪽지 개수 조회하기] diff --git a/src/main/java/com/yello/server/domain/vote/controller/VoteController.java b/src/main/java/com/yello/server/domain/vote/controller/VoteController.java index 7b8796a1..bba2f0a3 100644 --- a/src/main/java/com/yello/server/domain/vote/controller/VoteController.java +++ b/src/main/java/com/yello/server/domain/vote/controller/VoteController.java @@ -16,9 +16,11 @@ import com.yello.server.domain.vote.dto.response.VoteAvailableResponse; import com.yello.server.domain.vote.dto.response.VoteCreateResponse; import com.yello.server.domain.vote.dto.response.VoteDetailResponse; +import com.yello.server.domain.vote.dto.response.VoteFriendAndUserResponse; import com.yello.server.domain.vote.dto.response.VoteFriendResponse; import com.yello.server.domain.vote.dto.response.VoteListResponse; import com.yello.server.domain.vote.dto.response.VoteUnreadCountResponse; +import com.yello.server.domain.vote.entity.VoteType; import com.yello.server.domain.vote.service.VoteService; import com.yello.server.global.common.SuccessCode; import com.yello.server.global.common.annotation.AccessTokenUser; @@ -37,56 +39,69 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("api/v1/vote") +@RequestMapping("api") @RequiredArgsConstructor public class VoteController { private final VoteService voteService; private final NotificationService notificationService; - @GetMapping - public BaseResponse findAllMyVotes(@RequestParam Integer page, @AccessTokenUser User user) { + @GetMapping("/v1/vote") + public BaseResponse findAllMyVotes(@RequestParam Integer page, + @AccessTokenUser User user) { val data = voteService.findAllVotes(user.getId(), createPageableLimitTen(page)); return BaseResponse.success(READ_VOTE_SUCCESS, data); } - @GetMapping("/count") + @GetMapping("/v1/vote/count") public BaseResponse getUnreadVoteCount(@AccessTokenUser User user) { val data = voteService.getUnreadVoteCount(user.getId()); return BaseResponse.success(READ_VOTE_SUCCESS, data); } - @GetMapping("/friend") - public BaseResponse findAllFriendVotes(@RequestParam Integer page, @AccessTokenUser User user) { + @GetMapping("/v1/vote/friend") + public BaseResponse findAllFriendVotes(@RequestParam Integer page, + @AccessTokenUser User user) { val data = voteService.findAllFriendVotes(user.getId(), createPageableLimitTen(page)); return BaseResponse.success(READ_VOTE_SUCCESS, data); } - @GetMapping("/{voteId}") - public BaseResponse findVote(@PathVariable Long voteId, @AccessTokenUser User user) { + @GetMapping("/v1/vote/{voteId}") + public BaseResponse findVote(@PathVariable Long voteId, + @AccessTokenUser User user) { val data = voteService.findVoteById(voteId, user.getId()); return BaseResponse.success(READ_VOTE_SUCCESS, data); } - @PatchMapping("/{voteId}/keyword") - public BaseResponse checkKeyword(@PathVariable Long voteId, @AccessTokenUser User user) { + @GetMapping("/v2/vote/friend") + public BaseResponse findAllFriendVotesWithType( + @RequestParam("page") Integer page, @RequestParam(value = "type", required = false) String type, + @AccessTokenUser User user) { + val data = voteService.findAllFriendVotesWithType(user.getId(), createPageableLimitTen(page), type); + return BaseResponse.success(READ_VOTE_SUCCESS, data); + } + + @PatchMapping("/v1/vote/{voteId}/keyword") + public BaseResponse checkKeyword(@PathVariable Long voteId, + @AccessTokenUser User user) { val keywordCheckResponse = voteService.checkKeyword(user.getId(), voteId); return BaseResponse.success(CHECK_KEYWORD_SUCCESS, keywordCheckResponse); } - @GetMapping("/question") - public BaseResponse> findVoteQuestions(@AccessTokenUser User user) { + @GetMapping("/v1/vote/question") + public BaseResponse> findVoteQuestions( + @AccessTokenUser User user) { val data = voteService.findVoteQuestionList(user.getId()); return BaseResponse.success(READ_YELLO_VOTE_SUCCESS, data); } - @GetMapping("/available") + @GetMapping("/v1/vote/available") public BaseResponse checkVoteAvailable(@AccessTokenUser User user) { val data = voteService.checkVoteAvailable(user.getId()); return BaseResponse.success(READ_YELLO_START_SUCCESS, data); } - @PostMapping + @PostMapping("/v1/vote") public BaseResponse createVote( @AccessTokenUser User user, @RequestBody CreateVoteRequest request @@ -98,14 +113,16 @@ public BaseResponse createVote( return BaseResponse.success(CREATE_VOTE_SUCCESS, response); } - @PatchMapping("/{voteId}/name") - public BaseResponse revealNameHint(@AccessTokenUser User user, @PathVariable Long voteId) { + @PatchMapping("/v1/vote/{voteId}/name") + public BaseResponse revealNameHint(@AccessTokenUser User user, + @PathVariable Long voteId) { val data = voteService.revealNameHint(user.getId(), voteId); return BaseResponse.success(SuccessCode.REVEAL_NAME_HINT_SUCCESS, data); } - @PatchMapping("/{voteId}/fullname") - public BaseResponse revealFullName(@AccessTokenUser User user, @PathVariable Long voteId) { + @PatchMapping("/v1/vote/{voteId}/fullname") + public BaseResponse revealFullName(@AccessTokenUser User user, + @PathVariable Long voteId) { val data = voteService.revealFullName(user.getId(), voteId); return BaseResponse.success(SuccessCode.REVEAL_NAME_SUCCESS, data); } diff --git a/src/main/java/com/yello/server/domain/vote/dto/response/VoteFriendAndUserResponse.java b/src/main/java/com/yello/server/domain/vote/dto/response/VoteFriendAndUserResponse.java new file mode 100644 index 00000000..a5409340 --- /dev/null +++ b/src/main/java/com/yello/server/domain/vote/dto/response/VoteFriendAndUserResponse.java @@ -0,0 +1,20 @@ +package com.yello.server.domain.vote.dto.response; + +import java.util.List; +import lombok.Builder; + +@Builder +public record VoteFriendAndUserResponse( + Long totalCount, + List friendVotes + +) { + + public static VoteFriendAndUserResponse of(Long totalCount, List friendVotes) { + return VoteFriendAndUserResponse.builder() + .totalCount(totalCount) + .friendVotes(friendVotes) + .build(); + } +} + diff --git a/src/main/java/com/yello/server/domain/vote/dto/response/VoteFriendAndUserVO.java b/src/main/java/com/yello/server/domain/vote/dto/response/VoteFriendAndUserVO.java new file mode 100644 index 00000000..c4d953a0 --- /dev/null +++ b/src/main/java/com/yello/server/domain/vote/dto/response/VoteFriendAndUserVO.java @@ -0,0 +1,46 @@ +package com.yello.server.domain.vote.dto.response; + +import static com.yello.server.global.common.factory.TimeFactory.toFormattedString; + +import com.yello.server.domain.vote.entity.Vote; +import lombok.Builder; + +@Builder +public record VoteFriendAndUserVO( + Long id, + Long senderId, + String senderName, + String senderGender, + String senderYelloId, + String senderProfileImage, + Long receiverId, + String receiverName, + String receiverGender, + String receiverYelloId, + String receiverProfileImage, + VoteContentVO vote, + Boolean isHintUsed, + String createdAt, + Boolean isUserSenderVote +) { + + public static VoteFriendAndUserVO of(Vote vote, Boolean isUserSenderVote) { + return VoteFriendAndUserVO.builder() + .id(vote.getId()) + .senderId(vote.getSender().getId()) + .senderName(vote.getSender().getName()) + .senderGender(vote.getSender().getGender().name()) + .senderYelloId(vote.getSender().getYelloId()) + .senderProfileImage(vote.getSender().getProfileImage()) + .receiverId(vote.getReceiver().getId()) + .receiverGender(vote.getReceiver().getGender().name()) + .receiverName(vote.getReceiver().getName()) + .receiverYelloId(vote.getReceiver().getYelloId()) + .receiverProfileImage(vote.getReceiver().getProfileImage()) + .vote(VoteContentVO.of(vote)) + .isHintUsed(vote.getIsAnswerRevealed()) + .createdAt(toFormattedString(vote.getCreatedAt())) + .isUserSenderVote(isUserSenderVote) + .build(); + } +} diff --git a/src/main/java/com/yello/server/domain/vote/entity/VoteType.java b/src/main/java/com/yello/server/domain/vote/entity/VoteType.java new file mode 100644 index 00000000..6ba96e40 --- /dev/null +++ b/src/main/java/com/yello/server/domain/vote/entity/VoteType.java @@ -0,0 +1,27 @@ +package com.yello.server.domain.vote.entity; + +import java.text.MessageFormat; +import java.util.Arrays; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum VoteType { + SEND("send"); + + private final String intial; + + public static VoteType fromCode(String dbData) { + return Arrays.stream(VoteType.values()) + .filter(v -> v.getIntial().equals(dbData)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException( + MessageFormat.format("존재하지 않는 투표 타입입니다. {0}", dbData))); + } + + public String intial() { + return intial; + } + +} diff --git a/src/main/java/com/yello/server/domain/vote/entity/VoteTypeConverter.java b/src/main/java/com/yello/server/domain/vote/entity/VoteTypeConverter.java new file mode 100644 index 00000000..b125eb5a --- /dev/null +++ b/src/main/java/com/yello/server/domain/vote/entity/VoteTypeConverter.java @@ -0,0 +1,32 @@ +package com.yello.server.domain.vote.entity; + +import com.yello.server.domain.user.entity.Social; +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; +import lombok.extern.log4j.Log4j2; + +@Converter +@Log4j2 +public class VoteTypeConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(VoteType voteType) { + if (voteType == null) { + return null; + } + return voteType.getIntial(); + } + + @Override + public VoteType convertToEntityAttribute(String dbData) { + if (dbData == null) { + return null; + } + try { + return VoteType.fromCode(dbData); + } catch (IllegalArgumentException exception) { + log.error("failure to convert cause unexpected code" + dbData + exception); + throw exception; + } + } +} diff --git a/src/main/java/com/yello/server/domain/vote/repository/VoteRepository.java b/src/main/java/com/yello/server/domain/vote/repository/VoteRepository.java index 9880ef01..4c689bf1 100644 --- a/src/main/java/com/yello/server/domain/vote/repository/VoteRepository.java +++ b/src/main/java/com/yello/server/domain/vote/repository/VoteRepository.java @@ -32,4 +32,8 @@ public interface VoteRepository { Integer countOpenNameByReceiverUserId(Long userId); Integer countOpenFullNameByReceiverUserId(Long userId); + + List findUserSendReceivedByFriends(Long userId, Pageable pageable); + + Long countUserSendReceivedByFriends(Long userId); } diff --git a/src/main/java/com/yello/server/domain/vote/repository/VoteRepositoryImpl.java b/src/main/java/com/yello/server/domain/vote/repository/VoteRepositoryImpl.java index b25312ac..076ce7c2 100644 --- a/src/main/java/com/yello/server/domain/vote/repository/VoteRepositoryImpl.java +++ b/src/main/java/com/yello/server/domain/vote/repository/VoteRepositoryImpl.java @@ -1,7 +1,11 @@ package com.yello.server.domain.vote.repository; +import static com.yello.server.domain.friend.entity.QFriend.friend; +import static com.yello.server.domain.vote.entity.QVote.vote; import static com.yello.server.global.common.ErrorCode.NOT_FOUND_VOTE_EXCEPTION; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; import com.yello.server.domain.vote.entity.Vote; import com.yello.server.domain.vote.exception.VoteNotFoundException; import java.util.List; @@ -17,6 +21,7 @@ public class VoteRepositoryImpl implements VoteRepository { private final VoteJpaRepository voteJpaRepository; + private final JPAQueryFactory jpaQueryFactory; @Override public Vote save(Vote vote) { @@ -83,4 +88,40 @@ public Integer countOpenNameByReceiverUserId(Long userId) { public Integer countOpenFullNameByReceiverUserId(Long userId) { return voteJpaRepository.countOpenFullNameByReceiverUserId(userId); } + + @Override + public List findUserSendReceivedByFriends(Long userId, Pageable pageable) { + return jpaQueryFactory.selectFrom(vote) + .where(vote.sender.id.eq(userId) + .and(vote.receiver.id.in( + JPAExpressions + .select(friend.target.id) + .from(friend) + .where(friend.user.id.eq(userId) + .and(friend.deletedAt.isNull()) + ))) + .and(vote.sender.deletedAt.isNull()) + .and(vote.receiver.deletedAt.isNull())) + .orderBy(vote.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + } + + @Override + public Long countUserSendReceivedByFriends(Long userId) { + return jpaQueryFactory.select(vote.count()) + .from(vote) + .where(vote.sender.id.eq(userId) + .and(vote.receiver.id.in( + JPAExpressions + .select(friend.target.id) + .from(friend) + .where(friend.user.id.eq(userId) + .and(friend.deletedAt.isNull()) + ))) + .and(vote.sender.deletedAt.isNull()) + .and(vote.receiver.deletedAt.isNull())) + .fetchOne(); + } } diff --git a/src/main/java/com/yello/server/domain/vote/service/VoteService.java b/src/main/java/com/yello/server/domain/vote/service/VoteService.java index d9d87def..f2fc3823 100644 --- a/src/main/java/com/yello/server/domain/vote/service/VoteService.java +++ b/src/main/java/com/yello/server/domain/vote/service/VoteService.java @@ -5,12 +5,15 @@ import static com.yello.server.global.common.ErrorCode.LACK_TICKET_COUNT_EXCEPTION; import static com.yello.server.global.common.ErrorCode.LACK_USER_EXCEPTION; import static com.yello.server.global.common.ErrorCode.REVEAL_FULL_NAME_VOTE_EXCEPTION; +import static com.yello.server.global.common.ErrorCode.WRONG_VOTE_TYPE_FORBIDDEN; import static com.yello.server.global.common.factory.TimeFactory.minusTime; +import static com.yello.server.global.common.util.ConstantUtil.ALL_VOTE_TYPE; import static com.yello.server.global.common.util.ConstantUtil.CHECK_FULL_NAME; import static com.yello.server.global.common.util.ConstantUtil.COOL_DOWN_TIME; import static com.yello.server.global.common.util.ConstantUtil.MINUS_TICKET_COUNT; import static com.yello.server.global.common.util.ConstantUtil.NO_FRIEND_COUNT; import static com.yello.server.global.common.util.ConstantUtil.RANDOM_COUNT; +import static com.yello.server.global.common.util.ConstantUtil.USER_VOTE_TYPE; import com.yello.server.domain.cooldown.entity.Cooldown; import com.yello.server.domain.cooldown.repository.CooldownRepository; @@ -32,15 +35,19 @@ import com.yello.server.domain.vote.dto.response.VoteCountVO; import com.yello.server.domain.vote.dto.response.VoteCreateVO; import com.yello.server.domain.vote.dto.response.VoteDetailResponse; +import com.yello.server.domain.vote.dto.response.VoteFriendAndUserResponse; +import com.yello.server.domain.vote.dto.response.VoteFriendAndUserVO; import com.yello.server.domain.vote.dto.response.VoteFriendResponse; import com.yello.server.domain.vote.dto.response.VoteFriendVO; import com.yello.server.domain.vote.dto.response.VoteListResponse; import com.yello.server.domain.vote.dto.response.VoteResponse; import com.yello.server.domain.vote.dto.response.VoteUnreadCountResponse; import com.yello.server.domain.vote.entity.Vote; +import com.yello.server.domain.vote.entity.VoteType; import com.yello.server.domain.vote.exception.VoteForbiddenException; import com.yello.server.domain.vote.exception.VoteNotFoundException; import com.yello.server.domain.vote.repository.VoteRepository; +import com.yello.server.global.common.ErrorCode; import com.yello.server.infrastructure.rabbitmq.service.ProducerService; import java.time.LocalDateTime; import java.util.List; @@ -51,6 +58,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; @Service @Builder @@ -113,6 +121,34 @@ public VoteFriendResponse findAllFriendVotes(Long userId, Pageable pageable) { return VoteFriendResponse.of(totalCount, list); } + public VoteFriendAndUserResponse findAllFriendVotesWithType(Long userId, Pageable pageable, String voteType) { + + if(!StringUtils.hasText(voteType)) { + final Long totalCount = Long.valueOf(voteRepository.countAllReceivedByFriends(userId)); + final List list = voteRepository.findAllReceivedByFriends(userId, pageable) + .stream() + .filter(vote -> vote.getNameHint()!=-3) + .map(vote -> VoteFriendAndUserVO.of(vote, vote.getSender().getId().equals(userId))) + .toList(); + return VoteFriendAndUserResponse.of(totalCount, list); + } + + switch(VoteType.fromCode(voteType)) { + case SEND -> { + final Long totalCount = voteRepository.countUserSendReceivedByFriends(userId); + List list = + voteRepository.findUserSendReceivedByFriends(userId, pageable) + .stream() + .filter(vote -> vote.getNameHint()!=-3) + .map(vote -> VoteFriendAndUserVO.of(vote, vote.getSender().getId().equals(userId))) + .toList(); + return VoteFriendAndUserResponse.of(totalCount,list); + } + } + + throw new VoteForbiddenException(WRONG_VOTE_TYPE_FORBIDDEN); + } + @Transactional public KeywordCheckResponse checkKeyword(Long userId, Long voteId) { final Vote vote = voteRepository.getById(voteId); diff --git a/src/main/java/com/yello/server/global/common/ErrorCode.java b/src/main/java/com/yello/server/global/common/ErrorCode.java index 1e5f4022..b4b7ee82 100644 --- a/src/main/java/com/yello/server/global/common/ErrorCode.java +++ b/src/main/java/com/yello/server/global/common/ErrorCode.java @@ -64,6 +64,7 @@ public enum ErrorCode { "유효하지 않는 Google OAuth 2.0 refreshToken입니다. DBA에게 문의해주세요."), GOOGLE_SUBSCRIPTIONS_FORBIDDEN_EXCEPTION(FORBIDDEN, "이미 YELLO: PLUS를 구독한 상태입니다."), GOOGLE_SUBSCRIPTION_TRANSACTION_EXPIRED_EXCEPTION(FORBIDDEN, "이미 만료된 결제 내역의 영수증입니다."), + WRONG_VOTE_TYPE_FORBIDDEN(FORBIDDEN, "잘못된 투표 유형입니다."), /** * 404 NOT FOUND diff --git a/src/main/java/com/yello/server/global/common/util/ConstantUtil.java b/src/main/java/com/yello/server/global/common/util/ConstantUtil.java index c10af4d5..8e515274 100644 --- a/src/main/java/com/yello/server/global/common/util/ConstantUtil.java +++ b/src/main/java/com/yello/server/global/common/util/ConstantUtil.java @@ -67,6 +67,8 @@ public class ConstantUtil { public static final int NO_FRIEND_COUNT = 0; public static final int SUBSCRIBE_DAYS = 7; public static final int PLUS_BASIC_TIME = 0; + public static final String USER_VOTE_TYPE = "send"; + public static final String ALL_VOTE_TYPE = "all"; private ConstantUtil() { diff --git a/src/main/resources/static/docs/find-friend-votes-v2.html b/src/main/resources/static/docs/find-friend-votes-v2.html index a9aba8e9..1d226c3a 100644 --- a/src/main/resources/static/docs/find-friend-votes-v2.html +++ b/src/main/resources/static/docs/find-friend-votes-v2.html @@ -5,7 +5,7 @@ -친구 투표 전체 조회 (명세) +친구 투표 전체 조회 v2