Skip to content

Commit

Permalink
Merge pull request #41 from tukcomCD2024/feat/#36-backend-news-api
Browse files Browse the repository at this point in the history
Feat/#36 backend news api (요약된 뉴스 조회)
  • Loading branch information
yeonjy authored Mar 20, 2024
2 parents a703618 + 8c6031a commit 4d83604
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 9 deletions.
6 changes: 3 additions & 3 deletions backend/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ dependencies {
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
implementation 'org.jsoup:jsoup:1.15.3'

annotationProcessor 'org.projectlombok:lombok'

// jsoup 의존성 추가
implementation 'org.jsoup:jsoup:1.15.3'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.rollthedice.backend.domain.bookmark.controller;

import com.rollthedice.backend.domain.bookmark.service.BookmarkService;
import com.rollthedice.backend.domain.news.dto.response.NewsResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("bookmark")
public class BookmarkController {
private final BookmarkService bookmarkService;

@ResponseStatus(HttpStatus.OK)
@GetMapping("")
public List<NewsResponse> getBookmarked(final Pageable pageable) {
return bookmarkService.getBookmarkedNews(pageable);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.rollthedice.backend.domain.news.entity;
package com.rollthedice.backend.domain.bookmark.entity;

import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.news.entity.News;
import com.rollthedice.backend.global.config.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.rollthedice.backend.domain.bookmark.repository;

import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.bookmark.entity.Bookmark;
import com.rollthedice.backend.domain.news.entity.News;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
Boolean existsBookmarkByMemberAndNews(Member member, News news);

List<Bookmark> findAllByMemberOrderByCreatedAt(Member member, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.rollthedice.backend.domain.bookmark.service;

import com.rollthedice.backend.domain.bookmark.repository.BookmarkRepository;
import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.member.query.AuthService;
import com.rollthedice.backend.domain.news.dto.response.NewsResponse;
import com.rollthedice.backend.domain.news.entity.News;
import com.rollthedice.backend.domain.news.mapper.NewsMapper;
import com.rollthedice.backend.domain.news.service.NewsService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@RequiredArgsConstructor
@Service
public class BookmarkService {
private final AuthService authService;
private final BookmarkRepository bookmarkRepository;
private final NewsMapper newsMapper;


public boolean isBookmarked(Member member, News news) {
return bookmarkRepository.existsBookmarkByMemberAndNews(member, news);
}

@Transactional(readOnly = true)
public List<NewsResponse> getBookmarkedNews(Pageable pageable) {
Member member = authService.getMember();
return bookmarkRepository.findAllByMemberOrderByCreatedAt(member, pageable).stream()
.map(bookmark -> newsMapper.toResponse(bookmark.getNews(), true))
.collect(Collectors.toList());

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jsoup.select.Elements;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.Objects;
Expand All @@ -26,14 +27,15 @@ public class NewsCrawlingService {

private final NewsService newsService;

@Transactional
@Scheduled(cron = CRON, zone = ZONE)
public void scrap() throws IOException {
for (NewsCategory category : NewsCategory.values()) {
String categoryUrl = MAIN_URL + category.getNum();
String categoryName = category.getName();

scrapNewsUrls(categoryUrl);
for (final News news : newsService.getAllNews()) {
for (final News news : newsService.getNotCrawled()) {
scrapNewsContentsAndUpdate(categoryName, news);
}
}
Expand Down Expand Up @@ -61,7 +63,8 @@ private String scrapThumbnailUrl(final Element news) {
}
}

private void scrapNewsContentsAndUpdate(String categoryName, News news) throws IOException {
@Transactional
public void scrapNewsContentsAndUpdate(String categoryName, News news) throws IOException {
Document doc = Jsoup.connect(news.getUrl()).get();

String title = scrapTitle(doc);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.rollthedice.backend.domain.news.controller;

import com.rollthedice.backend.domain.crawling.NewsCrawlingService;
import com.rollthedice.backend.domain.news.dto.response.NewsResponse;
import com.rollthedice.backend.domain.news.service.NewsService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("news")
public class NewsController {
private final NewsService newsService;

@ResponseStatus(HttpStatus.OK)
@GetMapping("")
public List<NewsResponse> getNews(final Pageable pageable) {
return newsService.getNews(pageable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.rollthedice.backend.domain.news.dto.response;

import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NewsResponse {
private Long id;
private String title;
private String content;
private String thumbnail;
private String postDate;
private Boolean isBookmarked;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.ColumnDefault;

@Slf4j
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -24,13 +27,17 @@ public class News extends BaseTimeEntity {
private String category;
private String postDate;

@ColumnDefault("0L")
private long views;

@Builder
public News(String url, String thumbnailUrl) {
this.url = url;
this.thumbnailUrl = thumbnailUrl;
}

public void addNewsBody(String title, String content, String category, String postDate) {
log.info("now category name: {}", category);
this.title = title;
this.content = content;
this.category = category;
Expand All @@ -41,4 +48,7 @@ public void updateSummarizedContent(String content) {
this.content = content;
}

public void increaseView() {
views++;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.rollthedice.backend.domain.news.mapper;

import com.rollthedice.backend.domain.news.dto.response.NewsResponse;
import com.rollthedice.backend.domain.news.entity.News;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants.ComponentModel;

@Mapper(componentModel = ComponentModel.SPRING)
public interface NewsMapper {

NewsResponse toResponse(final News news, boolean isBookmarked);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package com.rollthedice.backend.domain.news.repository;

import com.rollthedice.backend.domain.news.entity.News;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface NewsRepository extends JpaRepository<News, Long> {
List<News> findAllByContentIsNull();
List<News> findAllByOrderByViewsDescCreatedAtDesc(
final Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
package com.rollthedice.backend.domain.news.service;

import com.rollthedice.backend.domain.bookmark.entity.Bookmark;
import com.rollthedice.backend.domain.bookmark.service.BookmarkService;
import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.member.query.AuthService;
import com.rollthedice.backend.domain.news.contentqueue.ContentProducer;
import com.rollthedice.backend.domain.news.dto.ContentMessageDto;
import com.rollthedice.backend.domain.news.dto.NewsUrlDto;
import com.rollthedice.backend.domain.news.dto.response.NewsResponse;
import com.rollthedice.backend.domain.news.entity.News;
import com.rollthedice.backend.domain.news.mapper.NewsMapper;
import com.rollthedice.backend.domain.news.repository.NewsRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Slf4j
@Service
@RequiredArgsConstructor
public class NewsService {
private final NewsRepository newsRepository;
private final AuthService authService;
private final ContentProducer contentProducer;
private final NewsRepository newsRepository;
private final NewsMapper newsMapper;
private final BookmarkService bookmarkService;


@Transactional
public void addNews(NewsUrlDto dto) {
newsRepository.save(News.builder().thumbnailUrl(dto.getThumbnailUrl()).url(dto.getUrl()).build());
}

@Transactional(readOnly = true)
public List<News> getAllNews() {
return newsRepository.findAll();
public List<News> getNotCrawled() {
return newsRepository.findAllByContentIsNull();
}

@Transactional
Expand All @@ -45,4 +57,13 @@ public void summarizeNewsContent() {
messages.add(new ContentMessageDto(n.getId(), n.getContent())));
messages.forEach(contentProducer::sendMessage);
}

@Transactional(readOnly = true)
public List<NewsResponse> getNews(final Pageable pageable) {
Member member = authService.getMember();
return newsRepository.findAllByOrderByViewsDescCreatedAtDesc(pageable).stream()
.map(news -> newsMapper.toResponse(
news, bookmarkService.isBookmarked(member, news)))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
-> authorizeRequests
.anyRequest().permitAll())
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.successHandler(oAuth2LoginSuccessHandler)
.failureHandler(oAuth2LoginFailureHandler)
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) //customUserService 설정
Expand Down

0 comments on commit 4d83604

Please sign in to comment.