From ffe33953ceff692bcef9c86bad73f49159dadd39 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Thu, 8 Aug 2024 08:40:03 +0900 Subject: [PATCH 01/20] =?UTF-8?q?Feat:=20Survey=20=EA=B4=80=EB=A0=A8=20API?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1(.gitkeep=20=EC=A0=9C=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../suppin/event/survey/controller/.gitkeep | 0 .../event/survey/controller/SurveyApi.java | 19 +++++++++++++++++++ .../controller/dto/SurveyRequestDTO.java | 4 ++++ .../controller/dto/SurveyResponseDTO.java | 4 ++++ .../suppin/event/survey/converter/.gitkeep | 0 .../survey/converter/SurveyConverter.java | 4 ++++ .../suppin/event/survey/exception/.gitkeep | 0 .../survey/exception/SurveyException.java | 13 +++++++++++++ .../cmc/suppin/event/survey/service/.gitkeep | 0 .../event/survey/service/SurveyService.java | 13 +++++++++++++ 10 files changed, 57 insertions(+) delete mode 100644 src/main/java/com/cmc/suppin/event/survey/controller/.gitkeep create mode 100644 src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java create mode 100644 src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java create mode 100644 src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java delete mode 100644 src/main/java/com/cmc/suppin/event/survey/converter/.gitkeep create mode 100644 src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java delete mode 100644 src/main/java/com/cmc/suppin/event/survey/exception/.gitkeep create mode 100644 src/main/java/com/cmc/suppin/event/survey/exception/SurveyException.java delete mode 100644 src/main/java/com/cmc/suppin/event/survey/service/.gitkeep create mode 100644 src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/.gitkeep b/src/main/java/com/cmc/suppin/event/survey/controller/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java new file mode 100644 index 0000000..94f5329 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -0,0 +1,19 @@ +package com.cmc.suppin.event.survey.controller; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +@RequiredArgsConstructor +@Validated +@Tag(name = "Event-Survey", description = "Survey 관련 API") +@RequestMapping("/api/v1/survey") +public class SurveyApi { + + +} diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java new file mode 100644 index 0000000..af21687 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java @@ -0,0 +1,4 @@ +package com.cmc.suppin.event.survey.controller.dto; + +public class SurveyRequestDTO { +} diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java new file mode 100644 index 0000000..8dc255b --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -0,0 +1,4 @@ +package com.cmc.suppin.event.survey.controller.dto; + +public class SurveyResponseDTO { +} diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/.gitkeep b/src/main/java/com/cmc/suppin/event/survey/converter/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java new file mode 100644 index 0000000..898ca5c --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -0,0 +1,4 @@ +package com.cmc.suppin.event.survey.converter; + +public class SurveyConverter { +} diff --git a/src/main/java/com/cmc/suppin/event/survey/exception/.gitkeep b/src/main/java/com/cmc/suppin/event/survey/exception/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/event/survey/exception/SurveyException.java b/src/main/java/com/cmc/suppin/event/survey/exception/SurveyException.java new file mode 100644 index 0000000..5cc6304 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/exception/SurveyException.java @@ -0,0 +1,13 @@ +package com.cmc.suppin.event.survey.exception; + +import com.cmc.suppin.global.exception.BaseErrorCode; +import com.cmc.suppin.global.exception.CustomException; +import lombok.Getter; + +@Getter +public class SurveyException extends CustomException { + + public SurveyException(BaseErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/com/cmc/suppin/event/survey/service/.gitkeep b/src/main/java/com/cmc/suppin/event/survey/service/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java new file mode 100644 index 0000000..96be10f --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -0,0 +1,13 @@ +package com.cmc.suppin.event.survey.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Slf4j +@RequiredArgsConstructor +@Transactional +public class SurveyService { +} From 081aee7078144f0736545c3d307fa9b91c49b982 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Thu, 8 Aug 2024 22:57:50 +0900 Subject: [PATCH 02/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../answer/domain/AnonymousParticipant.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java b/src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java index 111109d..a3c6fb1 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java +++ b/src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java @@ -25,12 +25,23 @@ public class AnonymousParticipant extends BaseDateTimeEntity { @JoinColumn(name = "survey_id") private Survey survey; - @Column(columnDefinition = "VARCHAR(13)", nullable = false) + @OneToMany(mappedBy = "anonymousParticipant") + private List answerList = new ArrayList<>(); + + private String name; + + private String address; + + private String email; + + @Column(columnDefinition = "VARCHAR(20)", nullable = false) private String phoneNumber; @Column(nullable = false) private Boolean isAgreed; - @OneToMany(mappedBy = "anonymousParticipant") - private List answerList = new ArrayList<>(); + private Boolean isWinner; + + private Boolean isChecked; + } From 9aadd97c57fbee4586eea4410c08b57dcddb376d Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Thu, 8 Aug 2024 22:58:16 +0900 Subject: [PATCH 03/20] =?UTF-8?q?Fix:=20=EC=A7=88=EB=AC=B8=20=EC=9C=A0?= =?UTF-8?q?=ED=98=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cmc/suppin/global/enums/QuestionType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cmc/suppin/global/enums/QuestionType.java b/src/main/java/com/cmc/suppin/global/enums/QuestionType.java index e22b96d..8e768c6 100644 --- a/src/main/java/com/cmc/suppin/global/enums/QuestionType.java +++ b/src/main/java/com/cmc/suppin/global/enums/QuestionType.java @@ -1,7 +1,7 @@ package com.cmc.suppin.global.enums; public enum QuestionType { - SHORT_ANSWER("주관식"), + SUBJECTIVE("주관식"), SINGLE_CHOICE("객관식(단일 선택)"), MULTIPLE_CHOICE("객관식(복수 선택)"); From 875163dca12e1eea29363efe4ef6c07c3879bb96 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Thu, 8 Aug 2024 22:58:52 +0900 Subject: [PATCH 04/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EA=B3=B5?= =?UTF-8?q?=EC=9C=A0=EC=9A=A9=20URL=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cmc/suppin/event/survey/domain/Survey.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java index 39053a5..e47e962 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java @@ -26,11 +26,16 @@ public class Survey extends BaseDateTimeEntity { @JoinColumn(name = "event_id") private Event event; + @OneToMany(mappedBy = "survey") private List questionList = new ArrayList<>(); @OneToMany(mappedBy = "survey") private List anonymousParticipantList = new ArrayList<>(); + @Column(columnDefinition = "TEXT") + private String url; + + private String uuid; } From 2ee774fca793bca4cfe08683e8881c50fc02f999 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Thu, 8 Aug 2024 23:07:23 +0900 Subject: [PATCH 05/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnonymousParticipantRepository.java | 7 +++ .../repository/AnswerOptionRepository.java | 7 +++ .../domain/repository/AnswerRepository.java | 7 +++ .../event/survey/controller/SurveyApi.java | 20 +++++++- .../controller/dto/SurveyRequestDTO.java | 34 ++++++++++++++ .../survey/converter/SurveyConverter.java | 45 ++++++++++++++++++ .../repository/QuestionOptionRepository.java | 8 ++++ .../domain/repository/QuestionRepository.java | 7 +++ .../domain/repository/SurveyRepository.java | 9 ++++ .../event/survey/service/SurveyService.java | 47 +++++++++++++++++++ 10 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java create mode 100644 src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java create mode 100644 src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java create mode 100644 src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionOptionRepository.java create mode 100644 src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java create mode 100644 src/main/java/com/cmc/suppin/event/survey/domain/repository/SurveyRepository.java diff --git a/src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java b/src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java new file mode 100644 index 0000000..b2d6c50 --- /dev/null +++ b/src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java @@ -0,0 +1,7 @@ +package com.cmc.suppin.answer.domain.repository; + +import com.cmc.suppin.answer.domain.AnonymousParticipant; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AnonymousParticipantRepository extends JpaRepository { +} diff --git a/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java b/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java new file mode 100644 index 0000000..1563822 --- /dev/null +++ b/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java @@ -0,0 +1,7 @@ +package com.cmc.suppin.answer.domain.repository; + +import com.cmc.suppin.answer.domain.AnswerOption; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AnswerOptionRepository extends JpaRepository { +} diff --git a/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java b/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java new file mode 100644 index 0000000..e469756 --- /dev/null +++ b/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java @@ -0,0 +1,7 @@ +package com.cmc.suppin.answer.domain.repository; + +import com.cmc.suppin.answer.domain.Answer; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AnswerRepository extends JpaRepository { +} diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 94f5329..fc4ecd9 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -1,9 +1,20 @@ package com.cmc.suppin.event.survey.controller; +import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -15,5 +26,12 @@ @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> createSurvey(@RequestBody @Valid SurveyRequestDTO.SurveyCreateDTO request, @CurrentAccount Account account) { + surveyService.createSurvey(request, account.userId()); + return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java index af21687..c561880 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java @@ -1,4 +1,38 @@ package com.cmc.suppin.event.survey.controller.dto; +import com.cmc.suppin.global.enums.QuestionType; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + public class SurveyRequestDTO { + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class SurveyCreateDTO { + private Long eventId; + private String name; + private String address; + private String email; + private String phoneNumber; + private Boolean isAgreed; + private List questions; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class QuestionDTO { + @NotEmpty(message = "질문 유형: SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))") + private QuestionType questionType; + private String questionText; + private List options; // 객관식 질문일 경우 선택지 리스트 + } + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index 898ca5c..db20043 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -1,4 +1,49 @@ package com.cmc.suppin.event.survey.converter; +import com.cmc.suppin.answer.domain.AnonymousParticipant; +import com.cmc.suppin.event.events.domain.Event; +import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; +import com.cmc.suppin.event.survey.domain.Question; +import com.cmc.suppin.event.survey.domain.QuestionOption; +import com.cmc.suppin.event.survey.domain.Survey; +import com.cmc.suppin.global.enums.QuestionType; + +import java.util.List; +import java.util.stream.Collectors; + public class SurveyConverter { + + public static Survey toSurveyEntity(Event event) { + return Survey.builder() + .event(event) + .build(); + } + + public static Question toQuestionEntity(SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO, Survey survey) { + return Question.builder() + .survey(survey) + .questionType(QuestionType.valueOf(questionDTO.getQuestionType().name())) + .questionText(questionDTO.getQuestionText()) + .build(); + } + + public static List toQuestionOptionEntities(List options, Question question) { + return options.stream() + .map(optionText -> QuestionOption.builder() + .question(question) + .optionText(optionText) + .build()) + .collect(Collectors.toList()); + } + + public static AnonymousParticipant toAnonymousParticipantEntity(SurveyRequestDTO.SurveyCreateDTO request, Survey survey) { + return AnonymousParticipant.builder() + .survey(survey) + .name(request.getName()) + .address(request.getAddress()) + .email(request.getEmail()) + .phoneNumber(request.getPhoneNumber()) + .isAgreed(request.getIsAgreed()) + .build(); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionOptionRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionOptionRepository.java new file mode 100644 index 0000000..687e618 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionOptionRepository.java @@ -0,0 +1,8 @@ +package com.cmc.suppin.event.survey.domain.repository; + +import com.cmc.suppin.event.survey.domain.QuestionOption; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QuestionOptionRepository extends JpaRepository { + +} diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java new file mode 100644 index 0000000..a09fc44 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java @@ -0,0 +1,7 @@ +package com.cmc.suppin.event.survey.domain.repository; + +import com.cmc.suppin.event.survey.domain.Question; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QuestionRepository extends JpaRepository { +} diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/SurveyRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/SurveyRepository.java new file mode 100644 index 0000000..afb0fc2 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/SurveyRepository.java @@ -0,0 +1,9 @@ +package com.cmc.suppin.event.survey.domain.repository; + +import com.cmc.suppin.event.survey.domain.Survey; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SurveyRepository extends JpaRepository { + + +} diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 96be10f..e3fb8ac 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -1,13 +1,60 @@ package com.cmc.suppin.event.survey.service; +import com.cmc.suppin.answer.domain.AnonymousParticipant; +import com.cmc.suppin.answer.domain.repository.AnonymousParticipantRepository; +import com.cmc.suppin.event.events.domain.Event; +import com.cmc.suppin.event.events.domain.repository.EventRepository; +import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; +import com.cmc.suppin.event.survey.converter.SurveyConverter; +import com.cmc.suppin.event.survey.domain.Question; +import com.cmc.suppin.event.survey.domain.QuestionOption; +import com.cmc.suppin.event.survey.domain.Survey; +import com.cmc.suppin.event.survey.domain.repository.QuestionOptionRepository; +import com.cmc.suppin.event.survey.domain.repository.QuestionRepository; +import com.cmc.suppin.event.survey.domain.repository.SurveyRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Slf4j @RequiredArgsConstructor @Transactional public class SurveyService { + + private final SurveyRepository surveyRepository; + private final QuestionRepository questionRepository; + private final QuestionOptionRepository questionOptionRepository; + private final EventRepository eventRepository; + private final AnonymousParticipantRepository anonymousParticipantRepository; + + @Transactional + public void createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { + // 이벤트 식별 + Event event = eventRepository.findById(request.getEventId()) + .orElseThrow(() -> new IllegalArgumentException("Event not found")); + + // Survey 엔티티 생성 및 저장 + Survey survey = SurveyConverter.toSurveyEntity(event); + surveyRepository.save(survey); + + // 각 질문 처리 및 저장 + for (SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO : request.getQuestions()) { + Question question = SurveyConverter.toQuestionEntity(questionDTO, survey); + questionRepository.save(question); + + // 객관식 복수 선택 질문인 경우 처리 및 저장 + if (questionDTO.getOptions() != null && !questionDTO.getOptions().isEmpty()) { + List options = SurveyConverter.toQuestionOptionEntities(questionDTO.getOptions(), question); + questionOptionRepository.saveAll(options); + } + } + + // 익명 설문 참여자 정보 저장 + AnonymousParticipant participant = SurveyConverter.toAnonymousParticipantEntity(request, survey); + anonymousParticipantRepository.save(participant); + } } From fcf801a95f542336f8ac4488d76277dc3559885e Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Thu, 8 Aug 2024 23:12:43 +0900 Subject: [PATCH 06/20] =?UTF-8?q?Refactor:=20SurveyRequestDTO=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/dto/SurveyRequestDTO.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java index c561880..bdb73f5 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java @@ -1,7 +1,7 @@ package com.cmc.suppin.event.survey.controller.dto; import com.cmc.suppin.global.enums.QuestionType; -import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -16,11 +16,15 @@ public class SurveyRequestDTO { @AllArgsConstructor @Builder public static class SurveyCreateDTO { + @NotBlank private Long eventId; + private String name; private String address; private String email; private String phoneNumber; + + @NotBlank private Boolean isAgreed; private List questions; @@ -29,8 +33,9 @@ public static class SurveyCreateDTO { @AllArgsConstructor @Builder public static class QuestionDTO { - @NotEmpty(message = "질문 유형: SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))") + @NotBlank(message = "질문 유형: SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))") private QuestionType questionType; + @NotBlank private String questionText; private List options; // 객관식 질문일 경우 선택지 리스트 } From 5702d6442bda935ea8c8c2d53ec79292898776b7 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 01:51:12 +0900 Subject: [PATCH 07/20] =?UTF-8?q?Feat:=20=EA=B0=9C=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=88=98=EC=A7=91=20=EA=B4=80=EB=A0=A8=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/PersonalInfoCollectOption.java | 24 +++++++++++++++++++ .../suppin/event/survey/domain/Survey.java | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/com/cmc/suppin/event/survey/domain/PersonalInfoCollectOption.java diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/PersonalInfoCollectOption.java b/src/main/java/com/cmc/suppin/event/survey/domain/PersonalInfoCollectOption.java new file mode 100644 index 0000000..d13918d --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/domain/PersonalInfoCollectOption.java @@ -0,0 +1,24 @@ +package com.cmc.suppin.event.survey.domain; + +import com.cmc.suppin.global.domain.BaseDateTimeEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class PersonalInfoCollectOption extends BaseDateTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "personal_info_id") + private Long id; + + @ManyToOne + @JoinColumn(name = "survey_id") + private Survey survey; + + @Column(nullable = false) + private String optionName; +} diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java index e47e962..a8d11ec 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java @@ -26,6 +26,8 @@ public class Survey extends BaseDateTimeEntity { @JoinColumn(name = "event_id") private Event event; + @OneToMany(mappedBy = "survey", cascade = CascadeType.ALL, orphanRemoval = true) + private List personalInfoList = new ArrayList<>(); @OneToMany(mappedBy = "survey") private List questionList = new ArrayList<>(); From 2cf7fefa845db24747c3273ad468ae9007986a91 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 01:51:38 +0900 Subject: [PATCH 08/20] =?UTF-8?q?Refactor:=20=EC=84=A4=EB=AC=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20API=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/SurveyRequestDTO.java | 25 +++++++------- .../survey/converter/SurveyConverter.java | 25 +++++++------- .../PersonalInfoCollectOptionRepository.java | 7 ++++ .../event/survey/service/SurveyService.java | 33 +++++++++++++------ 4 files changed, 55 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/cmc/suppin/event/survey/domain/repository/PersonalInfoCollectOptionRepository.java diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java index bdb73f5..ccb2b48 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java @@ -2,6 +2,7 @@ import com.cmc.suppin.global.enums.QuestionType; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -16,17 +17,10 @@ public class SurveyRequestDTO { @AllArgsConstructor @Builder public static class SurveyCreateDTO { - @NotBlank + @NotNull private Long eventId; - - private String name; - private String address; - private String email; - private String phoneNumber; - - @NotBlank - private Boolean isAgreed; - private List questions; + private List personalInfoOptionList; + private List questionList; @Getter @NoArgsConstructor @@ -35,9 +29,18 @@ public static class SurveyCreateDTO { public static class QuestionDTO { @NotBlank(message = "질문 유형: SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))") private QuestionType questionType; - @NotBlank + @NotBlank(message = "질문 내용을 입력해주세요") private String questionText; private List options; // 객관식 질문일 경우 선택지 리스트 } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class PersonalInfoOptionDTO { + @NotBlank(message = "개인정보 수집 항목을 입력해주세요") + private String optionName; + } } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index db20043..d4bf56a 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -1,12 +1,11 @@ package com.cmc.suppin.event.survey.converter; -import com.cmc.suppin.answer.domain.AnonymousParticipant; import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; +import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; import com.cmc.suppin.event.survey.domain.Question; import com.cmc.suppin.event.survey.domain.QuestionOption; import com.cmc.suppin.event.survey.domain.Survey; -import com.cmc.suppin.global.enums.QuestionType; import java.util.List; import java.util.stream.Collectors; @@ -22,28 +21,26 @@ public static Survey toSurveyEntity(Event event) { public static Question toQuestionEntity(SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO, Survey survey) { return Question.builder() .survey(survey) - .questionType(QuestionType.valueOf(questionDTO.getQuestionType().name())) + .questionType(questionDTO.getQuestionType()) .questionText(questionDTO.getQuestionText()) .build(); } public static List toQuestionOptionEntities(List options, Question question) { return options.stream() - .map(optionText -> QuestionOption.builder() + .map(option -> QuestionOption.builder() + .optionText(option) .question(question) - .optionText(optionText) .build()) .collect(Collectors.toList()); } - public static AnonymousParticipant toAnonymousParticipantEntity(SurveyRequestDTO.SurveyCreateDTO request, Survey survey) { - return AnonymousParticipant.builder() - .survey(survey) - .name(request.getName()) - .address(request.getAddress()) - .email(request.getEmail()) - .phoneNumber(request.getPhoneNumber()) - .isAgreed(request.getIsAgreed()) - .build(); + public static List toPersonalInfoCollectOptionEntities(List personalInfoOptions, Survey survey) { + return personalInfoOptions.stream() + .map(option -> PersonalInfoCollectOption.builder() + .optionName(option) + .survey(survey) + .build()) + .collect(Collectors.toList()); } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/PersonalInfoCollectOptionRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/PersonalInfoCollectOptionRepository.java new file mode 100644 index 0000000..de3f235 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/PersonalInfoCollectOptionRepository.java @@ -0,0 +1,7 @@ +package com.cmc.suppin.event.survey.domain.repository; + +import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PersonalInfoCollectOptionRepository extends JpaRepository { +} diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index e3fb8ac..f9bbc4e 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -1,23 +1,27 @@ package com.cmc.suppin.event.survey.service; -import com.cmc.suppin.answer.domain.AnonymousParticipant; -import com.cmc.suppin.answer.domain.repository.AnonymousParticipantRepository; import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.events.domain.repository.EventRepository; import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; import com.cmc.suppin.event.survey.converter.SurveyConverter; +import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; import com.cmc.suppin.event.survey.domain.Question; import com.cmc.suppin.event.survey.domain.QuestionOption; import com.cmc.suppin.event.survey.domain.Survey; +import com.cmc.suppin.event.survey.domain.repository.PersonalInfoCollectOptionRepository; import com.cmc.suppin.event.survey.domain.repository.QuestionOptionRepository; import com.cmc.suppin.event.survey.domain.repository.QuestionRepository; import com.cmc.suppin.event.survey.domain.repository.SurveyRepository; +import com.cmc.suppin.global.enums.UserStatus; +import com.cmc.suppin.member.domain.Member; +import com.cmc.suppin.member.domain.repository.MemberRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.stream.Collectors; @Service @Slf4j @@ -25,24 +29,37 @@ @Transactional public class SurveyService { + private final MemberRepository memberRepository; private final SurveyRepository surveyRepository; private final QuestionRepository questionRepository; private final QuestionOptionRepository questionOptionRepository; + private final PersonalInfoCollectOptionRepository personalInfoCollectOptionRepository; private final EventRepository eventRepository; - private final AnonymousParticipantRepository anonymousParticipantRepository; @Transactional public void createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { + // 사용자 식별 + Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + // 이벤트 식별 - Event event = eventRepository.findById(request.getEventId()) - .orElseThrow(() -> new IllegalArgumentException("Event not found")); + Event event = eventRepository.findByIdAndMemberId(request.getEventId(), member.getId()) + .orElseThrow(() -> new IllegalArgumentException("Event not found or does not belong to the user")); + // Survey 엔티티 생성 및 저장 Survey survey = SurveyConverter.toSurveyEntity(event); surveyRepository.save(survey); + // 각 개인정보 항목 처리 및 저장 + if (request.getPersonalInfoOptionList() != null && !request.getPersonalInfoOptionList().isEmpty()) { + List personalInfoOptions = SurveyConverter.toPersonalInfoCollectOptionEntities( + request.getPersonalInfoOptionList().stream().map(SurveyRequestDTO.SurveyCreateDTO.PersonalInfoOptionDTO::getOptionName).collect(Collectors.toList()), survey); + personalInfoCollectOptionRepository.saveAll(personalInfoOptions); + } + // 각 질문 처리 및 저장 - for (SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO : request.getQuestions()) { + for (SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO : request.getQuestionList()) { Question question = SurveyConverter.toQuestionEntity(questionDTO, survey); questionRepository.save(question); @@ -52,9 +69,5 @@ public void createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId questionOptionRepository.saveAll(options); } } - - // 익명 설문 참여자 정보 저장 - AnonymousParticipant participant = SurveyConverter.toAnonymousParticipantEntity(request, survey); - anonymousParticipantRepository.save(participant); } } From 522c4911e585df2dd2bcccac8df63045a58e99ff Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 02:41:58 +0900 Subject: [PATCH 09/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EB=AF=B8?= =?UTF-8?q?=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 21 ++++++---- .../controller/dto/SurveyResponseDTO.java | 41 +++++++++++++++++++ .../survey/converter/SurveyConverter.java | 30 ++++++++++++++ .../event/survey/service/SurveyService.java | 21 +++++++--- 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index fc4ecd9..081cd99 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -1,9 +1,9 @@ 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; @@ -13,10 +13,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @Slf4j @@ -30,8 +27,16 @@ public class SurveyApi { @PostMapping("/create") @Operation(summary = "설문지 생성 API", description = "QuestionType(Enum): SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))") - public ResponseEntity> createSurvey(@RequestBody @Valid SurveyRequestDTO.SurveyCreateDTO request, @CurrentAccount Account account) { - surveyService.createSurvey(request, account.userId()); - return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); + public ResponseEntity> createSurvey(@RequestBody @Valid SurveyRequestDTO.SurveyCreateDTO request, @CurrentAccount Account account) { + Long surveyId = surveyService.createSurvey(request, account.userId()); + return ResponseEntity.ok(ApiResponse.of(surveyId)); + } + + @GetMapping("/{surveyId}") + @Operation(summary = "설문지 조회 API", description = "Request: 설문지 ID, Response: 설문지 정보

" + + "SUBJEVTIVE: 주관식, SINGLE_CHOICE: 객관식(단일 선택), MULTIPLE_CHOICE: 객관식(복수 선택)") + public ResponseEntity> getSurvey(@PathVariable Long surveyId) { + SurveyResponseDTO.SurveyResultDTO response = surveyService.getSurvey(surveyId); + return ResponseEntity.ok(ApiResponse.of(response)); } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java index 8dc255b..f633cf6 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -1,4 +1,45 @@ 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.util.List; + public class SurveyResponseDTO { + + @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 personalInfoOptions; + private List questions; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class QuestionDTO { + private QuestionType questionType; + private String questionText; + private List options; + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class PersonalInfoOptionDTO { + private String optionName; + } + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index d4bf56a..af3ea07 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -2,6 +2,7 @@ import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; +import com.cmc.suppin.event.survey.controller.dto.SurveyResponseDTO; import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; import com.cmc.suppin.event.survey.domain.Question; import com.cmc.suppin.event.survey.domain.QuestionOption; @@ -43,4 +44,33 @@ public static List toPersonalInfoCollectOptionEntitie .build()) .collect(Collectors.toList()); } + + public static SurveyResponseDTO.SurveyResultDTO toSurveyResultDTO(Survey survey, Event event) { + List personalInfoOptions = survey.getPersonalInfoList().stream() + .map(option -> SurveyResponseDTO.SurveyResultDTO.PersonalInfoOptionDTO.builder() + .optionName(option.getOptionName()) + .build()) + .collect(Collectors.toList()); + + List questions = survey.getQuestionList().stream() + .map(question -> SurveyResponseDTO.SurveyResultDTO.QuestionDTO.builder() + .questionType(question.getQuestionType()) + .questionText(question.getQuestionText()) + .options(question.getQuestionOptionList().stream() + .map(QuestionOption::getOptionText) + .collect(Collectors.toList())) + .build()) + .collect(Collectors.toList()); + + return SurveyResponseDTO.SurveyResultDTO.builder() + .eventId(event.getId()) + .eventTitle(event.getTitle()) + .eventDescription(event.getDescription()) + .startDate(event.getStartDate().toString()) + .endDate(event.getEndDate().toString()) + .announcementDate(event.getAnnouncementDate().toString()) + .personalInfoOptions(personalInfoOptions) + .questions(questions) + .build(); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index f9bbc4e..8447b49 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -3,6 +3,7 @@ import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.events.domain.repository.EventRepository; import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; +import com.cmc.suppin.event.survey.controller.dto.SurveyResponseDTO; import com.cmc.suppin.event.survey.converter.SurveyConverter; import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; import com.cmc.suppin.event.survey.domain.Question; @@ -37,7 +38,7 @@ public class SurveyService { private final EventRepository eventRepository; @Transactional - public void createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { + public Long createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { // 사용자 식별 Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); @@ -46,21 +47,20 @@ public void createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId Event event = eventRepository.findByIdAndMemberId(request.getEventId(), member.getId()) .orElseThrow(() -> new IllegalArgumentException("Event not found or does not belong to the user")); - // Survey 엔티티 생성 및 저장 Survey survey = SurveyConverter.toSurveyEntity(event); - surveyRepository.save(survey); + Survey savedSurvey = surveyRepository.save(survey); // 각 개인정보 항목 처리 및 저장 if (request.getPersonalInfoOptionList() != null && !request.getPersonalInfoOptionList().isEmpty()) { List personalInfoOptions = SurveyConverter.toPersonalInfoCollectOptionEntities( - request.getPersonalInfoOptionList().stream().map(SurveyRequestDTO.SurveyCreateDTO.PersonalInfoOptionDTO::getOptionName).collect(Collectors.toList()), survey); + request.getPersonalInfoOptionList().stream().map(SurveyRequestDTO.SurveyCreateDTO.PersonalInfoOptionDTO::getOptionName).collect(Collectors.toList()), savedSurvey); personalInfoCollectOptionRepository.saveAll(personalInfoOptions); } // 각 질문 처리 및 저장 for (SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO : request.getQuestionList()) { - Question question = SurveyConverter.toQuestionEntity(questionDTO, survey); + Question question = SurveyConverter.toQuestionEntity(questionDTO, savedSurvey); questionRepository.save(question); // 객관식 복수 선택 질문인 경우 처리 및 저장 @@ -69,5 +69,16 @@ public void createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId questionOptionRepository.saveAll(options); } } + + return savedSurvey.getId(); // 저장된 설문의 ID 반환 + } + + @Transactional(readOnly = true) + public SurveyResponseDTO.SurveyResultDTO getSurvey(Long surveyId) { + Survey survey = surveyRepository.findById(surveyId) + .orElseThrow(() -> new IllegalArgumentException("Survey not found")); + + Event event = survey.getEvent(); + return SurveyConverter.toSurveyResultDTO(survey, event); } } From 0650e103cf18b2a25a4cca398e33e347731a60a7 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 02:51:55 +0900 Subject: [PATCH 10/20] =?UTF-8?q?Refactor:=20URL=20=EA=B3=B5=EC=9C=A0?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20survey=20uuid=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 6 +++--- .../controller/dto/SurveyResponseDTO.java | 9 +++++++++ .../event/survey/converter/SurveyConverter.java | 3 ++- .../cmc/suppin/event/survey/domain/Survey.java | 1 + .../event/survey/service/SurveyService.java | 17 +++++++++++------ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 081cd99..447147b 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -27,9 +27,9 @@ public class SurveyApi { @PostMapping("/create") @Operation(summary = "설문지 생성 API", description = "QuestionType(Enum): SUBJECTIVE(주관식), SINGLE_CHOICE(객관식(단일 선택)), MULTIPLE_CHOICE(객관식(복수 선택))") - public ResponseEntity> createSurvey(@RequestBody @Valid SurveyRequestDTO.SurveyCreateDTO request, @CurrentAccount Account account) { - Long surveyId = surveyService.createSurvey(request, account.userId()); - return ResponseEntity.ok(ApiResponse.of(surveyId)); + public ResponseEntity> 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}") diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java index f633cf6..9e90d45 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -10,6 +10,15 @@ public class SurveyResponseDTO { + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class SurveyCreateResponse { + private Long surveyId; + private String uuid; + } + @Getter @NoArgsConstructor @AllArgsConstructor diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index af3ea07..702d35c 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -13,9 +13,10 @@ public class SurveyConverter { - public static Survey toSurveyEntity(Event event) { + public static Survey toSurveyEntity(Event event, String uuid) { return Survey.builder() .event(event) + .uuid(uuid) .build(); } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java index a8d11ec..9854eb5 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java @@ -38,6 +38,7 @@ public class Survey extends BaseDateTimeEntity { @Column(columnDefinition = "TEXT") private String url; + @Column(nullable = false, updatable = false, unique = true) private String uuid; } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 8447b49..88293da 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -22,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; @Service @@ -38,7 +39,7 @@ public class SurveyService { private final EventRepository eventRepository; @Transactional - public Long createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { + public SurveyResponseDTO.SurveyCreateResponse createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { // 사용자 식별 Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); @@ -48,19 +49,20 @@ public Long createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId .orElseThrow(() -> new IllegalArgumentException("Event not found or does not belong to the user")); // Survey 엔티티 생성 및 저장 - Survey survey = SurveyConverter.toSurveyEntity(event); - Survey savedSurvey = surveyRepository.save(survey); + String uuid = UUID.randomUUID().toString(); + Survey survey = SurveyConverter.toSurveyEntity(event, uuid); + surveyRepository.save(survey); // 각 개인정보 항목 처리 및 저장 if (request.getPersonalInfoOptionList() != null && !request.getPersonalInfoOptionList().isEmpty()) { List personalInfoOptions = SurveyConverter.toPersonalInfoCollectOptionEntities( - request.getPersonalInfoOptionList().stream().map(SurveyRequestDTO.SurveyCreateDTO.PersonalInfoOptionDTO::getOptionName).collect(Collectors.toList()), savedSurvey); + request.getPersonalInfoOptionList().stream().map(SurveyRequestDTO.SurveyCreateDTO.PersonalInfoOptionDTO::getOptionName).collect(Collectors.toList()), survey); personalInfoCollectOptionRepository.saveAll(personalInfoOptions); } // 각 질문 처리 및 저장 for (SurveyRequestDTO.SurveyCreateDTO.QuestionDTO questionDTO : request.getQuestionList()) { - Question question = SurveyConverter.toQuestionEntity(questionDTO, savedSurvey); + Question question = SurveyConverter.toQuestionEntity(questionDTO, survey); questionRepository.save(question); // 객관식 복수 선택 질문인 경우 처리 및 저장 @@ -70,7 +72,10 @@ public Long createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId } } - return savedSurvey.getId(); // 저장된 설문의 ID 반환 + return SurveyResponseDTO.SurveyCreateResponse.builder() + .surveyId(survey.getId()) + .uuid(survey.getUuid()) + .build(); } @Transactional(readOnly = true) From c983b2a444d3242fa23a4006103a8fff4c85d3c6 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 03:23:14 +0900 Subject: [PATCH 11/20] =?UTF-8?q?Chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cmc/suppin/answer/controller/.gitkeep | 0 src/main/java/com/cmc/suppin/answer/converter/.gitkeep | 0 src/main/java/com/cmc/suppin/answer/exception/.gitkeep | 0 src/main/java/com/cmc/suppin/answer/service/.gitkeep | 0 .../survey}/domain/AnonymousParticipant.java | 5 ++--- .../cmc/suppin/{answer => event/survey}/domain/Answer.java | 3 +-- .../suppin/{answer => event/survey}/domain/AnswerOption.java | 3 +-- .../java/com/cmc/suppin/event/survey/domain/Question.java | 1 - .../com/cmc/suppin/event/survey/domain/QuestionOption.java | 1 - src/main/java/com/cmc/suppin/event/survey/domain/Survey.java | 1 - .../domain/repository/AnonymousParticipantRepository.java | 4 ++-- .../survey}/domain/repository/AnswerOptionRepository.java | 4 ++-- .../survey}/domain/repository/AnswerRepository.java | 4 ++-- 13 files changed, 10 insertions(+), 16 deletions(-) delete mode 100644 src/main/java/com/cmc/suppin/answer/controller/.gitkeep delete mode 100644 src/main/java/com/cmc/suppin/answer/converter/.gitkeep delete mode 100644 src/main/java/com/cmc/suppin/answer/exception/.gitkeep delete mode 100644 src/main/java/com/cmc/suppin/answer/service/.gitkeep rename src/main/java/com/cmc/suppin/{answer => event/survey}/domain/AnonymousParticipant.java (86%) rename src/main/java/com/cmc/suppin/{answer => event/survey}/domain/Answer.java (90%) rename src/main/java/com/cmc/suppin/{answer => event/survey}/domain/AnswerOption.java (85%) rename src/main/java/com/cmc/suppin/{answer => event/survey}/domain/repository/AnonymousParticipantRepository.java (58%) rename src/main/java/com/cmc/suppin/{answer => event/survey}/domain/repository/AnswerOptionRepository.java (57%) rename src/main/java/com/cmc/suppin/{answer => event/survey}/domain/repository/AnswerRepository.java (56%) diff --git a/src/main/java/com/cmc/suppin/answer/controller/.gitkeep b/src/main/java/com/cmc/suppin/answer/controller/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/answer/converter/.gitkeep b/src/main/java/com/cmc/suppin/answer/converter/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/answer/exception/.gitkeep b/src/main/java/com/cmc/suppin/answer/exception/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/answer/service/.gitkeep b/src/main/java/com/cmc/suppin/answer/service/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java b/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java similarity index 86% rename from src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java rename to src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java index a3c6fb1..6ec84c0 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/AnonymousParticipant.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java @@ -1,6 +1,5 @@ -package com.cmc.suppin.answer.domain; +package com.cmc.suppin.event.survey.domain; -import com.cmc.suppin.event.survey.domain.Survey; import com.cmc.suppin.global.domain.BaseDateTimeEntity; import jakarta.persistence.*; import lombok.*; @@ -34,7 +33,7 @@ public class AnonymousParticipant extends BaseDateTimeEntity { private String email; - @Column(columnDefinition = "VARCHAR(20)", nullable = false) + @Column(columnDefinition = "VARCHAR(20)") private String phoneNumber; @Column(nullable = false) diff --git a/src/main/java/com/cmc/suppin/answer/domain/Answer.java b/src/main/java/com/cmc/suppin/event/survey/domain/Answer.java similarity index 90% rename from src/main/java/com/cmc/suppin/answer/domain/Answer.java rename to src/main/java/com/cmc/suppin/event/survey/domain/Answer.java index c200f17..6a239aa 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/Answer.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Answer.java @@ -1,6 +1,5 @@ -package com.cmc.suppin.answer.domain; +package com.cmc.suppin.event.survey.domain; -import com.cmc.suppin.event.survey.domain.Question; import com.cmc.suppin.global.domain.BaseDateTimeEntity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/cmc/suppin/answer/domain/AnswerOption.java b/src/main/java/com/cmc/suppin/event/survey/domain/AnswerOption.java similarity index 85% rename from src/main/java/com/cmc/suppin/answer/domain/AnswerOption.java rename to src/main/java/com/cmc/suppin/event/survey/domain/AnswerOption.java index 6eac119..dda4506 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/AnswerOption.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/AnswerOption.java @@ -1,6 +1,5 @@ -package com.cmc.suppin.answer.domain; +package com.cmc.suppin.event.survey.domain; -import com.cmc.suppin.event.survey.domain.QuestionOption; import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.DynamicInsert; diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Question.java b/src/main/java/com/cmc/suppin/event/survey/domain/Question.java index 5df025f..48c48fc 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Question.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Question.java @@ -1,6 +1,5 @@ package com.cmc.suppin.event.survey.domain; -import com.cmc.suppin.answer.domain.Answer; import com.cmc.suppin.global.enums.QuestionType; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/QuestionOption.java b/src/main/java/com/cmc/suppin/event/survey/domain/QuestionOption.java index daae3eb..d21d337 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/QuestionOption.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/QuestionOption.java @@ -1,6 +1,5 @@ package com.cmc.suppin.event.survey.domain; -import com.cmc.suppin.answer.domain.AnswerOption; import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.DynamicInsert; diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java index 9854eb5..f359fb1 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Survey.java @@ -1,6 +1,5 @@ package com.cmc.suppin.event.survey.domain; -import com.cmc.suppin.answer.domain.AnonymousParticipant; import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.global.domain.BaseDateTimeEntity; import jakarta.persistence.*; diff --git a/src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java similarity index 58% rename from src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java rename to src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java index b2d6c50..274a8c8 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/repository/AnonymousParticipantRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java @@ -1,6 +1,6 @@ -package com.cmc.suppin.answer.domain.repository; +package com.cmc.suppin.event.survey.domain.repository; -import com.cmc.suppin.answer.domain.AnonymousParticipant; +import com.cmc.suppin.event.survey.domain.AnonymousParticipant; import org.springframework.data.jpa.repository.JpaRepository; public interface AnonymousParticipantRepository extends JpaRepository { diff --git a/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerOptionRepository.java similarity index 57% rename from src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java rename to src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerOptionRepository.java index 1563822..615ddd7 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerOptionRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerOptionRepository.java @@ -1,6 +1,6 @@ -package com.cmc.suppin.answer.domain.repository; +package com.cmc.suppin.event.survey.domain.repository; -import com.cmc.suppin.answer.domain.AnswerOption; +import com.cmc.suppin.event.survey.domain.AnswerOption; import org.springframework.data.jpa.repository.JpaRepository; public interface AnswerOptionRepository extends JpaRepository { diff --git a/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java similarity index 56% rename from src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java rename to src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java index e469756..4fb29be 100644 --- a/src/main/java/com/cmc/suppin/answer/domain/repository/AnswerRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java @@ -1,6 +1,6 @@ -package com.cmc.suppin.answer.domain.repository; +package com.cmc.suppin.event.survey.domain.repository; -import com.cmc.suppin.answer.domain.Answer; +import com.cmc.suppin.event.survey.domain.Answer; import org.springframework.data.jpa.repository.JpaRepository; public interface AnswerRepository extends JpaRepository { From 80599893ee6e154e8fd7946e3f6c21bd3bf87c5f Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 04:10:24 +0900 Subject: [PATCH 12/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EB=93=B1=EB=A1=9D=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 8 +++ .../controller/dto/SurveyRequestDTO.java | 50 +++++++++++++++++++ .../survey/converter/SurveyConverter.java | 31 ++++++++++-- .../survey/domain/AnonymousParticipant.java | 5 +- .../suppin/event/survey/domain/Answer.java | 2 +- .../event/survey/service/SurveyService.java | 42 +++++++++++++--- 6 files changed, 123 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 447147b..0455cd0 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -4,6 +4,7 @@ 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; @@ -39,4 +40,11 @@ public ResponseEntity> getSurvey( SurveyResponseDTO.SurveyResultDTO response = surveyService.getSurvey(surveyId); return ResponseEntity.ok(ApiResponse.of(response)); } + + @PostMapping("/reply") + @Operation(summary = "설문 답변 등록 API") + public ResponseEntity> saveSurveyAnswers(@RequestBody @Valid SurveyRequestDTO.SurveyAnswerDTO request) { + surveyService.saveSurveyAnswers(request); + return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java index ccb2b48..eec5c20 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java @@ -1,6 +1,7 @@ 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; @@ -12,6 +13,7 @@ public class SurveyRequestDTO { + // 설문 생성 요청 DTO @Getter @NoArgsConstructor @AllArgsConstructor @@ -43,4 +45,52 @@ public static class PersonalInfoOptionDTO { private String optionName; } } + + // 설문 답변 요청 DTO + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class SurveyAnswerDTO { + @NotNull + private Long surveyId; + @Valid + private ParticipantDTO participant; + @Valid + private List 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 answerOptions; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class AnswerOptionDTO { + @NotNull + private Long questionOptionId; + } + } + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index 702d35c..e4d075c 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -3,10 +3,7 @@ import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; import com.cmc.suppin.event.survey.controller.dto.SurveyResponseDTO; -import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; -import com.cmc.suppin.event.survey.domain.Question; -import com.cmc.suppin.event.survey.domain.QuestionOption; -import com.cmc.suppin.event.survey.domain.Survey; +import com.cmc.suppin.event.survey.domain.*; import java.util.List; import java.util.stream.Collectors; @@ -74,4 +71,30 @@ public static SurveyResponseDTO.SurveyResultDTO toSurveyResultDTO(Survey survey, .questions(questions) .build(); } + + public static AnonymousParticipant toAnonymousParticipant(SurveyRequestDTO.SurveyAnswerDTO.ParticipantDTO dto, Survey survey) { + return AnonymousParticipant.builder() + .survey(survey) + .name(dto.getName()) + .address(dto.getAddress()) + .email(dto.getEmail()) + .phoneNumber(dto.getPhoneNumber()) + .isAgreed(dto.getIsAgreed()) + .build(); + } + + public static Answer toAnswer(SurveyRequestDTO.SurveyAnswerDTO.AnswerDTO dto, Question question, AnonymousParticipant participant) { + return Answer.builder() + .question(question) + .anonymousParticipant(participant) + .answerText(dto.getAnswerText() != null ? dto.getAnswerText() : "") + .build(); + } + + public static AnswerOption toAnswerOption(SurveyRequestDTO.SurveyAnswerDTO.AnswerDTO.AnswerOptionDTO dto, Answer answer, QuestionOption questionOption) { + return AnswerOption.builder() + .answer(answer) + .questionOption(questionOption) + .build(); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java b/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java index 6ec84c0..88100f3 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java @@ -33,7 +33,9 @@ public class AnonymousParticipant extends BaseDateTimeEntity { private String email; - @Column(columnDefinition = "VARCHAR(20)") + private String instagramId; + + @Column(columnDefinition = "VARCHAR(20)", unique = true) private String phoneNumber; @Column(nullable = false) @@ -42,5 +44,4 @@ public class AnonymousParticipant extends BaseDateTimeEntity { private Boolean isWinner; private Boolean isChecked; - } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Answer.java b/src/main/java/com/cmc/suppin/event/survey/domain/Answer.java index 6a239aa..4cbd141 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Answer.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Answer.java @@ -32,7 +32,7 @@ public class Answer extends BaseDateTimeEntity { @OneToMany(mappedBy = "answer") private List answerOptionList = new ArrayList<>(); - @Column(columnDefinition = "TEXT", nullable = false) + @Column(columnDefinition = "TEXT") private String answerText; } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 88293da..b0a63da 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -5,14 +5,8 @@ import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; import com.cmc.suppin.event.survey.controller.dto.SurveyResponseDTO; import com.cmc.suppin.event.survey.converter.SurveyConverter; -import com.cmc.suppin.event.survey.domain.PersonalInfoCollectOption; -import com.cmc.suppin.event.survey.domain.Question; -import com.cmc.suppin.event.survey.domain.QuestionOption; -import com.cmc.suppin.event.survey.domain.Survey; -import com.cmc.suppin.event.survey.domain.repository.PersonalInfoCollectOptionRepository; -import com.cmc.suppin.event.survey.domain.repository.QuestionOptionRepository; -import com.cmc.suppin.event.survey.domain.repository.QuestionRepository; -import com.cmc.suppin.event.survey.domain.repository.SurveyRepository; +import com.cmc.suppin.event.survey.domain.*; +import com.cmc.suppin.event.survey.domain.repository.*; import com.cmc.suppin.global.enums.UserStatus; import com.cmc.suppin.member.domain.Member; import com.cmc.suppin.member.domain.repository.MemberRepository; @@ -37,6 +31,9 @@ public class SurveyService { private final QuestionOptionRepository questionOptionRepository; private final PersonalInfoCollectOptionRepository personalInfoCollectOptionRepository; private final EventRepository eventRepository; + private final AnonymousParticipantRepository anonymousParticipantRepository; + private final AnswerRepository answerRepository; + private final AnswerOptionRepository answerOptionRepository; @Transactional public SurveyResponseDTO.SurveyCreateResponse createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { @@ -78,6 +75,7 @@ public SurveyResponseDTO.SurveyCreateResponse createSurvey(SurveyRequestDTO.Surv .build(); } + // 생성된 설문지 조회 @Transactional(readOnly = true) public SurveyResponseDTO.SurveyResultDTO getSurvey(Long surveyId) { Survey survey = surveyRepository.findById(surveyId) @@ -86,4 +84,32 @@ public SurveyResponseDTO.SurveyResultDTO getSurvey(Long surveyId) { Event event = survey.getEvent(); return SurveyConverter.toSurveyResultDTO(survey, event); } + + // 설문 응답 저장 + @Transactional + public void saveSurveyAnswers(SurveyRequestDTO.SurveyAnswerDTO request) { + Survey survey = surveyRepository.findById(request.getSurveyId()) + .orElseThrow(() -> new IllegalArgumentException("Survey not found")); + + AnonymousParticipant participant = SurveyConverter.toAnonymousParticipant(request.getParticipant(), survey); + anonymousParticipantRepository.save(participant); + + for (SurveyRequestDTO.SurveyAnswerDTO.AnswerDTO answerDTO : request.getAnswers()) { + Question question = questionRepository.findById(answerDTO.getQuestionId()) + .orElseThrow(() -> new IllegalArgumentException("Question not found")); + + Answer answer = SurveyConverter.toAnswer(answerDTO, question, participant); + answerRepository.save(answer); + + if (answerDTO.getAnswerOptions() != null) { + for (SurveyRequestDTO.SurveyAnswerDTO.AnswerDTO.AnswerOptionDTO optionDTO : answerDTO.getAnswerOptions()) { + QuestionOption questionOption = questionOptionRepository.findById(optionDTO.getQuestionOptionId()) + .orElseThrow(() -> new IllegalArgumentException("QuestionOption not found")); + + AnswerOption answerOption = SurveyConverter.toAnswerOption(optionDTO, answer, questionOption); + answerOptionRepository.save(answerOption); + } + } + } + } } From 32d00bffbdf92282eda58764871b3a0119295fc8 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 04:16:52 +0900 Subject: [PATCH 13/20] =?UTF-8?q?Refactor:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=84=A4=EB=AC=B8=20=EC=B0=B8=EC=97=AC=EC=9E=90=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnonymousParticipantRepository.java | 2 ++ .../survey/exception/SurveyErrorCode.java | 28 +++++++++++++++++++ .../event/survey/service/SurveyService.java | 9 ++++++ 3 files changed, 39 insertions(+) create mode 100644 src/main/java/com/cmc/suppin/event/survey/exception/SurveyErrorCode.java diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java index 274a8c8..00ee9a9 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java @@ -4,4 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface AnonymousParticipantRepository extends JpaRepository { + boolean existsByPhoneNumberAndSurveyId(String phoneNumber, Long surveyId); + } diff --git a/src/main/java/com/cmc/suppin/event/survey/exception/SurveyErrorCode.java b/src/main/java/com/cmc/suppin/event/survey/exception/SurveyErrorCode.java new file mode 100644 index 0000000..e5f12c2 --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/exception/SurveyErrorCode.java @@ -0,0 +1,28 @@ +package com.cmc.suppin.event.survey.exception; + +import com.cmc.suppin.global.exception.BaseErrorCode; +import com.cmc.suppin.global.response.ErrorResponse; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public enum SurveyErrorCode implements BaseErrorCode { + + DUPLICATE_PHONENUMBER("survey-404/01", HttpStatus.CONFLICT, "이미 참여한 참가자입니다. 중복해서 참여할 수 없습니다."); + + + private final String code; + private final HttpStatus status; + private final String message; + + SurveyErrorCode(String code, HttpStatus status, String message) { + this.code = code; + this.status = status; + this.message = message; + } + + @Override + public ErrorResponse getErrorResponse() { + return ErrorResponse.of(code, message); + } +} diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index b0a63da..5eba271 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -7,6 +7,8 @@ import com.cmc.suppin.event.survey.converter.SurveyConverter; import com.cmc.suppin.event.survey.domain.*; import com.cmc.suppin.event.survey.domain.repository.*; +import com.cmc.suppin.event.survey.exception.SurveyErrorCode; +import com.cmc.suppin.event.survey.exception.SurveyException; import com.cmc.suppin.global.enums.UserStatus; import com.cmc.suppin.member.domain.Member; import com.cmc.suppin.member.domain.repository.MemberRepository; @@ -91,6 +93,13 @@ public void saveSurveyAnswers(SurveyRequestDTO.SurveyAnswerDTO request) { Survey survey = surveyRepository.findById(request.getSurveyId()) .orElseThrow(() -> new IllegalArgumentException("Survey not found")); + // 중복 핸드폰 번호 체크 + String phoneNumber = request.getParticipant().getPhoneNumber(); + boolean exists = anonymousParticipantRepository.existsByPhoneNumberAndSurveyId(phoneNumber, request.getSurveyId()); + if (exists) { + throw new SurveyException(SurveyErrorCode.DUPLICATE_PHONENUMBER); + } + AnonymousParticipant participant = SurveyConverter.toAnonymousParticipant(request.getParticipant(), survey); anonymousParticipantRepository.save(participant); From 92127d8112c10ca176caa1cdea285cecd535733e Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 05:40:29 +0900 Subject: [PATCH 14/20] =?UTF-8?q?Feat:=20=EC=A7=88=EB=AC=B8=EB=B3=84=20?= =?UTF-8?q?=EC=84=A4=EB=AC=B8=20=EC=9D=91=EB=8B=B5=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 7 +++++++ .../controller/dto/SurveyResponseDTO.java | 20 +++++++++++++++++++ .../survey/converter/SurveyConverter.java | 18 +++++++++++++++++ .../domain/repository/AnswerRepository.java | 3 +++ .../domain/repository/QuestionRepository.java | 5 +++++ .../event/survey/service/SurveyService.java | 15 ++++++++++++++ 6 files changed, 68 insertions(+) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 0455cd0..4b66bb0 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -47,4 +47,11 @@ public ResponseEntity> saveSurveyAnswers(@RequestBody @Valid S surveyService.saveSurveyAnswers(request); return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); } + + @GetMapping("/{surveyId}/answers/{questionId}") + @Operation(summary = "질문별 설문 응답 결과 조회 API", description = "Request: 설문지 ID와 질문 ID, Response: 해당 질문에 대한 응답 리스트") + public ResponseEntity> getSurveyAnswers(@PathVariable Long surveyId, @PathVariable Long questionId, @CurrentAccount Account account) { + SurveyResponseDTO.SurveyAnswerResultDTO response = surveyService.getSurveyAnswers(surveyId, questionId, account.userId()); + return ResponseEntity.ok(ApiResponse.of(response)); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java index 9e90d45..f0afa61 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -51,4 +51,24 @@ public static class PersonalInfoOptionDTO { private String optionName; } } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class SurveyAnswerResultDTO { + private Long questionId; + private String questionText; + private List answers; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class AnswerDTO { + private String participantName; + private String answerText; + private List selectedOptions; + } + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index e4d075c..26ab6ea 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -97,4 +97,22 @@ public static AnswerOption toAnswerOption(SurveyRequestDTO.SurveyAnswerDTO.Answe .questionOption(questionOption) .build(); } + + public static SurveyResponseDTO.SurveyAnswerResultDTO toSurveyAnswerResultDTO(Question question, List answers) { + List answerDTOs = answers.stream() + .map(answer -> SurveyResponseDTO.SurveyAnswerResultDTO.AnswerDTO.builder() + .participantName(answer.getAnonymousParticipant().getName()) + .answerText(answer.getAnswerText()) + .selectedOptions(answer.getAnswerOptionList().stream() + .map(answerOption -> answerOption.getQuestionOption().getOptionText()) + .collect(Collectors.toList())) + .build()) + .collect(Collectors.toList()); + + return SurveyResponseDTO.SurveyAnswerResultDTO.builder() + .questionId(question.getId()) + .questionText(question.getQuestionText()) + .answers(answerDTOs) + .build(); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java index 4fb29be..5296968 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java @@ -3,5 +3,8 @@ import com.cmc.suppin.event.survey.domain.Answer; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface AnswerRepository extends JpaRepository { + List findByQuestionId(Long questionId); } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java index a09fc44..09c9c80 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/QuestionRepository.java @@ -3,5 +3,10 @@ import com.cmc.suppin.event.survey.domain.Question; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface QuestionRepository extends JpaRepository { + + + Optional findByIdAndSurveyId(Long questionId, Long surveyId); } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 5eba271..80dfe2a 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -121,4 +121,19 @@ public void saveSurveyAnswers(SurveyRequestDTO.SurveyAnswerDTO request) { } } } + + // 설문 응답 결과 조회 + @Transactional(readOnly = true) + public SurveyResponseDTO.SurveyAnswerResultDTO getSurveyAnswers(Long surveyId, Long questionId, String userId) { + // 사용자 식별 + Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + + Question question = questionRepository.findByIdAndSurveyId(questionId, surveyId) + .orElseThrow(() -> new IllegalArgumentException("Question not found for the given survey")); + + List answers = answerRepository.findByQuestionId(questionId); + + return SurveyConverter.toSurveyAnswerResultDTO(question, answers); + } } From 486d5dd5e6edf00ddd5005ee661067566f5ce22d Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 05:40:59 +0900 Subject: [PATCH 15/20] =?UTF-8?q?Fix:=20PK=20=EB=B3=80=EA=B2=BD,=20eventId?= =?UTF-8?q?=20->=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cmc/suppin/event/survey/domain/Question.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/Question.java b/src/main/java/com/cmc/suppin/event/survey/domain/Question.java index 48c48fc..98c86a5 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/Question.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/Question.java @@ -17,7 +17,8 @@ public class Question { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long questionId; + @Column(name = "question_id") + private Long id; @ManyToOne @JoinColumn(name = "survey_id") From 873e70a0ab7f5e10adc062a8e677da8be2926fee Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 05:57:14 +0900 Subject: [PATCH 16/20] =?UTF-8?q?Refactor:=20=EC=84=A4=EB=AC=B8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EC=8B=9C,=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 9 +++- .../controller/dto/SurveyResponseDTO.java | 2 + .../survey/converter/SurveyConverter.java | 41 ++++++++++--------- .../domain/repository/AnswerRepository.java | 6 +-- .../event/survey/service/SurveyService.java | 12 ++++-- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 4b66bb0..42e5ff9 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -50,8 +50,13 @@ public ResponseEntity> saveSurveyAnswers(@RequestBody @Valid S @GetMapping("/{surveyId}/answers/{questionId}") @Operation(summary = "질문별 설문 응답 결과 조회 API", description = "Request: 설문지 ID와 질문 ID, Response: 해당 질문에 대한 응답 리스트") - public ResponseEntity> getSurveyAnswers(@PathVariable Long surveyId, @PathVariable Long questionId, @CurrentAccount Account account) { - SurveyResponseDTO.SurveyAnswerResultDTO response = surveyService.getSurveyAnswers(surveyId, questionId, account.userId()); + public ResponseEntity> 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)); } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java index f0afa61..95e2b46 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -60,6 +60,8 @@ public static class SurveyAnswerResultDTO { private Long questionId; private String questionText; private List answers; + private int totalPages; + private long totalElements; @Getter @NoArgsConstructor diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index 26ab6ea..3498f4a 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -4,6 +4,7 @@ import com.cmc.suppin.event.survey.controller.dto.SurveyRequestDTO; import com.cmc.suppin.event.survey.controller.dto.SurveyResponseDTO; import com.cmc.suppin.event.survey.domain.*; +import org.springframework.data.domain.Page; import java.util.List; import java.util.stream.Collectors; @@ -72,6 +73,26 @@ public static SurveyResponseDTO.SurveyResultDTO toSurveyResultDTO(Survey survey, .build(); } + public static SurveyResponseDTO.SurveyAnswerResultDTO toSurveyAnswerResultDTO(Question question, Page answersPage) { + List answers = answersPage.stream() + .map(answer -> SurveyResponseDTO.SurveyAnswerResultDTO.AnswerDTO.builder() + .participantName(answer.getAnonymousParticipant().getName()) + .answerText(answer.getAnswerText()) + .selectedOptions(answer.getAnswerOptionList().stream() + .map(answerOption -> answerOption.getQuestionOption().getOptionText()) + .collect(Collectors.toList())) + .build()) + .collect(Collectors.toList()); + + return SurveyResponseDTO.SurveyAnswerResultDTO.builder() + .questionId(question.getId()) + .questionText(question.getQuestionText()) + .answers(answers) + .totalPages(answersPage.getTotalPages()) + .totalElements(answersPage.getTotalElements()) + .build(); + } + public static AnonymousParticipant toAnonymousParticipant(SurveyRequestDTO.SurveyAnswerDTO.ParticipantDTO dto, Survey survey) { return AnonymousParticipant.builder() .survey(survey) @@ -87,7 +108,7 @@ public static Answer toAnswer(SurveyRequestDTO.SurveyAnswerDTO.AnswerDTO dto, Qu return Answer.builder() .question(question) .anonymousParticipant(participant) - .answerText(dto.getAnswerText() != null ? dto.getAnswerText() : "") + .answerText(dto.getAnswerText()) .build(); } @@ -97,22 +118,4 @@ public static AnswerOption toAnswerOption(SurveyRequestDTO.SurveyAnswerDTO.Answe .questionOption(questionOption) .build(); } - - public static SurveyResponseDTO.SurveyAnswerResultDTO toSurveyAnswerResultDTO(Question question, List answers) { - List answerDTOs = answers.stream() - .map(answer -> SurveyResponseDTO.SurveyAnswerResultDTO.AnswerDTO.builder() - .participantName(answer.getAnonymousParticipant().getName()) - .answerText(answer.getAnswerText()) - .selectedOptions(answer.getAnswerOptionList().stream() - .map(answerOption -> answerOption.getQuestionOption().getOptionText()) - .collect(Collectors.toList())) - .build()) - .collect(Collectors.toList()); - - return SurveyResponseDTO.SurveyAnswerResultDTO.builder() - .questionId(question.getId()) - .questionText(question.getQuestionText()) - .answers(answerDTOs) - .build(); - } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java index 5296968..a77bc26 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java @@ -1,10 +1,10 @@ package com.cmc.suppin.event.survey.domain.repository; import com.cmc.suppin.event.survey.domain.Answer; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; - public interface AnswerRepository extends JpaRepository { - List findByQuestionId(Long questionId); + Page findByQuestionId(Long questionId, Pageable pageable); } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 80dfe2a..4c8082a 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -14,6 +14,9 @@ import com.cmc.suppin.member.domain.repository.MemberRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -122,9 +125,9 @@ public void saveSurveyAnswers(SurveyRequestDTO.SurveyAnswerDTO request) { } } - // 설문 응답 결과 조회 + // 질문별 설문 응답 결과 조회 @Transactional(readOnly = true) - public SurveyResponseDTO.SurveyAnswerResultDTO getSurveyAnswers(Long surveyId, Long questionId, String userId) { + public SurveyResponseDTO.SurveyAnswerResultDTO getSurveyAnswers(Long surveyId, Long questionId, int page, int size, String userId) { // 사용자 식별 Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); @@ -132,8 +135,9 @@ public SurveyResponseDTO.SurveyAnswerResultDTO getSurveyAnswers(Long surveyId, L Question question = questionRepository.findByIdAndSurveyId(questionId, surveyId) .orElseThrow(() -> new IllegalArgumentException("Question not found for the given survey")); - List answers = answerRepository.findByQuestionId(questionId); + Pageable pageable = PageRequest.of(page - 1, size); + Page answersPage = answerRepository.findByQuestionId(questionId, pageable); - return SurveyConverter.toSurveyAnswerResultDTO(question, answers); + return SurveyConverter.toSurveyAnswerResultDTO(question, answersPage); } } From 0cdeec0aa0d41b1aa6ff71469b13c5d9eba0b216 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 07:02:55 +0900 Subject: [PATCH 17/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=EC=9E=90=20=EB=9E=9C=EB=8D=A4=20=EC=B6=94=EC=B2=A8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 8 +++ .../controller/dto/SurveyRequestDTO.java | 22 ++++++++ .../controller/dto/SurveyResponseDTO.java | 31 +++++++++++ .../survey/converter/SurveyConverter.java | 24 +++++++++ .../survey/domain/AnonymousParticipant.java | 4 ++ .../domain/repository/AnswerRepository.java | 21 ++++++++ .../event/survey/service/SurveyService.java | 52 +++++++++++++++++++ 7 files changed, 162 insertions(+) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 42e5ff9..5895c2f 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -59,4 +59,12 @@ public ResponseEntity> getS 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> selectRandomWinners( + @RequestBody @Valid SurveyRequestDTO.RandomSelectionRequestDTO request, @CurrentAccount Account account) { + SurveyResponseDTO.RandomSelectionResponseDTO response = surveyService.selectRandomWinners(request, account.userId()); + return ResponseEntity.ok(ApiResponse.of(response)); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java index eec5c20..992e07b 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyRequestDTO.java @@ -9,6 +9,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; import java.util.List; public class SurveyRequestDTO { @@ -93,4 +94,25 @@ public static class AnswerOptionDTO { } } } + + @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 keywords; + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java index 95e2b46..218dabc 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; import java.util.List; public class SurveyResponseDTO { @@ -73,4 +74,34 @@ public static class AnswerDTO { private List selectedOptions; } } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class RandomSelectionResponseDTO { + private SelectionCriteriaDTO selectionCriteria; + private List winners; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class WinnerDTO { + 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 keywords; + } + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index 3498f4a..a525b29 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -118,4 +118,28 @@ public static AnswerOption toAnswerOption(SurveyRequestDTO.SurveyAnswerDTO.Answe .questionOption(questionOption) .build(); } + + public static SurveyResponseDTO.RandomSelectionResponseDTO.SelectionCriteriaDTO toSelectionCriteriaDTO(SurveyRequestDTO.RandomSelectionRequestDTO request) { + return SurveyResponseDTO.RandomSelectionResponseDTO.SelectionCriteriaDTO.builder() + .winnerCount(request.getWinnerCount()) + .startDate(request.getStartDate()) + .endDate(request.getEndDate()) + .minLength(request.getMinLength()) + .keywords(request.getKeywords()) + .build(); + } + + public static SurveyResponseDTO.RandomSelectionResponseDTO.WinnerDTO toWinnerDTO(AnonymousParticipant participant, String answerText) { + return SurveyResponseDTO.RandomSelectionResponseDTO.WinnerDTO.builder() + .participantName(participant.getName()) + .answerText(answerText) + .build(); + } + + public static SurveyResponseDTO.RandomSelectionResponseDTO toRandomSelectionResponseDTO(List winners, SurveyResponseDTO.RandomSelectionResponseDTO.SelectionCriteriaDTO criteria) { + return SurveyResponseDTO.RandomSelectionResponseDTO.builder() + .winners(winners) + .selectionCriteria(criteria) + .build(); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java b/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java index 88100f3..5bdac04 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/AnonymousParticipant.java @@ -44,4 +44,8 @@ public class AnonymousParticipant extends BaseDateTimeEntity { private Boolean isWinner; private Boolean isChecked; + + public void setIsWinner(Boolean isWinner) { + this.isWinner = isWinner; + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java index a77bc26..8ed7fde 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java @@ -4,7 +4,28 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDateTime; +import java.util.List; public interface AnswerRepository extends JpaRepository { Page findByQuestionId(Long questionId, Pageable pageable); + + @Query("SELECT a FROM Answer a WHERE a.question.id = :questionId AND " + + "a.createdAt >= :startDate AND " + + "a.createdAt <= :endDate AND " + + "LENGTH(a.answerText) >= :minLength AND " + + "(:keywordList IS NULL OR SIZE(:keywordList) = 0 OR " + + "(LOWER(a.answerText) LIKE LOWER(CONCAT('%', :keywordList[0], '%')) " + + "OR LOWER(a.answerText) LIKE LOWER(CONCAT('%', :keywordList[1], '%')) " + + "OR LOWER(a.answerText) LIKE LOWER(CONCAT('%', :keywordList[2], '%')) ... ))") + List findEligibleAnswers(@Param("questionId") Long questionId, + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate, + @Param("minLength") Integer minLength, + @Param("keywordList") List keywordList); + } + diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 4c8082a..3324722 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -140,4 +141,55 @@ public SurveyResponseDTO.SurveyAnswerResultDTO getSurveyAnswers(Long surveyId, L return SurveyConverter.toSurveyAnswerResultDTO(question, answersPage); } + + @Transactional + public SurveyResponseDTO.RandomSelectionResponseDTO selectRandomWinners(SurveyRequestDTO.RandomSelectionRequestDTO request, String userId) { + // 사용자 식별 + Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + + // 설문 및 질문 식별 + Survey survey = surveyRepository.findById(request.getSurveyId()) + .orElseThrow(() -> new IllegalArgumentException("Survey not found")); + + Question question = questionRepository.findByIdAndSurveyId(request.getQuestionId(), request.getSurveyId()) + .orElseThrow(() -> new IllegalArgumentException("Question not found for the given survey")); + + // 키워드를 OR 조건으로 연결 + List keywordPatterns = request.getKeywords().stream() + .map(keyword -> "%" + keyword.toLowerCase() + "%") + .collect(Collectors.toList()); + + // 조건에 맞는 주관식 답변 조회 + List eligibleAnswers = answerRepository.findEligibleAnswers( + question.getId(), + request.getStartDate(), + request.getEndDate(), + request.getMinLength(), + keywordPatterns + ); + + // 랜덤 추첨 + Collections.shuffle(eligibleAnswers); + List selectedWinners = eligibleAnswers.stream() + .limit(request.getWinnerCount()) + .collect(Collectors.toList()); + + // 당첨자 업데이트 및 WinnerDTO 생성 + List winners = selectedWinners.stream() + .map(answer -> { + AnonymousParticipant participant = answer.getAnonymousParticipant(); + participant.setIsWinner(true); // isWinner 값을 True로 설정 + anonymousParticipantRepository.save(participant); // 저장 + + return SurveyConverter.toWinnerDTO(participant, answer.getAnswerText()); + }) + .collect(Collectors.toList()); + + // 선택 기준 생성 + SurveyResponseDTO.RandomSelectionResponseDTO.SelectionCriteriaDTO criteria = SurveyConverter.toSelectionCriteriaDTO(request); + + // 응답 객체 생성 + return SurveyConverter.toRandomSelectionResponseDTO(winners, criteria); + } } From cdf3c844717c25a20b2b3b349990171445e70645 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 07:25:15 +0900 Subject: [PATCH 18/20] =?UTF-8?q?Refactor:=20JPQL=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0,=20CriteriaBuilder=EB=A5=BC=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EB=8F=99=EC=A0=81=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnonymousParticipantRepository.java | 4 +++ .../domain/repository/AnswerRepository.java | 20 ++++++------ .../event/survey/service/SurveyService.java | 32 ++++++++++++++----- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java index 00ee9a9..be70c27 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java @@ -3,7 +3,11 @@ import com.cmc.suppin.event.survey.domain.AnonymousParticipant; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface AnonymousParticipantRepository extends JpaRepository { boolean existsByPhoneNumberAndSurveyId(String phoneNumber, Long surveyId); + Optional findByIdAndSurveyIdAndIsWinnerTrue(Long id, Long surveyId); + } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java index 8ed7fde..75dedda 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerRepository.java @@ -4,28 +4,26 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -import java.time.LocalDateTime; -import java.util.List; public interface AnswerRepository extends JpaRepository { Page findByQuestionId(Long questionId, Pageable pageable); + /* @Query("SELECT a FROM Answer a WHERE a.question.id = :questionId AND " + "a.createdAt >= :startDate AND " + "a.createdAt <= :endDate AND " + "LENGTH(a.answerText) >= :minLength AND " + - "(:keywordList IS NULL OR SIZE(:keywordList) = 0 OR " + - "(LOWER(a.answerText) LIKE LOWER(CONCAT('%', :keywordList[0], '%')) " + - "OR LOWER(a.answerText) LIKE LOWER(CONCAT('%', :keywordList[1], '%')) " + - "OR LOWER(a.answerText) LIKE LOWER(CONCAT('%', :keywordList[2], '%')) ... ))") + "(COALESCE(:keywords, NULL) IS NULL OR " + + "EXISTS (SELECT 1 FROM Answer a2 WHERE a2.id = a.id AND (" + + "LOWER(a2.answerText) LIKE LOWER(CONCAT('%', :#{#keywords[0]}, '%'))" + + " OR LOWER(a2.answerText) LIKE LOWER(CONCAT('%', :#{#keywords[1]}, '%'))" + + // 추가적인 키워드 OR 조건을 여기에 동적으로 추가해야 합니다. + ")))") List findEligibleAnswers(@Param("questionId") Long questionId, @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("minLength") Integer minLength, - @Param("keywordList") List keywordList); - + @Param("keywords") List keywords); + */ } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 3324722..c525244 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -40,6 +40,7 @@ public class SurveyService { private final AnonymousParticipantRepository anonymousParticipantRepository; private final AnswerRepository answerRepository; private final AnswerOptionRepository answerOptionRepository; + private final AnswerCustomRepository answerCustomRepository; @Transactional public SurveyResponseDTO.SurveyCreateResponse createSurvey(SurveyRequestDTO.SurveyCreateDTO request, String userId) { @@ -161,13 +162,9 @@ public SurveyResponseDTO.RandomSelectionResponseDTO selectRandomWinners(SurveyRe .collect(Collectors.toList()); // 조건에 맞는 주관식 답변 조회 - List eligibleAnswers = answerRepository.findEligibleAnswers( - question.getId(), - request.getStartDate(), - request.getEndDate(), - request.getMinLength(), - keywordPatterns - ); + List eligibleAnswers = answerCustomRepository.findEligibleAnswers( + request.getQuestionId(), request.getStartDate(), request.getEndDate(), + request.getMinLength(), request.getKeywords()); // 랜덤 추첨 Collections.shuffle(eligibleAnswers); @@ -186,10 +183,29 @@ public SurveyResponseDTO.RandomSelectionResponseDTO selectRandomWinners(SurveyRe }) .collect(Collectors.toList()); - // 선택 기준 생성 + // 응답시, 조건도 함께 포함해주기 위한 조건 객체 생성 SurveyResponseDTO.RandomSelectionResponseDTO.SelectionCriteriaDTO criteria = SurveyConverter.toSelectionCriteriaDTO(request); // 응답 객체 생성 return SurveyConverter.toRandomSelectionResponseDTO(winners, criteria); } + +// @Transactional(readOnly = true) +// public SurveyResponseDTO.WinnerDetailDTO getWinnerDetails(Long surveyId, Long participantId) { +// AnonymousParticipant participant = anonymousParticipantRepository.findByIdAndSurveyIdAndIsWinnerTrue(participantId, surveyId) +// .orElseThrow(() -> new IllegalArgumentException("Winner not found for the given survey")); +// +// // 모든 답변을 조회하여 응답 DTO로 변환 +// List answers = participant.getAnswerList().stream() +// .map(answer -> SurveyResponseDTO.WinnerDetailDTO.AnswerDetailDTO.builder() +// .questionText(answer.getQuestion().getQuestionText()) +// .answerText(answer.getAnswerText()) +// .selectedOptions(answer.getAnswerOptionList().stream() +// .map(answerOption -> answerOption.getQuestionOption().getOptionText()) +// .collect(Collectors.toList())) +// .build()) +// .collect(Collectors.toList()); +// +// return SurveyConverter.toWinnerDetailDTO(participant, answers); +// } } From 4b771542fdf27c2edaee2775f80906b9285749d6 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 07:25:54 +0900 Subject: [PATCH 19/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=EC=9E=90=20=EC=84=B8=EB=B6=80=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/survey/controller/SurveyApi.java | 9 ++++ .../controller/dto/SurveyResponseDTO.java | 24 ++++++++++ .../survey/converter/SurveyConverter.java | 12 +++++ .../repository/AnswerCustomRepository.java | 48 +++++++++++++++++++ .../event/survey/service/SurveyService.java | 36 +++++++------- 5 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerCustomRepository.java diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 5895c2f..49b0e9e 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -67,4 +67,13 @@ public ResponseEntity> 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> getWinnerDetails( + @PathVariable Long surveyId, @PathVariable Long participantId) { + SurveyResponseDTO.WinnerDetailDTO winnerDetails = surveyService.getWinnerDetails(surveyId, participantId); + return ResponseEntity.ok(ApiResponse.of(winnerDetails)); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java index 218dabc..d4c2d65 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/dto/SurveyResponseDTO.java @@ -88,6 +88,7 @@ public static class RandomSelectionResponseDTO { @AllArgsConstructor @Builder public static class WinnerDTO { + private Long participantId; private String participantName; private String answerText; } @@ -104,4 +105,27 @@ public static class SelectionCriteriaDTO { private List 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 answers; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class AnswerDetailDTO { + private String questionText; + private String answerText; + private List selectedOptions; // 객관식 질문의 경우 선택된 옵션 리스트 + } + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java index a525b29..498f47f 100644 --- a/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java +++ b/src/main/java/com/cmc/suppin/event/survey/converter/SurveyConverter.java @@ -131,6 +131,7 @@ public static SurveyResponseDTO.RandomSelectionResponseDTO.SelectionCriteriaDTO public static SurveyResponseDTO.RandomSelectionResponseDTO.WinnerDTO toWinnerDTO(AnonymousParticipant participant, String answerText) { return SurveyResponseDTO.RandomSelectionResponseDTO.WinnerDTO.builder() + .participantId(participant.getId()) .participantName(participant.getName()) .answerText(answerText) .build(); @@ -142,4 +143,15 @@ public static SurveyResponseDTO.RandomSelectionResponseDTO toRandomSelectionResp .selectionCriteria(criteria) .build(); } + + public static SurveyResponseDTO.WinnerDetailDTO toWinnerDetailDTO(AnonymousParticipant participant, List answers) { + return SurveyResponseDTO.WinnerDetailDTO.builder() + .name(participant.getName()) + .phoneNumber(participant.getPhoneNumber()) + .address(participant.getAddress()) + .email(participant.getEmail()) + .instagramId(participant.getInstagramId()) + .answers(answers) + .build(); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerCustomRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerCustomRepository.java new file mode 100644 index 0000000..1e9a02b --- /dev/null +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnswerCustomRepository.java @@ -0,0 +1,48 @@ +package com.cmc.suppin.event.survey.domain.repository; + +import com.cmc.suppin.event.survey.domain.Answer; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Repository +public class AnswerCustomRepository { + + @PersistenceContext + private EntityManager entityManager; + + public List findEligibleAnswers(Long questionId, LocalDateTime startDate, LocalDateTime endDate, Integer minLength, List keywords) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Answer.class); + Root answer = query.from(Answer.class); + + List predicates = new ArrayList<>(); + + // 기본 조건 추가 + predicates.add(cb.equal(answer.get("question").get("id"), questionId)); + predicates.add(cb.greaterThanOrEqualTo(answer.get("createdAt"), startDate)); + predicates.add(cb.lessThanOrEqualTo(answer.get("createdAt"), endDate)); + predicates.add(cb.ge(cb.length(answer.get("answerText")), minLength)); + + // 키워드 조건 추가 + if (keywords != null && !keywords.isEmpty()) { + List keywordPredicates = new ArrayList<>(); + for (String keyword : keywords) { + keywordPredicates.add(cb.like(cb.lower(answer.get("answerText")), "%" + keyword.toLowerCase() + "%")); + } + predicates.add(cb.or(keywordPredicates.toArray(new Predicate[0]))); + } + + query.where(predicates.toArray(new Predicate[0])); + + return entityManager.createQuery(query).getResultList(); + } +} diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index c525244..14ae580 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -190,22 +190,22 @@ public SurveyResponseDTO.RandomSelectionResponseDTO selectRandomWinners(SurveyRe return SurveyConverter.toRandomSelectionResponseDTO(winners, criteria); } -// @Transactional(readOnly = true) -// public SurveyResponseDTO.WinnerDetailDTO getWinnerDetails(Long surveyId, Long participantId) { -// AnonymousParticipant participant = anonymousParticipantRepository.findByIdAndSurveyIdAndIsWinnerTrue(participantId, surveyId) -// .orElseThrow(() -> new IllegalArgumentException("Winner not found for the given survey")); -// -// // 모든 답변을 조회하여 응답 DTO로 변환 -// List answers = participant.getAnswerList().stream() -// .map(answer -> SurveyResponseDTO.WinnerDetailDTO.AnswerDetailDTO.builder() -// .questionText(answer.getQuestion().getQuestionText()) -// .answerText(answer.getAnswerText()) -// .selectedOptions(answer.getAnswerOptionList().stream() -// .map(answerOption -> answerOption.getQuestionOption().getOptionText()) -// .collect(Collectors.toList())) -// .build()) -// .collect(Collectors.toList()); -// -// return SurveyConverter.toWinnerDetailDTO(participant, answers); -// } + @Transactional(readOnly = true) + public SurveyResponseDTO.WinnerDetailDTO getWinnerDetails(Long surveyId, Long participantId) { + AnonymousParticipant participant = anonymousParticipantRepository.findByIdAndSurveyIdAndIsWinnerTrue(participantId, surveyId) + .orElseThrow(() -> new IllegalArgumentException("Winner not found for the given survey")); + + // 모든 답변을 조회하여 응답 DTO로 변환 + List answers = participant.getAnswerList().stream() + .map(answer -> SurveyResponseDTO.WinnerDetailDTO.AnswerDetailDTO.builder() + .questionText(answer.getQuestion().getQuestionText()) + .answerText(answer.getAnswerText()) + .selectedOptions(answer.getAnswerOptionList().stream() + .map(answerOption -> answerOption.getQuestionOption().getOptionText()) + .collect(Collectors.toList())) + .build()) + .collect(Collectors.toList()); + + return SurveyConverter.toWinnerDetailDTO(participant, answers); + } } From 19ae2daf67e9bcd11ee66c7152cf95e42cd6cbe3 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Fri, 9 Aug 2024 07:36:49 +0900 Subject: [PATCH 20/20] =?UTF-8?q?Feat:=20=EC=84=A4=EB=AC=B8=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=EC=9E=90=20=EB=8B=A4=EC=8B=9C=20=EB=BD=91=EA=B8=B0(?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=EB=8B=B9=EC=B2=A8=EC=9E=90=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C)=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cmc/suppin/event/survey/controller/SurveyApi.java | 7 +++++++ .../repository/AnonymousParticipantRepository.java | 3 +++ .../cmc/suppin/event/survey/service/SurveyService.java | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java index 49b0e9e..16062b4 100644 --- a/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java +++ b/src/main/java/com/cmc/suppin/event/survey/controller/SurveyApi.java @@ -76,4 +76,11 @@ public ResponseEntity> getWinnerD SurveyResponseDTO.WinnerDetailDTO winnerDetails = surveyService.getWinnerDetails(surveyId, participantId); return ResponseEntity.ok(ApiResponse.of(winnerDetails)); } + + @DeleteMapping("/winners") + @Operation(summary = "당첨자 리스트 삭제 API", description = "해당 설문조사의 모든 당첨자들의 isWinner 값을 false로 변경합니다.") + public ResponseEntity> deleteWinners(@RequestParam Long surveyId) { + surveyService.deleteWinners(surveyId); + return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); + } } diff --git a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java index be70c27..6e94add 100644 --- a/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java +++ b/src/main/java/com/cmc/suppin/event/survey/domain/repository/AnonymousParticipantRepository.java @@ -3,6 +3,7 @@ import com.cmc.suppin.event.survey.domain.AnonymousParticipant; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface AnonymousParticipantRepository extends JpaRepository { @@ -10,4 +11,6 @@ public interface AnonymousParticipantRepository extends JpaRepository findByIdAndSurveyIdAndIsWinnerTrue(Long id, Long surveyId); + List findBySurveyIdAndIsWinnerTrue(Long surveyId); + } diff --git a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java index 14ae580..8125467 100644 --- a/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java +++ b/src/main/java/com/cmc/suppin/event/survey/service/SurveyService.java @@ -208,4 +208,13 @@ public SurveyResponseDTO.WinnerDetailDTO getWinnerDetails(Long surveyId, Long pa return SurveyConverter.toWinnerDetailDTO(participant, answers); } + + public void deleteWinners(Long surveyId) { + List participants = anonymousParticipantRepository.findBySurveyIdAndIsWinnerTrue(surveyId); + + for (AnonymousParticipant participant : participants) { + participant.setIsWinner(false); + anonymousParticipantRepository.save(participant); + } + } }