Skip to content

Commit c8466ac

Browse files
authored
[Feat] 내가 만든 투표 목록 조회 API 개발 (#57)
* feat: 내가 만든 투표 목록용 커서 파서 추가 (#37) * refactor: 투표 리스트 응답 DTO를 기능별 패키지로 분리 (active, mine, participated) (#37) * feat: 내가 만든 투표 목록 조회 API 응답 DTO 생성 (#37) * feat: 내가 만든 투표 목록 조회용 QueryDSL 리포지토리 구현 (#37) * refactor: 공개 그룹 조회 로직 GroupService로 분리 (#37) * feat: VoteOptionResultWithId DTO 추가 (voteId 포함) (#37) * feat: 내가 만든 투표 목록 조회 서비스 로직 구현 (#37) - getMyVotes 서비스 구현 - VoteResultService.getResultsWithVoteId() 메서드 추가 및 사용 - 투표에 대해 결과 집계 포함한 응답 구성 * feat: 내가 만든 투표 목록 조회 API 컨트롤러 구현 (#37) * refactor: VoteResultService를 사용해 getVoteResult 결과 집계 로직 분리 (#37) * feat: 커서에 포함된 voteId 유효성 검증 추가 (#37) - MyVote 목록 조회 시 전달된 커서의 voteId가 실제로 존재하는지 확인 - 존재하지 않으면 404(VOTE_NOT_FOUND) 예외 발생시킴 - 불필요한 쿼리 실행 방지 및 에러 원인 명확화
1 parent 2b02301 commit c8466ac

File tree

13 files changed

+305
-47
lines changed

13 files changed

+305
-47
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.moa.moa_server.domain.global.cursor;
2+
3+
import com.moa.moa_server.domain.vote.handler.VoteErrorCode;
4+
import com.moa.moa_server.domain.vote.handler.VoteException;
5+
import lombok.extern.slf4j.Slf4j;
6+
7+
import java.time.LocalDateTime;
8+
import java.time.format.DateTimeFormatter;
9+
import java.time.format.DateTimeParseException;
10+
11+
@Slf4j
12+
public record CreatedAtVoteIdCursor(LocalDateTime createdAt, Long voteId) {
13+
14+
/**
15+
* "createdAt_voteId" 형식의 커서를 파싱
16+
*/
17+
public static CreatedAtVoteIdCursor parse(String cursor) {
18+
try {
19+
String[] parts = cursor.split("_");
20+
if (parts.length != 2) {
21+
log.warn("[VoteMineCursor#parse] Cursor must contain exactly two parts.");
22+
throw new VoteException(VoteErrorCode.INVALID_CURSOR_FORMAT);
23+
}
24+
return new CreatedAtVoteIdCursor(
25+
LocalDateTime.parse(parts[0]),
26+
Long.parseLong(parts[1])
27+
);
28+
} catch (DateTimeParseException | NumberFormatException e) {
29+
log.warn("[VoteMineCursor#parse] Failed to parse cursor '{}': {}", cursor, e.toString());
30+
throw new VoteException(VoteErrorCode.INVALID_CURSOR_FORMAT);
31+
}
32+
}
33+
34+
public String encode() {
35+
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
36+
return createdAt.format(formatter) + "_" + voteId;
37+
}
38+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.moa.moa_server.domain.group.service;
2+
3+
import com.moa.moa_server.domain.group.entity.Group;
4+
import com.moa.moa_server.domain.group.repository.GroupRepository;
5+
import com.moa.moa_server.domain.vote.handler.VoteErrorCode;
6+
import com.moa.moa_server.domain.vote.handler.VoteException;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.stereotype.Service;
9+
10+
@Service
11+
@RequiredArgsConstructor
12+
public class GroupService {
13+
14+
private final GroupRepository groupRepository;
15+
16+
public Group getPublicGroup() {
17+
return groupRepository.findById(1L)
18+
.orElseThrow(() -> new VoteException(VoteErrorCode.GROUP_NOT_FOUND));
19+
}
20+
}

src/main/java/com/moa/moa_server/domain/vote/controller/VoteController.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import com.moa.moa_server.domain.vote.dto.request.VoteSubmitRequest;
66
import com.moa.moa_server.domain.vote.dto.response.VoteCreateResponse;
77
import com.moa.moa_server.domain.vote.dto.response.VoteDetailResponse;
8-
import com.moa.moa_server.domain.vote.dto.response.list.VoteListResponse;
8+
import com.moa.moa_server.domain.vote.dto.response.active.ActiveVoteResponse;
9+
import com.moa.moa_server.domain.vote.dto.response.mine.MyVoteResponse;
910
import com.moa.moa_server.domain.vote.dto.response.result.VoteResultResponse;
1011
import com.moa.moa_server.domain.vote.service.VoteService;
1112
import lombok.RequiredArgsConstructor;
@@ -62,12 +63,24 @@ public ResponseEntity<ApiResponse> getVoteResult(
6263
}
6364

6465
@GetMapping
65-
public VoteListResponse getActiveVotes(
66+
public ResponseEntity<ApiResponse> getActiveVotes(
6667
@AuthenticationPrincipal Long userId,
67-
@RequestParam(required = false) Integer groupId,
68+
@RequestParam(required = false) Long groupId,
6869
@RequestParam(required = false) String cursor,
6970
@RequestParam(required = false) Integer size
7071
) {
71-
return voteService.getActiveVotes(userId, groupId, cursor, size);
72+
ActiveVoteResponse response = voteService.getActiveVotes(userId, groupId, cursor, size);
73+
return ResponseEntity.ok(new ApiResponse("SUCCESS", response));
74+
}
75+
76+
@GetMapping("/mine")
77+
public ResponseEntity<ApiResponse> getMyVotes(
78+
@AuthenticationPrincipal Long userId,
79+
@RequestParam(required = false) Long groupId,
80+
@RequestParam(required = false) String cursor,
81+
@RequestParam(required = false) Integer size
82+
) {
83+
MyVoteResponse response = voteService.getMyVotes(userId, groupId, cursor, size);
84+
return ResponseEntity.ok(new ApiResponse("SUCCESS", response));
7285
}
7386
}

src/main/java/com/moa/moa_server/domain/vote/dto/response/list/VoteListItem.java renamed to src/main/java/com/moa/moa_server/domain/vote/dto/response/active/ActiveVoteItem.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
package com.moa.moa_server.domain.vote.dto.response.list;
1+
package com.moa.moa_server.domain.vote.dto.response.active;
22

33
import com.moa.moa_server.domain.vote.entity.Vote;
44

55
import java.time.LocalDateTime;
66

7-
public record VoteListItem(
7+
public record ActiveVoteItem(
88
Long voteId,
99
Long groupId,
1010
String authorNickname,
@@ -16,8 +16,8 @@ public record VoteListItem(
1616
String voteType
1717
) {
1818

19-
public static VoteListItem from(Vote vote) {
20-
return new VoteListItem(
19+
public static ActiveVoteItem from(Vote vote) {
20+
return new ActiveVoteItem(
2121
vote.getId(),
2222
vote.getGroup().getId(),
2323
vote.getUser().getNickname(),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.moa.moa_server.domain.vote.dto.response.active;
2+
3+
import java.util.List;
4+
5+
public record ActiveVoteResponse(
6+
List<ActiveVoteItem> votes,
7+
String nextCursor,
8+
boolean hasNext,
9+
int size
10+
) {}

src/main/java/com/moa/moa_server/domain/vote/dto/response/list/VoteListResponse.java

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.moa.moa_server.domain.vote.dto.response.mine;
2+
3+
import com.moa.moa_server.domain.vote.dto.response.result.VoteOptionResult;
4+
import com.moa.moa_server.domain.vote.entity.Vote;
5+
6+
import java.time.LocalDateTime;
7+
import java.util.List;
8+
9+
public record MyVoteItem(
10+
Long voteId,
11+
Long groupId,
12+
String content,
13+
String voteStatus,
14+
LocalDateTime createdAt,
15+
LocalDateTime closedAt,
16+
List<VoteOptionResultWithId> results
17+
) {
18+
19+
public static MyVoteItem from(Vote vote, List<VoteOptionResultWithId> results) {
20+
return new MyVoteItem(
21+
vote.getId(),
22+
vote.getGroup().getId(),
23+
vote.getContent(),
24+
vote.getVoteStatus().name(),
25+
vote.getCreatedAt(),
26+
vote.getClosedAt(),
27+
results
28+
);
29+
}
30+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.moa.moa_server.domain.vote.dto.response.mine;
2+
3+
import java.util.List;
4+
5+
public record MyVoteResponse(
6+
List<MyVoteItem> votes,
7+
String nextCursor,
8+
boolean hasNext,
9+
int size
10+
) {
11+
public static MyVoteResponse of(List<MyVoteItem> votes, String nextCursor, boolean hasNext) {
12+
return new MyVoteResponse(votes, nextCursor, hasNext, votes.size());
13+
}
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.moa.moa_server.domain.vote.dto.response.mine;
2+
3+
public record VoteOptionResultWithId(
4+
Long voteId,
5+
int optionNumber,
6+
int count,
7+
int ratio
8+
) {}

src/main/java/com/moa/moa_server/domain/vote/repository/VoteRepositoryCustom.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.moa.moa_server.domain.vote.repository;
22

3+
import com.moa.moa_server.domain.global.cursor.CreatedAtVoteIdCursor;
34
import com.moa.moa_server.domain.global.cursor.VoteClosedCursor;
45
import com.moa.moa_server.domain.group.entity.Group;
56
import com.moa.moa_server.domain.user.entity.User;
@@ -10,4 +11,5 @@
1011

1112
public interface VoteRepositoryCustom {
1213
List<Vote> findActiveVotes(List<Group> accessibleGroups, @Nullable VoteClosedCursor cursor, @Nullable User user, int size);
14+
List<Vote> findMyVotes(User user, List<Group> groups, @Nullable CreatedAtVoteIdCursor cursor, int size);
1315
}

0 commit comments

Comments
 (0)