Skip to content

Commit

Permalink
Merge pull request #59 from Move-Log/develop
Browse files Browse the repository at this point in the history
Merge develop to main
  • Loading branch information
EunbeenDev authored Jan 15, 2025
2 parents e5871fe + f9a3d9f commit 71d317b
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 2 deletions.
30 changes: 30 additions & 0 deletions src/main/java/com/movelog/domain/news/application/NewsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.movelog.domain.news.dto.request.NewsHeadLineReq;
import com.movelog.domain.news.dto.response.HeadLineRes;
import com.movelog.domain.news.dto.response.RecentKeywordsRes;
import com.movelog.domain.news.dto.response.RecentNewsRes;
import com.movelog.domain.record.domain.Keyword;
import com.movelog.domain.record.domain.VerbType;
import com.movelog.domain.record.exception.KeywordNotFoundException;
Expand All @@ -17,10 +18,13 @@
import com.movelog.global.config.security.token.UserPrincipal;
import com.movelog.global.util.S3Util;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -81,6 +85,32 @@ public List<RecentKeywordsRes> getRecentKeywords(UserPrincipal userPrincipal) {
.toList();
}

public List<RecentNewsRes> getRecentNews(UserPrincipal userPrincipal, Integer page) {
User user = validateUser(userPrincipal);
// User user = userRepository.findById(5L).orElseThrow(UserNotFoundException::new);

//페이징 객체 생성
Pageable pageable = PageRequest.of(page, 15);

// 최근 일주일간 생성한 뉴스 목록 조회
LocalDateTime createdAt = LocalDateTime.now().minusDays(7);
List<News> recentNews = newsRepository.findRecentNewsByUser(user, createdAt, pageable);

// 최신순 정렬
recentNews.sort((n1, n2) -> n2.getCreatedAt().compareTo(n1.getCreatedAt()));

return recentNews.stream()
.map(news -> RecentNewsRes.builder()
.newsId(news.getNewsId())
.newsImageUrl(news.getNewsUrl())
.headLine(news.getHeadLine())
.noun(news.getKeyword().getKeyword())
.verb(VerbType.getStringVerbType(news.getKeyword().getVerbType()))
.createdAt(news.getCreatedAt())
.build())
.toList();
}


// User 정보 검증
private User validateUser(UserPrincipal userPrincipal) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
package com.movelog.domain.news.domain.repository;

import com.movelog.domain.news.domain.News;
import com.movelog.domain.user.domain.User;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;

@Repository
public interface NewsRepository extends JpaRepository<News, Long> {

@Query("SELECT n FROM News n " +
"JOIN n.keyword k " +
"WHERE k.user = :user " +
"AND n.createdAt > :createdAt " +
"ORDER BY n.createdAt DESC")
List<News> findRecentNewsByUser(
@Param("user") User user,
@Param("createdAt") LocalDateTime createdAt,
Pageable pageable
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.movelog.domain.news.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class RecentNewsRes {

@Schema( type = "int", example ="1", description="뉴스 ID")
private Long newsId;

@Schema( type = "String", example ="https://movelog.s3.ap-northeast-2.amazonaws.com/record/2021-08-01/1.jpg", description="뉴스 이미지 url")
private String newsImageUrl;

@Schema( type = "String", example ="5년 만의 첫 도전, 무엇이 그를 움직이게 했나?", description="뉴스 헤드라인 추천 내용입니다.")
private String headLine;

@Schema( type = "String", example = "헬스", description="명사")
private String noun;

@Schema( type = "String", example = "했어요", description="동사")
private String verb;

@Schema( type = "LocalDateTime", example ="2021-08-01T00:00:00", description="뉴스 생성 시간")
private LocalDateTime createdAt;


}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.movelog.domain.news.dto.request.NewsHeadLineReq;
import com.movelog.domain.news.dto.response.HeadLineRes;
import com.movelog.domain.news.dto.response.RecentKeywordsRes;
import com.movelog.domain.news.dto.response.RecentNewsRes;
import com.movelog.global.config.security.token.CurrentUser;
import com.movelog.global.config.security.token.UserPrincipal;
import com.movelog.global.payload.Message;
Expand Down Expand Up @@ -55,7 +56,7 @@ public ResponseEntity<?> createHeadLine(
}


@Operation(summary = "뉴스 생성 및 저장 API(기존 이미지 기록 기반)", description = "사용자의 기존 기록 이미지로 뉴스를 생성합니다.")
@Operation(summary = "뉴스 생성 및 저장 API", description = "새로운 뉴스를 생성하고 저장합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "뉴스 생성 및 저장 성공",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = Message.class))),
Expand Down Expand Up @@ -90,6 +91,28 @@ public ResponseEntity<?> getRecentKeywords(
return ResponseEntity.ok(ApiResponseUtil.success(response));
}

@Operation(summary = "최근 뉴스 목록 조회 API", description = "최근 일주일간 생성한 뉴스 목록을 1페이지 당 15개씩 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "최근 뉴스 목록 조회 성공",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "array", implementation = RecentNewsRes.class))),
@ApiResponse(responseCode = "400", description = "최근 뉴스 목록 조회 실패",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping("/week")
public ResponseEntity<?> getRecentNews(
@Parameter(description = "Access Token을 입력해주세요.", required = true)
@AuthenticationPrincipal UserPrincipal userPrincipal,
@Parameter(description = "뉴스 목록의 페이지 번호를 입력해주세요. **Page는 1부터 시작됩니다!**", required = true)
@RequestParam(value = "page", required = false, defaultValue = "0") Integer page
) {
List<RecentNewsRes> response = newsService.getRecentNews(userPrincipal, page);
return ResponseEntity.ok(ApiResponseUtil.success(response));
}







Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.movelog.domain.record.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class SearchKeywordReq {
@Schema( type = "String", example ="헬스", description="검색할 명사")
private String searchKeyword;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.movelog.domain.record.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class SearchKeywordRes {

@Schema( type = "int", example = "1", description="키워드 ID")
private Long keywordId;

@Schema( type = "String", example ="헬스", description="검색어가 포함된 명사")
private String noun;

@Schema( type = "String", example ="했어요", description="명사에 해당하는 동사")
private String verb;

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.movelog.domain.record.presentation;

import com.movelog.domain.record.dto.request.CreateRecordReq;
import com.movelog.domain.record.dto.request.SearchKeywordReq;
import com.movelog.domain.record.dto.response.RecentRecordImagesRes;
import com.movelog.domain.record.dto.response.SearchKeywordRes;
import com.movelog.domain.record.dto.response.TodayRecordStatus;
import com.movelog.domain.record.service.RecordService;
import com.movelog.global.config.security.token.UserPrincipal;
Expand Down Expand Up @@ -81,5 +83,23 @@ public ResponseEntity<?> retrieveRecentRecordImages(
}


@Operation(summary = "기록 내 명사 검색 API", description = "사용자가 생성한 기록 중 명사를 통해 동사-명사 쌍을 검색하는 API입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "기록 내 명사 검색 성공",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "array", implementation = SearchKeywordRes.class))),
@ApiResponse(responseCode = "400", description = "기록 내 명사 검색 실패",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping("/search")
public ResponseEntity<?> searchKeyword(
@Parameter(description = "User의 토큰을 입력해주세요.", required = true) @AuthenticationPrincipal UserPrincipal userPrincipal,
@Parameter(description = "검색할 명사를 입력해주세요.", required = true) @RequestBody SearchKeywordReq searchKeywordReq
) {
List<SearchKeywordRes> result = recordService.searchKeyword(userPrincipal, searchKeywordReq);
return ResponseEntity.ok(ApiResponseUtil.success(result));
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public interface KeywordRepository extends JpaRepository<Keyword,Long> {
boolean existsByUserAndKeywordAndVerbType(User user, String noun, VerbType verbType);

Keyword findByUserAndKeywordAndVerbType(User user, String noun, VerbType verbType);

List<Keyword> findAllByUserAndKeywordContaining(User user, String keyword);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.movelog.domain.record.domain.Record;
import com.movelog.domain.record.domain.VerbType;
import com.movelog.domain.record.dto.request.CreateRecordReq;
import com.movelog.domain.record.dto.request.SearchKeywordReq;
import com.movelog.domain.record.dto.response.RecentRecordImagesRes;
import com.movelog.domain.record.dto.response.SearchKeywordRes;
import com.movelog.domain.record.dto.response.TodayRecordStatus;
import com.movelog.domain.record.repository.KeywordRepository;
import com.movelog.domain.record.repository.RecordRepository;
Expand All @@ -19,6 +21,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.text.Collator;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
Expand Down Expand Up @@ -131,6 +134,33 @@ public List<RecentRecordImagesRes> retrieveRecentRecordImages(UserPrincipal user

}


public List<SearchKeywordRes> searchKeyword(UserPrincipal userPrincipal, SearchKeywordReq searchKeywordReq) {
User user = validUserById(userPrincipal.getId());
// User user = validUserById(5L);
String keyword = searchKeywordReq.getSearchKeyword();
List<Keyword> keywords = keywordRepository.findAllByUserAndKeywordContaining(user, keyword);
keywords.forEach(k -> log.info("Keyword in DB: {}", k.getKeyword()));

// Collator 생성
Collator collator = Collator.getInstance(Locale.KOREA);

// Collator를 이용한 오름차순 정렬
List<SearchKeywordRes> sortedResults = keywords.stream()
.map(k -> SearchKeywordRes.builder()
.keywordId(k.getKeywordId())
.noun(k.getKeyword())
.verb(VerbType.getStringVerbType(k.getVerbType()))
.build())
.sorted((o1, o2) -> collator.compare(o1.getNoun(), o2.getNoun())) // Collator로 비교
.collect(Collectors.toList());

// 정렬된 명사 목록 출력
sortedResults.forEach(r -> log.info("Sorted Noun: {}", r.getNoun()));

return sortedResults;
}

private User validUserById(Long userId) {
Optional<User> userOptional = userRepository.findById(userId);
return userOptional.get();
Expand All @@ -153,5 +183,4 @@ private void validateCreateRecordReq(CreateRecordReq createRecordReq) {
throw new IllegalArgumentException("noun is required.");
}
}

}

0 comments on commit 71d317b

Please sign in to comment.