Skip to content

Commit

Permalink
Merge pull request #97 from UMC-TripPiece/refactor/92
Browse files Browse the repository at this point in the history
[refactor] 여행기, 지도 검색 관련 api 코드 리팩토링 및 중복  코드 개선, 커스텀 어노테이션 생성
  • Loading branch information
oyatplum authored Jan 5, 2025
2 parents 58b35d2 + 7e0e010 commit 2ba9800
Show file tree
Hide file tree
Showing 25 changed files with 327 additions and 218 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ dependencies {
implementation('org.projectlombok:lombok')
/* Email 전송 관련 */
implementation 'org.springframework.boot:spring-boot-starter-mail:3.1.2'

/* 메일 HTML 작성 관련 */
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
/* AOP 관련 */
implementation 'org.springframework.boot:spring-boot-starter-aop'
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public enum ErrorStatus implements BaseErrorCode {
NOT_FOUND_USER(HttpStatus.NOT_FOUND, "USER404", "해당 유저를 찾을 수 없습니다."),
NOT_FOUND_CITY(HttpStatus.NOT_FOUND, "CITY404", "해당 도시를 찾을 수 없습니다."),
NOT_FOUND_TRAVEL(HttpStatus.NOT_FOUND, "TRAVEL404", "해당 여행기를 찾을 수 없습니다."),
NOT_FOUND_COUNTRY(HttpStatus.NOT_FOUND, "COUNTRY404", "해당 국가를 찾을 수 없습니다."),
NOT_FOUND_CITY_COUNTRY(HttpStatus.NOT_FOUND, "CITY_COUNTRY404", "해당 도시와 국가를 찾을 수 없습니다."),

// s3 관련 오류
PICTURE_EXTENSION_ERROR(HttpStatus.BAD_REQUEST, "PICTURE400", "이미지의 확장자가 잘못되었습니다."),
Expand All @@ -37,16 +39,27 @@ public enum ErrorStatus implements BaseErrorCode {
TEXT_LENGTH_100_ERROR(HttpStatus.BAD_REQUEST, "TEXT401", "글자 수가 100자를 초과하였습니다."),

// 사용자 관련 오류
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER404", "존재하지 않는 사용자입니다."),
INVALID_PROFILE_IMAGE(HttpStatus.BAD_REQUEST, "PROFILE400", "잘못된 프로필 이미지 형식입니다."),
PROFILE_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "PROFILE500", "프로필 업데이트에 실패했습니다."),
INVALID_TOKEN(HttpStatus.BAD_REQUEST, "TOKEN401", "유효하지 않은 토큰입니다."),

// 여행 조각 관련 오류
NOT_FOUND_TRIPPIECE(HttpStatus.NOT_FOUND, "TRIPPIECE404", "여행 조각이 존재하지 않습니다"),
INVALID_TRIPPIECE_SORT_OPTION(HttpStatus.BAD_REQUEST, "TRIPPIECE401", "유효하지 않은 정렬 조건입니다."),

// 여행기 관련 오류
INVALID_TRAVEL_PARARM(HttpStatus.BAD_REQUEST, "TRAVEL400", "유요하지 않은 파라미터입니다.");
INVALID_TRAVEL_PARARM(HttpStatus.BAD_REQUEST, "TRAVEL400", "유효하지 않은 파라미터입니다."),
TRAVEL_INPROGRESS(HttpStatus.BAD_REQUEST, "TRAVEL400", "현재 진행 중인 여행기가 있습니다."),
MISSING_TRAVEL_THUMBNAIL(HttpStatus.BAD_REQUEST,"TRAVEL400","썸네일이 필요합니다."),
INVALID_TRAVEL_DATE(HttpStatus.BAD_REQUEST, "TRAVEL400", "여행 기간이 유효하지 않습니다."),
INVALID_TRAVEL_TITLE(HttpStatus.BAD_REQUEST, "TRAVEL400", "여행기 제목이 유효하지 않습니다."),
TRAVEL_COMPLETED(HttpStatus.BAD_REQUEST, "TRAVEL400", "해당 여행은 이미 종료되었습니다."),

// 요청 정보를 가지고 올 수 없을 때
REQUEST_INFO_NOT_FOUND(HttpStatus.NOT_FOUND, "REQUEST404", "요청 정보를 가져올 수 없습니다."),

// 지도 관련 오류
INVALID_CITY_COUNTRY_RELATION(HttpStatus.BAD_REQUEST, "MAP400", "유효하지 않은 도시와 국가 관계입니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,36 @@
public class ExceptionAdvice extends ResponseEntityExceptionHandler {


@ExceptionHandler
public ResponseEntity<Object> validation(ConstraintViolationException e, WebRequest request) {
String errorMessage = e.getConstraintViolations().stream()
.map(constraintViolation -> constraintViolation.getMessage())
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ApiResponse<Void>> handleConstraintViolationException(ConstraintViolationException ex) {
// 첫 번째 메시지 추출
String errorMessage = ex.getConstraintViolations()
.stream()
.findFirst()
.orElseThrow(() -> new RuntimeException("ConstraintViolationException 추출 도중 에러 발생"));
.map(violation -> violation.getMessage())
.orElse("유효성 검사에 실패했습니다.");

return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY,request);
return ResponseEntity.badRequest()
.body(ApiResponse.onFailure("VALIDATION_ERROR", errorMessage, null));
}


@Override
public ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode status, WebRequest request) {

// 모든 필드 에러 메시지 추출
Map<String, String> errors = new LinkedHashMap<>();
e.getBindingResult().getFieldErrors().forEach(fieldError -> {
String fieldName = fieldError.getField();
String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse("");
errors.put(fieldName, errorMessage);
});

e.getBindingResult().getFieldErrors().stream()
.forEach(fieldError -> {
String fieldName = fieldError.getField();
String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse("");
errors.merge(fieldName, errorMessage, (existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage);
});
// 사용자 정의 에러 응답 생성
ApiResponse<Object> body = ApiResponse.onFailure("VALIDATION_ERROR", "유효성 검사 실패", errors);

return handleExceptionInternalArgs(e,HttpHeaders.EMPTY,ErrorStatus.valueOf("_BAD_REQUEST"),request,errors);
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ErrorReasonDTO getErrorReason() {
return this.code.getReason();
}

public ErrorReasonDTO getErrorReasonHttpStatus(){
public ErrorReasonDTO getErrorReasonHttpStatus() {
return this.code.getReasonHttpStatus();
}
}
42 changes: 42 additions & 0 deletions src/main/java/umc/TripPiece/config/ValidatorConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package umc.TripPiece.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.JpaRepository;
import umc.TripPiece.apiPayload.code.status.ErrorStatus;
import umc.TripPiece.domain.TripPiece;
import umc.TripPiece.repository.CityRepository;
import umc.TripPiece.repository.MapRepository;
import umc.TripPiece.repository.TravelRepository;
import umc.TripPiece.repository.UserRepository;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ValidatorConfig {
@Bean
public Map<Class<?>, JpaRepository<?, Long>> repositoryMap(
UserRepository userRepository,
CityRepository cityRepository,
MapRepository mapRepository,
TravelRepository travelRepository
) {
Map<Class<?>, JpaRepository<?, Long>> map = new HashMap<>();
map.put(umc.TripPiece.domain.User.class, userRepository);
map.put(umc.TripPiece.domain.City.class, cityRepository);
map.put(umc.TripPiece.domain.Map.class, mapRepository);
map.put(umc.TripPiece.domain.Travel.class, travelRepository);
return map;
}

@Bean
public Map<Class<?>, ErrorStatus> errorStatusMap() {
Map<Class<?>, ErrorStatus> map = new HashMap<>();
map.put(umc.TripPiece.domain.User.class, ErrorStatus.NOT_FOUND_USER);
map.put(umc.TripPiece.domain.City.class, ErrorStatus.NOT_FOUND_CITY);
map.put(umc.TripPiece.domain.Map.class, ErrorStatus.NOT_FOUND_MAP);
map.put(umc.TripPiece.domain.Travel.class, ErrorStatus.NOT_FOUND_TRAVEL);
return map;
}
}
17 changes: 0 additions & 17 deletions src/main/java/umc/TripPiece/converter/CityConverter.java

This file was deleted.

10 changes: 10 additions & 0 deletions src/main/java/umc/TripPiece/converter/MapConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,14 @@ public static MapResponseDto.getMarkerResponse toMarkerResponseDto(Map map, Stri
.cityName(cityName)
.build();
}
public static MapResponseDto.searchDto toSearchDto(City city){
return new MapResponseDto.searchDto(
city.getName(),
city.getCountry().getName(),
city.getComment(),
city.getCountry().getCountryImage(),
city.getLogCount(),
city.getId()
);
}
}
2 changes: 1 addition & 1 deletion src/main/java/umc/TripPiece/repository/CityRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.List;

public interface CityRepository extends JpaRepository<City, Long> {
List<City> findByNameContainingIgnoreCase(String name);
List<City> findByNameIgnoreCase(String name);
List<City> findByCountryId(Long countryId);

@Query("SELECT COUNT(DISTINCT t.city) FROM Travel t WHERE t.user.id = :userId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
import java.util.List;

public interface CountryRepository extends JpaRepository<Country, Long> {
List<Country> findByNameContainingIgnoreCase(String name);
List<Country> findByNameIgnoreCase(String name);
}
50 changes: 0 additions & 50 deletions src/main/java/umc/TripPiece/service/CityService.java

This file was deleted.

4 changes: 2 additions & 2 deletions src/main/java/umc/TripPiece/service/ExploreService.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public class ExploreService {

@Transactional
public List<ExploreResponseDto.ExploreListDto> searchTravels(String query, String sort){
List<City> cities = cityRepository.findByNameContainingIgnoreCase(query);
List<Country> countries = countryRepository.findByNameContainingIgnoreCase(query);
List<City> cities = cityRepository.findByNameIgnoreCase(query);
List<Country> countries = countryRepository.findByNameIgnoreCase(query);
Set<Long> cityIds = new HashSet<>();
cities.forEach(city -> cityIds.add(city.getId()));

Expand Down
31 changes: 27 additions & 4 deletions src/main/java/umc/TripPiece/service/MapService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
import umc.TripPiece.domain.User;
import umc.TripPiece.domain.enums.Color;
import umc.TripPiece.domain.jwt.JWTUtil;
import umc.TripPiece.repository.MapRepository;
import umc.TripPiece.repository.CityRepository;
import umc.TripPiece.repository.TravelRepository;
import umc.TripPiece.repository.UserRepository;
import umc.TripPiece.repository.*;
import umc.TripPiece.web.dto.request.MapRequestDto;
import umc.TripPiece.web.dto.response.MapResponseDto;
import umc.TripPiece.web.dto.response.MapStatsResponseDto;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

Expand All @@ -33,6 +31,7 @@ public class MapService {
private final JWTUtil jwtUtil;
private final TravelRepository travelRepository;
private final UserRepository userRepository;
private final CountryRepository countryRepository;

// 유저별 맵 리스트를 조회하는 메소드
public List<MapResponseDto> getMapsByUserId(Long userId) {
Expand Down Expand Up @@ -116,4 +115,28 @@ public MapResponseDto updateMultipleMapColors(Long mapId, List<String> colorStri
Map updatedMap = mapRepository.save(map);
return MapConverter.toMapResponseDto(updatedMap);
}

public List<MapResponseDto.searchDto> searchCitiesCountry(String keyword) {
if (keyword == null || keyword.trim().isEmpty()) {
return Collections.emptyList(); // 빈 리스트를 반환
}

List<City> cities = cityRepository.findByNameIgnoreCase(keyword);
List<Country> countries = countryRepository.findByNameIgnoreCase(keyword);

List<MapResponseDto.searchDto> searched = new ArrayList<>();

if (!cities.isEmpty()){
searched.addAll(cities.stream().map(MapConverter::toSearchDto).toList());
}

if(!countries.isEmpty()){
countries.forEach(country -> {
List<City> citiesInCountry = cityRepository.findByCountryId(country.getId());
searched.addAll(citiesInCountry.stream().map(MapConverter::toSearchDto).toList());
});
}

return searched;
}
}
Loading

0 comments on commit 2ba9800

Please sign in to comment.