diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/api/SearchHistoryController.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/api/SearchHistoryController.java deleted file mode 100644 index 28607f4..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/api/SearchHistoryController.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.api; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.solucitation.midpoint_backend.domain.community_board.dto.PostResponseDto; -import com.solucitation.midpoint_backend.domain.history.dto.SearchHistoryRequestDto; -import com.solucitation.midpoint_backend.domain.history.dto.SearchHistoryResponseDto; -import com.solucitation.midpoint_backend.domain.history.service.SearchHistoryService; -import com.solucitation.midpoint_backend.domain.member.dto.ValidationErrorResponse; -import com.solucitation.midpoint_backend.domain.member.entity.Member; -import com.solucitation.midpoint_backend.domain.member.service.MemberService; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validator; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.*; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -@Slf4j -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/search-history") -public class SearchHistoryController { - private final MemberService memberService; - private final ObjectMapper objectMapper; - private final Validator validator; - private final SearchHistoryService searchHistoryService; - - /** - * 검색 결과에서 저장하고 싶은 장소를 리스트로 받아와서 저장합니다. - * - * @param authentication 인증 정보 - * @param neighborhood 동 정보 - * @param historyDtoJson 장소 정보 - * @return 장소를 성공적으로 저장하면 201 CREATED를 반환합니다. - * 로그인을 하지 않고 시도 시 401 UNAUTHORIZED를 반환합니다. - * 사용자를 찾을 수 없는 경우 404 NOT_FOUND를 반환합니다. - * 저장하려는 장소 정보에 문제가 있을 때 (필드가 공백이거나 null일 경우) 400 BAD_REQUEST를 반환합니다. - * 기타 사유로 저장 실패 시 500 INTERNAL_SERVER_ERROR를 반환합니다. - * @throws JsonProcessingException - */ - @PostMapping("") - public ResponseEntity saveHistory(Authentication authentication, - @RequestPart("neighborhood") String neighborhood, - @RequestPart("historyDto") String historyDtoJson) throws JsonProcessingException { - try { - if (neighborhood.isEmpty()|| neighborhood.isBlank()) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(Map.of("error", "EMPTY_FIELD", "message", "동 정보가 누락되었습니다.")); - } - - List placeList = objectMapper.readValue(historyDtoJson, - objectMapper.getTypeFactory().constructCollectionType(List.class, SearchHistoryRequestDto.class)); - - if (authentication == null || !authentication.isAuthenticated()) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(Map.of("error", "UNAUTHORIZED", "message", "해당 서비스를 이용하기 위해서는 로그인이 필요합니다.")); - } - - String memberEmail = authentication.getName(); - Member member = memberService.getMemberByEmail(memberEmail); - if (member == null) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(Map.of("error", "USER_NOT_FOUND", "message", "사용자를 찾을 수 없습니다.")); - } - - for (SearchHistoryRequestDto place : placeList) { // 리스트 내의 모든 요소에 대한 검증 수행 - Set> violations = validator.validate(place); - if (!violations.isEmpty()) { - List fieldErrors = violations.stream() - .map(violation -> new ValidationErrorResponse.FieldError(violation.getPropertyPath().toString(), violation.getMessage())) - .collect(Collectors.toList()); - ValidationErrorResponse errorResponse = new ValidationErrorResponse(fieldErrors); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); - } - } - searchHistoryService.save(neighborhood, memberEmail, placeList); - return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("message", "장소를 저장하였습니다.")); - - } catch (IllegalArgumentException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); - } catch (RuntimeException e) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(Map.of("error", "USER_NOT_FOUND", "message", "사용자를 찾을 수 없습니다.")); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("error", e.getMessage(), "message", "장소 저장 중 오류가 발생하였습니다.")); - } - } - - /** - * 사용자가 지금까지 저장한 검색 정보를 전부 최신순부터 가져옵니다. - * - * @param authentication 인증 정보 - * @return 검색 기록 조회가 성공하면 200 OK와 결과 리스트를 반환합니다. - * 로그인을 하지 않고 시도 시 401 UNAUTHORIZED를 반환합니다. - * 사용자를 찾을 수 없는 경우 404 NOT_FOUND를 반환합니다. - * 기타 사유로 저장 실패 시 500 INTERNAL_SERVER_ERROR를 반환합니다. - */ - @GetMapping("") - public ResponseEntity getHistory(Authentication authentication) { - try { - if (authentication == null || !authentication.isAuthenticated()) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(Map.of("error", "UNAUTHORIZED", "message", "해당 서비스를 이용하기 위해서는 로그인이 필요합니다.")); - } - - String memberEmail = authentication.getName(); - Member member = memberService.getMemberByEmail(memberEmail); - if (member == null) { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .body(Map.of("error", "USER_NOT_FOUND", "message", "사용자를 찾을 수 없습니다.")); - } - - List searchHistoryResponseDtos = searchHistoryService.getHistory(member); - return ResponseEntity.ok(searchHistoryResponseDtos); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("error", e.getMessage(), "message", "검색 기록 조회 중 오류가 발생하였습니다.")); - } - } - -} diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/dto/SearchHistoryRequestDto.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/dto/SearchHistoryRequestDto.java deleted file mode 100644 index ef988b9..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/dto/SearchHistoryRequestDto.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.dto; - -import jakarta.validation.constraints.NotBlank; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class SearchHistoryRequestDto { - @NotBlank(message = "장소ID가 누락되었습니다.") - private String placeId; - - @NotBlank(message = "장소명이 누락되었습니다.") - private String placeName; - - @NotBlank(message = "주소가 누락되었습니다.") - private String placeAddress; - - private String imageUrl; -} diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/dto/SearchHistoryResponseDto.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/dto/SearchHistoryResponseDto.java deleted file mode 100644 index 69370c9..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/dto/SearchHistoryResponseDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class SearchHistoryResponseDto { - private String neighborhood; - private LocalDateTime serachTime; - private List places = new ArrayList<>(); - - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class PlaceDto { - private String placeId; // 장소의 고유 ID - private String placeName; // 장소의 이름 - private String placeAddress; // 장소의 주소 - private String imageUrl; // 장소 이미지 - } -} diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/entity/PlaceInfo.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/entity/PlaceInfo.java deleted file mode 100644 index 8af41e1..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/entity/PlaceInfo.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.entity; - -import jakarta.persistence.*; -import lombok.*; - -@Entity -@Table(name = "place_info") -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Builder -public class PlaceInfo { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "place_info_id") - private Long id; - - @Column(name="place_id", nullable = false) - private String placeId; - - @ManyToOne - @JoinColumn(name = "search_history_id", nullable = false) - private SearchHistory searchHistory; - - @Column(name = "place_name", nullable = false) - private String name; - - @Column(name = "place_address", nullable = false) - private String address; - - @Column(name="place_image_url", nullable = false) - private String imageUrl; -} diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/entity/SearchHistory.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/entity/SearchHistory.java deleted file mode 100644 index eec409e..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/entity/SearchHistory.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.entity; - -import com.solucitation.midpoint_backend.domain.member.entity.Member; -import jakarta.persistence.*; -import lombok.*; -import org.hibernate.annotations.CreationTimestamp; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -import static jakarta.persistence.FetchType.LAZY; - -@Entity -@Table(name = "search_history") -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Builder -public class SearchHistory { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "search_history_id") - private Long id; - - @Column(name="neighborhood", nullable = false) - private String neighborhood; // 동 정보 ex. 청파동 - - @ManyToOne(fetch = LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member member; - - @CreationTimestamp - @Column(name = "search_date", nullable = false, updatable = false) - private LocalDateTime searchDate; - - @OneToMany(mappedBy = "searchHistory", fetch = LAZY, cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List placeList = new ArrayList<>(); -} diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/repository/SearchHistoryRepository.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/repository/SearchHistoryRepository.java deleted file mode 100644 index 8d5b42a..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/repository/SearchHistoryRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.repository; - -import com.solucitation.midpoint_backend.domain.history.entity.SearchHistory; -import com.solucitation.midpoint_backend.domain.member.entity.Member; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public interface SearchHistoryRepository extends JpaRepository { - List findByMemberOrderBySearchDateDesc(Member member); -} \ No newline at end of file diff --git a/src/main/java/com/solucitation/midpoint_backend/domain/history/service/SearchHistoryService.java b/src/main/java/com/solucitation/midpoint_backend/domain/history/service/SearchHistoryService.java deleted file mode 100644 index 25a7800..0000000 --- a/src/main/java/com/solucitation/midpoint_backend/domain/history/service/SearchHistoryService.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.solucitation.midpoint_backend.domain.history.service; - -import com.solucitation.midpoint_backend.domain.history.dto.SearchHistoryRequestDto; -import com.solucitation.midpoint_backend.domain.history.dto.SearchHistoryResponseDto; -import com.solucitation.midpoint_backend.domain.history.entity.PlaceInfo; -import com.solucitation.midpoint_backend.domain.history.entity.SearchHistory; -import com.solucitation.midpoint_backend.domain.history.repository.SearchHistoryRepository; -import com.solucitation.midpoint_backend.domain.member.entity.Member; -import com.solucitation.midpoint_backend.domain.member.service.MemberService; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - - -@Service -@RequiredArgsConstructor -public class SearchHistoryService { - @Value("${cloud.aws.s3.bucket}") - private String bucket; - - private final String defaultImageUrl = String.format("https://%s.s3.%s.amazonaws.com/%s", bucket, "ap-northeast-2", "place_default_image.png"); - private final SearchHistoryRepository searchHistoryRepository; - private final MemberService memberService; - - @Transactional - public void save(String neighborhood, String memberEmail, List placeDtos) { - Member member = memberService.getMemberByEmail(memberEmail); - - SearchHistory searchHistory = SearchHistory.builder() - .member(member) - .neighborhood(neighborhood) - .build(); - - for (SearchHistoryRequestDto dto : placeDtos) { - String imageUrl = dto.getImageUrl(); - if (imageUrl == null || imageUrl.trim().isEmpty()) { // 이미지 url이 없거나 공백 - imageUrl = defaultImageUrl; - } - PlaceInfo placeInfo = PlaceInfo.builder() - .name(dto.getPlaceName()) - .placeId(dto.getPlaceId()) - .address(dto.getPlaceAddress()) - .imageUrl(imageUrl) - .searchHistory(searchHistory) // searchHistory 필드 설정 - .build(); - searchHistory.getPlaceList().add(placeInfo); - } - searchHistoryRepository.save(searchHistory); - } - - @Transactional(readOnly = true) - public List getHistory(Member member) { - List histories = searchHistoryRepository.findByMemberOrderBySearchDateDesc(member); - - return histories.stream() - .map(history -> new SearchHistoryResponseDto( - history.getNeighborhood(), - history.getSearchDate(), - history.getPlaceList().stream() - .map(placeInfo -> new SearchHistoryResponseDto.PlaceDto( - placeInfo.getPlaceId(), - placeInfo.getName(), - placeInfo.getAddress(), - placeInfo.getImageUrl() - )) - .collect(Collectors.toList()) - )) - .collect(Collectors.toList()); - } -}