Skip to content

Commit

Permalink
Merge branch 'main' into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
stopmin committed Jul 21, 2024
2 parents b043194 + 592fc0e commit cafc41d
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 60 deletions.
27 changes: 16 additions & 11 deletions src/main/java/gyeongdan/article/controller/ArticleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import gyeongdan.article.service.ArticleService;
import gyeongdan.util.CommonResponse;
import gyeongdan.util.JwtUtil;
import gyeongdan.util.annotation.LoginAuthenticated;
import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand All @@ -19,20 +23,22 @@
@RestController
@RequestMapping("/api/article")
@RequiredArgsConstructor
@Slf4j
public class ArticleController {

private final ArticleService articleService;
private final JwtUtil jwtUtil;

// 게시글 상세 조회
// 단, 유저가 보면 조회기록 저장하고, 유저가 아닌 경우 조회수만 증가시키기.
// 단, 유저가 보면 조회기록 저장하고, 유저가 아닌 경우 조회수만 증가시키기
@GetMapping("/detail")
public ResponseEntity<?> getArticle(@RequestParam Long id,
@RequestHeader @Nullable String accessToken) { // id : 기사id, access token : 유저의 접근 권한
@RequestHeader(name = "Authorization") @Nullable String accessToken) { // id : 기사id, access token : 유저의 접근 권한
Optional<Long> userId = Optional.empty();
if (accessToken != null && !accessToken.isEmpty()) {
userId = jwtUtil.getUserId(jwtUtil.resolveToken(accessToken));
}

// 기사 조회
Article article = articleService.getValidArticleById(id, userId);
// 조회수 증가
Expand All @@ -47,20 +53,19 @@ public ResponseEntity<?> getArticle(@RequestParam Long id,
@GetMapping("")
public ResponseEntity<?> getArticles() {
List<ArticleAllResponse> articles = articleService.getValidArticles();
return ResponseEntity.ok(new CommonResponse<>(articles, "게시글 조회 성공", true));
return ResponseEntity.ok(new CommonResponse<>(articles, "게시글 모두 조회 성공", true));
}

// 최근 조회한 기사 3개 가져오기
@LoginAuthenticated
@GetMapping("/recent")
public ResponseEntity<?> getRecentViewedArticles(@RequestHeader @Nullable String accessToken) {
// userId 디코딩
Optional<Long> userId = Optional.empty();
if (accessToken != null && !accessToken.isEmpty()) {
userId = jwtUtil.getUserId(jwtUtil.resolveToken(accessToken));
}
public ResponseEntity<?> getRecentViewedArticles() {
// 현재 로그인한 사용자의 ID를 가져옴
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Long userId = Long.valueOf(authentication.getName());

// 최근 조회한 기사 3개 가져오기
List<Article> recentViewedArticles = articleService.getRecentViewedArticles(userId.orElse(null));
List<Article> recentViewedArticles = articleService.getRecentViewedArticles(userId);

List<ArticleAllResponse> finalResponse = recentViewedArticles.stream()
.map(article -> new ArticleAllResponse(
Expand All @@ -77,7 +82,7 @@ public ResponseEntity<?> getRecentViewedArticles(@RequestHeader @Nullable String
return ResponseEntity.ok(new CommonResponse<>(finalResponse, "가장 최근에 조회한 게시글 3개 조회 성공", true));
}

// 이번주 가장 인기 있는 기사 10개 조회 (조회수 기준)
// 금주 가장 인기 있는 기사 10개 조회 (조회수 기준)
@GetMapping("/popular")
public ResponseEntity<?> getPopularArticles() {
List<PopularArticleResponse> popularArticles = articleService.getPopularArticles();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,30 @@
import gyeongdan.article.dto.ArticleAllResponse;
import gyeongdan.article.service.RecommendService;
import gyeongdan.util.CommonResponse;
import gyeongdan.util.JwtUtil;
import jakarta.annotation.Nullable;
import gyeongdan.util.annotation.LoginAuthenticated;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/article")
@RequiredArgsConstructor
public class RecommendController {
private final JwtUtil jwtUtil;
private final RecommendService recommendService;

@LoginAuthenticated
@GetMapping("/recommend")
public ResponseEntity<?> getUserTypeArticles(@RequestHeader @Nullable String accessToken) { // id : 기사id, access token : 유저의 접근 권한
Optional<Long> userId = Optional.empty();
if (accessToken != null && !accessToken.isEmpty()) {
userId = jwtUtil.getUserId(jwtUtil.resolveToken(accessToken));
}
public ResponseEntity<?> getUserTypeArticles() {
// 현재 로그인한 사용자의 ID를 가져옴
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Long userId = Long.valueOf(authentication.getName());

// 추천 알고리즘
// 추천 알고리즘 결과를 List에 저장 후 반환
List<ArticleAllResponse> articles = recommendService.recommendArticleById(userId);

return ResponseEntity.ok(new CommonResponse<>(articles, "추천 기사 10개 가져오기 성공", true));
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package gyeongdan.article.repository;

import gyeongdan.article.domain.Article;
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 ArticleJpaRepository extends JpaRepository<Article, Long> {
// 게시물 전체 조회 : publishedAt이 없는 경우 createdAt으로 정렬. isValid 값이 True인 경우만 조회
@Query("SELECT a FROM Article a WHERE a.isValid = true ORDER BY " +
"CASE WHEN a.publishedAt IS NULL THEN 1 ELSE 0 END, " +
"a.publishedAt DESC, " +
"a.createdAt DESC")
List<Article> findAllValidOrderByPublishedAtOrCreatedAtDesc();

List<Article> findTop10ByPublishedAtBetweenOrderByViewCountDesc(LocalDateTime startOfWeek, LocalDateTime endOfDay);
List<Article> findTop10ByPublishedAtBetweenAndIsValidTrueOrderByViewCountDesc(LocalDateTime startOfWeek, LocalDateTime endOfDay);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ public interface ArticleRepository {

Article save(Article article);

List<Article> findAllValidOrderByPublishedAtOrCreatedAtDesc();
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ public Article save(Article article) {
return articleJpaRepository.save(article);
}


public List<Article> findAllValidOrderByPublishedAtOrCreatedAtDesc() {
return articleJpaRepository.findAllValidOrderByPublishedAtOrCreatedAtDesc();
}
}
47 changes: 25 additions & 22 deletions src/main/java/gyeongdan/article/service/ArticleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import gyeongdan.article.repository.ArticleViewHistoryJpaRepository;
import gyeongdan.user.service.UserManageService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -30,15 +29,35 @@ public class ArticleService {
private final UserManageService userManageService;
private final ArticleJpaRepository articleJpaRepository;

// 게시글 상세 조회
public Article getValidArticleById(Long id, Optional<Long> userId) {
Article article = articleRepository.findById(id);
checkArticleVisible(article); // id에 해당하는 게시글이 있는지 확인. 없으면 예외 발생
Article article = articleRepository.findById(id); // id에 해당하는 기사 가져오기

checkArticleVisible(article); // id에 해당하는 기사가 있는지 확인. 없으면 예외 발생

userId.ifPresent(aLong -> saveViewHistory(aLong, article)); // 만약 userId가 존재하면 조회 기록 저장
if (userId.isPresent()) {
saveViewHistory(userId.get(), article); // 조회 기록 저장
}

return article;
}

// 게시물 전체 조회
public List<ArticleAllResponse> getValidArticles() {
List<Article> articles = articleRepository.findAllValidOrderByPublishedAtOrCreatedAtDesc();
return articles.stream()
.filter(Article::isValid)
.map(article -> new ArticleAllResponse(
article.getId(),
article.getSimpleTitle(),
article.getSimpleContent(),
article.getViewCount(),
article.getCategory(),
Optional.ofNullable(article.getImageUrl()),
article.getPublishedAt()
))
.collect(Collectors.toList());
}

private static void checkArticleVisible(Article article) {
if (Boolean.FALSE.equals(article.getIsValid())) {
Expand Down Expand Up @@ -69,22 +88,6 @@ public Long updateArticle(ArticleUpdateRequest articleUpdateRequest) {
}


public List<ArticleAllResponse> getValidArticles() {
List<Article> articles = articleRepository.findAll();
return articles.stream()
.filter(Article::isValid)
.map(article -> new ArticleAllResponse(
article.getId(),
article.getSimpleTitle(),
article.getSimpleContent(),
article.getViewCount(),
article.getCategory(),
Optional.ofNullable(article.getImageUrl()),
article.getPublishedAt()
))
.collect(Collectors.toList());
}

// 조회수 증가 메서드
public void incrementViewCount(Article article) {
article.setViewCount(article.getViewCount() + 1);
Expand All @@ -109,14 +112,14 @@ public List<Article> getRecentViewedArticles(Long userId) {
.collect(Collectors.toList());
}

// 이번 주 가장 인기 있는 기사 5개 가져오는 메서드 (조회수 기준)
// 금주 가장 인기 있는 기사 10개 가져오는 메서드 (조회수 기준)
public List<PopularArticleResponse> getPopularArticles() {
// 오늘을 기준으로 이번 주의 시작과 끝을 구함 (월요일부터 일요일까지)
LocalDate today = LocalDate.now();
LocalDateTime mondayDateTime = today.with(DayOfWeek.MONDAY).atStartOfDay();
LocalDateTime sundayDateTime = today.with(DayOfWeek.SUNDAY).plusDays(1).atStartOfDay();

List<Article> articles = articleJpaRepository.findTop10ByPublishedAtBetweenOrderByViewCountDesc(mondayDateTime, sundayDateTime);
List<Article> articles = articleJpaRepository.findTop10ByPublishedAtBetweenAndIsValidTrueOrderByViewCountDesc(mondayDateTime, sundayDateTime);

return articles.stream()
.map(article -> new PopularArticleResponse(article.getId(), article.getSimpleTitle(), article.getViewCount()))
Expand Down
10 changes: 3 additions & 7 deletions src/main/java/gyeongdan/article/service/RecommendService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,10 @@ public class RecommendService {
private final UserTypeJpaRepository userTypeJpaRepository;
private final ArticleJpaRepository articleJpaRepository;

public List<ArticleAllResponse> recommendArticleById(Optional<Long> userId) {
if (userId.isEmpty()) {
throw new IllegalArgumentException( "아직 유형검사를 하지 않은 유저입니다.");
}

public List<ArticleAllResponse> recommendArticleById(Long userId) {
// (1) 사용자 id에 해당하는 UserType을 가져옴
UserType userType = userTypeJpaRepository.findByuserId(userId.get())
.orElseThrow(() -> new IllegalArgumentException( "아직 유형검사를 하지 않은 유저입니다."));
UserType userType = userTypeJpaRepository.findTopByUserIdOrderByIdDesc(userId)
.orElseThrow(() -> new IllegalArgumentException("아직 유형검사를 하지 않은 유저입니다."));

// (2) UserType에서 가장 값이 높은 3개의 타입값을 추출하기
Map<String, Long> userTypeValues = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
public class EconomicTermController {
private final EconomicTermService economicTermService;

@GetMapping("/search")
@PostMapping("/search")
public ResponseEntity<?> search(@RequestBody String question) throws Exception {
Map<String, String> data = economicTermService.findEconomicTerm(question);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

@Repository
public interface UserTypeJpaRepository extends JpaRepository<UserType, Long> {
Optional<UserType> findTopByUserIdOrderByIdDesc(Long userId);

Optional<UserType> findByuserId(Long user_id);

Optional<List<UserType>> findByUserId(Long userId);
Optional<List<UserType>> findAllByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void saveUserType(Long userId, UserTypeTestResult userTypeTestResult) {
public UserTypeRecord getUserType(Long userId) {
checkUserExist(userId);

List<UserType> userTypes = userTypeJpaRepository.findByUserId(userId)
List<UserType> userTypes = userTypeJpaRepository.findAllByUserId(userId)
.orElseThrow(() -> new IllegalArgumentException("사용자의 유저 타입이 존재하지 않습니다."));

if (userTypes.isEmpty()) {
Expand Down

0 comments on commit cafc41d

Please sign in to comment.