Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/9: Survey 관련 API 개발 #30

Merged
merged 20 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ffe3395
Feat: Survey 관련 API 구현 클래스 생성(.gitkeep 제거)
yxhwxn Aug 7, 2024
081aee7
Feat: 설문 참여자 정보 관련 컬럼 추가
yxhwxn Aug 8, 2024
9aadd97
Fix: 질문 유형 변경
yxhwxn Aug 8, 2024
875163d
Feat: 설문 공유용 URL을 위한 컬럼 추가
yxhwxn Aug 8, 2024
2ee774f
Feat: 설문 생성 API 구현
yxhwxn Aug 8, 2024
fcf801a
Refactor: SurveyRequestDTO 유효성 검사 추가
yxhwxn Aug 8, 2024
5702d64
Feat: 개인정보수집 관련 엔티티 추가
yxhwxn Aug 8, 2024
2cf7fef
Refactor: 설문 생성 API 수정
yxhwxn Aug 8, 2024
522c491
Feat: 설문 미리보기 조회 API 구현
yxhwxn Aug 8, 2024
0650e10
Refactor: URL 공유를 위한 survey uuid 추가
yxhwxn Aug 8, 2024
c983b2a
Chore: 패키지 구조 수정
yxhwxn Aug 8, 2024
8059989
Feat: 설문 응답 등록 API 구현
yxhwxn Aug 8, 2024
32d00bf
Refactor: 중복 설문 참여자 에러케이스 추가
yxhwxn Aug 8, 2024
92127d8
Feat: 질문별 설문 응답 리스트 조회 API 구현
yxhwxn Aug 8, 2024
486d5dd
Fix: PK 변경, eventId -> id
yxhwxn Aug 8, 2024
873e70a
Refactor: 설문 결과 리스트 응답 시, 페이지네이션 적용
yxhwxn Aug 8, 2024
0cdeec0
Feat: 설문 당첨자 랜덤 추첨 결과 조회 API 구현
yxhwxn Aug 8, 2024
cdf3c84
Refactor: JPQL 에러 해결, CriteriaBuilder를 활용한 동적 쿼리 생성 방식으로 변경
yxhwxn Aug 8, 2024
4b77154
Feat: 설문 당첨자 세부정보 조회 API 구현
yxhwxn Aug 8, 2024
19ae2da
Feat: 설문 당첨자 다시 뽑기(기존 당첨자 삭제) API 구현
yxhwxn Aug 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.cmc.suppin.event.survey.controller;

import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO;
import com.cmc.suppin.event.survey.controller.dto.SurveyResponseDTO;
import com.cmc.suppin.event.survey.service.SurveyService;
import com.cmc.suppin.global.response.ApiResponse;
import com.cmc.suppin.global.response.ResponseCode;
import com.cmc.suppin.global.security.reslover.Account;
import com.cmc.suppin.global.security.reslover.CurrentAccount;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
@RequiredArgsConstructor
@Validated
@Tag(name = "Event-Survey", description = "Survey 관련 API")
@RequestMapping("/api/v1/survey")
public class SurveyApi {

private final SurveyService surveyService;

@PostMapping("/create")
@Operation(summary = "설문지 생성 API", description = "QuestionType(Enum): SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))")
public ResponseEntity<ApiResponse<SurveyResponseDTO.SurveyCreateResponse>> createSurvey(@RequestBody @Valid SurveyRequestDTO.SurveyCreateDTO request, @CurrentAccount Account account) {
SurveyResponseDTO.SurveyCreateResponse response = surveyService.createSurvey(request, account.userId());
return ResponseEntity.ok(ApiResponse.of(response));
}

@GetMapping("/{surveyId}")
@Operation(summary = "설문지 조회 API", description = "Request: 설문지 ID, Response: 설문지 정보 <br><br>" +
"SUBJEVTIVE: 주관식, SINGLE_CHOICE: 객관식(단일 선택), MULTIPLE_CHOICE: 객관식(복수 선택)")
public ResponseEntity<ApiResponse<SurveyResponseDTO.SurveyResultDTO>> getSurvey(@PathVariable Long surveyId) {
SurveyResponseDTO.SurveyResultDTO response = surveyService.getSurvey(surveyId);
return ResponseEntity.ok(ApiResponse.of(response));
}

@PostMapping("/reply")
@Operation(summary = "설문 답변 등록 API")
public ResponseEntity<ApiResponse<Void>> saveSurveyAnswers(@RequestBody @Valid SurveyRequestDTO.SurveyAnswerDTO request) {
surveyService.saveSurveyAnswers(request);
return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS));
}

@GetMapping("/{surveyId}/answers/{questionId}")
@Operation(summary = "질문별 설문 응답 결과 조회 API", description = "Request: 설문지 ID와 질문 ID, Response: 해당 질문에 대한 응답 리스트")
public ResponseEntity<ApiResponse<SurveyResponseDTO.SurveyAnswerResultDTO>> getSurveyAnswers(
@PathVariable Long surveyId,
@PathVariable Long questionId,
@RequestParam int page,
@RequestParam int size,
@CurrentAccount Account account) {
SurveyResponseDTO.SurveyAnswerResultDTO response = surveyService.getSurveyAnswers(surveyId, questionId, page, size, account.userId());
return ResponseEntity.ok(ApiResponse.of(response));
}

@PostMapping("/draft")
@Operation(summary = "당첨자 랜덤 추첨 결과 리스트 조회 API(설문 이벤트)", description = "주관식 답변 중 조건을 설정하여 랜덤으로 당첨자를 추첨합니다.")
public ResponseEntity<ApiResponse<SurveyResponseDTO.RandomSelectionResponseDTO>> selectRandomWinners(
@RequestBody @Valid SurveyRequestDTO.RandomSelectionRequestDTO request, @CurrentAccount Account account) {
SurveyResponseDTO.RandomSelectionResponseDTO response = surveyService.selectRandomWinners(request, account.userId());
return ResponseEntity.ok(ApiResponse.of(response));
}

// 당첨자 세부 정보 조회 API
@GetMapping("/winners/{surveyId}/{participantId}")
@Operation(summary = "당첨자 세부 정보 조회 API", description = "설문 이벤트의 당첨자(익명 참여자) 정보를 조회하며, 해당 참여자가 응답한 모든 설문 내용을 반환합니다.")
public ResponseEntity<ApiResponse<SurveyResponseDTO.WinnerDetailDTO>> getWinnerDetails(
@PathVariable Long surveyId, @PathVariable Long participantId) {
SurveyResponseDTO.WinnerDetailDTO winnerDetails = surveyService.getWinnerDetails(surveyId, participantId);
return ResponseEntity.ok(ApiResponse.of(winnerDetails));
}

@DeleteMapping("/winners")
@Operation(summary = "당첨자 리스트 삭제 API", description = "해당 설문조사의 모든 당첨자들의 isWinner 값을 false로 변경합니다.")
public ResponseEntity<ApiResponse<Void>> deleteWinners(@RequestParam Long surveyId) {
surveyService.deleteWinners(surveyId);
return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.cmc.suppin.event.survey.controller.dto;

import com.cmc.suppin.global.enums.QuestionType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

public class SurveyRequestDTO {

// 설문 생성 요청 DTO
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SurveyCreateDTO {
@NotNull
private Long eventId;
private List<PersonalInfoOptionDTO> personalInfoOptionList;
private List<QuestionDTO> questionList;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class QuestionDTO {
@NotBlank(message = "질문 유형: SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))")
private QuestionType questionType;
@NotBlank(message = "질문 내용을 입력해주세요")
private String questionText;
private List<String> options; // 객관식 질문일 경우 선택지 리스트
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class PersonalInfoOptionDTO {
@NotBlank(message = "개인정보 수집 항목을 입력해주세요")
private String optionName;
}
}

// 설문 답변 요청 DTO
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SurveyAnswerDTO {
@NotNull
private Long surveyId;
@Valid
private ParticipantDTO participant;
@Valid
private List<AnswerDTO> answers;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ParticipantDTO {
private String name;
private String address;
private String email;
private String phoneNumber;
private String instagramId;
@NotNull
private Boolean isAgreed;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class AnswerDTO {
@NotNull
private Long questionId;
private String answerText;
private List<AnswerOptionDTO> answerOptions;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class AnswerOptionDTO {
@NotNull
private Long questionOptionId;
}
}
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class RandomSelectionRequestDTO {
@NotNull
private Long surveyId;
@NotNull
private Long questionId;
@NotNull
private Integer winnerCount;
@NotNull
private LocalDateTime startDate;
@NotNull
private LocalDateTime endDate;
@NotNull
private Integer minLength;
@NotNull
private List<String> keywords;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.cmc.suppin.event.survey.controller.dto;

import com.cmc.suppin.global.enums.QuestionType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

public class SurveyResponseDTO {

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SurveyCreateResponse {
private Long surveyId;
private String uuid;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SurveyResultDTO {
private Long eventId;
private String eventTitle;
private String eventDescription;
private String startDate;
private String endDate;
private String announcementDate;
private List<PersonalInfoOptionDTO> personalInfoOptions;
private List<QuestionDTO> questions;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class QuestionDTO {
private QuestionType questionType;
private String questionText;
private List<String> options;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class PersonalInfoOptionDTO {
private String optionName;
}
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SurveyAnswerResultDTO {
private Long questionId;
private String questionText;
private List<AnswerDTO> answers;
private int totalPages;
private long totalElements;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class AnswerDTO {
private String participantName;
private String answerText;
private List<String> selectedOptions;
}
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class RandomSelectionResponseDTO {
private SelectionCriteriaDTO selectionCriteria;
private List<WinnerDTO> winners;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class WinnerDTO {
private Long participantId;
private String participantName;
private String answerText;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SelectionCriteriaDTO {
private Integer winnerCount;
private LocalDateTime startDate;
private LocalDateTime endDate;
private Integer minLength;
private List<String> keywords;
}
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class WinnerDetailDTO {
private String name;
private String phoneNumber;
private String address;
private String email;
private String instagramId;
private List<AnswerDetailDTO> answers;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class AnswerDetailDTO {
private String questionText;
private String answerText;
private List<String> selectedOptions; // 객관식 질문의 경우 선택된 옵션 리스트
}
}
}
Empty file.
Loading
Loading