diff --git a/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java b/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java index b02e01c..56955c4 100644 --- a/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java +++ b/src/main/java/com/arom/with_travel/domain/member/service/MemberSignupService.java @@ -60,20 +60,14 @@ public MemberSignupResponseDto registerWithSurvey(String email, // surveyRepository.save(survey); // }); - SurveyRequestDto s = req.getSurvey(); // 단일 설문 + SurveyRequestDto s = req.getSurvey(); Survey survey = surveyRepository.findByMemberIdAndIsDeletedFalse(member.getId()) .map(existing -> { - existing.update( - s.getEnergyLevel() - // 이후 섹션 늘리면 여기에 추가 - ); + existing.update(s); return existing; }) - .orElseGet(() -> Survey.create( - member, - s.getEnergyLevel() - // 이후 섹션 늘리면 여기에 추가 - )); + .orElseGet(() -> Survey.create(member, s)); + surveyRepository.save(survey); member.markAdditionalDataChecked(); diff --git a/src/main/java/com/arom/with_travel/domain/survey/Survey.java b/src/main/java/com/arom/with_travel/domain/survey/Survey.java index 8de1c3f..de4efb9 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/Survey.java +++ b/src/main/java/com/arom/with_travel/domain/survey/Survey.java @@ -1,16 +1,19 @@ package com.arom.with_travel.domain.survey; import com.arom.with_travel.domain.member.Member; -import com.arom.with_travel.domain.survey.enums.EnergyLevel; +import com.arom.with_travel.domain.survey.dto.request.SurveyRequestDto; +import com.arom.with_travel.domain.survey.enums.*; import com.arom.with_travel.global.entity.BaseEntity; import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; +import java.util.HashSet; +import java.util.Set; + @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -26,18 +29,94 @@ public class Survey extends BaseEntity { @JoinColumn(name = "member_id", nullable = false) private Member member; - @NotNull + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_energy_levels", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "energy_level", length = 50) + @Enumerated(EnumType.STRING) + private Set energyLevels = new HashSet<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_travel_goals", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "travel_goal", length = 50) + @Enumerated(EnumType.STRING) + private Set travelGoals = new HashSet<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_travel_paces", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "travel_pace", length = 50) + @Enumerated(EnumType.STRING) + private Set travelPaces = new HashSet<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_comm_styles", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "comm_style", length = 50) + @Enumerated(EnumType.STRING) + private Set commStyles = new HashSet<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_personalities", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "personality", length = 50) + @Enumerated(EnumType.STRING) + private Set recordTendencies = new HashSet<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_companion_styles", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "companion_style", length = 50) + @Enumerated(EnumType.STRING) + private Set companionStyles = new HashSet<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "survey_spend_patterns", joinColumns = @JoinColumn(name = "survey_id")) + @Column(name = "spend_pattern", length = 50) @Enumerated(EnumType.STRING) - @Column(name = "energy_level", nullable = false, length = 50) - private EnergyLevel energyLevel; + private Set spendPatterns = new HashSet<>(); - private Survey(Member member, EnergyLevel energyLevel) { - this.energyLevel = energyLevel; +// @Column(name = "introduction", length = 1000) +// private String introduction; + + // ---- 생성/수정 로직 ---- + private Survey(Member member, + Set energyLevels, + Set travelGoals, + Set travelPaces, + Set commStyles, + Set recordTendencies, + Set companionStyles, + Set spendPatterns) { + this.energyLevels = safe(energyLevels); + this.travelGoals = safe(travelGoals); + this.travelPaces = safe(travelPaces); + this.commStyles = safe(commStyles); + this.recordTendencies = safe(recordTendencies); + this.companionStyles= safe(companionStyles); + this.spendPatterns = safe(spendPatterns); +// this.introduction = introduction; linkMember(member); } - public static Survey create(Member member, EnergyLevel energyLevel) { - return new Survey(member, energyLevel); + public static Survey create(Member member, SurveyRequestDto dto){ + return new Survey( + member, + dto.getEnergyLevels(), dto.getTravelGoals(), dto.getTravelPaces(), + dto.getCommStyles(), dto.getRecordTendencies(), dto.getCompanionStyles(), + dto.getSpendPatterns() +// dto.getIntroduction() + ); + } + + public void update(SurveyRequestDto dto){ + this.energyLevels = safe(dto.getEnergyLevels()); + this.travelGoals = safe(dto.getTravelGoals()); + this.travelPaces = safe(dto.getTravelPaces()); + this.commStyles = safe(dto.getCommStyles()); + this.recordTendencies = safe(dto.getRecordTendencies()); + this.companionStyles = safe(dto.getCompanionStyles()); + this.spendPatterns = safe(dto.getSpendPatterns()); +// this.introduction = dto.getIntroduction(); + } + + private Set safe(Set in){ + return (in == null) ? new HashSet<>() : new HashSet<>(in); } private void linkMember(Member newMember) { @@ -49,8 +128,4 @@ private void linkMember(Member newMember) { newMember.setSurvey(this); } } - - public void update(EnergyLevel energyLevel) { - this.energyLevel = energyLevel; - } } diff --git a/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java b/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java index 1309a07..251c5e1 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java +++ b/src/main/java/com/arom/with_travel/domain/survey/dto/request/SurveyRequestDto.java @@ -1,24 +1,29 @@ package com.arom.with_travel.domain.survey.dto.request; -import com.arom.with_travel.domain.survey.enums.EnergyLevel; +import com.arom.with_travel.domain.survey.enums.*; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.Set; + @Getter @NoArgsConstructor @AllArgsConstructor @Builder public class SurveyRequestDto { - @NotNull(message = "energyLevel은 필수입니다.") - private EnergyLevel energyLevel; -// @NotNull TravelGoal travelGoal, -// @NotNull TravelPace travelPace, -// @NotNull CommStyle commStyle, -// @NotNull Personality personality, -// @NotNull CompanionStyle companionStyle, -// @NotNull SpendPattern spendPattern + private Set energyLevels; + private Set travelGoals; + private Set travelPaces; + private Set commStyles; + private Set recordTendencies; + private Set companionStyles; + private Set spendPatterns; + +// @Size(max = 1000, message = "자기소개는 최대 1000자입니다.") +// private String introduction; } diff --git a/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java b/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java index bb5a9bf..781b3d0 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java +++ b/src/main/java/com/arom/with_travel/domain/survey/dto/response/SurveyResponseDto.java @@ -1,13 +1,14 @@ package com.arom.with_travel.domain.survey.dto.response; import com.arom.with_travel.domain.survey.Survey; -import com.arom.with_travel.domain.survey.enums.EnergyLevel; +import com.arom.with_travel.domain.survey.enums.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.List; +import java.util.Set; @Getter @NoArgsConstructor @@ -17,22 +18,28 @@ public class SurveyResponseDto { private Long surveyId; private Long memberId; - private EnergyLevel energyLevel; - // 나중에 추가되면 주석 해제 - // private TravelGoal travelGoal; - // private TravelPace travelPace; - // private CommStyle commStyle; - // private Personality personality; - // private CompanionStyle companionStyle; - // private SpendPattern spendPattern; + private Set energyLevels; + private Set travelGoals; + private Set travelPaces; + private Set commStyles; + private Set recordTendencies; + private Set companionStyles; + private Set spendPatterns; + +// private String introduction; public static SurveyResponseDto from(Survey s) { return SurveyResponseDto.builder() .surveyId(s.getId()) .memberId(s.getMember().getId()) - .energyLevel(s.getEnergyLevel()) - // .travelGoal(s.getTravelGoal()) - // ... + .energyLevels(s.getEnergyLevels()) + .travelGoals(s.getTravelGoals()) + .travelPaces(s.getTravelPaces()) + .commStyles(s.getCommStyles()) + .recordTendencies(s.getRecordTendencies()) + .companionStyles(s.getCompanionStyles()) + .spendPatterns(s.getSpendPatterns()) +// .introduction(s.getIntroduction()) .build(); } } diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/CommStyle.java b/src/main/java/com/arom/with_travel/domain/survey/enums/CommStyle.java new file mode 100644 index 0000000..78b8110 --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/CommStyle.java @@ -0,0 +1,35 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum CommStyle implements SurveyEnum { + + TALKATIVE("TALKATIVE", "#수다쟁이"), + QUIET("QUIET", "#조용한편"), + REACTION_KING("REACTION_KING", "#리액션킹"), + RESPECT_ME_TIME("RESPECT_ME_TIME", "#개인시간존중"); + + private final String code; + private final String label; + + @Override public String getCode(){return code;} + @Override public String getLabel(){return label;} + + @JsonValue + public String json(){return code;} + + @JsonCreator + public static CommStyle from(String value){ + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.getCode().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_TRAVELGOAL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/CompanionStyle.java b/src/main/java/com/arom/with_travel/domain/survey/enums/CompanionStyle.java new file mode 100644 index 0000000..3e5a8e2 --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/CompanionStyle.java @@ -0,0 +1,35 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum CompanionStyle implements SurveyEnum { + + LEADER("LEADER", "#리더발휘"), + FOLLOWER("FOLLOWER", "#따라가는편"), + OPINION_GIVER("OPINION_GIVER", "#의견제시"), + MOOD_MAKER("MOOD_MAKER", "#분위기메이커"); + + private final String code; + private final String label; + + @Override public String getCode(){return code;} + @Override public String getLabel(){return label;} + + @JsonValue + public String json(){return code;} + + @JsonCreator + public static CompanionStyle from(String value){ + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.getCode().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_TRAVELGOAL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/RecordTendency.java b/src/main/java/com/arom/with_travel/domain/survey/enums/RecordTendency.java new file mode 100644 index 0000000..d4b67b2 --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/RecordTendency.java @@ -0,0 +1,35 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum RecordTendency implements SurveyEnum { + + LIFE_SHOT_HUNTER("LIFE_SHOT_HUNTER", "#인생샷헌터"), + CANT_SKIP_SELFIE("CANT_SKIP_SELFIE", "#셀카는못참지"), + LOVE_RECORDING("LOVE_RECORDING", "#기록좋아"), + EYES_ONLY("EYES_ONLY", "#눈으로만감상"); + + private final String code; + private final String label; + + @Override public String getCode(){return code;} + @Override public String getLabel(){return label;} + + @JsonValue + public String json(){return code;} + + @JsonCreator + public static RecordTendency from(String value){ + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.getCode().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_TRAVELGOAL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/SpendPattern.java b/src/main/java/com/arom/with_travel/domain/survey/enums/SpendPattern.java new file mode 100644 index 0000000..4da4345 --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/SpendPattern.java @@ -0,0 +1,34 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum SpendPattern implements SurveyEnum { + + VALUE_FOR_MONEY("VALUE_FOR_MONEY", "#가성비추구"), + VALUE_INVESTING("VALUE_INVESTING", "#가치투자"), + FLEX("FLEX", "#플렉스"); + + private final String code; + private final String label; + + @Override public String getCode(){return code;} + @Override public String getLabel(){return label;} + + @JsonValue + public String json(){return code;} + + @JsonCreator + public static SpendPattern from(String value){ + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.getCode().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_TRAVELGOAL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/TravelGoal.java b/src/main/java/com/arom/with_travel/domain/survey/enums/TravelGoal.java new file mode 100644 index 0000000..b7c6c8e --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/TravelGoal.java @@ -0,0 +1,36 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum TravelGoal implements SurveyEnum { + + HOTPLACE_HUNTER("HOTPLACE_HUNTER", "#핫플탐방러"), + LOCAL_VIBE("LOCAL_VIBE", "#현지감성"), + FOOD_LOVER("FOOD_LOVER", "#맛집러버"), + ACTIVITY("ACTIVITY", "#액티비티광"), + HEALING_FIRST("HEALING_FIRST", "#힐링우선"); + + private final String code; + private final String label; + + @Override public String getCode(){return code;} + @Override public String getLabel(){return label;} + + @JsonValue + public String json(){return code;} + + @JsonCreator + public static TravelGoal from(String value){ + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.getCode().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_TRAVELGOAL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/enums/TravelPace.java b/src/main/java/com/arom/with_travel/domain/survey/enums/TravelPace.java new file mode 100644 index 0000000..37f3558 --- /dev/null +++ b/src/main/java/com/arom/with_travel/domain/survey/enums/TravelPace.java @@ -0,0 +1,35 @@ +package com.arom.with_travel.domain.survey.enums; + +import com.arom.with_travel.global.exception.BaseException; +import com.arom.with_travel.global.exception.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum TravelPace implements SurveyEnum { + + TIGHT_SCHEDULE("TIGHT_SCHEDULE", "#타이트스케줄"), + RELAXED("RELAXED", "#여유만만"), + SPONTANEOUS("SPONTANEOUS", "#즉흥여행"), + PLAN_B_READY("PLAN_B_READY", "#플랜B준비완료"); + + private final String code; + private final String label; + + @Override public String getCode(){return code;} + @Override public String getLabel(){return label;} + + @JsonValue + public String json(){return code;} + + @JsonCreator + public static TravelPace from(String value){ + return Arrays.stream(values()) + .filter(v -> v.name().equalsIgnoreCase(value) || v.getCode().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> BaseException.from(ErrorCode.INVALID_SURVEY_TRAVELGOAL)); + } +} diff --git a/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java b/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java index 2561dc3..62cc1ae 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java +++ b/src/main/java/com/arom/with_travel/domain/survey/repository/SurveyRepository.java @@ -1,6 +1,5 @@ package com.arom.with_travel.domain.survey.repository; -import com.arom.with_travel.domain.member.Member; import com.arom.with_travel.domain.survey.Survey; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java b/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java index d169671..0f04d7b 100644 --- a/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java +++ b/src/main/java/com/arom/with_travel/domain/survey/service/SurveyService.java @@ -26,29 +26,10 @@ public void saveSurvey(String email, SurveyRequestDto dto) { .orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND)); Survey survey = surveyRepository.findByMemberIdAndIsDeletedFalse(member.getId()) - .map(existing -> { - existing.update( - dto.getEnergyLevel() -// dto.getTravelGoal(), -// dto.getTravelPace(), -// dto.getCommStyle(), -// dto.getPersonality(), -// dto.getCompanionStyle(), -// dto.getSpendPattern() - ); - return existing; - }) - .orElseGet(() -> Survey.create( - member, - dto.getEnergyLevel() -// dto.getTravelGoal(), -// dto.getTravelPace(), -// dto.getCommStyle(), -// dto.getPersonality(), -// dto.getCompanionStyle(), -// dto.getSpendPattern() - )); + .map(existing -> { existing.update(dto); return existing; }) + .orElseGet(() -> Survey.create(member, dto)); + // 기존 엔티티는 dirty checking으로 flush, 신규는 persist surveyRepository.save(survey); } diff --git a/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java b/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java index d244190..26a8b8e 100644 --- a/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java +++ b/src/main/java/com/arom/with_travel/global/exception/error/ErrorCode.java @@ -55,7 +55,13 @@ public enum ErrorCode { INVALID_SURVEY_ANSWER("SVY-0001", "설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), OVER_ANSWER_LIMIT("SVY-0002", "답변 개수가 초과되었습니다..", ErrorDisplayType.POPUP), INVALID_SURVEY_QUESTION("SVY-0003", "설문 질문이 비어있습니다.", ErrorDisplayType.POPUP), - INVALID_SURVEY_ENERGYLEVEL("SVY-0004", "에너지레벨 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_ENERGYLEVEL("SVY-0004", "에너지 레벨 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_TRAVELGOAL("SVY-0005", "여행 목적 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_TRAVELPACE("SVY-0006", "여행 페이스 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_COMMSTYLE("SVY-0007", "소통 스타일 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_RECORDTENDENCY("SVY-0008", "기록 성향 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_COMPANIONSTYLE("SVY-0009", "동행 스타일 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), + INVALID_SURVEY_SPENDPATTERN("SVY-00010", "소비 패턴 설문 답변이 비어있습니다.", ErrorDisplayType.POPUP), // image INVALID_IMG_TYPE("IMG-0000", "지원하지 않는 이미지 형식입니다.", ErrorDisplayType.POPUP),