Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public enum ErrorCode {

// 설문 (SURVEY)
SURVEY_RESULT_NOT_FOUND(HttpStatus.NOT_FOUND, "설문 결과를 찾을 수 없습니다."),
AVATAR_PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "아바타 프로필을 찾을 수 없습니다."),

// 휴대폰 인증 (VERIFICATION)
SMS_SEND_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "SMS 발송에 실패했습니다."),
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/com/dduru/gildongmu/config/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.dduru.gildongmu.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
public class CacheConfig {

@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues();

return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(defaultConfig)
.build();
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/dduru/gildongmu/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import com.dduru.gildongmu.common.resolver.CurrentUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.nio.charset.StandardCharsets;
import java.util.List;

@Configuration
Expand All @@ -18,4 +21,14 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(currentUserArgumentResolver);
}

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.stream()
.filter(converter -> converter instanceof StringHttpMessageConverter)
.forEach(converter -> {
StringHttpMessageConverter stringConverter = (StringHttpMessageConverter) converter;
stringConverter.setDefaultCharset(StandardCharsets.UTF_8);
});
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/dduru/gildongmu/survey/domain/AvatarProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.dduru.gildongmu.survey.domain;

import com.dduru.gildongmu.common.entity.BaseTimeEntity;
import com.dduru.gildongmu.survey.domain.enums.AvatarType;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "avatar_profiles")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AvatarProfile extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Enumerated(EnumType.STRING)
@Column(name = "avatar_type", nullable = false, unique = true)
private AvatarType avatarType;

@Column(nullable = false, length = 200)
private String description;

@Column(nullable = false, columnDefinition = "TEXT")
private String personality;

@Column(nullable = false, columnDefinition = "TEXT")
private String strength;

@Column(nullable = false, columnDefinition = "TEXT")
private String tip;

@Column(nullable = false, columnDefinition = "JSON")
private String tags;

@Builder
public AvatarProfile(AvatarType avatarType, String description, String personality, String strength, String tip, String tags) {
this.avatarType = avatarType;
this.description = description;
this.personality = personality;
this.strength = strength;
this.tip = tip;
this.tags = tags;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,4 @@ public int getCode() {
public String getText() {
return text;
}

public String getDescription() {
return switch (this) {
case TTUR_POGUNI -> "안정형 가성비 조용러 (내 페이스대로 차분히)";
case TTUR_MOOD -> "안정형 플랙스 감성러 (분위기와 여유로운 힐링)";
case TTUR_MALLANGI -> "안정형 가성비 배려러 (함께 가며 챙겨주는 다정함)";
case TTUR_SWEET -> "안정형 플랙스 미식 힐링러 (다같이 맛있는 미식 힐링)";
case TTUR_POPO -> "모험형 가성비 실속러 (알뜰하고 영리한 실속 탐험)";
case TTUR_SPARKLE -> "모험형 플랙스 경험러 (즉흥적이고 화려한 경험)";
case TTUR_GLIMMING -> "모험형 가성비 미식 모험러 (북적이는 로컬 맛집 탐방)";
case TTUR_PADO -> "모험형 플랙스 인싸러 (에너지 넘치는 즉흥 끝판왕)";
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.dduru.gildongmu.survey.dto;

import java.util.List;

public record AvatarProfileResponse(
String description,
String personality,
String strength,
String tip,
List<String> tags
) {
}
12 changes: 6 additions & 6 deletions src/main/java/com/dduru/gildongmu/survey/dto/SurveyResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.dduru.gildongmu.survey.domain.TravelTendency;
import com.dduru.gildongmu.survey.domain.enums.AvatarType;
import com.dduru.gildongmu.survey.service.AvatarProfileProvider;
import com.dduru.gildongmu.survey.service.AvatarProfileService;

import java.util.List;

Expand All @@ -20,9 +20,9 @@ public record SurveyResponse(
String tip,
List<String> tags
) {
public static SurveyResponse from(TravelTendency travelTendency, AvatarProfileProvider avatarProfileProvider) {
public static SurveyResponse from(TravelTendency travelTendency, AvatarProfileService avatarProfileService) {
AvatarType avatarType = travelTendency.getAvatarType();
AvatarProfileProvider.AvatarProfile profile = avatarProfileProvider.getProfile(avatarType);
AvatarProfileResponse profile = avatarProfileService.getProfile(avatarType);

return new SurveyResponse(
travelTendency.getR().doubleValue(),
Expand All @@ -32,7 +32,7 @@ public static SurveyResponse from(TravelTendency travelTendency, AvatarProfilePr
avatarType.getCode(),
avatarType.name(),
avatarType.getText(),
avatarType.getDescription(),
profile.description(),
profile.personality(),
profile.strength(),
profile.tip(),
Expand All @@ -43,14 +43,14 @@ public static SurveyResponse from(TravelTendency travelTendency, AvatarProfilePr
public static SurveyResponse of(
double r, double w, double s, double p,
AvatarType avatarType,
AvatarProfileProvider.AvatarProfile profile
AvatarProfileResponse profile
) {
return new SurveyResponse(
r, w, s, p,
avatarType.getCode(),
avatarType.name(),
avatarType.getText(),
avatarType.getDescription(),
profile.description(),
profile.personality(),
profile.strength(),
profile.tip(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.dduru.gildongmu.survey.exception;

import com.dduru.gildongmu.common.exception.BusinessException;
import com.dduru.gildongmu.common.exception.ErrorCode;
import com.dduru.gildongmu.survey.domain.enums.AvatarType;

public class AvatarProfileNotFoundException extends BusinessException {
public AvatarProfileNotFoundException() {
super(ErrorCode.AVATAR_PROFILE_NOT_FOUND, "아바타 프로필을 찾을 수 없습니다");
}

public AvatarProfileNotFoundException(String message) {
super(ErrorCode.AVATAR_PROFILE_NOT_FOUND, message);
}

public static AvatarProfileNotFoundException of(AvatarType avatarType) {
return new AvatarProfileNotFoundException("아바타 프로필을 찾을 수 없습니다. avatarType=" + avatarType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dduru.gildongmu.survey.repository;

import com.dduru.gildongmu.survey.domain.AvatarProfile;
import com.dduru.gildongmu.survey.domain.enums.AvatarType;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface AvatarProfileRepository extends JpaRepository<AvatarProfile, Long> {
Optional<AvatarProfile> findByAvatarType(AvatarType avatarType);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.dduru.gildongmu.survey.service;

import com.dduru.gildongmu.common.util.JsonConverter;
import com.dduru.gildongmu.survey.domain.AvatarProfile;
import com.dduru.gildongmu.survey.domain.enums.AvatarType;
import com.dduru.gildongmu.survey.dto.AvatarProfileResponse;
import com.dduru.gildongmu.survey.exception.AvatarProfileNotFoundException;
import com.dduru.gildongmu.survey.repository.AvatarProfileRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AvatarProfileService {

private final AvatarProfileRepository avatarProfileRepository;
private final JsonConverter jsonConverter;

@Cacheable(value = "avatarProfiles", key = "#avatarType.name()")
public AvatarProfileResponse getProfile(AvatarType avatarType) {
log.info("아바타 프로필 조회 - avatarType: {}", avatarType);

AvatarProfile profile = avatarProfileRepository.findByAvatarType(avatarType)
.orElseThrow(() -> {
log.error("아바타 프로필을 찾을 수 없음 - avatarType: {}", avatarType);
return AvatarProfileNotFoundException.of(avatarType);
});

List<String> tags = jsonConverter.convertJsonToList(profile.getTags());

log.info("아바타 프로필 조회 완료 - avatarType: {}", avatarType);
return new AvatarProfileResponse(
profile.getDescription(),
profile.getPersonality(),
profile.getStrength(),
profile.getTip(),
tags
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.dduru.gildongmu.survey.domain.Survey;
import com.dduru.gildongmu.survey.domain.TravelTendency;
import com.dduru.gildongmu.survey.domain.enums.AvatarType;
import com.dduru.gildongmu.survey.dto.AvatarProfileResponse;
import com.dduru.gildongmu.survey.dto.SurveyRequest;
import com.dduru.gildongmu.survey.dto.SurveyResponse;
import com.dduru.gildongmu.survey.exception.SurveyResultNotFoundException;
Expand All @@ -30,7 +31,7 @@ public class SurveyService {
private final SurveyConverter surveyConverter;
private final TravelTendencyCalculator tendencyCalculator;
private final AvatarMatcher avatarMatcher;
private final AvatarProfileProvider avatarProfileProvider;
private final AvatarProfileService avatarProfileService;
private final UserRepository userRepository;

public SurveyResponse submitSurvey(Long userId, SurveyRequest request) {
Expand All @@ -44,7 +45,7 @@ public SurveyResponse submitSurvey(Long userId, SurveyRequest request) {

saveOrUpdateTravelTendency(user, scores, avatarType);

AvatarProfileProvider.AvatarProfile profile = avatarProfileProvider.getProfile(avatarType);
AvatarProfileResponse profile = avatarProfileService.getProfile(avatarType);

log.info("설문조사 제출 완료 - userId: {}, avatarType: {}, 점수: R={}, W={}, S={}, P={}",
userId, avatarType, scores.r(), scores.w(), scores.s(), scores.p());
Expand All @@ -60,7 +61,7 @@ public SurveyResponse getMySurveyResult(Long userId) {
.orElseThrow(SurveyResultNotFoundException::of);

log.info("설문 결과 조회 완료 - userId: {}, avatarType: {}", userId, travelTendency.getAvatarType());
return SurveyResponse.from(travelTendency, avatarProfileProvider);
return SurveyResponse.from(travelTendency, avatarProfileService);
}

private Survey saveOrUpdateSurvey(User user, SurveyRequest request) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ spring:
activate:
on-profile: dev
datasource:
url: jdbc:mysql://localhost:3307/dduru
url: jdbc:mysql://localhost:3307/dduru?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Seoul
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ springdoc:
tags-sorter: alpha
disable-swagger-default-url: true
validator-url: none
default-consumes-media-type: application/json
default-produces-media-type: application/json
default-consumes-media-type: application/json;charset=UTF-8
default-produces-media-type: application/json;charset=UTF-8
Loading