diff --git a/build.gradle b/build.gradle index 2d19b2c..8b03ef5 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,7 @@ dependencies { compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' + implementation 'org.hibernate:hibernate-spatial:6.6.1.Final' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/com/potatocake/everymoment/config/GeometryConfig.java b/src/main/java/com/potatocake/everymoment/config/GeometryConfig.java new file mode 100644 index 0000000..d9b6d39 --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/config/GeometryConfig.java @@ -0,0 +1,14 @@ +package com.potatocake.everymoment.config; + +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.PrecisionModel; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class GeometryConfig { + @Bean + public GeometryFactory geometryFactory() { + return new GeometryFactory(new PrecisionModel(), 4326); + } +} diff --git a/src/main/java/com/potatocake/everymoment/controller/DiaryController.java b/src/main/java/com/potatocake/everymoment/controller/DiaryController.java index 8d52071..02d2d1d 100644 --- a/src/main/java/com/potatocake/everymoment/controller/DiaryController.java +++ b/src/main/java/com/potatocake/everymoment/controller/DiaryController.java @@ -1,5 +1,6 @@ package com.potatocake.everymoment.controller; +import com.potatocake.everymoment.dto.LocationPoint; import com.potatocake.everymoment.dto.SuccessResponse; import com.potatocake.everymoment.dto.request.CommentRequest; import com.potatocake.everymoment.dto.request.DiaryAutoCreateRequest; @@ -87,8 +88,8 @@ public ResponseEntity> getMyDiaries( @RequestParam(required = false) String keyword, @Parameter(description = "이모지 필터") @RequestParam(required = false) String emoji, - @Parameter(description = "카테고리 ID") - @RequestParam(required = false) Long category, + @Parameter(description = "카테고리") + @RequestParam(required = false) String category, @Parameter(description = "특정 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, @Parameter(description = "시작 날짜") @@ -103,6 +104,7 @@ public ResponseEntity> getMyDiaries( @RequestParam(defaultValue = "10") int size ) { Long memberId = memberDetails.getId(); + DiaryFilterRequest diaryFilterRequest = DiaryFilterRequest.builder() .keyword(keyword) .emoji(emoji) @@ -110,7 +112,7 @@ public ResponseEntity> getMyDiaries( .date(date) .from(from) .until(until) - .bookmark(bookmark) + .isBookmark(bookmark) .key(key) .size(size) .build(); @@ -137,6 +139,22 @@ public ResponseEntity> getMyDiary( .body(SuccessResponse.ok(response)); } + @Operation(summary = "특정 일기 위경도 조회", description = "특정 일기에 있는 위경도를 조회합니다.") + @ApiResponse(responseCode = "200", description = "위경도 조회 성공") + @GetMapping("/{diaryId}/location") + public ResponseEntity> getLocation( + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "조회할 일기 ID", required = true) + @PathVariable Long diaryId) { + Long memberId = memberDetails.getId(); + + LocationPoint response = diaryService.getDiaryLocation(memberId, diaryId); + + return ResponseEntity.ok() + .body(SuccessResponse.ok(response)); + } + @Operation(summary = "일기 수정", description = "기존 일기를 수정합니다.") @ApiResponse(responseCode = "200", description = "일기 수정 성공") @PatchMapping("/{diaryId}") @@ -213,8 +231,8 @@ public ResponseEntity> getFriendDiaries( @RequestParam(required = false) String keyword, @Parameter(description = "이모지 필터") @RequestParam(required = false) String emoji, - @Parameter(description = "카테고리 ID") - @RequestParam(required = false) Long category, + @Parameter(description = "카테고리") + @RequestParam(required = false) String category, @Parameter(description = "특정 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, @Parameter(description = "시작 날짜") @@ -229,6 +247,7 @@ public ResponseEntity> getFriendDiaries( @RequestParam(defaultValue = "10") int size ) { Long memberId = memberDetails.getId(); + DiaryFilterRequest diaryFilterRequest = DiaryFilterRequest.builder() .keyword(keyword) .emoji(emoji) @@ -236,7 +255,7 @@ public ResponseEntity> getFriendDiaries( .date(date) .from(from) .until(until) - .bookmark(bookmark) + .isBookmark(bookmark) .key(key) .size(size) .build(); diff --git a/src/main/java/com/potatocake/everymoment/dto/LocationPoint.java b/src/main/java/com/potatocake/everymoment/dto/LocationPoint.java index 58baa90..a9ab297 100644 --- a/src/main/java/com/potatocake/everymoment/dto/LocationPoint.java +++ b/src/main/java/com/potatocake/everymoment/dto/LocationPoint.java @@ -1,16 +1,13 @@ package com.potatocake.everymoment.dto; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; @AllArgsConstructor @Getter +@Builder public class LocationPoint { private double latitude; private double longitude; - - @Override - public String toString() { - return latitude + "/" + longitude; - } } diff --git a/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java index 7675f25..720a07e 100644 --- a/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java +++ b/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java @@ -1,6 +1,10 @@ package com.potatocake.everymoment.dto.request; import java.time.LocalDate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import lombok.Builder; import lombok.Getter; @@ -9,11 +13,26 @@ public class DiaryFilterRequest { private String keyword; private String emoji; - private Long category; + private String category; private LocalDate date; private LocalDate from; private LocalDate until; - private Boolean bookmark; + private Boolean isBookmark; private int key; private int size; + + public List getEmojis() { + return (emoji != null && !emoji.isEmpty()) + ? Arrays.asList(emoji.split(",")) + : Collections.emptyList(); + } + + public List getCategories() { + return (category != null && !category.isEmpty()) + ? Arrays.stream(category.split(",")) + .map(Long::parseLong) + .collect(Collectors.toList()) + : Collections.emptyList(); + } + } diff --git a/src/main/java/com/potatocake/everymoment/dto/response/FriendDiaryResponse.java b/src/main/java/com/potatocake/everymoment/dto/response/FriendDiaryResponse.java index b442af3..47ea8da 100644 --- a/src/main/java/com/potatocake/everymoment/dto/response/FriendDiaryResponse.java +++ b/src/main/java/com/potatocake/everymoment/dto/response/FriendDiaryResponse.java @@ -16,8 +16,7 @@ public class FriendDiaryResponse { private List categories; private String locationName; private String emoji; - private List file; private String content; - private Integer likeCount; + private LikeCountResponse likeCount; private LocalDateTime createAt; } diff --git a/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java b/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java index 2b98a84..a92bc41 100644 --- a/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java +++ b/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java @@ -14,7 +14,6 @@ public class MyDiaryResponse { private String locationName; private boolean isBookmark; private String emoji; - private List file; private String content; private LocalDateTime createAt; } diff --git a/src/main/java/com/potatocake/everymoment/entity/Diary.java b/src/main/java/com/potatocake/everymoment/entity/Diary.java index a8abca1..23d7583 100644 --- a/src/main/java/com/potatocake/everymoment/entity/Diary.java +++ b/src/main/java/com/potatocake/everymoment/entity/Diary.java @@ -14,6 +14,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.locationtech.jts.geom.Point; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -32,9 +33,8 @@ public class Diary extends BaseTimeEntity { @Lob private String content; - //point는 mysql연결 뒤에 - @Column(length = 250, nullable = false) - private String locationPoint; // Java에서는 문자열로 처리 + @Column(nullable = false) + private Point locationPoint; @Column(length = 50, nullable = false) private String locationName; @@ -59,7 +59,7 @@ public void updateContent(String content) { } } - public void updateLocationPoint(String locationPoint) { + public void updateLocationPoint(Point locationPoint) { if (locationPoint != null) { this.locationPoint = locationPoint; } diff --git a/src/main/java/com/potatocake/everymoment/exception/ErrorResponse.java b/src/main/java/com/potatocake/everymoment/exception/ErrorResponse.java index 54b7db8..0358ebf 100644 --- a/src/main/java/com/potatocake/everymoment/exception/ErrorResponse.java +++ b/src/main/java/com/potatocake/everymoment/exception/ErrorResponse.java @@ -11,9 +11,9 @@ @JsonInclude(Include.NON_NULL) public class ErrorResponse { - private int code; - private String message; - private Map validation; + private final int code; + private final String message; + private final Map validation; @Builder public ErrorResponse(int code, String message) { diff --git a/src/main/java/com/potatocake/everymoment/repository/DiaryCategoryRepository.java b/src/main/java/com/potatocake/everymoment/repository/DiaryCategoryRepository.java index 2989173..2533727 100644 --- a/src/main/java/com/potatocake/everymoment/repository/DiaryCategoryRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/DiaryCategoryRepository.java @@ -1,9 +1,14 @@ package com.potatocake.everymoment.repository; +import com.potatocake.everymoment.entity.Diary; import com.potatocake.everymoment.entity.DiaryCategory; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface DiaryCategoryRepository extends JpaRepository { List findByCategoryId(Long categoryId); + + List findByDiary(Diary diary); + + void deleteByDiary(Diary diary); } diff --git a/src/main/java/com/potatocake/everymoment/repository/FileRepository.java b/src/main/java/com/potatocake/everymoment/repository/FileRepository.java index 04fac6d..93db866 100644 --- a/src/main/java/com/potatocake/everymoment/repository/FileRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/FileRepository.java @@ -9,6 +9,10 @@ public interface FileRepository extends JpaRepository { List findByDiaryId(Long diaryId); + List findByDiary(Diary diary); + + File findByDiaryAndOrder(Diary diary, int order); + void deleteByDiary(Diary diary); } diff --git a/src/main/java/com/potatocake/everymoment/security/MemberDetails.java b/src/main/java/com/potatocake/everymoment/security/MemberDetails.java index b4fedca..3a5dd31 100644 --- a/src/main/java/com/potatocake/everymoment/security/MemberDetails.java +++ b/src/main/java/com/potatocake/everymoment/security/MemberDetails.java @@ -17,7 +17,7 @@ public class MemberDetails implements UserDetails { private final Member member; - private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Override public Collection getAuthorities() { diff --git a/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java b/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java index 53a2951..63c7d5a 100644 --- a/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java +++ b/src/main/java/com/potatocake/everymoment/security/filter/LoginFilter.java @@ -33,7 +33,7 @@ public class LoginFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final MemberRepository memberRepository; - private boolean postOnly = true; + private final boolean postOnly = true; public LoginFilter(String filterProcessesUrl, ObjectMapper objectMapper, JwtUtil jwtUtil, MemberRepository memberRepository, AuthenticationManager authenticationManager) { diff --git a/src/main/java/com/potatocake/everymoment/service/CommentService.java b/src/main/java/com/potatocake/everymoment/service/CommentService.java index b0d7663..085ed0c 100644 --- a/src/main/java/com/potatocake/everymoment/service/CommentService.java +++ b/src/main/java/com/potatocake/everymoment/service/CommentService.java @@ -12,7 +12,6 @@ import com.potatocake.everymoment.repository.CommentRepository; import com.potatocake.everymoment.repository.DiaryRepository; import com.potatocake.everymoment.repository.MemberRepository; -import com.potatocake.everymoment.security.MemberDetails; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -20,8 +19,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/com/potatocake/everymoment/service/DiaryService.java b/src/main/java/com/potatocake/everymoment/service/DiaryService.java index 2e3747d..8293530 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiaryService.java +++ b/src/main/java/com/potatocake/everymoment/service/DiaryService.java @@ -1,10 +1,11 @@ package com.potatocake.everymoment.service; +import com.potatocake.everymoment.dto.LocationPoint; +import com.potatocake.everymoment.dto.request.CategoryRequest; import com.potatocake.everymoment.dto.request.DiaryAutoCreateRequest; import com.potatocake.everymoment.dto.request.DiaryFilterRequest; import com.potatocake.everymoment.dto.request.DiaryManualCreateRequest; import com.potatocake.everymoment.dto.response.CategoryResponse; -import com.potatocake.everymoment.dto.response.FileResponse; import com.potatocake.everymoment.dto.response.MyDiariesResponse; import com.potatocake.everymoment.dto.response.MyDiaryResponse; import com.potatocake.everymoment.dto.response.MyDiarySimpleResponse; @@ -12,19 +13,24 @@ import com.potatocake.everymoment.dto.response.ThumbnailResponse; import com.potatocake.everymoment.entity.Diary; import com.potatocake.everymoment.entity.DiaryCategory; +import com.potatocake.everymoment.entity.File; import com.potatocake.everymoment.entity.Member; import com.potatocake.everymoment.entity.Notification; import com.potatocake.everymoment.exception.ErrorCode; import com.potatocake.everymoment.exception.GlobalException; +import com.potatocake.everymoment.repository.CategoryRepository; import com.potatocake.everymoment.repository.DiaryCategoryRepository; import com.potatocake.everymoment.repository.DiaryRepository; +import com.potatocake.everymoment.repository.FileRepository; import com.potatocake.everymoment.repository.MemberRepository; import com.potatocake.everymoment.repository.NotificationRepository; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.Specification; @@ -40,6 +46,9 @@ public class DiaryService { private final DiaryCategoryRepository diaryCategoryRepository; private final NotificationRepository notificationRepository; private final MemberRepository memberRepository; + private final CategoryRepository categoryRepository; + private final FileRepository fileRepository; + private final GeometryFactory geometryFactory; // 자동 일기 저장 (LocationPoint, Name, Adress 만 저장) public NotificationResponse createDiaryAuto(Long memberId, DiaryAutoCreateRequest diaryAutoCreateRequest) { @@ -47,9 +56,14 @@ public NotificationResponse createDiaryAuto(Long memberId, DiaryAutoCreateReques Member currentMember = memberRepository.findById(memberId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); + double longitude = diaryAutoCreateRequest.getLocationPoint().getLongitude(); + double latitude = diaryAutoCreateRequest.getLocationPoint().getLatitude(); + + Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude)); + Diary diary = Diary.builder() .member(currentMember) - .locationPoint(diaryAutoCreateRequest.getLocationPoint().toString()) + .locationPoint(point) .locationName(diaryAutoCreateRequest.getLocationName()) .address(diaryAutoCreateRequest.getAddress()) .build(); @@ -86,10 +100,16 @@ public void createDiaryManual(Long memberId, DiaryManualCreateRequest diaryManua Member currentMember = memberRepository.findById(memberId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); + + double longitude = diaryManualCreateRequest.getLocationPoint().getLongitude(); + double latitude = diaryManualCreateRequest.getLocationPoint().getLatitude(); + + Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude)); + Diary diary = Diary.builder() .member(currentMember) .content(diaryManualCreateRequest.getContent()) - .locationPoint(diaryManualCreateRequest.getLocationPoint().toString()) + .locationPoint(point) .locationName(diaryManualCreateRequest.getLocationName()) .address(diaryManualCreateRequest.getAddress()) .emoji(diaryManualCreateRequest.getEmoji()) @@ -99,46 +119,51 @@ public void createDiaryManual(Long memberId, DiaryManualCreateRequest diaryManua Diary savedDiary = diaryRepository.save(diary); - Long diaryId = savedDiary.getId(); //카테고리 저장 - //파일 저장 + List categoryRequestList = diaryManualCreateRequest.getCategories(); + for (CategoryRequest categoryRequest : categoryRequestList) { + Long categoryId = categoryRequest.getCategoryId(); + + DiaryCategory diaryCategory = DiaryCategory.builder() + .diary(savedDiary) + .category(categoryRepository.findById(categoryId) + .map(category -> { + // Category가 현재 사용자의 소유인지 확인 + category.checkOwner(currentMember.getId()); + return category; + }) + .orElseThrow(() -> new GlobalException(ErrorCode.CATEGORY_NOT_FOUND))) + .build(); + + diaryCategoryRepository.save(diaryCategory); + + } } // 내 일기 전체 조회 (타임라인) @Transactional(readOnly = true) public MyDiariesResponse getMyDiaries(Long memberId, DiaryFilterRequest diaryFilterRequest) { - //member 가져옴 + // member 가져옴 Member currentMember = memberRepository.findById(memberId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); Page diaryPage; - if (diaryFilterRequest.getCategory() == null) { - // category가 null인 경우 - Specification spec = DiarySpecification.filterDiaries(diaryFilterRequest.getKeyword(), - diaryFilterRequest.getEmoji(), diaryFilterRequest.getDate(), diaryFilterRequest.getFrom(), - diaryFilterRequest.getUntil(), diaryFilterRequest.getBookmark()) - .and((root, query, builder) -> builder.equal(root.get("member"), currentMember)); - - diaryPage = diaryRepository.findAll(spec, - PageRequest.of(diaryFilterRequest.getKey(), diaryFilterRequest.getSize())); - } else { - // category가 있는 경우 - DiaryCategory에서 category 같은 것 찾음 - List diaryCategoryList = diaryCategoryRepository.findByCategoryId( - diaryFilterRequest.getCategory()); - - // Diary 중에 memberId같은 것 가져옴 - List DiaryIdList = diaryCategoryList.stream() - .filter(diaryCategory -> diaryCategory.getDiary().getMember() - .equals(currentMember)) // memberId가 일치하는 경우 필터링 - .map(diaryCategory -> diaryCategory.getDiary().getId()) - .collect(Collectors.toList()); - - // 가져온 DiaryId로 일기 찾음 - Specification spec = (root, query, builder) -> root.get("id").in(DiaryIdList); - diaryPage = diaryRepository.findAll(spec, - PageRequest.of(diaryFilterRequest.getKey(), diaryFilterRequest.getSize())); - } + List categoryIds = diaryFilterRequest.getCategories(); + List emojis = diaryFilterRequest.getEmojis(); + + Specification spec = DiarySpecification.filterDiaries( + diaryFilterRequest.getKeyword(), + emojis, + categoryIds, + diaryFilterRequest.getDate(), + diaryFilterRequest.getFrom(), + diaryFilterRequest.getUntil(), + diaryFilterRequest.getIsBookmark()) + .and((root, query, builder) -> builder.equal(root.get("member"), currentMember)); + + diaryPage = diaryRepository.findAll(spec, PageRequest.of(diaryFilterRequest.getKey(), diaryFilterRequest.getSize())); + List diaryDTOs = diaryPage.getContent().stream() .map(this::convertToMyDiarySimpleResponseDto) @@ -159,17 +184,54 @@ public MyDiaryResponse getMyDiary(Long memberId, Long diaryId) { return convertToMyDiaryResponseDto(diary); } + // 내 일기 위치 조회 + public LocationPoint getDiaryLocation(Long memberId, Long diaryId) { + Diary diary = getExistDiary(memberId, diaryId); + Point point = diary.getLocationPoint(); + + return LocationPoint.builder() + .latitude(point.getX()) + .longitude(point.getY()) + .build(); + } + // 내 일기 수정 public void updateDiary(Long memberId, Long diaryId, DiaryManualCreateRequest diaryManualCreateRequest) { Diary existingDiary = getExistDiary(memberId, diaryId); - //카테고리 업데이트 - //파일 업데이트 + // 카테고리 업데이트 + List categoryRequestList = diaryManualCreateRequest.getCategories(); + if (categoryRequestList != null && !categoryRequestList.isEmpty()) { + diaryCategoryRepository.deleteByDiary(existingDiary); + + for (CategoryRequest categoryRequest : categoryRequestList) { + Long categoryId = categoryRequest.getCategoryId(); + + DiaryCategory diaryCategory = DiaryCategory.builder() + .diary(existingDiary) + .category(categoryRepository.findById(categoryId) + .map(category -> { + category.checkOwner(memberId); + return category; + }) + .orElseThrow(() -> new GlobalException(ErrorCode.CATEGORY_NOT_FOUND))) + .build(); + + diaryCategoryRepository.save(diaryCategory); + } + } + + if (diaryManualCreateRequest.getLocationPoint() != null) { + double longitude = diaryManualCreateRequest.getLocationPoint().getLongitude(); + double latitude = diaryManualCreateRequest.getLocationPoint().getLatitude(); + + Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude)); + + existingDiary.updateLocationPoint(point); + } //다이어리 업데이트 existingDiary.updateContent(diaryManualCreateRequest.getContent()); - existingDiary.updateLocationPoint(diaryManualCreateRequest.getLocationPoint() != null - ? diaryManualCreateRequest.getLocationPoint().toString() : null); existingDiary.updateLocationName(diaryManualCreateRequest.getLocationName()); existingDiary.updateAddress(diaryManualCreateRequest.getAddress()); existingDiary.updateEmoji(diaryManualCreateRequest.getEmoji()); @@ -212,22 +274,14 @@ private Diary getExistDiary(Long memberId, Long diaryId) { //상세 조회시 일기DTO 변환 private MyDiaryResponse convertToMyDiaryResponseDto(Diary savedDiary) { - //카테고리 찾음 - CategoryResponse categoryResponse = CategoryResponse.builder() - .id(1L) - .categoryName("일상") - .build(); - List categoryResponseList = new ArrayList<>(); - categoryResponseList.add(categoryResponse); - - //파일 찾음 - FileResponse fileResponse = FileResponse.builder() - .id(1L) - .imageUrl("image1.url") - .order(1) - .build(); - List fileResponseList = new ArrayList<>(); - fileResponseList.add(fileResponse); + // 카테고리 찾음 + List diaryCategories = diaryCategoryRepository.findByDiary(savedDiary); + List categoryResponseList = diaryCategories.stream() + .map(diaryCategory -> CategoryResponse.builder() + .id(diaryCategory.getCategory().getId()) + .categoryName(diaryCategory.getCategory().getCategoryName()) + .build()) + .collect(Collectors.toList()); return MyDiaryResponse.builder() .id(savedDiary.getId()) @@ -236,7 +290,6 @@ private MyDiaryResponse convertToMyDiaryResponseDto(Diary savedDiary) { .locationName(savedDiary.getLocationName()) .isBookmark(savedDiary.isBookmark()) .emoji(savedDiary.getEmoji()) - .file(fileResponseList) .content(savedDiary.getContent()) .createAt(savedDiary.getCreateAt()) .build(); @@ -244,11 +297,14 @@ private MyDiaryResponse convertToMyDiaryResponseDto(Diary savedDiary) { //일기 전체 불러올 때, 일기DTO 변환 private MyDiarySimpleResponse convertToMyDiarySimpleResponseDto(Diary savedDiary) { - //파일 찾음 - ThumbnailResponse thumbnailResponse = ThumbnailResponse.builder() - .id(1L) - .imageUrl("image1.url") - .build(); + File thumbnailFile = fileRepository.findByDiaryAndOrder(savedDiary, 1); + ThumbnailResponse thumbnailResponse = null; + if (thumbnailFile != null) { + thumbnailResponse = ThumbnailResponse.builder() + .id(thumbnailFile.getId()) + .imageUrl(thumbnailFile.getImageUrl()) + .build(); + } return MyDiarySimpleResponse.builder() .id(savedDiary.getId()) diff --git a/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java b/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java index 7554307..3a9826d 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java +++ b/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java @@ -2,28 +2,37 @@ import com.potatocake.everymoment.entity.Diary; +import com.potatocake.everymoment.entity.DiaryCategory; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.util.List; import org.springframework.data.jpa.domain.Specification; import java.time.LocalDate; public class DiarySpecification { - public static Specification filterDiaries(String keyword, String emoji, LocalDate date, LocalDate from, LocalDate until, Boolean isBookmark) { + public static Specification filterDiaries( + String keyword, List emojis, List categories, + LocalDate date, LocalDate from, LocalDate until, Boolean isBookmark) { return (Root root, CriteriaQuery query, CriteriaBuilder builder) -> { Predicate predicate = builder.conjunction(); if (keyword != null) { predicate = builder.and(predicate, builder.like(root.get("content"), "%" + keyword + "%")); } - if (emoji != null) { - predicate = builder.and(predicate, builder.equal(root.get("emoji"), emoji)); + if (emojis != null && !emojis.isEmpty()) { + predicate = builder.and(predicate, root.get("emoji").in(emojis)); + } + + if (categories != null && !categories.isEmpty()) { + Join diaryCategoryJoin = root.join("diaryCategories"); + predicate = builder.and(predicate, diaryCategoryJoin.get("category").get("id").in(categories)); } - // 날짜 필터링 (null일 경우 오늘 날짜로 기본값 설정) LocalDate filterDate = (date != null) ? date : LocalDate.now(); predicate = builder.and(predicate, builder.equal(root.get("createAt").as(LocalDate.class), filterDate)); diff --git a/src/main/java/com/potatocake/everymoment/service/FriendDiaryService.java b/src/main/java/com/potatocake/everymoment/service/FriendDiaryService.java index 58292da..fb83595 100644 --- a/src/main/java/com/potatocake/everymoment/service/FriendDiaryService.java +++ b/src/main/java/com/potatocake/everymoment/service/FriendDiaryService.java @@ -2,22 +2,24 @@ import com.potatocake.everymoment.dto.request.DiaryFilterRequest; import com.potatocake.everymoment.dto.response.CategoryResponse; -import com.potatocake.everymoment.dto.response.FileResponse; import com.potatocake.everymoment.dto.response.FriendDiariesResponse; import com.potatocake.everymoment.dto.response.FriendDiaryResponse; import com.potatocake.everymoment.dto.response.FriendDiarySimpleResponse; +import com.potatocake.everymoment.dto.response.LikeCountResponse; import com.potatocake.everymoment.dto.response.ThumbnailResponse; import com.potatocake.everymoment.entity.Diary; import com.potatocake.everymoment.entity.DiaryCategory; +import com.potatocake.everymoment.entity.File; import com.potatocake.everymoment.entity.Friend; import com.potatocake.everymoment.entity.Member; import com.potatocake.everymoment.exception.ErrorCode; import com.potatocake.everymoment.exception.GlobalException; import com.potatocake.everymoment.repository.DiaryCategoryRepository; import com.potatocake.everymoment.repository.DiaryRepository; +import com.potatocake.everymoment.repository.FileRepository; import com.potatocake.everymoment.repository.FriendRepository; +import com.potatocake.everymoment.repository.LikeRepository; import com.potatocake.everymoment.repository.MemberRepository; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -35,6 +37,8 @@ public class FriendDiaryService { private final DiaryCategoryRepository diaryCategoryRepository; private final FriendRepository friendRepository; private final MemberRepository memberRepository; + private final FileRepository fileRepository; + private final LikeRepository likeRepository; //친구 일기 조회 public FriendDiariesResponse getFriendDiaries(Long memberId, DiaryFilterRequest diaryFilterRequest) { @@ -48,32 +52,21 @@ public FriendDiariesResponse getFriendDiaries(Long memberId, DiaryFilterRequest Page diaryPage; - if (diaryFilterRequest.getCategory() == null) { - // category가 null인 경우 - Specification spec = DiarySpecification.filterDiaries(diaryFilterRequest.getKeyword(), - diaryFilterRequest.getEmoji(), diaryFilterRequest.getDate(), diaryFilterRequest.getFrom(), - diaryFilterRequest.getUntil(), diaryFilterRequest.getBookmark()) - .and((root, query, builder) -> root.get("member").in(friendIdList)); // memberIds 목록에서 검색 - - diaryPage = diaryRepository.findAll(spec, - PageRequest.of(diaryFilterRequest.getKey(), diaryFilterRequest.getSize())); - } else { - // category가 있는 경우 - DiaryCategory에서 category 같은 것 찾음 - List diaryCategories = diaryCategoryRepository.findByCategoryId( - diaryFilterRequest.getCategory()); - - // Diary중에 memberId같은 것 가져옴 - List filteredDiaryIds = diaryCategories.stream() - .filter(diaryCategory -> friendIdList.contains( - diaryCategory.getDiary().getMember())) // memberIds 목록에서 필터링 - .map(diaryCategory -> diaryCategory.getDiary().getId()) - .collect(Collectors.toList()); - - // 가져온 diaryId로 일기 찾음 - Specification spec = (root, query, builder) -> root.get("id").in(filteredDiaryIds); - diaryPage = diaryRepository.findAll(spec, - PageRequest.of(diaryFilterRequest.getKey(), diaryFilterRequest.getSize())); - } + // 카테고리와 이모지가 여러 개 전달될 수 있으므로 이를 리스트로 변환 + List categoryIds = diaryFilterRequest.getCategories(); + List emojis = diaryFilterRequest.getEmojis(); + + Specification spec = DiarySpecification.filterDiaries( + diaryFilterRequest.getKeyword(), + emojis, + categoryIds, + diaryFilterRequest.getDate(), + diaryFilterRequest.getFrom(), + diaryFilterRequest.getUntil(), + diaryFilterRequest.getIsBookmark()) + .and((root, query, builder) -> root.get("member").in(friendIdList)); + + diaryPage = diaryRepository.findAll(spec, PageRequest.of(diaryFilterRequest.getKey(), diaryFilterRequest.getSize())); List friendDiarySimpleResponseList = diaryPage.getContent().stream() .map(this::convertToFriendDiariesResponseDTO) @@ -81,12 +74,10 @@ public FriendDiariesResponse getFriendDiaries(Long memberId, DiaryFilterRequest Integer nextPage = diaryPage.hasNext() ? diaryFilterRequest.getKey() + 1 : null; - FriendDiariesResponse friendDiariesResponse = FriendDiariesResponse.builder() + return FriendDiariesResponse.builder() .diaries(friendDiarySimpleResponseList) .next(nextPage) .build(); - - return friendDiariesResponse; } // 친구 다이어리 하나 조회 @@ -106,34 +97,30 @@ public FriendDiaryResponse getFriendDiary(Long memberId, Long diaryId) { if (!friendIdList.contains(diary.getMember())) { throw new GlobalException(ErrorCode.FRIEND_NOT_FOUND); } - //카테고리 찾음 - CategoryResponse categoryResponseDTO = CategoryResponse.builder() - .id(1L) - .categoryName("일상") - .build(); - List categoryResponseDTOList = new ArrayList<>(); - categoryResponseDTOList.add(categoryResponseDTO); - - //파일 찾음 - FileResponse fileResponse = FileResponse.builder() - .id(1L) - .imageUrl("image1.url") - .order(1) - .build(); - List fileResponseDTOList = new ArrayList<>(); - fileResponseDTOList.add(fileResponse); + + // 카테고리 찾음 + List diaryCategories = diaryCategoryRepository.findByDiary(diary); + List categoryResponseList = diaryCategories.stream() + .map(diaryCategory -> CategoryResponse.builder() + .id(diaryCategory.getCategory().getId()) + .categoryName(diaryCategory.getCategory().getCategoryName()) + .build()) + .collect(Collectors.toList()); //like 갯수 반환 - Integer likeCount = 11; + Long likeCount = likeRepository.countByDiary(diary); + + LikeCountResponse count = LikeCountResponse.builder() + .likeCount(likeCount) + .build(); FriendDiaryResponse diaryResponseDTO = FriendDiaryResponse.builder() .id(diary.getId()) - .categories(categoryResponseDTOList) + .categories(categoryResponseList) .locationName(diary.getLocationName()) .emoji(diary.getEmoji()) - .file(fileResponseDTOList) .content(diary.getContent()) - .likeCount(likeCount) + .likeCount(count) .createAt(diary.getCreateAt()) .build(); @@ -142,11 +129,14 @@ public FriendDiaryResponse getFriendDiary(Long memberId, Long diaryId) { //친구 일기 DTO변환 private FriendDiarySimpleResponse convertToFriendDiariesResponseDTO(Diary savedDiary) { - //파일 찾음 - ThumbnailResponse thumbnailResponse = ThumbnailResponse.builder() - .id(1L) - .imageUrl("image1.url") - .build(); + File thumbnailFile = fileRepository.findByDiaryAndOrder(savedDiary, 1); + ThumbnailResponse thumbnailResponse = null; + if (thumbnailFile != null) { + thumbnailResponse = ThumbnailResponse.builder() + .id(thumbnailFile.getId()) + .imageUrl(thumbnailFile.getImageUrl()) + .build(); + } return FriendDiarySimpleResponse.builder() .id(savedDiary.getId()) diff --git a/src/main/java/com/potatocake/everymoment/service/FriendService.java b/src/main/java/com/potatocake/everymoment/service/FriendService.java index 8238bd1..fe2e327 100644 --- a/src/main/java/com/potatocake/everymoment/service/FriendService.java +++ b/src/main/java/com/potatocake/everymoment/service/FriendService.java @@ -6,11 +6,13 @@ import com.potatocake.everymoment.dto.response.OneFriendDiariesResponse; import com.potatocake.everymoment.dto.response.ThumbnailResponse; import com.potatocake.everymoment.entity.Diary; +import com.potatocake.everymoment.entity.File; import com.potatocake.everymoment.entity.Friend; import com.potatocake.everymoment.entity.Member; import com.potatocake.everymoment.exception.ErrorCode; import com.potatocake.everymoment.exception.GlobalException; import com.potatocake.everymoment.repository.DiaryRepository; +import com.potatocake.everymoment.repository.FileRepository; import com.potatocake.everymoment.repository.FriendRepository; import com.potatocake.everymoment.repository.MemberRepository; import java.time.LocalDate; @@ -32,6 +34,7 @@ public class FriendService { private final FriendRepository friendRepository; private final MemberRepository memberRepository; private final DiaryRepository diaryRepository; + private final FileRepository fileRepository; //특정 친구 일기 조회 @Transactional(readOnly = true) @@ -49,7 +52,7 @@ public OneFriendDiariesResponse OneFriendDiariesResponse(Long memberid, Long fri Pageable pageable = PageRequest.of(key, size); Page diaries = diaryRepository.findAll( - DiarySpecification.filterDiaries(null, null, date, null, null, null) + DiarySpecification.filterDiaries(null, null, null, date, null, null, null) .and((root, query, builder) -> builder.equal(root.get("member").get("id"), friendId)), pageable); @@ -124,10 +127,14 @@ public void toggleCloseFriend(Long memberId, Long friendId) { //다이어리 DTO 변환 private FriendDiarySimpleResponse convertToFriendDiariesResponseDTO(Diary savedDiary) { //파일 찾음 - ThumbnailResponse thumbnailResponse = ThumbnailResponse.builder() - .id(1L) - .imageUrl("image1.url") - .build(); + File thumbnailFile = fileRepository.findByDiaryAndOrder(savedDiary, 1); + ThumbnailResponse thumbnailResponse = null; + if (thumbnailFile != null) { + thumbnailResponse = ThumbnailResponse.builder() + .id(thumbnailFile.getId()) + .imageUrl(thumbnailFile.getImageUrl()) + .build(); + } return FriendDiarySimpleResponse.builder() .id(savedDiary.getId())