Skip to content

Commit 94c8c19

Browse files
authored
Merge pull request #207 from DevKor-github/feat/vote
Feat: 장소 투표 기능
2 parents 767e9f1 + 8d7b3e0 commit 94c8c19

21 files changed

Lines changed: 578 additions & 1 deletion
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package devkor.com.teamcback.domain.vote.controller;
2+
3+
import devkor.com.teamcback.domain.vote.dto.response.ChangeVoteStatusRes;
4+
import devkor.com.teamcback.domain.vote.service.VoteService;
5+
import devkor.com.teamcback.global.response.CommonResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.Parameter;
8+
import io.swagger.v3.oas.annotations.media.Content;
9+
import io.swagger.v3.oas.annotations.media.Schema;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.web.bind.annotation.GetMapping;
14+
import org.springframework.web.bind.annotation.PathVariable;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.RestController;
17+
18+
@RestController
19+
@RequiredArgsConstructor
20+
@RequestMapping("/api/admin/votes")
21+
public class AdminVoteController {
22+
private final VoteService voteService;
23+
24+
/**
25+
* 투표 상태 변경
26+
*/
27+
@Operation(summary = "투표 상태 변경", description = "투표 상태 변경")
28+
@ApiResponses(value = {
29+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
30+
@ApiResponse(responseCode = "404", description = "Not Found",
31+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
32+
})
33+
@GetMapping("/{voteId}")
34+
public CommonResponse<ChangeVoteStatusRes> changeVoteStatus(
35+
@Parameter(description = "투표 ID", example = "1")
36+
@PathVariable(name = "voteId") Long voteId) {
37+
return CommonResponse.success(voteService.changeVoteStatus(voteId));
38+
}
39+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package devkor.com.teamcback.domain.vote.controller;
2+
3+
import devkor.com.teamcback.domain.vote.dto.request.SaveVoteRecordReq;
4+
import devkor.com.teamcback.domain.vote.dto.response.GetVoteRes;
5+
import devkor.com.teamcback.domain.vote.dto.response.SaveVoteRecordRes;
6+
import devkor.com.teamcback.domain.vote.service.VoteService;
7+
import devkor.com.teamcback.global.response.CommonResponse;
8+
import devkor.com.teamcback.global.security.UserDetailsImpl;
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.Parameter;
11+
import io.swagger.v3.oas.annotations.media.Content;
12+
import io.swagger.v3.oas.annotations.media.Schema;
13+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
14+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
15+
import lombok.RequiredArgsConstructor;
16+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
@RestController
20+
@RequiredArgsConstructor
21+
@RequestMapping("/api/votes")
22+
public class VoteController {
23+
private final VoteService voteService;
24+
25+
/**
26+
* 장소별 투표 정보 조회
27+
* @param placeId 장소 ID
28+
*/
29+
@Operation(summary = "투표 정보 조회", description = "투표 정보 조회")
30+
@ApiResponses(value = {
31+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
32+
@ApiResponse(responseCode = "404", description = "Not Found",
33+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
34+
})
35+
@GetMapping("/topics/{voteTopicId}/places/{placeId}")
36+
public CommonResponse<GetVoteRes> getVoteByPlace(
37+
@Parameter(description = "투표 주제 ID", example = "1")
38+
@PathVariable(name = "voteTopicId") Long voteTopicId,
39+
@Parameter(description = "장소 ID", example = "1")
40+
@PathVariable(name = "placeId") Long placeId) {
41+
return CommonResponse.success(voteService.getVoteByPlace(voteTopicId, placeId));
42+
}
43+
44+
/**
45+
* 투표 기록 저장
46+
* @param userDetail 사용자 정보
47+
*/
48+
@Operation(summary = "투표 저장", description = "투표 저장")
49+
@ApiResponses(value = {
50+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
51+
@ApiResponse(responseCode = "404", description = "Not Found",
52+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
53+
})
54+
@PostMapping("")
55+
public CommonResponse<SaveVoteRecordRes> saveVoteRecord(
56+
@Parameter(description = "사용자정보")
57+
@AuthenticationPrincipal UserDetailsImpl userDetail,
58+
@Parameter(description = "투표 저장")
59+
@RequestBody SaveVoteRecordReq req
60+
) {
61+
Long userId = userDetail == null ? null : userDetail.getUser().getUserId();
62+
return CommonResponse.success(voteService.saveVoteRecord(userId, req));
63+
}
64+
65+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package devkor.com.teamcback.domain.vote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
7+
@Getter
8+
@AllArgsConstructor
9+
public class SaveVoteRecordReq {
10+
@Schema(description = "투표 id", example = "1")
11+
private Long voteId;
12+
13+
@Schema(description = "투표 옵션 id", example = "1")
14+
private Long voteOptionId;
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package devkor.com.teamcback.domain.vote.dto.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
6+
@Schema(description = "투표 종료 완료")
7+
@JsonIgnoreProperties
8+
public class ChangeVoteStatusRes {
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package devkor.com.teamcback.domain.vote.dto.response;
2+
3+
import com.querydsl.core.annotations.QueryProjection;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Getter;
6+
7+
@Schema(description = "투표 항목 조회 결과")
8+
@Getter
9+
public class GetVoteOptionRes {
10+
11+
@Schema(description = "투표 항목 ID", example = "1")
12+
private Long voteOptionId;
13+
14+
@Schema(description = "투표 항목", example = "있다")
15+
private String optionText;
16+
17+
@Schema(description = "투표수", example = "5")
18+
private int voteCount;
19+
20+
@QueryProjection
21+
public GetVoteOptionRes(Long voteOptionId, String optionText, int voteCount) {
22+
this.voteOptionId = voteOptionId;
23+
this.optionText = optionText;
24+
this.voteCount = voteCount;
25+
}
26+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package devkor.com.teamcback.domain.vote.dto.response;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Getter;
5+
6+
import java.util.List;
7+
8+
@Schema(description = "투표 목록 조회 결과")
9+
@Getter
10+
public class GetVoteRes {
11+
12+
@Schema(description = "투표 ID", example = "1")
13+
private Long voteId;
14+
15+
@Schema(description = "투표 상태", example = "종료")
16+
private String status;
17+
18+
@Schema(description = "투표 주제 ID", example = "1")
19+
private Long voteTopicId;
20+
21+
@Schema(description = "투표 주제", example = "콘센트 유무")
22+
private String topic;
23+
24+
@Schema(description = "장소 ID", example = "1")
25+
private Long placeId;
26+
27+
@Schema(description = "장소명", example = "101호")
28+
private String placeName;
29+
30+
@Schema(description = "투표 항목 리스트")
31+
private List<GetVoteOptionRes> optionList;
32+
33+
public GetVoteRes(Long voteId, String status, Long voteTopicId, String topic, Long placeId, String placeName, List<GetVoteOptionRes> optionList) {
34+
this.voteId = voteId;
35+
this.voteTopicId = voteTopicId;
36+
this.topic = topic;
37+
this.status = status;
38+
this.placeId = placeId;
39+
this.placeName = placeName;
40+
this.optionList = optionList;
41+
}
42+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package devkor.com.teamcback.domain.vote.dto.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
6+
@Schema(description = "투표 내용 저장 완료")
7+
@JsonIgnoreProperties
8+
public class SaveVoteRecordRes {
9+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package devkor.com.teamcback.domain.vote.entity;
2+
3+
import devkor.com.teamcback.domain.common.entity.BaseEntity;
4+
import devkor.com.teamcback.global.exception.exception.GlobalException;
5+
import jakarta.persistence.*;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.Setter;
9+
10+
import static devkor.com.teamcback.domain.vote.entity.VoteStatus.CLOSED;
11+
import static devkor.com.teamcback.domain.vote.entity.VoteStatus.OPEN;
12+
import static devkor.com.teamcback.global.response.ResultCode.CLOSED_VOTE;
13+
14+
@Entity
15+
@Getter
16+
@Setter
17+
@Table(name = "tb_vote")
18+
@NoArgsConstructor
19+
public class Vote extends BaseEntity {
20+
@Id
21+
@GeneratedValue(strategy = GenerationType.IDENTITY)
22+
private Long id;
23+
24+
@Column(nullable = false)
25+
private Long voteTopicId;
26+
27+
@Column
28+
private Long placeId;
29+
30+
@Column
31+
@Enumerated(EnumType.STRING)
32+
private VoteStatus status;
33+
34+
public void checkStatus() {
35+
if(this.status == CLOSED) throw new GlobalException(CLOSED_VOTE);
36+
}
37+
38+
public void changeStatus() {
39+
if(this.status == CLOSED) this.status = OPEN;
40+
else this.status = CLOSED;
41+
}
42+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package devkor.com.teamcback.domain.vote.entity;
2+
3+
import devkor.com.teamcback.domain.common.entity.BaseEntity;
4+
import jakarta.persistence.*;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
import lombok.Setter;
8+
9+
@Entity
10+
@Getter
11+
@Setter
12+
@Table(name = "tb_vote_option")
13+
@NoArgsConstructor
14+
public class VoteOption extends BaseEntity {
15+
16+
@Id
17+
@GeneratedValue(strategy = GenerationType.IDENTITY)
18+
private Long id;
19+
20+
@Column(nullable = false)
21+
private Long voteTopicId;
22+
23+
@Column(nullable = false)
24+
private String optionText;
25+
26+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package devkor.com.teamcback.domain.vote.entity;
2+
3+
import devkor.com.teamcback.domain.common.entity.BaseEntity;
4+
import jakarta.persistence.*;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
import lombok.Setter;
8+
9+
@Entity
10+
@Getter
11+
@Setter
12+
@Table(name = "tb_vote_record")
13+
@NoArgsConstructor
14+
public class VoteRecord extends BaseEntity {
15+
@Id
16+
@GeneratedValue(strategy = GenerationType.IDENTITY)
17+
private Long id;
18+
19+
@Column(nullable = false)
20+
private Long userId;
21+
22+
@Column(nullable = false)
23+
private Long voteId;
24+
25+
@Column
26+
private Long voteOptionId;
27+
28+
public VoteRecord(Long userId, Long voteId, Long voteOptionId) {
29+
this.userId = userId;
30+
this.voteId = voteId;
31+
this.voteOptionId = voteOptionId;
32+
}
33+
}

0 commit comments

Comments
 (0)