Skip to content

Commit d1a4e9e

Browse files
authored
Merge pull request #111 from prgrms-web-devcourse-final-project/feat/22-post
Feat[post] 게시판 투표 기능
2 parents c5b6678 + 0be29b5 commit d1a4e9e

34 files changed

+1807
-307
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,158 @@
11
package com.ai.lawyer.domain.poll.controller;
22

3+
import com.ai.lawyer.domain.poll.dto.PollCreateDto;
4+
import com.ai.lawyer.domain.poll.dto.PollDto;
5+
import com.ai.lawyer.domain.poll.dto.PollVoteDto;
6+
import com.ai.lawyer.domain.poll.entity.PollVote;
7+
import com.ai.lawyer.domain.poll.entity.PollOptions;
8+
import com.ai.lawyer.domain.poll.entity.PollStatics;
9+
import com.ai.lawyer.domain.poll.service.PollService;
10+
import com.ai.lawyer.domain.post.dto.PostDetailDto;
11+
import com.ai.lawyer.domain.post.service.PostService;
12+
import io.swagger.v3.oas.annotations.Operation;
13+
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.security.core.Authentication;
17+
import org.springframework.security.core.context.SecurityContextHolder;
18+
import org.springframework.web.bind.annotation.*;
19+
20+
import org.springframework.web.server.ResponseStatusException;
21+
22+
import java.util.List;
23+
24+
@Tag(name = "Poll API", description = "투표 관련 API")
25+
@RestController
26+
@RequestMapping("/api/polls")
27+
@RequiredArgsConstructor
328
public class PollController {
29+
30+
private final PollService pollService;
31+
private final PostService postService;
32+
33+
@Operation(summary = "투표 단일 조회")
34+
@GetMapping("/{pollId}")
35+
public PollDto getPoll(@PathVariable Long pollId) {
36+
return pollService.getPoll(pollId);
37+
}
38+
39+
@Operation(summary = "투표 옵션 목록 조회")
40+
@GetMapping("/{pollId}/options")
41+
public List<PollOptions> getPollOptions(@PathVariable Long pollId) {
42+
return pollService.getPollOptions(pollId);
43+
}
44+
45+
@Operation(summary = "투표하기")
46+
@PostMapping("/{pollId}/vote")
47+
public ResponseEntity<?> vote(@PathVariable Long pollId, @RequestParam Long pollItemsId) {
48+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
49+
Long memberId = Long.parseLong(authentication.getName());
50+
return ResponseEntity.ok(pollService.vote(pollId, pollItemsId, memberId));
51+
}
52+
53+
@Operation(summary = "투표 통계 조회")
54+
@GetMapping("/{pollId}/statics")
55+
public List<PollStatics> getPollStatics(@PathVariable Long pollId) {
56+
return pollService.getPollStatics(pollId);
57+
}
58+
59+
@Operation(summary = "투표 종료")
60+
@PutMapping("/{pollId}/close")
61+
public void closePoll(@PathVariable Long pollId) {
62+
pollService.closePoll(pollId);
63+
}
64+
65+
@Operation(summary = "투표 삭제")
66+
@DeleteMapping("/{pollId}")
67+
public void deletePoll(@PathVariable Long pollId) {
68+
pollService.deletePoll(pollId);
69+
}
70+
71+
@Operation(summary = "진행중인 투표 Top 1 조회")
72+
@GetMapping("/top/ongoing")
73+
public PollDto getTopOngoingPoll() {
74+
return pollService.getTopPollByStatus(PollDto.PollStatus.ONGOING);
75+
}
76+
77+
@Operation(summary = "종료된 투표 Top 1 조회")
78+
@GetMapping("/top/closed")
79+
public PollDto getTopClosedPoll() {
80+
return pollService.getTopPollByStatus(PollDto.PollStatus.CLOSED);
81+
}
82+
83+
// @Operation(summary = "진행중인 투표 상세 조회")
84+
// @GetMapping("/top/ongoing-detail")
85+
// public PostDetailDto getTopOngoingPollDetail() {
86+
// PollDto pollDto = pollService.getTopPollByStatus(PollDto.PollStatus.ONGOING);
87+
// return postService.getPostDetailById(pollDto.getPostId());
88+
// }
89+
//
90+
// @Operation(summary = "종료된 투표 상세 조회")
91+
// @GetMapping("/top/closed-detail")
92+
// public PostDetailDto getTopClosedPollDetail() {
93+
// PollDto pollDto = pollService.getTopPollByStatus(PollDto.PollStatus.CLOSED);
94+
// return postService.getPostDetailById(pollDto.getPostId());
95+
// }
96+
97+
@Operation(summary = "투표 생성")
98+
@PostMapping("")
99+
public ResponseEntity<?> createPoll(@RequestBody PollCreateDto pollCreateDto) {
100+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
101+
Long memberId = Long.parseLong(authentication.getName());
102+
return ResponseEntity.ok(pollService.createPoll(pollCreateDto, memberId));
103+
}
104+
105+
@Operation(summary = "투표 수정")
106+
@PutMapping("/{pollId}")
107+
public PollDto updatePoll(@PathVariable Long pollId, @RequestBody com.ai.lawyer.domain.poll.dto.PollUpdateDto pollUpdateDto) {
108+
return pollService.updatePoll(pollId, pollUpdateDto);
109+
}
110+
111+
@Operation(summary = "진행중인 투표 전체 목록 조회")
112+
@GetMapping("/ongoing")
113+
public List<PollDto> getOngoingPolls() {
114+
return pollService.getPollsByStatus(PollDto.PollStatus.ONGOING);
115+
}
116+
117+
@Operation(summary = "종료된 투표 전체 목록 조회")
118+
@GetMapping("/closed")
119+
public List<PollDto> getClosedPolls() {
120+
return pollService.getPollsByStatus(PollDto.PollStatus.CLOSED);
121+
}
122+
123+
@Operation(summary = "종료된 투표 Top N 조회")
124+
@GetMapping("/top/closed-list") //검색조건 : pi/polls/top/closed-list?size=3
125+
public List<PollDto> getTopClosedPolls(@RequestParam(defaultValue = "3") int size) {
126+
return pollService.getTopNPollsByStatus(PollDto.PollStatus.CLOSED, size);
127+
}
128+
129+
@Operation(summary = "진행중인 투표 Top N 조회")
130+
@GetMapping("/top/ongoing-list") //검색조건 : api/polls/top/ongoing-list?size=3
131+
public List<PollDto> getTopOngoingPolls(@RequestParam(defaultValue = "3") int size) {
132+
return pollService.getTopNPollsByStatus(PollDto.PollStatus.ONGOING, size);
133+
}
134+
135+
@Operation(summary = "index(순번)로 투표하기 - Swagger 편의용")
136+
@PostMapping("/{pollId}/vote-by-index")
137+
public ResponseEntity<?> voteByIndex(@PathVariable Long pollId, @RequestParam int index) {
138+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
139+
Long memberId = Long.parseLong(authentication.getName());
140+
141+
List<PollOptions> options = pollService.getPollOptions(pollId);
142+
if (index < 1 || index > options.size()) {
143+
throw new ResponseStatusException(org.springframework.http.HttpStatus.BAD_REQUEST, "index가 옵션 범위를 벗어났습니다.");
144+
}
145+
Long pollItemsId = options.get(index - 1).getPollItemsId();
146+
return ResponseEntity.ok(pollService.vote(pollId, pollItemsId, memberId));
147+
}
148+
149+
@ExceptionHandler(ResponseStatusException.class)
150+
public ResponseEntity<Object> handleResponseStatusException(ResponseStatusException ex) {
151+
int code = ex.getStatusCode().value();
152+
String message = ex.getReason();
153+
return ResponseEntity.status(code).body(new java.util.HashMap<String, Object>() {{
154+
put("code", code);
155+
put("message", message);
156+
}});
157+
}
4158
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
import java.util.List;
9+
import java.time.LocalDateTime;
10+
11+
@Data
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@Builder
15+
public class PollCreateDto {
16+
@Schema(description = "게시글 ID", example = "123")
17+
private Long postId;
18+
@Schema(description = "투표 제목", example = "당신의 선택은?")
19+
private String voteTitle;
20+
@Schema(description = "투표 항목(2개 필수)", example = "[{\"content\": \"항목1 내용\"}, {\"content\": \"항목2 내용\"}]")
21+
private List<PollOptionCreateDto> pollOptions;
22+
private LocalDateTime reservedCloseAt;
23+
}

backend/src/main/java/com/ai/lawyer/domain/poll/dto/PollDto.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import lombok.*;
44
import java.time.LocalDateTime;
5+
import java.util.List;
56

67
@Data
78
@NoArgsConstructor
@@ -14,9 +15,11 @@ public class PollDto {
1415
private PollStatus status;
1516
private LocalDateTime createdAt;
1617
private LocalDateTime closedAt;
18+
private LocalDateTime expectedCloseAt;
19+
private List<PollOptionDto> pollOptions;
20+
private Long totalVoteCount;
1721

1822
public enum PollStatus {
1923
ONGOING, CLOSED
2024
}
2125
}
22-
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
@Builder
13+
public class PollOptionCreateDto {
14+
@Schema(description = "투표 항목 내용", example = "항목1 내용")
15+
private String content;
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import lombok.*;
4+
5+
@Data
6+
@NoArgsConstructor
7+
@AllArgsConstructor
8+
@Builder
9+
public class PollOptionDto {
10+
private Long pollItemsId;
11+
private String content;
12+
private Long voteCount;
13+
private java.util.List<PollStaticsDto> statics;
14+
private int pollOptionIndex;
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
@Builder
13+
public class PollOptionUpdateDto {
14+
@Schema(description = "투표 항목 id", example = "1")
15+
private Long pollItemsId;
16+
17+
@Schema(description = "투표 항목 내용", example = "항목1 내용")
18+
private String content;
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import lombok.*;
4+
5+
@Data
6+
@NoArgsConstructor
7+
@AllArgsConstructor
8+
@Builder
9+
public class PollStaticsDto {
10+
private String gender;
11+
private String ageGroup;
12+
private Long voteCount;
13+
}
14+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
import java.util.List;
9+
import java.time.LocalDateTime;
10+
11+
@Data
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@Builder
15+
public class PollUpdateDto {
16+
@Schema(description = "투표 제목", example = "당신의 선택은?")
17+
private String voteTitle;
18+
@Schema(description = "투표 항목(2개 필수)", example = "[{\"content\": \"항목1 내용\"}, {\"content\": \"항목2 내용\"}]")
19+
private List<PollOptionUpdateDto> pollOptions;
20+
private LocalDateTime reservedCloseAt;
21+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@NoArgsConstructor
10+
@AllArgsConstructor
11+
@Builder
12+
public class PollVoteDto {
13+
private Long pollVoteId;
14+
private Long pollId;
15+
private Long pollItemsId;
16+
private Long memberId;
17+
private Long voteCount;
18+
}
19+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.ai.lawyer.domain.poll.dto;
2+
3+
import com.ai.lawyer.domain.post.dto.PostDto;
4+
import lombok.*;
5+
6+
@Data
7+
@NoArgsConstructor
8+
@AllArgsConstructor
9+
@Builder
10+
public class PollWithPostDto {
11+
private PollDto poll;
12+
private PostDto post;
13+
}
14+

0 commit comments

Comments
 (0)