From 7a9f21ddddcc799c4c86c933baca2c90ae038757 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 09:45:09 +0900 Subject: [PATCH 001/105] =?UTF-8?q?[#2]=20feat=20:=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/java/com/example/Schedule.java | 13 +++++++++++++ .../java/com/example/application/NewsService.java | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 schedule/src/main/java/com/example/Schedule.java create mode 100644 schedule/src/main/java/com/example/application/NewsService.java diff --git a/schedule/src/main/java/com/example/Schedule.java b/schedule/src/main/java/com/example/Schedule.java new file mode 100644 index 00000000..6ed563b3 --- /dev/null +++ b/schedule/src/main/java/com/example/Schedule.java @@ -0,0 +1,13 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableScheduling +public class Schedule { + public static void main(String[] args) { + SpringApplication.run(Schedule.class, args); + } +} diff --git a/schedule/src/main/java/com/example/application/NewsService.java b/schedule/src/main/java/com/example/application/NewsService.java new file mode 100644 index 00000000..c78d26d9 --- /dev/null +++ b/schedule/src/main/java/com/example/application/NewsService.java @@ -0,0 +1,4 @@ +package com.example.application; + +public class NewsService { +} From 7c5fedd7d4c90e6b1a8f40d1683e12c0f88d6f58 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:04:29 +0900 Subject: [PATCH 002/105] =?UTF-8?q?[#2]=20feat=20:=20common=20schedule=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/build.gradle b/common/build.gradle index 916fa177..635bf8d2 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -13,6 +13,8 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation 'org.springframework.boot:spring-boot-starter-web' + + implementation project(":schedule") } test { From 4f3aedee445e79ef2576c806a7202504f27bf012 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:04:45 +0900 Subject: [PATCH 003/105] =?UTF-8?q?[#2]=20feat=20:=20schedule=20yml=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/resources/application.yml | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 schedule/src/main/resources/application.yml diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml new file mode 100644 index 00000000..c5175d42 --- /dev/null +++ b/schedule/src/main/resources/application.yml @@ -0,0 +1,37 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/itcast + driver-class-name: com.mysql.cj.jdbc.Driver + username: root + password: root + hikari: + minimum-idle: 5 + maximum-pool-size: 20 + idle-timeout: 30000 + max-lifetime: 1800000 + connection-timeout: 30000 + h2: + console: + enabled: true + jpa : + hibernate: + ddl-auto: update + show-sql: true + application: + name : Schedule + scheduler: + cron: + crawl-every-3-hour: "0 0 0/3 1/1 * ? *" + save-data-every-day: "0 0 1 1/1 * ? *" + delete-aging-data: "0 0 0 1 * ?" + timeout: + connection: 5000 + + crawler: + naver-it-url: "https://news.naver.com/breakingnews/section/105/283" + + logging: + level: + root: INFO + org.jsoup: DEBUG + From 6ca25ba2aa97ebe82f3a30ee96d72d5ac43d456a Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:05:02 +0900 Subject: [PATCH 004/105] =?UTF-8?q?[#2]=20feat=20:=20schedule=20build=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/build.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/schedule/build.gradle b/schedule/build.gradle index ad223154..492c3d6e 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -12,6 +12,14 @@ repositories { dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + + implementation 'org.springframework.boot:spring-boot-starter' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + implementation 'mysql:mysql-connector-java:8.0.33' + + // 크롤링 + implementation 'org.jsoup:jsoup:1.18.3' } test { From 5b93c1b596b5d1430262848e40dfde5ff4d12bd2 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:05:16 +0900 Subject: [PATCH 005/105] =?UTF-8?q?[#2]=20feat=20:=20schedule=20exception?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/example/exception/NewsException.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 schedule/src/main/java/com/example/exception/NewsException.java diff --git a/schedule/src/main/java/com/example/exception/NewsException.java b/schedule/src/main/java/com/example/exception/NewsException.java new file mode 100644 index 00000000..07c914e2 --- /dev/null +++ b/schedule/src/main/java/com/example/exception/NewsException.java @@ -0,0 +1,4 @@ +package com.example.exception; + +public enum NewsException { +} From a635930999ba5a6e850ff55d28c0ff70cd6bafb8 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:05:32 +0900 Subject: [PATCH 006/105] =?UTF-8?q?[#2]=20feat=20:=20schedule=20=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=EB=A7=81=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/application/NewsService.java | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/schedule/src/main/java/com/example/application/NewsService.java b/schedule/src/main/java/com/example/application/NewsService.java index c78d26d9..bc56cfaf 100644 --- a/schedule/src/main/java/com/example/application/NewsService.java +++ b/schedule/src/main/java/com/example/application/NewsService.java @@ -1,4 +1,59 @@ package com.example.application; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Service public class NewsService { -} + + @Value("${spring.crawler.naver-it-url}") + private String naverUrl; + + + public List newsCrawling() throws IOException { + Document document = Jsoup.connect(naverUrl).get(); + Elements articles = document.select(".sa_thumb_inner"); + + List links = new ArrayList<>(); + articles.forEach(article -> { + String link = article.select("a").attr("href"); + links.add(link); + }); + + + links.forEach(link -> { + + try { + Document url = Jsoup.connect(link).get(); + String titles = url.select("#title_area").text(); + String content = url.select("#dic_area").text(); + String date = + url.select(".media_end_head_info_datestamp_bunch").text(); + String thumbnail = + url.selectFirst("meta[property=og:image]").attr("content"); + + System.out.println(link); + System.out.println(titles); + System.out.println(content); + System.out.println(date); + if (thumbnail.isEmpty()){ + System.out.println("썸네일이 없습니다"); + } + System.out.println(thumbnail); + } catch (IOException e) { + throw new RuntimeException(e); + } + + }); + + return links; + } +} \ No newline at end of file From e397af290655365c5e534e9d22071f2d0109c262 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:05:42 +0900 Subject: [PATCH 007/105] =?UTF-8?q?[#2]=20feat=20:=20schedule=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=8B=A4=ED=96=89=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/java/com/example/Schedule.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/schedule/src/main/java/com/example/Schedule.java b/schedule/src/main/java/com/example/Schedule.java index 6ed563b3..30ffd0ad 100644 --- a/schedule/src/main/java/com/example/Schedule.java +++ b/schedule/src/main/java/com/example/Schedule.java @@ -1,13 +1,25 @@ package com.example; +import com.example.application.NewsService; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; +import java.util.List; + @SpringBootApplication @EnableScheduling public class Schedule { public static void main(String[] args) { SpringApplication.run(Schedule.class, args); } + + @Bean + CommandLineRunner run(NewsService newsService) { + return args -> { + newsService.newsCrawling(); + }; + } } From fa7abd8e8e372ef6abe0757cd3130a5508edecf1 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:46:02 +0900 Subject: [PATCH 008/105] =?UTF-8?q?[#2]=20feat=20:=20schedule=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20news=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/{ => itcast}/domain/news/News.java | 8 +++--- .../src/main/java/com/example/Schedule.java | 25 ------------------- 2 files changed, 4 insertions(+), 29 deletions(-) rename common/src/main/java/{ => itcast}/domain/news/News.java (88%) delete mode 100644 schedule/src/main/java/com/example/Schedule.java diff --git a/common/src/main/java/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java similarity index 88% rename from common/src/main/java/domain/news/News.java rename to common/src/main/java/itcast/domain/news/News.java index 65873b92..3d49cc90 100644 --- a/common/src/main/java/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -1,7 +1,7 @@ -package domain.news; +package itcast.domain.news; -import domain.news.enums.NewsStatus; -import domain.user.enums.Interest; +import itcast.domain.news.enums.NewsStatus; +import itcast.domain.user.enums.Interest; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -13,7 +13,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import domain.BaseEntity; +import itcast.domain.BaseEntity; @Getter @Entity diff --git a/schedule/src/main/java/com/example/Schedule.java b/schedule/src/main/java/com/example/Schedule.java deleted file mode 100644 index 30ffd0ad..00000000 --- a/schedule/src/main/java/com/example/Schedule.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.example; - -import com.example.application.NewsService; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.scheduling.annotation.EnableScheduling; - -import java.util.List; - -@SpringBootApplication -@EnableScheduling -public class Schedule { - public static void main(String[] args) { - SpringApplication.run(Schedule.class, args); - } - - @Bean - CommandLineRunner run(NewsService newsService) { - return args -> { - newsService.newsCrawling(); - }; - } -} From 1ce7d8ab0238ca0af1c173a5b08a5d2188267914 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 6 Dec 2024 20:46:21 +0900 Subject: [PATCH 009/105] =?UTF-8?q?[#2]=20fix=20:=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => itcast}/domain/news/enums/NewsStatus.java | 2 +- .../{ => itcast}/domain/newsHistory/NewsHistory.java | 8 ++++---- .../src/main/java/itcast/ScheduleApplication.java | 12 +++++++++++- .../news}/application/NewsService.java | 2 +- .../news}/exception/NewsException.java | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) rename common/src/main/java/{ => itcast}/domain/news/enums/NewsStatus.java (61%) rename common/src/main/java/{ => itcast}/domain/newsHistory/NewsHistory.java (84%) rename schedule/src/main/java/{com/example => itcast/news}/application/NewsService.java (98%) rename schedule/src/main/java/{com/example => itcast/news}/exception/NewsException.java (50%) diff --git a/common/src/main/java/domain/news/enums/NewsStatus.java b/common/src/main/java/itcast/domain/news/enums/NewsStatus.java similarity index 61% rename from common/src/main/java/domain/news/enums/NewsStatus.java rename to common/src/main/java/itcast/domain/news/enums/NewsStatus.java index 2a7f7a65..346b78de 100644 --- a/common/src/main/java/domain/news/enums/NewsStatus.java +++ b/common/src/main/java/itcast/domain/news/enums/NewsStatus.java @@ -1,4 +1,4 @@ -package domain.news.enums; +package itcast.domain.news.enums; public enum NewsStatus { SUMMARY, diff --git a/common/src/main/java/domain/newsHistory/NewsHistory.java b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java similarity index 84% rename from common/src/main/java/domain/newsHistory/NewsHistory.java rename to common/src/main/java/itcast/domain/newsHistory/NewsHistory.java index ccc6d074..4a3671cf 100644 --- a/common/src/main/java/domain/newsHistory/NewsHistory.java +++ b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java @@ -1,8 +1,8 @@ -package domain.newsHistory; +package itcast.domain.newsHistory; -import domain.BaseEntity; -import domain.news.News; -import domain.user.User; +import itcast.domain.BaseEntity; +import itcast.domain.news.News; +import itcast.domain.user.User; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; diff --git a/schedule/src/main/java/itcast/ScheduleApplication.java b/schedule/src/main/java/itcast/ScheduleApplication.java index 3351faba..f96162db 100644 --- a/schedule/src/main/java/itcast/ScheduleApplication.java +++ b/schedule/src/main/java/itcast/ScheduleApplication.java @@ -1,11 +1,21 @@ package itcast; +import itcast.news.application.NewsService; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); } -} + + @Bean + CommandLineRunner run(NewsService newsService) { + return args -> { + newsService.newsCrawling(); + }; + } +} \ No newline at end of file diff --git a/schedule/src/main/java/com/example/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java similarity index 98% rename from schedule/src/main/java/com/example/application/NewsService.java rename to schedule/src/main/java/itcast/news/application/NewsService.java index bc56cfaf..b98cb09a 100644 --- a/schedule/src/main/java/com/example/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -1,4 +1,4 @@ -package com.example.application; +package itcast.news.application; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; diff --git a/schedule/src/main/java/com/example/exception/NewsException.java b/schedule/src/main/java/itcast/news/exception/NewsException.java similarity index 50% rename from schedule/src/main/java/com/example/exception/NewsException.java rename to schedule/src/main/java/itcast/news/exception/NewsException.java index 07c914e2..f989d51b 100644 --- a/schedule/src/main/java/com/example/exception/NewsException.java +++ b/schedule/src/main/java/itcast/news/exception/NewsException.java @@ -1,4 +1,4 @@ -package com.example.exception; +package itcast.news.exception; public enum NewsException { } From 9c1ef4d04ee171ecd99815b4c473a0a1fd4bf98d Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 11:03:03 +0900 Subject: [PATCH 010/105] =?UTF-8?q?[#2]=20feat=20:=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/{ => itcast}/domain/BaseEntity.java | 2 +- .../src/main/java/{ => itcast}/domain/admin/Admin.java | 4 ++-- .../src/main/java/{ => itcast}/domain/blog/Blog.java | 8 ++++---- .../{ => itcast}/domain/blog/enums/BlogStatus.java | 2 +- .../{ => itcast}/domain/blogHistory/BlogHistory.java | 8 ++++---- .../src/main/java/{ => itcast}/domain/user/User.java | 10 +++++----- .../{ => itcast}/domain/user/enums/ArticleType.java | 2 +- .../java/{ => itcast}/domain/user/enums/Interest.java | 2 +- .../{ => itcast}/domain/user/enums/SendingType.java | 2 +- .../main/java/itcast/news/application/NewsService.java | 5 +---- 10 files changed, 21 insertions(+), 24 deletions(-) rename common/src/main/java/{ => itcast}/domain/BaseEntity.java (97%) rename common/src/main/java/{ => itcast}/domain/admin/Admin.java (89%) rename common/src/main/java/{ => itcast}/domain/blog/Blog.java (89%) rename common/src/main/java/{ => itcast}/domain/blog/enums/BlogStatus.java (61%) rename common/src/main/java/{ => itcast}/domain/blogHistory/BlogHistory.java (84%) rename common/src/main/java/{ => itcast}/domain/user/User.java (84%) rename common/src/main/java/{ => itcast}/domain/user/enums/ArticleType.java (58%) rename common/src/main/java/{ => itcast}/domain/user/enums/Interest.java (60%) rename common/src/main/java/{ => itcast}/domain/user/enums/SendingType.java (59%) diff --git a/common/src/main/java/domain/BaseEntity.java b/common/src/main/java/itcast/domain/BaseEntity.java similarity index 97% rename from common/src/main/java/domain/BaseEntity.java rename to common/src/main/java/itcast/domain/BaseEntity.java index 9afe97af..efdfba11 100644 --- a/common/src/main/java/domain/BaseEntity.java +++ b/common/src/main/java/itcast/domain/BaseEntity.java @@ -1,4 +1,4 @@ -package domain; +package itcast.domain; import jakarta.persistence.Column; import jakarta.persistence.EntityListeners; diff --git a/common/src/main/java/domain/admin/Admin.java b/common/src/main/java/itcast/domain/admin/Admin.java similarity index 89% rename from common/src/main/java/domain/admin/Admin.java rename to common/src/main/java/itcast/domain/admin/Admin.java index 38ad192e..f7257a50 100644 --- a/common/src/main/java/domain/admin/Admin.java +++ b/common/src/main/java/itcast/domain/admin/Admin.java @@ -1,6 +1,6 @@ -package domain.admin; +package itcast.domain.admin; -import domain.BaseEntity; +import itcast.domain.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/common/src/main/java/domain/blog/Blog.java b/common/src/main/java/itcast/domain/blog/Blog.java similarity index 89% rename from common/src/main/java/domain/blog/Blog.java rename to common/src/main/java/itcast/domain/blog/Blog.java index bbf3a376..8defff00 100644 --- a/common/src/main/java/domain/blog/Blog.java +++ b/common/src/main/java/itcast/domain/blog/Blog.java @@ -1,8 +1,8 @@ -package domain.blog; +package itcast.domain.blog; -import domain.BaseEntity; -import domain.blog.enums.BlogStatus; -import domain.user.enums.Interest; +import itcast.domain.BaseEntity; +import itcast.domain.blog.enums.BlogStatus; +import itcast.domain.user.enums.Interest; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; diff --git a/common/src/main/java/domain/blog/enums/BlogStatus.java b/common/src/main/java/itcast/domain/blog/enums/BlogStatus.java similarity index 61% rename from common/src/main/java/domain/blog/enums/BlogStatus.java rename to common/src/main/java/itcast/domain/blog/enums/BlogStatus.java index 3948d335..d489de94 100644 --- a/common/src/main/java/domain/blog/enums/BlogStatus.java +++ b/common/src/main/java/itcast/domain/blog/enums/BlogStatus.java @@ -1,4 +1,4 @@ -package domain.blog.enums; +package itcast.domain.blog.enums; public enum BlogStatus { SUMMARY, diff --git a/common/src/main/java/domain/blogHistory/BlogHistory.java b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java similarity index 84% rename from common/src/main/java/domain/blogHistory/BlogHistory.java rename to common/src/main/java/itcast/domain/blogHistory/BlogHistory.java index a7a50e65..47253de3 100644 --- a/common/src/main/java/domain/blogHistory/BlogHistory.java +++ b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java @@ -1,8 +1,8 @@ -package domain.blogHistory; +package itcast.domain.blogHistory; -import domain.BaseEntity; -import domain.news.News; -import domain.user.User; +import itcast.domain.BaseEntity; +import itcast.domain.news.News; +import itcast.domain.user.User; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; diff --git a/common/src/main/java/domain/user/User.java b/common/src/main/java/itcast/domain/user/User.java similarity index 84% rename from common/src/main/java/domain/user/User.java rename to common/src/main/java/itcast/domain/user/User.java index 0a442df7..03dc68fe 100644 --- a/common/src/main/java/domain/user/User.java +++ b/common/src/main/java/itcast/domain/user/User.java @@ -1,9 +1,9 @@ -package domain.user; +package itcast.domain.user; -import domain.BaseEntity; -import domain.user.enums.ArticleType; -import domain.user.enums.Interest; -import domain.user.enums.SendingType; +import itcast.domain.BaseEntity; +import itcast.domain.user.enums.ArticleType; +import itcast.domain.user.enums.Interest; +import itcast.domain.user.enums.SendingType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; diff --git a/common/src/main/java/domain/user/enums/ArticleType.java b/common/src/main/java/itcast/domain/user/enums/ArticleType.java similarity index 58% rename from common/src/main/java/domain/user/enums/ArticleType.java rename to common/src/main/java/itcast/domain/user/enums/ArticleType.java index 8f520617..29457b2a 100644 --- a/common/src/main/java/domain/user/enums/ArticleType.java +++ b/common/src/main/java/itcast/domain/user/enums/ArticleType.java @@ -1,4 +1,4 @@ -package domain.user.enums; +package itcast.domain.user.enums; public enum ArticleType { NEWS, diff --git a/common/src/main/java/domain/user/enums/Interest.java b/common/src/main/java/itcast/domain/user/enums/Interest.java similarity index 60% rename from common/src/main/java/domain/user/enums/Interest.java rename to common/src/main/java/itcast/domain/user/enums/Interest.java index ab5c33ab..e9f55900 100644 --- a/common/src/main/java/domain/user/enums/Interest.java +++ b/common/src/main/java/itcast/domain/user/enums/Interest.java @@ -1,4 +1,4 @@ -package domain.user.enums; +package itcast.domain.user.enums; public enum Interest { FRONTEND, diff --git a/common/src/main/java/domain/user/enums/SendingType.java b/common/src/main/java/itcast/domain/user/enums/SendingType.java similarity index 59% rename from common/src/main/java/domain/user/enums/SendingType.java rename to common/src/main/java/itcast/domain/user/enums/SendingType.java index 1c17207b..03c6e4df 100644 --- a/common/src/main/java/domain/user/enums/SendingType.java +++ b/common/src/main/java/itcast/domain/user/enums/SendingType.java @@ -1,4 +1,4 @@ -package domain.user.enums; +package itcast.domain.user.enums; public enum SendingType { KAKAO, diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index b98cb09a..2747beec 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -17,7 +17,6 @@ public class NewsService { @Value("${spring.crawler.naver-it-url}") private String naverUrl; - public List newsCrawling() throws IOException { Document document = Jsoup.connect(naverUrl).get(); Elements articles = document.select(".sa_thumb_inner"); @@ -28,7 +27,6 @@ public List newsCrawling() throws IOException { links.add(link); }); - links.forEach(link -> { try { @@ -40,6 +38,7 @@ public List newsCrawling() throws IOException { String thumbnail = url.selectFirst("meta[property=og:image]").attr("content"); + // 저장 부분 System.out.println(link); System.out.println(titles); System.out.println(content); @@ -51,9 +50,7 @@ public List newsCrawling() throws IOException { } catch (IOException e) { throw new RuntimeException(e); } - }); - return links; } } \ No newline at end of file From 3ec410d024ac2ca2d6bdc751254977f140bc1305 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 15:42:48 +0900 Subject: [PATCH 011/105] =?UTF-8?q?[#2]=20feat=20:=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EB=AC=B8=EC=9E=90=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=9C=ED=8C=90=EC=9D=BC=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 2747beec..00e4fcea 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -7,28 +7,32 @@ import org.springframework.stereotype.Service; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.Objects; @Service public class NewsService { @Value("${spring.crawler.naver-it-url}") - private String naverUrl; + private String Url; - public List newsCrawling() throws IOException { - Document document = Jsoup.connect(naverUrl).get(); + public void newsCrawling() throws IOException { + Document document = Jsoup.connect(Url).get(); Elements articles = document.select(".sa_thumb_inner"); List links = new ArrayList<>(); articles.forEach(article -> { + if (links.size() >= 10) { + return; + } + String link = article.select("a").attr("href"); links.add(link); }); links.forEach(link -> { - try { Document url = Jsoup.connect(link).get(); String titles = url.select("#title_area").text(); @@ -38,12 +42,17 @@ public List newsCrawling() throws IOException { String thumbnail = url.selectFirst("meta[property=og:image]").attr("content"); - // 저장 부분 + titles = cleanContent(titles); + content = cleanContent(content); + LocalDateTime sendAt = convertDateTime(date); + + // dto 저장 System.out.println(link); System.out.println(titles); System.out.println(content); - System.out.println(date); - if (thumbnail.isEmpty()){ + System.out.println(sendAt); + + if (thumbnail.isEmpty()) { System.out.println("썸네일이 없습니다"); } System.out.println(thumbnail); @@ -51,6 +60,32 @@ public List newsCrawling() throws IOException { throw new RuntimeException(e); } }); - return links; + } + + private LocalDateTime convertDateTime(String info) { + String[] parts = info.split(" "); + String date = parts[0]; + String ampm = parts[1]; + String time = parts[2]; + + date = date.replaceAll("입력", ""); + String[] timeParts = time.split(":"); + int hour = Integer.parseInt(timeParts[0]); + + if (ampm.equals("오후") && hour != 12) { + hour += 12; + } + + String timeDate = date + " " + String.format("%02d", hour) + ":" + timeParts[1]; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd. HH:mm"); + LocalDateTime localDateTime = LocalDateTime.parse(timeDate, formatter); + return localDateTime; + } + + private String cleanContent(String info) { + info = info.replaceAll("\\[.*?\\]", "") + .replaceAll("\\(.*?\\)", "") + .trim(); + return info; } } \ No newline at end of file From 4cc8c94ce6b2e411120cf9230f271fe84a090ffb Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:12:22 +0900 Subject: [PATCH 012/105] =?UTF-8?q?[#2]=20fix=20:=20=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=93=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/build.gradle | 1 - schedule/build.gradle | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/build.gradle b/common/build.gradle index 4c11b1eb..0fcc3c8d 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -22,7 +22,6 @@ dependencies { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation project(":schedule") } test { diff --git a/schedule/build.gradle b/schedule/build.gradle index 492c3d6e..c598fe73 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -20,6 +20,8 @@ dependencies { // 크롤링 implementation 'org.jsoup:jsoup:1.18.3' + + implementation project(':common') } test { From 944c47ca6c0e480e8e3b5b7860a173997ef5204b Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:17:19 +0900 Subject: [PATCH 013/105] =?UTF-8?q?[#2]=20feat=20:=20requestDto=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/dto/request/CreateNewsRequest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java diff --git a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java new file mode 100644 index 00000000..da37e90a --- /dev/null +++ b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java @@ -0,0 +1,24 @@ +package itcast.news.dto.request; + +import itcast.domain.news.News; + +import java.time.LocalDateTime; + +public record CreateNewsRequest( + String title, + String originalContent, + String link, + String thumbnail, + LocalDateTime sendAt +) { + public News toEntity( + String title, String originalContent, String link, String thumbnail, LocalDateTime publishedAt){ + return News.builder() + .title(title) + .originalContent(originalContent) + .link(link) + .thumbnail(thumbnail) + .publishedAt(publishedAt) + .build(); + } +} From a8f002700ee862907ee842b31aa6206c0633bd14 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:17:37 +0900 Subject: [PATCH 014/105] =?UTF-8?q?[#2]=20feat=20:=20repository=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/repository/NewsRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 schedule/src/main/java/itcast/news/repository/NewsRepository.java diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java new file mode 100644 index 00000000..530a2ffb --- /dev/null +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -0,0 +1,7 @@ +package itcast.news.repository; + +import itcast.domain.news.News; +import org.springframework.data.repository.CrudRepository; + +public interface NewsRepository extends CrudRepository { +} From 19d10ce9d7f83941d1e0fb38287112d83b68c4e7 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:19:03 +0900 Subject: [PATCH 015/105] =?UTF-8?q?[#2]=20feat=20:=20=EB=B9=8C=EB=8D=94=20?= =?UTF-8?q?=EB=B0=8F=20Enum=20Default=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/domain/news/News.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 3d49cc90..a4caa23a 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -2,13 +2,8 @@ import itcast.domain.news.enums.NewsStatus; import itcast.domain.user.enums.Interest; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; + import java.time.LocalDateTime; import lombok.AccessLevel; import lombok.Getter; @@ -39,7 +34,6 @@ public class News extends BaseEntity { @Column(nullable = false) private LocalDateTime publishedAt; - @Column(nullable = false) private Long rating; @Column(nullable = false) @@ -51,4 +45,23 @@ public class News extends BaseEntity { private NewsStatus status; private LocalDateTime sendAt; + + @PrePersist + protected void setDefaultStatus() { + if (status == null) { + this.status = NewsStatus.ORIGINAL; + } + if (interest == null) { + this.interest = Interest.NEWS; + } + } + + @Builder + public News(String title, String originalContent, String link, String thumbnail, LocalDateTime publishedAt) { + this.title = title; + this.originalContent = originalContent; + this.link = link; + this.thumbnail = thumbnail; + this.publishedAt = publishedAt; + } } From 709056fcbb931b7eb458a84d11bfacf45c098cc6 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:19:25 +0900 Subject: [PATCH 016/105] =?UTF-8?q?[#2]=20fix=20:=20entity=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index a4caa23a..05306bdd 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -5,7 +5,9 @@ import jakarta.persistence.*; import java.time.LocalDateTime; + import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import itcast.domain.BaseEntity; @@ -22,10 +24,12 @@ public class News extends BaseEntity { @Column(nullable = false) private String title; - @Column(nullable = false) + @Lob + @Column(columnDefinition = "TEXT") private String content; - @Column(nullable = false) + @Lob + @Column(nullable = false,columnDefinition = "TEXT") private String originalContent; @Enumerated(EnumType.STRING) From 329670699ccdca80442abb5711149578067f8fa2 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:19:44 +0900 Subject: [PATCH 017/105] =?UTF-8?q?[#2]=20fix=20:=20Enum=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/user/enums/Interest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/itcast/domain/user/enums/Interest.java b/common/src/main/java/itcast/domain/user/enums/Interest.java index e9f55900..ccc17f15 100644 --- a/common/src/main/java/itcast/domain/user/enums/Interest.java +++ b/common/src/main/java/itcast/domain/user/enums/Interest.java @@ -2,5 +2,6 @@ public enum Interest { FRONTEND, - BACKEND + BACKEND, + NEWS } From e0abd3747990073b08e5db25240145d43312585a Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 9 Dec 2024 17:21:01 +0900 Subject: [PATCH 018/105] =?UTF-8?q?[#2]=20feat=20:=20DB=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 00e4fcea..ef051501 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -1,5 +1,8 @@ package itcast.news.application; +import itcast.news.dto.request.CreateNewsRequest; +import itcast.news.repository.NewsRepository; +import lombok.RequiredArgsConstructor; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; @@ -13,11 +16,14 @@ import java.util.List; @Service +@RequiredArgsConstructor public class NewsService { @Value("${spring.crawler.naver-it-url}") private String Url; + private final NewsRepository newsRepository; + public void newsCrawling() throws IOException { Document document = Jsoup.connect(Url).get(); Elements articles = document.select(".sa_thumb_inner"); @@ -44,18 +50,14 @@ public void newsCrawling() throws IOException { titles = cleanContent(titles); content = cleanContent(content); - LocalDateTime sendAt = convertDateTime(date); + LocalDateTime publishedAt = convertDateTime(date); // dto 저장 - System.out.println(link); - System.out.println(titles); - System.out.println(content); - System.out.println(sendAt); - if (thumbnail.isEmpty()) { System.out.println("썸네일이 없습니다"); } - System.out.println(thumbnail); + CreateNewsRequest newsRequest = new CreateNewsRequest(titles, content, link, thumbnail, publishedAt); + newsRepository.save(newsRequest.toEntity(titles, content, link, thumbnail, publishedAt)); } catch (IOException e) { throw new RuntimeException(e); } From ed00e891ebdbe760835b100dd9ba3da1ccda73bf Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 10:18:54 +0900 Subject: [PATCH 019/105] =?UTF-8?q?[#26]=20feat=20:=20issue26=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=ED=99=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/build.gradle | 9 +++++---- schedule/src/main/java/itcast/ScheduleApplication.java | 4 +++- .../main/java/itcast/news/common/ScheduleConfig.java | 10 ++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 schedule/src/main/java/itcast/news/common/ScheduleConfig.java diff --git a/schedule/build.gradle b/schedule/build.gradle index c598fe73..f28c4437 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -12,16 +12,17 @@ repositories { dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + implementation project(':common') - implementation 'org.springframework.boot:spring-boot-starter' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - + // mysql implementation 'mysql:mysql-connector-java:8.0.33' // 크롤링 implementation 'org.jsoup:jsoup:1.18.3' - implementation project(':common') + // 배치 + implementation 'org.springframework.batch:spring-batch-test' + testImplementation 'org.springframework.boot:spring-boot-starter-batch' } test { diff --git a/schedule/src/main/java/itcast/ScheduleApplication.java b/schedule/src/main/java/itcast/ScheduleApplication.java index f96162db..4de47f5b 100644 --- a/schedule/src/main/java/itcast/ScheduleApplication.java +++ b/schedule/src/main/java/itcast/ScheduleApplication.java @@ -1,12 +1,14 @@ package itcast; import itcast.news.application.NewsService; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication +@EnableBatchProcessing public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); @@ -18,4 +20,4 @@ CommandLineRunner run(NewsService newsService) { newsService.newsCrawling(); }; } -} \ No newline at end of file +} diff --git a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java b/schedule/src/main/java/itcast/news/common/ScheduleConfig.java new file mode 100644 index 00000000..6ff49269 --- /dev/null +++ b/schedule/src/main/java/itcast/news/common/ScheduleConfig.java @@ -0,0 +1,10 @@ +package itcast.news.common; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class ScheduleConfig { + +} From 08beff5f30b21798edb2dcec858e856cdf80dd37 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 16:09:51 +0900 Subject: [PATCH 020/105] =?UTF-8?q?[#26]=20feat=20:=20=EB=B0=B0=EC=B9=98?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=ED=99=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/common/ScheduleConfig.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java b/schedule/src/main/java/itcast/news/common/ScheduleConfig.java index 6ff49269..3d13b873 100644 --- a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java +++ b/schedule/src/main/java/itcast/news/common/ScheduleConfig.java @@ -1,10 +1,73 @@ package itcast.news.common; +import itcast.news.application.NewsService; +import itcast.news.common.schedule.NewsSchedule; import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; @Configuration @RequiredArgsConstructor +@EnableBatchProcessing public class ScheduleConfig { + private final JobRepository jobRepository; + private final PlatformTransactionManager transactionManager; + private final NewsService newsService; + + @Bean(name = "crawlNewsJob") + public Job crawlNewsJob() { + return new JobBuilder("crawlNewsJob", jobRepository) + .start(fetchLatestNewsStep()) + .build(); + } + + @Bean + public Step fetchLatestNewsStep() { + return new StepBuilder("fetchLatestNewsStep", jobRepository) + .tasklet((contribution, chunkContext) ->{ + newsService.newsCrawling(); + return RepeatStatus.FINISHED; + },transactionManager) + .build(); + } + + @Bean(name = "notificationsJob") + public Job notificationsJob() { + return new JobBuilder("notificationsJob",jobRepository) + .start(notificationsStep()) + .build(); + } + + @Bean + public Step notificationsStep() { + return new StepBuilder("notificationsStep",jobRepository) + .tasklet((contribution, chunkContext) -> { + return RepeatStatus.FINISHED; + },transactionManager) + .build(); + } + + @Bean(name = "deleteOldDataJob") + public Job deleteOldDataJob() { + return new JobBuilder("deleteOldDataJob", jobRepository) + .start(removeDataStep()) + .build(); + } + + private Step removeDataStep() { + return new StepBuilder("removeDataStep",jobRepository) + .tasklet((contribution, chunkContext) -> { + return RepeatStatus.FINISHED; + },transactionManager) + .build(); + } } From 19ff1f9854c7c141d88324ea27ad66a86ee2a71b Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 16:10:16 +0900 Subject: [PATCH 021/105] =?UTF-8?q?[#26]=20feat=20:=20=EB=B0=B0=EC=B9=98?= =?UTF-8?q?=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/java/itcast/ScheduleApplication.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/schedule/src/main/java/itcast/ScheduleApplication.java b/schedule/src/main/java/itcast/ScheduleApplication.java index 4de47f5b..0bfed07f 100644 --- a/schedule/src/main/java/itcast/ScheduleApplication.java +++ b/schedule/src/main/java/itcast/ScheduleApplication.java @@ -1,14 +1,12 @@ package itcast; import itcast.news.application.NewsService; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication -@EnableBatchProcessing public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); From 7a9a57fe64475ed6e3453a43392fa0e4bfa2324f Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 16:10:33 +0900 Subject: [PATCH 022/105] =?UTF-8?q?[#26]=20feat=20:=20=EB=B0=B0=EC=B9=98?= =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schedule/build.gradle b/schedule/build.gradle index f28c4437..10c37f89 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -21,8 +21,8 @@ dependencies { implementation 'org.jsoup:jsoup:1.18.3' // 배치 - implementation 'org.springframework.batch:spring-batch-test' - testImplementation 'org.springframework.boot:spring-boot-starter-batch' + implementation 'org.springframework.boot:spring-boot-starter-batch' + testImplementation 'org.springframework.batch:spring-batch-test' } test { From 91db0400adbce28631272ca572629e13ccbd339e Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 16:11:16 +0900 Subject: [PATCH 023/105] =?UTF-8?q?[#26]=20feat=20:=203=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=EB=A7=88=EB=8B=A4=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=ED=99=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/common/schedule/NewsSchedule.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java new file mode 100644 index 00000000..bebd9a01 --- /dev/null +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -0,0 +1,36 @@ +package itcast.news.common.schedule; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.JobParametersInvalidException; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; +import org.springframework.batch.core.repository.JobRestartException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; + + +@Component +@RequiredArgsConstructor +public class NewsSchedule { + @Qualifier("crawlNewsJob") + private final Job crawlingJob; + private final JobLauncher jobLauncher; + + @Scheduled(cron = "${spring.scheduler.cron.crawl-every-3-hour}") + public void scheduleNewsCrawling() throws + JobInstanceAlreadyCompleteException, + JobExecutionAlreadyRunningException, + JobParametersInvalidException, + JobRestartException { + System.out.println("crawling...."); + jobLauncher.run(crawlingJob,new JobParametersBuilder() + .addDate("date", new Date()) + .toJobParameters()); + } +} \ No newline at end of file From eee10cdfc4176fb3fd1f353a9d46923dfc742a26 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 16:11:30 +0900 Subject: [PATCH 024/105] =?UTF-8?q?[#26]=20feat=20:=EB=B0=B0=EC=B9=98=20ym?= =?UTF-8?q?l=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/resources/application.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index c5175d42..1b5b8480 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -4,12 +4,9 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root - hikari: - minimum-idle: 5 - maximum-pool-size: 20 - idle-timeout: 30000 - max-lifetime: 1800000 - connection-timeout: 30000 + batch: + jdbc: + initialize-schema: always h2: console: enabled: true @@ -17,13 +14,14 @@ spring: hibernate: ddl-auto: update show-sql: true + database-platform: org.hibernate.dialect.MySQL5Dialect application: name : Schedule scheduler: cron: - crawl-every-3-hour: "0 0 0/3 1/1 * ? *" - save-data-every-day: "0 0 1 1/1 * ? *" - delete-aging-data: "0 0 0 1 * ?" + crawl-every-3-hour: "0 0 */3 * * ? " + save-data-every-day: "0 0 1 * * ?" + delete-aging-data: "0 0 0 1 0/6 ?" timeout: connection: 5000 @@ -33,5 +31,4 @@ spring: logging: level: root: INFO - org.jsoup: DEBUG - + org.jsoup: DEBUG \ No newline at end of file From 7d480eb3cdea4cda5ca04c3ca9cd3978ac11715e Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 21:10:05 +0900 Subject: [PATCH 025/105] =?UTF-8?q?[#26]=20fix=20:=20enum=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20default=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/itcast/domain/news/News.java | 17 +++++------------ .../news/dto/request/CreateNewsRequest.java | 4 ++++ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 05306bdd..a463a236 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -29,7 +29,7 @@ public class News extends BaseEntity { private String content; @Lob - @Column(nullable = false,columnDefinition = "TEXT") + @Column(nullable = false, columnDefinition = "TEXT") private String originalContent; @Enumerated(EnumType.STRING) @@ -50,21 +50,14 @@ public class News extends BaseEntity { private LocalDateTime sendAt; - @PrePersist - protected void setDefaultStatus() { - if (status == null) { - this.status = NewsStatus.ORIGINAL; - } - if (interest == null) { - this.interest = Interest.NEWS; - } - } - @Builder - public News(String title, String originalContent, String link, String thumbnail, LocalDateTime publishedAt) { + public News(String title, String originalContent, String link, Interest interest, NewsStatus status, + String thumbnail, LocalDateTime publishedAt) { this.title = title; this.originalContent = originalContent; this.link = link; + this.interest = interest; + this.status = status; this.thumbnail = thumbnail; this.publishedAt = publishedAt; } diff --git a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java index da37e90a..434647a4 100644 --- a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java +++ b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java @@ -1,6 +1,8 @@ package itcast.news.dto.request; import itcast.domain.news.News; +import itcast.domain.news.enums.NewsStatus; +import itcast.domain.user.enums.Interest; import java.time.LocalDateTime; @@ -17,6 +19,8 @@ public News toEntity( .title(title) .originalContent(originalContent) .link(link) + .interest(Interest.NEWS) + .status(NewsStatus.ORIGINAL) .thumbnail(thumbnail) .publishedAt(publishedAt) .build(); From 701aa772fe4abf24381b483f1c10aeb65778e702 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 21:10:22 +0900 Subject: [PATCH 026/105] =?UTF-8?q?[#26]=20fix=20:=20=ED=9E=88=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/blogHistory/BlogHistory.java | 33 ------------------- .../domain/newsHistory/NewsHistory.java | 33 ------------------- 2 files changed, 66 deletions(-) diff --git a/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java index 47253de3..e69de29b 100644 --- a/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java +++ b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java @@ -1,33 +0,0 @@ -package itcast.domain.blogHistory; - -import itcast.domain.BaseEntity; -import itcast.domain.news.News; -import itcast.domain.user.User; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class BlogHistory extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "news_id") - private News news; -} diff --git a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java index 4a3671cf..e69de29b 100644 --- a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java +++ b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java @@ -1,33 +0,0 @@ -package itcast.domain.newsHistory; - -import itcast.domain.BaseEntity; -import itcast.domain.news.News; -import itcast.domain.user.User; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class NewsHistory extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "news_id") - private News news; -} From 7ce4f921098e0fec5caeb90f996b4ddd89670dec Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 21:10:32 +0900 Subject: [PATCH 027/105] =?UTF-8?q?[#26]=20fix=20:=20=ED=8C=A8=EC=B9=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/common/ScheduleConfig.java | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 schedule/src/main/java/itcast/news/common/ScheduleConfig.java diff --git a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java b/schedule/src/main/java/itcast/news/common/ScheduleConfig.java deleted file mode 100644 index 3d13b873..00000000 --- a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java +++ /dev/null @@ -1,73 +0,0 @@ -package itcast.news.common; - -import itcast.news.application.NewsService; -import itcast.news.common.schedule.NewsSchedule; -import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; -import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.PlatformTransactionManager; - -@Configuration -@RequiredArgsConstructor -@EnableBatchProcessing -public class ScheduleConfig { - private final JobRepository jobRepository; - private final PlatformTransactionManager transactionManager; - private final NewsService newsService; - - @Bean(name = "crawlNewsJob") - public Job crawlNewsJob() { - return new JobBuilder("crawlNewsJob", jobRepository) - .start(fetchLatestNewsStep()) - .build(); - } - - @Bean - public Step fetchLatestNewsStep() { - return new StepBuilder("fetchLatestNewsStep", jobRepository) - .tasklet((contribution, chunkContext) ->{ - newsService.newsCrawling(); - return RepeatStatus.FINISHED; - },transactionManager) - .build(); - } - - @Bean(name = "notificationsJob") - public Job notificationsJob() { - return new JobBuilder("notificationsJob",jobRepository) - .start(notificationsStep()) - .build(); - } - - @Bean - public Step notificationsStep() { - return new StepBuilder("notificationsStep",jobRepository) - .tasklet((contribution, chunkContext) -> { - return RepeatStatus.FINISHED; - },transactionManager) - .build(); - } - - @Bean(name = "deleteOldDataJob") - public Job deleteOldDataJob() { - return new JobBuilder("deleteOldDataJob", jobRepository) - .start(removeDataStep()) - .build(); - } - - private Step removeDataStep() { - return new StepBuilder("removeDataStep",jobRepository) - .tasklet((contribution, chunkContext) -> { - return RepeatStatus.FINISHED; - },transactionManager) - .build(); - } - -} From 002a0a327c07866e901d3a221f36d420aea7d1ec Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 21:10:51 +0900 Subject: [PATCH 028/105] =?UTF-8?q?[#26]=20fix=20:=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A5=B4=EB=9F=AC=EB=A1=9C=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/common/schedule/NewsSchedule.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java index bebd9a01..b6f5a912 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -1,5 +1,6 @@ package itcast.news.common.schedule; +import itcast.news.application.NewsService; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobParametersBuilder; @@ -12,25 +13,18 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.io.IOException; import java.util.Date; - @Component @RequiredArgsConstructor public class NewsSchedule { - @Qualifier("crawlNewsJob") - private final Job crawlingJob; - private final JobLauncher jobLauncher; - @Scheduled(cron = "${spring.scheduler.cron.crawl-every-3-hour}") - public void scheduleNewsCrawling() throws - JobInstanceAlreadyCompleteException, - JobExecutionAlreadyRunningException, - JobParametersInvalidException, - JobRestartException { + private final NewsService newsService; + + @Scheduled(cron = "${spring.scheduler.cron.news}") + public void scheduleNewsCrawling() throws IOException { System.out.println("crawling...."); - jobLauncher.run(crawlingJob,new JobParametersBuilder() - .addDate("date", new Date()) - .toJobParameters()); + newsService.newsCrawling(); } } \ No newline at end of file From 4e8bee2fedd91ded90ea8b08280be380fdd20b1b Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 21:41:52 +0900 Subject: [PATCH 029/105] =?UTF-8?q?[#26]=20fix=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 10 ++++++++-- schedule/src/main/java/itcast/ScheduleApplication.java | 6 ++++-- .../main/java/itcast/news/application/NewsService.java | 4 ++-- .../itcast/news/dto/request/CreateNewsRequest.java | 6 +++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index a463a236..5295c24a 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -51,8 +51,14 @@ public class News extends BaseEntity { private LocalDateTime sendAt; @Builder - public News(String title, String originalContent, String link, Interest interest, NewsStatus status, - String thumbnail, LocalDateTime publishedAt) { + public News( + String title, + String originalContent, + String link, + Interest interest, + NewsStatus status, + String thumbnail, + LocalDateTime publishedAt) { this.title = title; this.originalContent = originalContent; this.link = link; diff --git a/schedule/src/main/java/itcast/ScheduleApplication.java b/schedule/src/main/java/itcast/ScheduleApplication.java index 0bfed07f..0024a89d 100644 --- a/schedule/src/main/java/itcast/ScheduleApplication.java +++ b/schedule/src/main/java/itcast/ScheduleApplication.java @@ -5,17 +5,19 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); } - @Bean +/* @Bean CommandLineRunner run(NewsService newsService) { return args -> { newsService.newsCrawling(); }; - } + }*/ } diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index ef051501..de6b0917 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -20,12 +20,12 @@ public class NewsService { @Value("${spring.crawler.naver-it-url}") - private String Url; + private String url; private final NewsRepository newsRepository; public void newsCrawling() throws IOException { - Document document = Jsoup.connect(Url).get(); + Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); List links = new ArrayList<>(); diff --git a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java index 434647a4..702e6183 100644 --- a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java +++ b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java @@ -14,7 +14,11 @@ public record CreateNewsRequest( LocalDateTime sendAt ) { public News toEntity( - String title, String originalContent, String link, String thumbnail, LocalDateTime publishedAt){ + String title, + String originalContent, + String link, + String thumbnail, + LocalDateTime publishedAt){ return News.builder() .title(title) .originalContent(originalContent) From 73a63cb576fa8769e74c944bcdbc1849ba42b580 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 10 Dec 2024 21:46:07 +0900 Subject: [PATCH 030/105] =?UTF-8?q?[#2]=20fix=20:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/domain/news/News.java | 21 +++++++++---------- .../news/dto/request/CreateNewsRequest.java | 12 ++++++++++- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 05306bdd..ac7445c4 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -50,21 +50,20 @@ public class News extends BaseEntity { private LocalDateTime sendAt; - @PrePersist - protected void setDefaultStatus() { - if (status == null) { - this.status = NewsStatus.ORIGINAL; - } - if (interest == null) { - this.interest = Interest.NEWS; - } - } - @Builder - public News(String title, String originalContent, String link, String thumbnail, LocalDateTime publishedAt) { + public News( + String title, + String originalContent, + String link, + String thumbnail, + Interest interest, + NewsStatus status, + LocalDateTime publishedAt) { this.title = title; this.originalContent = originalContent; this.link = link; + this.interest = interest; + this.status = status; this.thumbnail = thumbnail; this.publishedAt = publishedAt; } diff --git a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java index da37e90a..c8d3ef15 100644 --- a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java +++ b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java @@ -1,6 +1,8 @@ package itcast.news.dto.request; import itcast.domain.news.News; +import itcast.domain.news.enums.NewsStatus; +import itcast.domain.user.enums.Interest; import java.time.LocalDateTime; @@ -12,11 +14,19 @@ public record CreateNewsRequest( LocalDateTime sendAt ) { public News toEntity( - String title, String originalContent, String link, String thumbnail, LocalDateTime publishedAt){ + String title, + String originalContent, + String link, + Interest interest, + NewsStatus status, + String thumbnail, + LocalDateTime publishedAt){ return News.builder() .title(title) .originalContent(originalContent) .link(link) + .interest(Interest.NEWS) + .status(NewsStatus.ORIGINAL) .thumbnail(thumbnail) .publishedAt(publishedAt) .build(); From 85d985278b57fd1cd9a3ebd73f82fe3e60f92174 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 10:28:15 +0900 Subject: [PATCH 031/105] =?UTF-8?q?[#2]=20fix=20:=20=EB=B0=B0=EC=B9=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/build.gradle | 1 - schedule/build.gradle | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 0fcc3c8d..eb9d32a5 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -21,7 +21,6 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation 'org.springframework.boot:spring-boot-starter-web' - } test { diff --git a/schedule/build.gradle b/schedule/build.gradle index c598fe73..c596b8df 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -12,16 +12,13 @@ repositories { dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + implementation project(':common') - implementation 'org.springframework.boot:spring-boot-starter' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - + // mysql implementation 'mysql:mysql-connector-java:8.0.33' // 크롤링 implementation 'org.jsoup:jsoup:1.18.3' - - implementation project(':common') } test { From 42c1a4ccf0854b5458c0c18d065e3ff916025c96 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 10:28:43 +0900 Subject: [PATCH 032/105] =?UTF-8?q?[#2]=20fix=20:=20entity=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/dto/request/CreateNewsRequest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java index c8d3ef15..702e6183 100644 --- a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java +++ b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java @@ -17,8 +17,6 @@ public News toEntity( String title, String originalContent, String link, - Interest interest, - NewsStatus status, String thumbnail, LocalDateTime publishedAt){ return News.builder() From c04b968852885601da13fa21726b39f7db36e345 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 10:42:14 +0900 Subject: [PATCH 033/105] =?UTF-8?q?[#2]=20fix=20:=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC,=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/news/application/NewsService.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index ef051501..10a81d91 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -20,17 +20,19 @@ public class NewsService { @Value("${spring.crawler.naver-it-url}") - private String Url; + private String url; + private final int LINK_SIZE = 10; + private final int HOUR = 12; private final NewsRepository newsRepository; public void newsCrawling() throws IOException { - Document document = Jsoup.connect(Url).get(); + Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); List links = new ArrayList<>(); articles.forEach(article -> { - if (links.size() >= 10) { + if (links.size() >= LINK_SIZE) { return; } @@ -74,8 +76,8 @@ private LocalDateTime convertDateTime(String info) { String[] timeParts = time.split(":"); int hour = Integer.parseInt(timeParts[0]); - if (ampm.equals("오후") && hour != 12) { - hour += 12; + if (ampm.equals("오후") && hour != HOUR) { + hour += HOUR; } String timeDate = date + " " + String.format("%02d", hour) + ":" + timeParts[1]; From be2ea4570c400fd618702bdb11aa7219278e718f Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 10:42:59 +0900 Subject: [PATCH 034/105] =?UTF-8?q?[#2]=20fix=20:=20repository=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/repository/NewsRepository.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index 530a2ffb..52b8be14 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -1,7 +1,12 @@ package itcast.news.repository; import itcast.domain.news.News; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; -public interface NewsRepository extends CrudRepository { +import java.util.List; + +public interface NewsRepository extends JpaRepository { + @Query("select n.link from News n") + List findLinks(); } From b9c7d2c64f85f5e99383a0f9cfe98a4058c335cc Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 11:12:51 +0900 Subject: [PATCH 035/105] =?UTF-8?q?[#2]=20fix=20:=20EOL=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/java/itcast/ScheduleApplication.java | 2 +- schedule/src/main/java/itcast/news/application/NewsService.java | 2 +- schedule/src/main/resources/application.yml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/schedule/src/main/java/itcast/ScheduleApplication.java b/schedule/src/main/java/itcast/ScheduleApplication.java index f96162db..0bfed07f 100644 --- a/schedule/src/main/java/itcast/ScheduleApplication.java +++ b/schedule/src/main/java/itcast/ScheduleApplication.java @@ -18,4 +18,4 @@ CommandLineRunner run(NewsService newsService) { newsService.newsCrawling(); }; } -} \ No newline at end of file +} diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 10a81d91..5c261d8f 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -92,4 +92,4 @@ private String cleanContent(String info) { .trim(); return info; } -} \ No newline at end of file +} diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index c5175d42..4b23d9d1 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -34,4 +34,3 @@ spring: level: root: INFO org.jsoup: DEBUG - From af1cb8a04d36f2614e52690b6773e02cc584c124 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:09:15 +0900 Subject: [PATCH 036/105] =?UTF-8?q?[#2]=20feat=20:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 19 ++++++++++++++++--- .../news/repository/NewsRepository.java | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 5c261d8f..a64b613c 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -14,6 +14,7 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -31,16 +32,27 @@ public void newsCrawling() throws IOException { Elements articles = document.select(".sa_thumb_inner"); List links = new ArrayList<>(); + articles.forEach(article -> { if (links.size() >= LINK_SIZE) { return; } - String link = article.select("a").attr("href"); - links.add(link); + links.add(link); }); + List isValidLinks = newsRepository.findAllLinks(); + + List validLinks = links + .stream() + .filter(link -> !isValidLinks.contains(link)) + .distinct() + .collect(Collectors.toList()); - links.forEach(link -> { + if(validLinks.isEmpty()) { + throw new RuntimeException("No links found"); + } + + validLinks.forEach(link -> { try { Document url = Jsoup.connect(link).get(); String titles = url.select("#title_area").text(); @@ -92,4 +104,5 @@ private String cleanContent(String info) { .trim(); return info; } + } diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index 52b8be14..957be302 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -8,5 +8,5 @@ public interface NewsRepository extends JpaRepository { @Query("select n.link from News n") - List findLinks(); + List findAllLinks(); } From 7bc208b6a8236524a128d5fa76e7c9dff956f4f9 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:09:24 +0900 Subject: [PATCH 037/105] =?UTF-8?q?[#2]=20feat=20:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index 4b23d9d1..ff690853 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -17,6 +17,7 @@ spring: hibernate: ddl-auto: update show-sql: true + database-platform: org.hibernate.dialect.MySQLDialect application: name : Schedule scheduler: From 03725751a6e482ad07abcfdeed84fa48862663db Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:37:39 +0900 Subject: [PATCH 038/105] =?UTF-8?q?[#28]=20fix=20:=20=EB=B0=B0=EC=B9=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=ED=95=84=EC=9A=94=20=EC=97=86=EB=8A=94=20?= =?UTF-8?q?entity=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/blogHistory/BlogHistory.java | 33 --------- .../domain/newsHistory/NewsHistory.java | 33 --------- .../itcast/news/common/ScheduleConfig.java | 73 ------------------- .../news/common/schedule/NewsSchedule.java | 29 ++------ 4 files changed, 7 insertions(+), 161 deletions(-) delete mode 100644 common/src/main/java/itcast/domain/blogHistory/BlogHistory.java delete mode 100644 common/src/main/java/itcast/domain/newsHistory/NewsHistory.java delete mode 100644 schedule/src/main/java/itcast/news/common/ScheduleConfig.java diff --git a/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java deleted file mode 100644 index 47253de3..00000000 --- a/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java +++ /dev/null @@ -1,33 +0,0 @@ -package itcast.domain.blogHistory; - -import itcast.domain.BaseEntity; -import itcast.domain.news.News; -import itcast.domain.user.User; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class BlogHistory extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "news_id") - private News news; -} diff --git a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java deleted file mode 100644 index 4a3671cf..00000000 --- a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java +++ /dev/null @@ -1,33 +0,0 @@ -package itcast.domain.newsHistory; - -import itcast.domain.BaseEntity; -import itcast.domain.news.News; -import itcast.domain.user.User; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class NewsHistory extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "news_id") - private News news; -} diff --git a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java b/schedule/src/main/java/itcast/news/common/ScheduleConfig.java deleted file mode 100644 index 3d13b873..00000000 --- a/schedule/src/main/java/itcast/news/common/ScheduleConfig.java +++ /dev/null @@ -1,73 +0,0 @@ -package itcast.news.common; - -import itcast.news.application.NewsService; -import itcast.news.common.schedule.NewsSchedule; -import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; -import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.PlatformTransactionManager; - -@Configuration -@RequiredArgsConstructor -@EnableBatchProcessing -public class ScheduleConfig { - private final JobRepository jobRepository; - private final PlatformTransactionManager transactionManager; - private final NewsService newsService; - - @Bean(name = "crawlNewsJob") - public Job crawlNewsJob() { - return new JobBuilder("crawlNewsJob", jobRepository) - .start(fetchLatestNewsStep()) - .build(); - } - - @Bean - public Step fetchLatestNewsStep() { - return new StepBuilder("fetchLatestNewsStep", jobRepository) - .tasklet((contribution, chunkContext) ->{ - newsService.newsCrawling(); - return RepeatStatus.FINISHED; - },transactionManager) - .build(); - } - - @Bean(name = "notificationsJob") - public Job notificationsJob() { - return new JobBuilder("notificationsJob",jobRepository) - .start(notificationsStep()) - .build(); - } - - @Bean - public Step notificationsStep() { - return new StepBuilder("notificationsStep",jobRepository) - .tasklet((contribution, chunkContext) -> { - return RepeatStatus.FINISHED; - },transactionManager) - .build(); - } - - @Bean(name = "deleteOldDataJob") - public Job deleteOldDataJob() { - return new JobBuilder("deleteOldDataJob", jobRepository) - .start(removeDataStep()) - .build(); - } - - private Step removeDataStep() { - return new StepBuilder("removeDataStep",jobRepository) - .tasklet((contribution, chunkContext) -> { - return RepeatStatus.FINISHED; - },transactionManager) - .build(); - } - -} diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java index bebd9a01..a73b2ae9 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -1,36 +1,21 @@ package itcast.news.common.schedule; +import itcast.news.application.NewsService; import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.JobParametersInvalidException; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; -import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; -import org.springframework.batch.core.repository.JobRestartException; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import java.util.Date; - +import java.io.IOException; @Component @RequiredArgsConstructor public class NewsSchedule { - @Qualifier("crawlNewsJob") - private final Job crawlingJob; - private final JobLauncher jobLauncher; + private NewsService newsService; @Scheduled(cron = "${spring.scheduler.cron.crawl-every-3-hour}") - public void scheduleNewsCrawling() throws - JobInstanceAlreadyCompleteException, - JobExecutionAlreadyRunningException, - JobParametersInvalidException, - JobRestartException { + public void scheduleNewsCrawling() throws IOException { System.out.println("crawling...."); - jobLauncher.run(crawlingJob,new JobParametersBuilder() - .addDate("date", new Date()) - .toJobParameters()); + newsService.newsCrawling(); + System.out.println("crawling finished."); } -} \ No newline at end of file +} From 081d9f3f4147d0d2c8c512bcd8987ba34cd6ae3b Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:38:07 +0900 Subject: [PATCH 039/105] =?UTF-8?q?[#28]=20fix=20:=20=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=93=A4=20common=20=EB=AA=A8=EB=93=88=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/schedule/build.gradle b/schedule/build.gradle index 492c3d6e..dde4bb44 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -16,6 +16,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation project(":common") + implementation 'mysql:mysql-connector-java:8.0.33' // 크롤링 From 9c1b86ee29e2fe90125ff5dbc4b44a50f92a1d0c Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:45:44 +0900 Subject: [PATCH 040/105] =?UTF-8?q?[#28]=20fix=20:=20=EC=9E=84=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/news/common/schedule/NewsSchedule.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java index b6f5a912..9621d937 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -2,19 +2,10 @@ import itcast.news.application.NewsService; import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.JobParametersInvalidException; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; -import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; -import org.springframework.batch.core.repository.JobRestartException; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.io.IOException; -import java.util.Date; @Component @RequiredArgsConstructor @@ -26,5 +17,6 @@ public class NewsSchedule { public void scheduleNewsCrawling() throws IOException { System.out.println("crawling...."); newsService.newsCrawling(); + System.out.println("crawled End"); } } \ No newline at end of file From 10f9c7535d9d6c71144f6c5a14c1a30bf34db9c9 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:49:25 +0900 Subject: [PATCH 041/105] =?UTF-8?q?[#26]=20fix=20:=20=EC=9E=84=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95,=20cron=EB=AA=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/common/schedule/NewsSchedule.java | 12 ++---------- schedule/src/main/resources/application.yml | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java index b6f5a912..44f08e0a 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -2,19 +2,10 @@ import itcast.news.application.NewsService; import lombok.RequiredArgsConstructor; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.JobParametersInvalidException; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; -import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; -import org.springframework.batch.core.repository.JobRestartException; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.io.IOException; -import java.util.Date; @Component @RequiredArgsConstructor @@ -22,9 +13,10 @@ public class NewsSchedule { private final NewsService newsService; - @Scheduled(cron = "${spring.scheduler.cron.news}") + @Scheduled(cron = "${spring.scheduler.cron.news-crawling}") public void scheduleNewsCrawling() throws IOException { System.out.println("crawling...."); newsService.newsCrawling(); + System.out.println("crawling finished."); } } \ No newline at end of file diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index b7c40ca1..618885fa 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -22,7 +22,7 @@ spring: name : Schedule scheduler: cron: - crawl-every-3-hour: "0 0 */3 * * ? " + news-crawling: "0 0 */3 * * ? " save-data-every-day: "0 0 1 * * ?" delete-aging-data: "0 0 0 1 0/6 ?" timeout: From b0426e1cacbbc5ec739003845daa2dd4c54b4a83 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 12:59:29 +0900 Subject: [PATCH 042/105] =?UTF-8?q?[#26]=20fix=20:=20=ED=81=AC=EB=A1=A0=20?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95,=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/itcast/news/application/NewsService.java | 4 ++-- .../main/java/itcast/news/common/schedule/NewsSchedule.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index f396104f..2b03b527 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -88,8 +88,8 @@ private LocalDateTime convertDateTime(String info) { String[] timeParts = time.split(":"); int hour = Integer.parseInt(timeParts[0]); - if (ampm.equals("오후") && hour != 12) { - hour += 12; + if (ampm.equals("오후") && hour != HOUR) { + hour += HOUR; } String timeDate = date + " " + String.format("%02d", hour) + ":" + timeParts[1]; diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java index 9621d937..41b644ed 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -13,7 +13,7 @@ public class NewsSchedule { private final NewsService newsService; - @Scheduled(cron = "${spring.scheduler.cron.news}") + @Scheduled(cron = "${spring.scheduler.cron.news-crawling}") public void scheduleNewsCrawling() throws IOException { System.out.println("crawling...."); newsService.newsCrawling(); From 6311467a514b5fca1327a8fcef4d4956252d48b1 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 13:04:25 +0900 Subject: [PATCH 043/105] =?UTF-8?q?[#28]=20feat=20:=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20entity=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/blogHistory/BlogHistory.java | 0 common/src/main/java/itcast/domain/newsHistory/NewsHistory.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 common/src/main/java/itcast/domain/blogHistory/BlogHistory.java delete mode 100644 common/src/main/java/itcast/domain/newsHistory/NewsHistory.java diff --git a/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java deleted file mode 100644 index e69de29b..00000000 diff --git a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java deleted file mode 100644 index e69de29b..00000000 From b32c4f2f6b47542e2568bf9d2832e09c7cf02924 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 13:06:22 +0900 Subject: [PATCH 044/105] =?UTF-8?q?[#28]=20feat=20:=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20=EC=82=AD=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/blogHistory/BlogHistory.java | 33 +++++++++++++++++++ .../domain/newsHistory/NewsHistory.java | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 common/src/main/java/itcast/domain/blogHistory/BlogHistory.java create mode 100644 common/src/main/java/itcast/domain/newsHistory/NewsHistory.java diff --git a/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java new file mode 100644 index 00000000..47253de3 --- /dev/null +++ b/common/src/main/java/itcast/domain/blogHistory/BlogHistory.java @@ -0,0 +1,33 @@ +package itcast.domain.blogHistory; + +import itcast.domain.BaseEntity; +import itcast.domain.news.News; +import itcast.domain.user.User; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class BlogHistory extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "news_id") + private News news; +} diff --git a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java new file mode 100644 index 00000000..4a3671cf --- /dev/null +++ b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java @@ -0,0 +1,33 @@ +package itcast.domain.newsHistory; + +import itcast.domain.BaseEntity; +import itcast.domain.news.News; +import itcast.domain.user.User; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NewsHistory extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "news_id") + private News news; +} From a025d19f7c961195402624227ec727973e76c310 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 16:44:04 +0900 Subject: [PATCH 045/105] =?UTF-8?q?[#26]=20fix=20:=20dev=EB=82=B4=EC=9A=A9?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/domain/CommonApplication.java | 11 +++++++++++ common/src/main/java/itcast/domain/news/News.java | 1 + 2 files changed, 12 insertions(+) create mode 100644 common/src/main/java/itcast/domain/CommonApplication.java diff --git a/common/src/main/java/itcast/domain/CommonApplication.java b/common/src/main/java/itcast/domain/CommonApplication.java new file mode 100644 index 00000000..231bdb4a --- /dev/null +++ b/common/src/main/java/itcast/domain/CommonApplication.java @@ -0,0 +1,11 @@ +package itcast.domain; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CommonApplication { + public static void main(String[] args) { + SpringApplication.run(CommonApplication.class, args); + } +} diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 0d096593..74f13f50 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -5,6 +5,7 @@ import itcast.domain.user.enums.Interest; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; From 7df2eac0124f6b47d93cf60b6be224abd3ba94d3 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 11 Dec 2024 17:08:53 +0900 Subject: [PATCH 046/105] =?UTF-8?q?[#26]=20fix=20:=20yml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index 9a4fa683..b03e07ed 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: datasource: - url: jdbc:mysql://localhost:3306/itcast + url: jdbc:mysql://localhost:3308/itcast driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 1234 From 61e08ad1a25ab2aa7185fbbb157d6058da73a815 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 14:08:28 +0900 Subject: [PATCH 047/105] =?UTF-8?q?[#28]=20fix=20:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=ED=99=98=EA=B2=BD=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/ScheduleApplication.java | 10 -------- .../itcast/ai/application/GPTServiceTest.java | 3 ++- .../news/application/NewsServiceTest.java | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 schedule/src/test/java/itcast/news/application/NewsServiceTest.java diff --git a/schedule/src/main/java/itcast/ScheduleApplication.java b/schedule/src/main/java/itcast/ScheduleApplication.java index 0024a89d..8baad8c6 100644 --- a/schedule/src/main/java/itcast/ScheduleApplication.java +++ b/schedule/src/main/java/itcast/ScheduleApplication.java @@ -1,10 +1,7 @@ package itcast; -import itcast.news.application.NewsService; -import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @@ -13,11 +10,4 @@ public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); } - -/* @Bean - CommandLineRunner run(NewsService newsService) { - return args -> { - newsService.newsCrawling(); - }; - }*/ } diff --git a/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java b/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java index daa87b16..f6d84979 100644 --- a/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java +++ b/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java @@ -14,10 +14,11 @@ import itcast.ai.dto.response.GPTSummaryResponse; import itcast.domain.blog.Blog; import itcast.domain.blog.enums.BlogStatus; -import itcast.domain.blog.repository.BlogRepository; import itcast.domain.user.enums.Interest; import java.util.Collections; import java.util.Optional; + +import itcast.news.repository.BlogRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java new file mode 100644 index 00000000..f2787fc8 --- /dev/null +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -0,0 +1,25 @@ +package itcast.news.application; + +import itcast.news.repository.NewsRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class NewsServiceTest { + + @InjectMocks + private NewsService newsService; + + @Mock + private NewsRepository newsRepository; + + @Test + @DisplayName("크롤링 성공 테스트") + void crawlingSuccessTest() { + + } +} From 7d7d99f01bb2f0352b0f69057368f0f4f01ec4b1 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 14:12:33 +0900 Subject: [PATCH 048/105] =?UTF-8?q?[#37]=20fix=20:=20repository=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/java/itcast/ai/application/GPTService.java | 2 +- .../java/itcast/{news => blog}/repository/BlogRepository.java | 2 +- .../src/test/java/itcast/ai/application/GPTServiceTest.java | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) rename schedule/src/main/java/itcast/{news => blog}/repository/BlogRepository.java (87%) diff --git a/schedule/src/main/java/itcast/ai/application/GPTService.java b/schedule/src/main/java/itcast/ai/application/GPTService.java index c93781b9..bef15026 100644 --- a/schedule/src/main/java/itcast/ai/application/GPTService.java +++ b/schedule/src/main/java/itcast/ai/application/GPTService.java @@ -9,7 +9,7 @@ import itcast.domain.blog.enums.BlogStatus; import itcast.domain.user.enums.Interest; import itcast.exception.ItCastApplicationException; -import itcast.news.repository.BlogRepository; +import itcast.blog.repository.BlogRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/schedule/src/main/java/itcast/news/repository/BlogRepository.java b/schedule/src/main/java/itcast/blog/repository/BlogRepository.java similarity index 87% rename from schedule/src/main/java/itcast/news/repository/BlogRepository.java rename to schedule/src/main/java/itcast/blog/repository/BlogRepository.java index 585fecc1..1f4719f6 100644 --- a/schedule/src/main/java/itcast/news/repository/BlogRepository.java +++ b/schedule/src/main/java/itcast/blog/repository/BlogRepository.java @@ -1,4 +1,4 @@ -package itcast.news.repository; +package itcast.blog.repository; import itcast.domain.blog.Blog; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java b/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java index 113d1741..e8fabd66 100644 --- a/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java +++ b/schedule/src/test/java/itcast/ai/application/GPTServiceTest.java @@ -16,11 +16,10 @@ import itcast.domain.blog.enums.BlogStatus; import itcast.domain.user.enums.Interest; import itcast.exception.ItCastApplicationException; -import itcast.news.repository.BlogRepository; +import itcast.blog.repository.BlogRepository; import java.util.Collections; import java.util.Optional; -import itcast.news.repository.BlogRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; From 3675ca477dd393dab0cc4ee0040abd2f04b426e1 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 14:24:50 +0900 Subject: [PATCH 049/105] =?UTF-8?q?[#37]=20feat=20:=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../itcast/news/application/NewsService.java | 3 +++ .../news/common/schedule/AlarmSchedule.java | 22 +++++++++++++++++++ schedule/src/main/resources/application.yml | 3 ++- 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java diff --git a/build.gradle b/build.gradle index 5d7183df..dea3f1c5 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ subprojects { dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' - implementation 'org.springframework.boot:spring-boot-starter-web' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' runtimeOnly 'com.h2database:h2' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 7d409a90..421671d3 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -77,6 +77,9 @@ public void newsCrawling() throws IOException { }); } + public void newsAlarm() { + } + private LocalDateTime convertDateTime(String info) { String[] parts = info.split(" "); String date = parts[0]; diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java new file mode 100644 index 00000000..17d946cb --- /dev/null +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -0,0 +1,22 @@ +package itcast.news.common.schedule; + +import itcast.news.application.NewsService; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import reactor.core.scheduler.Scheduler; + +@Component +@RequiredArgsConstructor +public class AlarmSchedule { + private final NewsService newsService; + + @Scheduled(cron = "${spring.scheduler.cron.alarm-Scheduled}") + public void CreateAlarmSchedule() { + System.out.println("crawling...."); + newsService.newsAlarm(); + System.out.println("crawled End"); + + } +} diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index b03e07ed..dc5c54f6 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -8,6 +8,7 @@ spring: console: enabled: true jpa : + database: mysql hibernate: ddl-auto: update show-sql: true @@ -19,7 +20,7 @@ spring: scheduler: cron: news-crawling: "0 0 */3 * * ? " - save-data-every-day: "0 0 1 * * ?" + alarm-Scheduled: "0 0 1 1/1 * ?" delete-aging-data: "0 0 0 1 0/6 ?" timeout: connection: 5000 From 23bfacac34070f9da255f3aedd94c7c72c16d074 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 15:05:16 +0900 Subject: [PATCH 050/105] =?UTF-8?q?[#37]=20feat=20:=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/news/application/NewsService.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 421671d3..67223b74 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -1,5 +1,6 @@ package itcast.news.application; +import itcast.domain.news.News; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; import lombok.RequiredArgsConstructor; @@ -9,7 +10,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.beans.Transient; import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -24,6 +27,8 @@ public class NewsService { private String url; private final int LINK_SIZE = 10; private final int HOUR = 12; + private final int YESTERDAY = 1; + private final int ALARM_HOUR = 7; private final NewsRepository newsRepository; @@ -77,7 +82,15 @@ public void newsCrawling() throws IOException { }); } + @Transient public void newsAlarm() { + LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); + List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); + + LocalDateTime today = LocalDateTime.now().minusHours(ALARM_HOUR); + createdAlarm.forEach(alarm -> { + alarm.newsUpdate(today); + }); } private LocalDateTime convertDateTime(String info) { @@ -106,4 +119,6 @@ private String cleanContent(String info) { .trim(); return info; } + + } From 12ed3b75d3e77370fe1b08b2de7dbe7ed9de0276 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 15:05:50 +0900 Subject: [PATCH 051/105] =?UTF-8?q?[#37]=20feat=20:=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20entity=20=EB=84=A3=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 74f13f50..ce170c62 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -92,4 +92,7 @@ public News( this.thumbnail = thumbnail; this.publishedAt = publishedAt; } + public void newsUpdate(LocalDateTime sendAt) { + this.sendAt = sendAt; + } } From 2e720994bdcbd57b1d8e07dfad717b2fa49f22de Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 15:06:30 +0900 Subject: [PATCH 052/105] =?UTF-8?q?[#37]=20feat=20:=20=EC=96=B4=EC=A0=9C?= =?UTF-8?q?=20=ED=81=AC=EB=A1=A4=EB=A7=81=ED=95=9C=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/itcast/news/repository/NewsRepository.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index 957be302..1fd16a98 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -3,10 +3,14 @@ import itcast.domain.news.News; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.time.LocalDate; import java.util.List; public interface NewsRepository extends JpaRepository { @Query("select n.link from News n") List findAllLinks(); + @Query("select n from News n where function('DATE',n.createdAt) = :yesterday ") + List findAllByCreatedAt(@Param("yesterday") LocalDate yesterday); } From 78334b3a7ea73dc3cdf581373a93aadbc757c5c9 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 15:09:40 +0900 Subject: [PATCH 053/105] =?UTF-8?q?[#37]=20fix=20:=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/common/schedule/AlarmSchedule.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index 17d946cb..b4b6e5e5 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -2,10 +2,8 @@ import itcast.news.application.NewsService; import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import reactor.core.scheduler.Scheduler; @Component @RequiredArgsConstructor @@ -17,6 +15,5 @@ public void CreateAlarmSchedule() { System.out.println("crawling...."); newsService.newsAlarm(); System.out.println("crawled End"); - } } From 70c7d715b6364ffeccb2b243e0dec32c34e69f55 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 15:29:40 +0900 Subject: [PATCH 054/105] =?UTF-8?q?[#37]=20fix=20:=20=EC=95=8C=EB=9E=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/itcast/news/application/NewsService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 67223b74..17715790 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -29,6 +29,7 @@ public class NewsService { private final int HOUR = 12; private final int YESTERDAY = 1; private final int ALARM_HOUR = 7; + private final int ALARM_DAY = 2; private final NewsRepository newsRepository; @@ -87,9 +88,9 @@ public void newsAlarm() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); - LocalDateTime today = LocalDateTime.now().minusHours(ALARM_HOUR); + LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); createdAlarm.forEach(alarm -> { - alarm.newsUpdate(today); + alarm.newsUpdate(sendAt); }); } From 28c7e1bb1300d7dbc2171235eef741fafc9ad6e4 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 15:55:33 +0900 Subject: [PATCH 055/105] =?UTF-8?q?[#37]=20fix=20:=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20=ED=8A=B8=EB=A0=8C=EC=A0=9D=EC=85=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=9E=98=EB=AA=BB=EB=90=9C=20int=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 4 ++-- .../src/main/java/itcast/news/application/NewsService.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index ce170c62..00fbaa01 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -38,7 +38,7 @@ public class News extends BaseEntity { @Column(nullable = false) private LocalDateTime publishedAt; - private int rating; + private Interest rating; @Column(nullable = false) private String link; @@ -57,7 +57,7 @@ public News( String originalContent, Interest interest, LocalDateTime publishedAt, - int rating, + Interest rating, String link, String thumbnail, NewsStatus status, diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 17715790..32e8b54e 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -3,6 +3,7 @@ import itcast.domain.news.News; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -83,7 +84,7 @@ public void newsCrawling() throws IOException { }); } - @Transient + @Transactional public void newsAlarm() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); From 60ab06bb4b3bd8b796c4a58f444f2cdbf4479e37 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 19:54:35 +0900 Subject: [PATCH 056/105] =?UTF-8?q?[#28]=20fix=20:=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/domain/news/News.java | 3 +- .../itcast/news/application/NewsService.java | 55 ++++++++++++------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 00fbaa01..576841c3 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -92,7 +92,8 @@ public News( this.thumbnail = thumbnail; this.publishedAt = publishedAt; } - public void newsUpdate(LocalDateTime sendAt) { + public void newsUpdate(LocalDateTime sendAt, NewsStatus status) { this.sendAt = sendAt; + this.status = status; } } diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 32e8b54e..5db6789a 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -1,6 +1,7 @@ package itcast.news.application; import itcast.domain.news.News; +import itcast.domain.news.enums.NewsStatus; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; import jakarta.transaction.Transactional; @@ -38,27 +39,11 @@ public void newsCrawling() throws IOException { Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); - List links = new ArrayList<>(); - articles.forEach(article -> { - if (links.size() >= LINK_SIZE) { - return; - } - String link = article.select("a").attr("href"); - links.add(link); - }); - List isValidLinks = newsRepository.findAllLinks(); - - List validLinks = links - .stream() - .filter(link -> !isValidLinks.contains(link)) - .distinct() - .collect(Collectors.toList()); + List links = findLinks(); - if(validLinks.isEmpty()) { - throw new RuntimeException("No links found"); - } + links = isValidLinks(links); - validLinks.forEach(link -> { + links.forEach(link -> { try { Document url = Jsoup.connect(link).get(); String titles = url.select("#title_area").text(); @@ -84,6 +69,36 @@ public void newsCrawling() throws IOException { }); } + public List findLinks() throws IOException { + Document document = Jsoup.connect(url).get(); + Elements articles = document.select(".sa_thumb_inner"); + + List links = new ArrayList<>(); + articles.forEach(article -> { + if (links.size() >= LINK_SIZE) { + return; + } + String link = article.select("a").attr("href"); + links.add(link); + }); + return links; + } + + private List isValidLinks(List links) { + List isValidLinks = newsRepository.findAllLinks(); + + List validLinks = links + .stream() + .filter(link -> !isValidLinks.contains(link)) + .distinct() + .collect(Collectors.toList()); + + if(validLinks.isEmpty()) { + throw new RuntimeException("No links found"); + } + return validLinks; + } + @Transactional public void newsAlarm() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); @@ -91,7 +106,7 @@ public void newsAlarm() { LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); createdAlarm.forEach(alarm -> { - alarm.newsUpdate(sendAt); + alarm.newsUpdate(sendAt, NewsStatus.SUMMARY); }); } From 332bbfec4cc9de838c651f42a56ea50c5dbe2857 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 20:30:41 +0900 Subject: [PATCH 057/105] =?UTF-8?q?[#26]=20feat=20:=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EC=8A=A4=EC=BC=80=EC=A5=B4?= =?UTF-8?q?=EB=9F=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 25 ++++++++++++------- .../news/common/schedule/AlarmSchedule.java | 4 +-- .../news/common/schedule/OldDataSchedule.java | 21 ++++++++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 schedule/src/main/java/itcast/news/common/schedule/OldDataSchedule.java diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 5db6789a..a90a778d 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -69,6 +69,22 @@ public void newsCrawling() throws IOException { }); } + @Transactional + public void newsAlarm() { + LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); + List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); + + LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); + createdAlarm.forEach(alarm -> { + alarm.newsUpdate(sendAt, NewsStatus.SUMMARY); + }); + } + + @Transactional + public void deleteOldData() throws IOException { + newsRepository.deleteOldNews(); + } + public List findLinks() throws IOException { Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); @@ -99,16 +115,7 @@ private List isValidLinks(List links) { return validLinks; } - @Transactional - public void newsAlarm() { - LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); - List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); - LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); - createdAlarm.forEach(alarm -> { - alarm.newsUpdate(sendAt, NewsStatus.SUMMARY); - }); - } private LocalDateTime convertDateTime(String info) { String[] parts = info.split(" "); diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index b4b6e5e5..03f4a318 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -12,8 +12,8 @@ public class AlarmSchedule { @Scheduled(cron = "${spring.scheduler.cron.alarm-Scheduled}") public void CreateAlarmSchedule() { - System.out.println("crawling...."); + System.out.println("alarm schedule...."); newsService.newsAlarm(); - System.out.println("crawled End"); + System.out.println("alarm schedule Finish"); } } diff --git a/schedule/src/main/java/itcast/news/common/schedule/OldDataSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/OldDataSchedule.java new file mode 100644 index 00000000..cd7d4eb3 --- /dev/null +++ b/schedule/src/main/java/itcast/news/common/schedule/OldDataSchedule.java @@ -0,0 +1,21 @@ +package itcast.news.common.schedule; + +import itcast.news.application.NewsService; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class OldDataSchedule { + private final NewsService newsService; + + @Scheduled(cron = "${spring.scheduler.cron.old-delete-data}") + public void scheduleNewsCrawling() throws IOException { + System.out.println("deleting old data...."); + newsService.deleteOldData(); + System.out.println("deleting old data Finish"); + } +} From 6b4dcfc8de5471e8ac4a55f8545eb88a32d415bb Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 20:31:23 +0900 Subject: [PATCH 058/105] =?UTF-8?q?[#26]=20fix=20:=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=EC=8B=9C=EC=9E=91/=EB=81=9D=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/common/schedule/NewsSchedule.java | 2 +- .../src/main/java/itcast/news/repository/NewsRepository.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java index 41b644ed..c30177e9 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/NewsSchedule.java @@ -17,6 +17,6 @@ public class NewsSchedule { public void scheduleNewsCrawling() throws IOException { System.out.println("crawling...."); newsService.newsCrawling(); - System.out.println("crawled End"); + System.out.println("crawled Finish"); } } \ No newline at end of file diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index 1fd16a98..94017d8a 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -2,6 +2,7 @@ import itcast.domain.news.News; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -13,4 +14,7 @@ public interface NewsRepository extends JpaRepository { List findAllLinks(); @Query("select n from News n where function('DATE',n.createdAt) = :yesterday ") List findAllByCreatedAt(@Param("yesterday") LocalDate yesterday); + @Modifying + @Query("DELETE FROM News n WHERE n.createdAt <= CURRENT_DATE - 6 MONTH") + void deleteOldNews(); } From 70c29c2829656c3af86ba885cf963e7bb56115ad Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 20:32:05 +0900 Subject: [PATCH 059/105] =?UTF-8?q?[#28]=20feat=20:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/NewsServiceTest.java | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index f2787fc8..2b7c9f30 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -1,17 +1,30 @@ package itcast.news.application; import itcast.news.repository.NewsRepository; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + + +@SpringBootTest @ExtendWith(MockitoExtension.class) public class NewsServiceTest { - @InjectMocks + @Autowired private NewsService newsService; @Mock @@ -19,7 +32,26 @@ public class NewsServiceTest { @Test @DisplayName("크롤링 성공 테스트") - void crawlingSuccessTest() { + void crawlingSuccessTest() throws IOException { + // give + String url = "https://news.naver.com/breakingnews/section/105/283"; + Document document = Jsoup.connect(url).get(); + + // when + Elements articles = document.select(".sa_thumb_inner"); + // then + assertFalse(articles.isEmpty(), "Articles should not be empty"); + } + + @Test + @DisplayName("링크 크롤링 성공 테스트") + void linkCrawlingSuccessTest() throws IOException { + // give + String url = "https://news.naver.com/breakingnews/section/105/283"; + Document document = Jsoup.connect(url).get(); + List links = new ArrayList<>(); + // when + // then } } From 4e08a302c33d46fecdef4ce51a75e89464392a1b Mon Sep 17 00:00:00 2001 From: junseok708 Date: Thu, 12 Dec 2024 20:35:09 +0900 Subject: [PATCH 060/105] Delete schedule/src/test/java/itcast/news/application/NewsServiceTest.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 테스트 코드 삭제 --- .../news/application/NewsServiceTest.java | 57 ------------------- 1 file changed, 57 deletions(-) delete mode 100644 schedule/src/test/java/itcast/news/application/NewsServiceTest.java diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java deleted file mode 100644 index 2b7c9f30..00000000 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package itcast.news.application; - -import itcast.news.repository.NewsRepository; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - - -@SpringBootTest -@ExtendWith(MockitoExtension.class) -public class NewsServiceTest { - - @Autowired - private NewsService newsService; - - @Mock - private NewsRepository newsRepository; - - @Test - @DisplayName("크롤링 성공 테스트") - void crawlingSuccessTest() throws IOException { - // give - String url = "https://news.naver.com/breakingnews/section/105/283"; - Document document = Jsoup.connect(url).get(); - - // when - Elements articles = document.select(".sa_thumb_inner"); - - // then - assertFalse(articles.isEmpty(), "Articles should not be empty"); - } - - @Test - @DisplayName("링크 크롤링 성공 테스트") - void linkCrawlingSuccessTest() throws IOException { - // give - String url = "https://news.naver.com/breakingnews/section/105/283"; - Document document = Jsoup.connect(url).get(); - List links = new ArrayList<>(); - // when - // then - } -} From 1ce2ee5bba53b0413229aa5e43f6b4b36b2dc7ef Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 20:41:02 +0900 Subject: [PATCH 061/105] =?UTF-8?q?[#26]=20fix=20:=20integer=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index 576841c3..fd42e0fb 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -38,7 +38,7 @@ public class News extends BaseEntity { @Column(nullable = false) private LocalDateTime publishedAt; - private Interest rating; + private Integer rating; @Column(nullable = false) private String link; @@ -57,7 +57,7 @@ public News( String originalContent, Interest interest, LocalDateTime publishedAt, - Interest rating, + Integer rating, String link, String thumbnail, NewsStatus status, From cd70d20abe9693a8aac37c09f8e4a6317a5640d6 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 12 Dec 2024 20:50:58 +0900 Subject: [PATCH 062/105] =?UTF-8?q?[#26]=20fix=20:=20static=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index a90a778d..41932c2e 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -27,20 +27,16 @@ public class NewsService { @Value("${spring.crawler.naver-it-url}") private String url; - private final int LINK_SIZE = 10; - private final int HOUR = 12; - private final int YESTERDAY = 1; - private final int ALARM_HOUR = 7; - private final int ALARM_DAY = 2; + private static final int LINK_SIZE = 10; + private static final int HOUR = 12; + private static final int YESTERDAY = 1; + private static final int ALARM_HOUR = 7; + private static final int ALARM_DAY = 2; private final NewsRepository newsRepository; public void newsCrawling() throws IOException { - Document document = Jsoup.connect(url).get(); - Elements articles = document.select(".sa_thumb_inner"); - List links = findLinks(); - links = isValidLinks(links); links.forEach(link -> { @@ -69,22 +65,6 @@ public void newsCrawling() throws IOException { }); } - @Transactional - public void newsAlarm() { - LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); - List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); - - LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); - createdAlarm.forEach(alarm -> { - alarm.newsUpdate(sendAt, NewsStatus.SUMMARY); - }); - } - - @Transactional - public void deleteOldData() throws IOException { - newsRepository.deleteOldNews(); - } - public List findLinks() throws IOException { Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); @@ -115,7 +95,21 @@ private List isValidLinks(List links) { return validLinks; } + @Transactional + public void newsAlarm() { + LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); + List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); + LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); + createdAlarm.forEach(alarm -> { + alarm.newsUpdate(sendAt, NewsStatus.SUMMARY); + }); + } + + @Transactional + public void deleteOldData() throws IOException { + newsRepository.deleteOldNews(); + } private LocalDateTime convertDateTime(String info) { String[] parts = info.split(" "); From c9f5bf7606fd7cc89d5bc82ddf62bc8f23c3490f Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 13 Dec 2024 12:27:29 +0900 Subject: [PATCH 063/105] =?UTF-8?q?[#26]=20feat=20:=20=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=EB=A7=81=20=EA=B8=B0=EB=8A=A5=EC=97=90=20Ai=EC=9A=94=EC=95=BD?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/domain/blog/Blog.java | 4 +-- .../main/java/itcast/domain/news/News.java | 15 ++++++++--- .../java/itcast/exception/ErrorCodes.java | 1 + .../itcast/ai/application/GPTService.java | 27 ++++++++++++++++--- .../main/java/itcast/ai/client/GPTClient.java | 25 +++++++++++------ .../ai/dto/response/GPTSummaryResponse.java | 4 +-- .../itcast/news/application/NewsService.java | 19 ++++++++----- schedule/src/main/resources/application.yml | 19 ++++++++++++- 8 files changed, 89 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/itcast/domain/blog/Blog.java b/common/src/main/java/itcast/domain/blog/Blog.java index 8366bc2c..1429ca5a 100644 --- a/common/src/main/java/itcast/domain/blog/Blog.java +++ b/common/src/main/java/itcast/domain/blog/Blog.java @@ -47,7 +47,7 @@ public class Blog extends BaseEntity { @Column(nullable = false) private LocalDateTime publishedAt; - private Long rating; + private Integer rating; @Column(nullable = false) private String link; @@ -81,7 +81,7 @@ public Blog( public void applySummaryUpdate( final String content, final Interest interest, - final Long rating, + final Integer rating, final BlogStatus status ) { this.content = content; diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index fd42e0fb..36570039 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -7,7 +7,6 @@ import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; - import lombok.NoArgsConstructor; import java.time.LocalDateTime; @@ -53,7 +52,6 @@ public class News extends BaseEntity { @Builder public News( String title, - String content, String originalContent, Interest interest, LocalDateTime publishedAt, @@ -92,8 +90,19 @@ public News( this.thumbnail = thumbnail; this.publishedAt = publishedAt; } - public void newsUpdate(LocalDateTime sendAt, NewsStatus status) { + public void newsUpdate(LocalDateTime sendAt) { this.sendAt = sendAt; + } + + public void applySummaryUpdate( + final String content, + final Interest interest, + final Integer rating, + final NewsStatus status + ) { + this.content = content; + this.interest = interest; + this.rating = rating; this.status = status; } } diff --git a/common/src/main/java/itcast/exception/ErrorCodes.java b/common/src/main/java/itcast/exception/ErrorCodes.java index 7f2fc642..04751ccc 100644 --- a/common/src/main/java/itcast/exception/ErrorCodes.java +++ b/common/src/main/java/itcast/exception/ErrorCodes.java @@ -5,6 +5,7 @@ public enum ErrorCodes { BLOG_NOT_FOUND("블로그가 유효하지 않습니다.", 1001L, HttpStatus.NOT_FOUND), + NEWS_NOT_FOUND("뉴스가 유효하지 않습니다.", 2001L, HttpStatus.NOT_FOUND), BAD_REQUEST("BAD_REQUEST", 9404L, HttpStatus.BAD_REQUEST), BAD_REQUEST_JSON_PARSE_ERROR("[BAD_REQUEST] JSON_PARSE_ERROR - 올바른 JSON 형식이 아님", 9405L, HttpStatus.BAD_REQUEST), diff --git a/schedule/src/main/java/itcast/ai/application/GPTService.java b/schedule/src/main/java/itcast/ai/application/GPTService.java index bef15026..82670e86 100644 --- a/schedule/src/main/java/itcast/ai/application/GPTService.java +++ b/schedule/src/main/java/itcast/ai/application/GPTService.java @@ -1,15 +1,20 @@ package itcast.ai.application; import static itcast.exception.ErrorCodes.BLOG_NOT_FOUND; +import static itcast.exception.ErrorCodes.NEWS_NOT_FOUND; import itcast.ai.client.GPTClient; import itcast.ai.dto.request.GPTSummaryRequest; import itcast.ai.dto.response.GPTSummaryResponse; import itcast.domain.blog.Blog; import itcast.domain.blog.enums.BlogStatus; +import itcast.domain.news.News; +import itcast.domain.news.enums.NewsStatus; +import itcast.domain.user.enums.ArticleType; import itcast.domain.user.enums.Interest; import itcast.exception.ItCastApplicationException; import itcast.blog.repository.BlogRepository; +import itcast.news.repository.NewsRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,12 +25,28 @@ public class GPTService { private final GPTClient gptClient; private final BlogRepository blogRepository; + private final NewsRepository newsRepository; @Transactional - public void updateBlogBySummaryContent(final GPTSummaryRequest gptSummaryRequest) { - final GPTSummaryResponse response = gptClient.sendRequest(gptSummaryRequest); + public void updateNewsBySummaryContent(final GPTSummaryRequest gptSummaryRequest, final Long newsId) { + final GPTSummaryResponse response = gptClient.sendRequest(gptSummaryRequest, ArticleType.NEWS); - final Blog blog = blogRepository.findById(1L) + final News news = newsRepository.findById(newsId) + .orElseThrow(() -> new ItCastApplicationException(NEWS_NOT_FOUND)); + + news.applySummaryUpdate( + response.getSummary(), + Interest.from(response.getCategory()), + response.getRating(), + NewsStatus.SUMMARY + ); + } + + @Transactional + public void updateBlogBySummaryContent(final GPTSummaryRequest gptSummaryRequest, final Long blogId) { + final GPTSummaryResponse response = gptClient.sendRequest(gptSummaryRequest, ArticleType.BLOG); + + final Blog blog = blogRepository.findById(blogId) .orElseThrow(() -> new ItCastApplicationException(BLOG_NOT_FOUND)); blog.applySummaryUpdate( diff --git a/schedule/src/main/java/itcast/ai/client/GPTClient.java b/schedule/src/main/java/itcast/ai/client/GPTClient.java index 146406c8..c8e3598d 100644 --- a/schedule/src/main/java/itcast/ai/client/GPTClient.java +++ b/schedule/src/main/java/itcast/ai/client/GPTClient.java @@ -5,6 +5,8 @@ import itcast.ai.dto.response.GPTSummaryResponse; import java.util.List; import java.util.Map; + +import itcast.domain.user.enums.ArticleType; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; @@ -18,8 +20,11 @@ public class GPTClient { @Value("${openai.secret-key}") private String secretKey; - @Value("${openai.prompt}") - private String prompt; + @Value("${openai.news-prompt}") + private String newsPrompt; + + @Value("${openai.blog-prompt}") + private String blogPrompt; public GPTClient() { this.webClient = WebClient.builder() @@ -27,27 +32,31 @@ public GPTClient() { .build(); } - public GPTSummaryResponse sendRequest(final GPTSummaryRequest gptSummaryRequest) { + public GPTSummaryResponse sendRequest(final GPTSummaryRequest gptSummaryRequest, final ArticleType type) { return webClient.post() .uri("/v1/chat/completions") .header(HttpHeaders.AUTHORIZATION, "Bearer " + secretKey) .header(HttpHeaders.CONTENT_TYPE, "application/json") - .bodyValue(toRequestBody(gptSummaryRequest)) + .bodyValue(toRequestBody(gptSummaryRequest,type)) .retrieve() .bodyToMono(GPTSummaryResponse.class) .block(); } - private Map toRequestBody(final GPTSummaryRequest gptSummaryRequest) { + private Map toRequestBody(final GPTSummaryRequest gptSummaryRequest, final ArticleType type) { return Map.of( "model", gptSummaryRequest.model(), - "messages", toMessages(gptSummaryRequest.message()), + "messages", toMessages(gptSummaryRequest.message(),type), "temperature", gptSummaryRequest.temperature() ); } - private List toMessages(final Message message) { - message.addPrompt(prompt); + private List toMessages(final Message message, final ArticleType type) { + if(type.equals(ArticleType.NEWS)) { + message.addPrompt(newsPrompt); + return List.of(message); + } + message.addPrompt(blogPrompt); return List.of(message); } } diff --git a/schedule/src/main/java/itcast/ai/dto/response/GPTSummaryResponse.java b/schedule/src/main/java/itcast/ai/dto/response/GPTSummaryResponse.java index 2259fe74..4f317fde 100644 --- a/schedule/src/main/java/itcast/ai/dto/response/GPTSummaryResponse.java +++ b/schedule/src/main/java/itcast/ai/dto/response/GPTSummaryResponse.java @@ -23,8 +23,8 @@ public String getSummary() { return extractField(2).replace(",", ""); } - public Long getRating() { - return Long.parseLong(extractField(3)); + public Integer getRating() { + return Integer.parseInt(extractField(3)); } private String extractField(final int index) { diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 41932c2e..70abc93f 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -1,7 +1,9 @@ package itcast.news.application; +import itcast.ai.Message; +import itcast.ai.application.GPTService; +import itcast.ai.dto.request.GPTSummaryRequest; import itcast.domain.news.News; -import itcast.domain.news.enums.NewsStatus; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; import jakarta.transaction.Transactional; @@ -12,7 +14,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import java.beans.Transient; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; @@ -34,6 +35,7 @@ public class NewsService { private static final int ALARM_DAY = 2; private final NewsRepository newsRepository; + private final GPTService gptService; public void newsCrawling() throws IOException { List links = findLinks(); @@ -53,15 +55,20 @@ public void newsCrawling() throws IOException { content = cleanContent(content); LocalDateTime publishedAt = convertDateTime(date); - // dto 저장 if (thumbnail.isEmpty()) { System.out.println("썸네일이 없습니다"); } + CreateNewsRequest newsRequest = new CreateNewsRequest(titles, content, link, thumbnail, publishedAt); - newsRepository.save(newsRequest.toEntity(titles, content, link, thumbnail, publishedAt)); + News news = newsRepository.save(newsRequest.toEntity(titles, content, link, thumbnail, publishedAt)); + Message message = new Message("user", content); + GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini",message,0.7f); + + gptService.updateNewsBySummaryContent(request,news.getId()); } catch (IOException e) { throw new RuntimeException(e); } + }); } @@ -80,7 +87,7 @@ public List findLinks() throws IOException { return links; } - private List isValidLinks(List links) { + List isValidLinks(List links) { List isValidLinks = newsRepository.findAllLinks(); List validLinks = links @@ -102,7 +109,7 @@ public void newsAlarm() { LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); createdAlarm.forEach(alarm -> { - alarm.newsUpdate(sendAt, NewsStatus.SUMMARY); + alarm.newsUpdate(sendAt); }); } diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index dc5c54f6..d011e146 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -33,7 +33,7 @@ spring: root: INFO org.jsoup: DEBUG openai: - prompt: | + blog-prompt: | \n\n 내용을 보고 요약해주세요. 그리고 GPT가 생각하기에 프론트엔드 쪽인지 백엔드 쪽인지 판별해주고 읽었을때 점수를 매겨줄 수 있을까요? **필수 조건**: @@ -49,4 +49,21 @@ openai: “category” : [FRONTEND or BACKEND] “summary” : [요약 내용] “rating” : [점수] + } + news-prompt: | + \n\n + 내용을 보고 요약해주세요. 그리고 GPT가 읽었을때 점수를 매겨줄 수 있을까요? + **필수 조건**: + 1. 응답 형식을 필수적으로 맞춰서 작성해야합니다. + 2. 카테고리를 정할 때 무조건 NEWS로 주어야 합니다. + 3. 점수는 1에서 10까지의 정수로 알려주시고 GPT가 생각하기에 내용에 대한 유익한 점수를 매겨줄 수 있나요? + **점수 조건**: + 1. 유익성 (4점) : 독자가 얻을 수 있는 실질적인 정보와 지식의 가치. + 2. 작성의 명료성 (3점) : 내용이 얼마나 명확하고 이해하기 쉽게 작성되었는지 평가. + 3. 독창성 및 참신함 (3점) : 내용이 새로운 관점을 제시하거나 기존 정보를 창의적으로 다루었는지 평가. + **응답 형식**: + { + “category” : NEWS + “summary” : [요약 내용] + “rating” : [점수] } \ No newline at end of file From 556ebd3d31360da6fd3b4df50cfdb8e78173340d Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 13 Dec 2024 14:26:32 +0900 Subject: [PATCH 064/105] =?UTF-8?q?[#28]=20feat=20:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 16 +-- .../news/application/NewsServiceTest.java | 99 +++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 schedule/src/test/java/itcast/news/application/NewsServiceTest.java diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 70abc93f..b5e1e4e9 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -118,22 +118,26 @@ public void deleteOldData() throws IOException { newsRepository.deleteOldNews(); } - private LocalDateTime convertDateTime(String info) { + LocalDateTime convertDateTime(String info) { String[] parts = info.split(" "); - String date = parts[0]; - String ampm = parts[1]; - String time = parts[2]; + String word = parts[0]; + String date = parts[1]; + String ampm = parts[2]; + String time = parts[3]; - date = date.replaceAll("입력", ""); + word = word.replaceAll("입력", ""); String[] timeParts = time.split(":"); int hour = Integer.parseInt(timeParts[0]); if (ampm.equals("오후") && hour != HOUR) { hour += HOUR; } + if (ampm.equals("오전")&& hour == HOUR) { + hour = 0; + } String timeDate = date + " " + String.format("%02d", hour) + ":" + timeParts[1]; - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd. HH:mm"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); LocalDateTime localDateTime = LocalDateTime.parse(timeDate, formatter); return localDateTime; } diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java new file mode 100644 index 00000000..73c48ded --- /dev/null +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -0,0 +1,99 @@ +package itcast.news.application; + +import itcast.news.repository.NewsRepository; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +public class NewsServiceTest { + + @InjectMocks + private NewsService newsService; + + @Mock + private NewsRepository newsRepository; + + @Test + @DisplayName("크롤링 테스트") + void SuccessCrawling() throws IOException { + // give + String url = "https://news.naver.com/breakingnews/section/105/283"; + Document document = Jsoup.connect(url).get(); + + // when + Elements articles = document.select(".sa_thumb_inner"); + + // then + assertFalse(articles.isEmpty(), "링크가 없음"); + } + + @Test + @DisplayName("중복 된 메소드 체크 테스트") + void isValidLinksTest() { + // give + List existingLinks = Arrays.asList( + "https://example.com/1", + "https://example.com/2", + "https://example.com/3"); + when(newsRepository.findAllLinks()).thenReturn(existingLinks); + + List linksToCheck = Arrays.asList( + "https://example.com/1", // 존재하는 링크 + "https://example.com/4", // 새로운 링크 + "https://example.com/5" // 새로운 링크 + ); + + // when + List validLinks = newsService.isValidLinks(linksToCheck); + + // then + assertEquals(2, validLinks.size(), "Valid links 크기가 2이 아닙니다"); + assertTrue(validLinks.contains("https://example.com/4"), + "Valid links에 'https://example.com/4'가 포함되지 않았습니다"); + assertTrue(validLinks.contains("https://example.com/5"), + "Valid links should contain 'https://example.com/5'가 포함되지 않았습니다"); + } + + @Test + @DisplayName("convertDateTime 메서드 테스트") + void convertDateTimeTest() { + // give + String testPmDate = "입력 2020-01-01 오후 01:01"; + String testAmDate = "입력 2020-01-01 오전 01:01"; + String testNoonDate = "입력 2020-01-01 오후 12:01"; + String testMidnighDate = "입력 2020-01-01 오전 12:01"; + + // when + LocalDateTime convertDatePm = newsService.convertDateTime(testPmDate); + LocalDateTime convertDateAm = newsService.convertDateTime(testAmDate); + LocalDateTime convertDateNoon = newsService.convertDateTime(testNoonDate); + LocalDateTime convertDateMidnight = newsService.convertDateTime(testMidnighDate); + + // then + assertTrue(convertDatePm.isEqual(LocalDateTime.of(2020, 01, 01, 13, 01)), + "오후 시간이 올바르지 않습니다."); + assertTrue(convertDateAm.isEqual(LocalDateTime.of(2020, 01, 01, 01, 01)), + "오전 시간이 올바르지 않습니다."); + assertTrue(convertDateNoon.isEqual(LocalDateTime.of(2020, 01, 01, 12, 01)), + "오후 12시 시간이 올바르지 않습니다."); + assertTrue(convertDateMidnight.isEqual(LocalDateTime.of(2020, 1, 1, 0, 1)), + "오전 12시 시간이 올바르지 않습니다."); + } + +} From 5b2514a80d6ff1f7afa33fd62aca36f6685977c4 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 13 Dec 2024 20:06:23 +0900 Subject: [PATCH 065/105] =?UTF-8?q?[#28]=20feat=20:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 10 +-- .../news/application/NewsServiceTest.java | 90 ++++++++++++++++++- 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index b5e1e4e9..32c011b1 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -38,7 +38,7 @@ public class NewsService { private final GPTService gptService; public void newsCrawling() throws IOException { - List links = findLinks(); + List links = findLinks(url); links = isValidLinks(links); links.forEach(link -> { @@ -72,7 +72,7 @@ public void newsCrawling() throws IOException { }); } - public List findLinks() throws IOException { + public List findLinks(String url) throws IOException { Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); @@ -95,10 +95,6 @@ List isValidLinks(List links) { .filter(link -> !isValidLinks.contains(link)) .distinct() .collect(Collectors.toList()); - - if(validLinks.isEmpty()) { - throw new RuntimeException("No links found"); - } return validLinks; } @@ -142,7 +138,7 @@ LocalDateTime convertDateTime(String info) { return localDateTime; } - private String cleanContent(String info) { + public String cleanContent(String info) { info = info.replaceAll("\\[.*?\\]", "") .replaceAll("\\(.*?\\)", "") .trim(); diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index 73c48ded..67481f69 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -1,23 +1,30 @@ package itcast.news.application; +import itcast.domain.news.News; import itcast.news.repository.NewsRepository; +import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -43,6 +50,23 @@ void SuccessCrawling() throws IOException { assertFalse(articles.isEmpty(), "링크가 없음"); } + @Test + @DisplayName("알람 보내는 메소드 테스트") + void testUpdateNews() { + // given + LocalDate yesterday = LocalDate.now().minusDays(1); + News mockNews = mock(News.class); + + Mockito.when(newsRepository.findAllByCreatedAt(yesterday)) + .thenReturn(Collections.singletonList(mockNews)); + + // When + newsService.newsAlarm(); + + // Then + verify(mockNews).newsUpdate(Mockito.any(LocalDateTime.class)); + } + @Test @DisplayName("중복 된 메소드 체크 테스트") void isValidLinksTest() { @@ -96,4 +120,68 @@ void convertDateTimeTest() { "오전 12시 시간이 올바르지 않습니다."); } + @Test + @DisplayName("cleanContent 메소드 테스트") + void cleanContent() { + // give + String testWord = "[아니] (이건) 안녕"; + + // when + String cleanWord = newsService.cleanContent(testWord); + + // then + assertTrue(cleanWord.equals("안녕"), "문장이 올바르지 않습니다"); + } + + @Test + @DisplayName("오레된 데이터 삭제 메소드 테스트") + void testDeleteOldNews() throws IOException { + // When + newsService.deleteOldData(); + + // Then + // newsRepository.deleteOldNews 호출 여부 검증 + verify(newsRepository, Mockito.times(1)).deleteOldNews(); + } + + @Test + @DisplayName("링크를 찾아 저장하는 메소드 테스트") + void findLinksTest() throws IOException { + // give + String url = "http://example.com"; + Document document = mock(Document.class); + Elements articles = new Elements(); + + Element mockElement1 = mock(Element.class); + Elements elements1 = mock(Elements.class); + when(mockElement1.select("a")).thenReturn(elements1); + when(elements1.attr("href")).thenReturn("http://example.com/link1"); + + Element mockElement2 = mock(Element.class); + Elements elements2 = mock(Elements.class); + when(mockElement2.select("a")).thenReturn(elements2); + when(elements2.attr("href")).thenReturn("http://example.com/link2"); + + articles.add(mockElement1); + articles.add(mockElement2); + when(document.select(".sa_thumb_inner")).thenReturn(articles); + + Connection connection = mock(Connection.class); + when(connection.get()).thenReturn(document); + + try (MockedStatic jsoupMock = mockStatic(Jsoup.class)) { + jsoupMock.when(() -> Jsoup.connect(url)).thenReturn(connection); + + // when + List links = newsService.findLinks(url); + + // then + assertNotNull(links); + assertEquals(2, links.size()); + assertEquals("http://example.com/link1", links.get(0)); + assertEquals("http://example.com/link2", links.get(1)); + } + } + + } From e4783b7a434df947369eb4774fe3eaf1c2efbf93 Mon Sep 17 00:00:00 2001 From: jubseok Date: Sun, 15 Dec 2024 19:49:04 +0900 Subject: [PATCH 066/105] =?UTF-8?q?[#29]=20feat=20:=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EB=93=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/exception/ErrorCodes.java | 6 +++ schedule/build.gradle | 2 + .../itcast/news/application/NewsService.java | 38 ++++++++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/itcast/exception/ErrorCodes.java b/common/src/main/java/itcast/exception/ErrorCodes.java index 04751ccc..9aba84d5 100644 --- a/common/src/main/java/itcast/exception/ErrorCodes.java +++ b/common/src/main/java/itcast/exception/ErrorCodes.java @@ -7,10 +7,16 @@ public enum ErrorCodes { BLOG_NOT_FOUND("블로그가 유효하지 않습니다.", 1001L, HttpStatus.NOT_FOUND), NEWS_NOT_FOUND("뉴스가 유효하지 않습니다.", 2001L, HttpStatus.NOT_FOUND), + // 뉴스 exception + INVALID_NEWS_CONTENT("뉴스의 내용이 없습니다",2002L,HttpStatus.BAD_REQUEST), + INVALID_NEWS_DATE("출판 날짜 형식이 아닙니다",2003L ,HttpStatus.BAD_REQUEST), + + BAD_REQUEST("BAD_REQUEST", 9404L, HttpStatus.BAD_REQUEST), BAD_REQUEST_JSON_PARSE_ERROR("[BAD_REQUEST] JSON_PARSE_ERROR - 올바른 JSON 형식이 아님", 9405L, HttpStatus.BAD_REQUEST), INTERNAL_SERVER_ERROR("INTERNAL_SERVER_ERROR", 9999L, HttpStatus.INTERNAL_SERVER_ERROR); + public final String message; public final Long code; public final HttpStatus status; diff --git a/schedule/build.gradle b/schedule/build.gradle index c67e7def..22350f8b 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -18,6 +18,8 @@ tasks.jar { } dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation project(':common') implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'com.amazonaws:aws-java-sdk-ses:1.12.3' diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 32c011b1..96335a6d 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -4,6 +4,7 @@ import itcast.ai.application.GPTService; import itcast.ai.dto.request.GPTSummaryRequest; import itcast.domain.news.News; +import itcast.exception.ItCastApplicationException; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; import jakarta.transaction.Transactional; @@ -22,6 +23,8 @@ import java.util.List; import java.util.stream.Collectors; +import static itcast.exception.ErrorCodes.*; + @Service @RequiredArgsConstructor public class NewsService { @@ -37,7 +40,7 @@ public class NewsService { private final NewsRepository newsRepository; private final GPTService gptService; - public void newsCrawling() throws IOException { + public void newsCrawling() throws IOException, ItCastApplicationException { List links = findLinks(url); links = isValidLinks(links); @@ -62,9 +65,9 @@ public void newsCrawling() throws IOException { CreateNewsRequest newsRequest = new CreateNewsRequest(titles, content, link, thumbnail, publishedAt); News news = newsRepository.save(newsRequest.toEntity(titles, content, link, thumbnail, publishedAt)); Message message = new Message("user", content); - GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini",message,0.7f); + GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini", message, 0.7f); - gptService.updateNewsBySummaryContent(request,news.getId()); + gptService.updateNewsBySummaryContent(request, news.getId()); } catch (IOException e) { throw new RuntimeException(e); } @@ -89,7 +92,9 @@ public List findLinks(String url) throws IOException { List isValidLinks(List links) { List isValidLinks = newsRepository.findAllLinks(); - + if (isValidLinks.isEmpty()) { + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); + } List validLinks = links .stream() .filter(link -> !isValidLinks.contains(link)) @@ -103,42 +108,55 @@ public void newsAlarm() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); + if(createdAlarm.isEmpty()) { + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); + } LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); createdAlarm.forEach(alarm -> { + if (alarm == null){ + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); + } alarm.newsUpdate(sendAt); }); } @Transactional - public void deleteOldData() throws IOException { + public void deleteOldData() { newsRepository.deleteOldNews(); } LocalDateTime convertDateTime(String info) { + if (info == null || info.trim().isEmpty()) { + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); + } String[] parts = info.split(" "); - String word = parts[0]; + if (parts.length != 4) { + throw new ItCastApplicationException(INVALID_NEWS_DATE); + } String date = parts[1]; String ampm = parts[2]; String time = parts[3]; - word = word.replaceAll("입력", ""); String[] timeParts = time.split(":"); int hour = Integer.parseInt(timeParts[0]); if (ampm.equals("오후") && hour != HOUR) { hour += HOUR; } - if (ampm.equals("오전")&& hour == HOUR) { + if (ampm.equals("오전") && hour == HOUR) { hour = 0; } String timeDate = date + " " + String.format("%02d", hour) + ":" + timeParts[1]; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); - LocalDateTime localDateTime = LocalDateTime.parse(timeDate, formatter); - return localDateTime; + return LocalDateTime.parse(timeDate, formatter); } public String cleanContent(String info) { + if (info == null || info.trim().isEmpty()) { + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); + } + info = info.replaceAll("\\[.*?\\]", "") .replaceAll("\\(.*?\\)", "") .trim(); From c669f1a8f2306a6cb86363173d80951d0825cc47 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 16 Dec 2024 00:59:55 +0900 Subject: [PATCH 067/105] =?UTF-8?q?[#29]=20feat=20:=20=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=93=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/build.gradle | 4 ++-- schedule/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 42d75d27..70514044 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -10,11 +10,11 @@ repositories { } tasks.bootJar { - enabled = true + enabled = false } tasks.jar { - enabled = false + enabled = true } dependencies { diff --git a/schedule/build.gradle b/schedule/build.gradle index 22350f8b..bca89814 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -10,11 +10,11 @@ repositories { } tasks.bootJar { - enabled = true + enabled = false } tasks.jar { - enabled = false + enabled = true } dependencies { From 3648c1cc2c43daf28690d9c1731dff2c25d098c4 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 16 Dec 2024 12:06:32 +0900 Subject: [PATCH 068/105] =?UTF-8?q?[#29]=20fix=20:=20comment=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 8 ++++---- .../java/itcast/news/application/NewsService.java | 15 ++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index e2c306e2..6494606c 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -7,7 +7,6 @@ import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; - import lombok.NoArgsConstructor; import java.time.LocalDateTime; @@ -29,7 +28,7 @@ public class News extends BaseEntity { private String content; @Lob - @Column(nullable = false,columnDefinition = "TEXT") + @Column(nullable = false, columnDefinition = "TEXT") private String originalContent; @Enumerated(EnumType.STRING) @@ -38,7 +37,7 @@ public class News extends BaseEntity { @Column(nullable = false) private LocalDateTime publishedAt; - private int rating; + private Integer rating; @Column(nullable = false) private String link; @@ -80,9 +79,9 @@ public News( String title, String originalContent, String link, - String thumbnail, Interest interest, NewsStatus status, + String thumbnail, LocalDateTime publishedAt) { this.title = title; this.originalContent = originalContent; @@ -92,6 +91,7 @@ public News( this.thumbnail = thumbnail; this.publishedAt = publishedAt; } + public void newsUpdate(LocalDateTime sendAt) { this.sendAt = sendAt; } diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 96335a6d..27dc6401 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -40,7 +40,7 @@ public class NewsService { private final NewsRepository newsRepository; private final GPTService gptService; - public void newsCrawling() throws IOException, ItCastApplicationException { + public void newsCrawling() throws IOException { List links = findLinks(url); links = isValidLinks(links); @@ -59,19 +59,17 @@ public void newsCrawling() throws IOException, ItCastApplicationException { LocalDateTime publishedAt = convertDateTime(date); if (thumbnail.isEmpty()) { - System.out.println("썸네일이 없습니다"); + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); } CreateNewsRequest newsRequest = new CreateNewsRequest(titles, content, link, thumbnail, publishedAt); News news = newsRepository.save(newsRequest.toEntity(titles, content, link, thumbnail, publishedAt)); Message message = new Message("user", content); GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini", message, 0.7f); - gptService.updateNewsBySummaryContent(request, news.getId()); } catch (IOException e) { - throw new RuntimeException(e); + throw new ItCastApplicationException(CRAWLING_PARSE_ERROR); } - }); } @@ -99,7 +97,7 @@ List isValidLinks(List links) { .stream() .filter(link -> !isValidLinks.contains(link)) .distinct() - .collect(Collectors.toList()); + .toList(); return validLinks; } @@ -108,12 +106,12 @@ public void newsAlarm() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); - if(createdAlarm.isEmpty()) { + if (createdAlarm.isEmpty()) { throw new ItCastApplicationException(INVALID_NEWS_CONTENT); } LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); createdAlarm.forEach(alarm -> { - if (alarm == null){ + if (alarm == null) { throw new ItCastApplicationException(INVALID_NEWS_CONTENT); } alarm.newsUpdate(sendAt); @@ -163,5 +161,4 @@ public String cleanContent(String info) { return info; } - } From 40f969e7e925eec7d83ffd309e2d0f31d6f77f12 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 16 Dec 2024 12:06:40 +0900 Subject: [PATCH 069/105] =?UTF-8?q?[#29]=20fix=20:=20comment=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/common/schedule/AlarmSchedule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index 03f4a318..60f0d0e3 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -11,7 +11,7 @@ public class AlarmSchedule { private final NewsService newsService; @Scheduled(cron = "${spring.scheduler.cron.alarm-Scheduled}") - public void CreateAlarmSchedule() { + public void createAlarmSchedule() { System.out.println("alarm schedule...."); newsService.newsAlarm(); System.out.println("alarm schedule Finish"); From c3950eeea0ac5ebfdd8741988bb1ecdd76a601c2 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 16 Dec 2024 12:45:51 +0900 Subject: [PATCH 070/105] =?UTF-8?q?[#29]=20fix=20:=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B6=80=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/exception/ErrorCodes.java | 1 + .../src/main/java/itcast/news/application/NewsService.java | 7 +++---- .../test/java/itcast/news/application/NewsServiceTest.java | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/itcast/exception/ErrorCodes.java b/common/src/main/java/itcast/exception/ErrorCodes.java index 5f264a41..b473913d 100644 --- a/common/src/main/java/itcast/exception/ErrorCodes.java +++ b/common/src/main/java/itcast/exception/ErrorCodes.java @@ -10,6 +10,7 @@ public enum ErrorCodes { // 뉴스 exception INVALID_NEWS_CONTENT("뉴스의 내용이 없습니다",2002L,HttpStatus.BAD_REQUEST), INVALID_NEWS_DATE("출판 날짜 형식이 아닙니다",2003L ,HttpStatus.BAD_REQUEST), + CRAWLING_PARSE_ERROR("크롤링에 실패했습니다",2004L,HttpStatus.BAD_REQUEST), BAD_REQUEST("BAD_REQUEST", 9404L, HttpStatus.BAD_REQUEST), diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index ef87a9ce..d3e9563b 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -1,8 +1,8 @@ package itcast.news.application; -import itcast.ai.Message; import itcast.ai.application.GPTService; import itcast.ai.dto.request.GPTSummaryRequest; +import itcast.ai.dto.request.Message; import itcast.domain.news.News; import itcast.exception.ItCastApplicationException; import itcast.news.dto.request.CreateNewsRequest; @@ -31,7 +31,7 @@ public class NewsService { private static final int LINK_SIZE = 10; private static final int HOUR = 12; - private static final int YESTERDAY = 2; + private static final int YESTERDAY = 1; private static final int ALARM_HOUR = 7; private static final int ALARM_DAY = 2; @@ -94,12 +94,11 @@ List isValidLinks(List links) { if (isValidLinks.isEmpty()) { throw new ItCastApplicationException(INVALID_NEWS_CONTENT); } - List validLinks = links + return links .stream() .filter(link -> !isValidLinks.contains(link)) .distinct() .toList(); - return validLinks; } @Transactional diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index 67481f69..ee26a757 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -183,5 +183,9 @@ void findLinksTest() throws IOException { } } + @Test + @DisplayName("newsCrawling 메소드 테스트") + void newsCrawlingTest(){ + } } From 6fcc572fa8fec855a13e294f7287eb683968052d Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 16 Dec 2024 12:52:48 +0900 Subject: [PATCH 071/105] =?UTF-8?q?[#29]=20fix=20:=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B6=80=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 480c97d4..e7cad7d6 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -10,11 +10,11 @@ repositories { } tasks.bootJar { - enabled = false + enabled = true } tasks.jar { - enabled = true + enabled = false } dependencies { From efdc9f1ffc909dab70217fc69bf4b45d31834df8 Mon Sep 17 00:00:00 2001 From: jubseok Date: Mon, 16 Dec 2024 12:57:41 +0900 Subject: [PATCH 072/105] =?UTF-8?q?[#29]=20fix=20:=20=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=93=A4=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/build.gradle | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/schedule/build.gradle b/schedule/build.gradle index fe0dba80..ae6a6ef3 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -18,14 +18,18 @@ tasks.jar { } dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + implementation project(':common') + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'com.amazonaws:aws-java-sdk-ses:1.12.3' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-webflux' + + implementation 'com.amazonaws:aws-java-sdk-ses:1.12.3' implementation 'org.jsoup:jsoup:1.18.3' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation project(':common') + implementation 'org.json:json:20240303' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' // monitoring implementation 'org.springframework.boot:spring-boot-starter-actuator' From aa454ea0df4a70d1a6346a7d8563cfd0cf5a5d69 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 17 Dec 2024 20:16:16 +0900 Subject: [PATCH 073/105] =?UTF-8?q?[#29]=20fix:=20=ED=81=B4=EB=A1=A0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 +++---- common/build.gradle | 4 ++-- .../main/java/itcast/domain/news/News.java | 2 ++ schedule/build.gradle | 7 +++++-- .../itcast/ai/application/GPTService.java | 1 + .../itcast/news/application/NewsService.java | 11 ++++------- .../news/application/NewsServiceTest.java | 19 ++++++++----------- 7 files changed, 25 insertions(+), 26 deletions(-) diff --git a/build.gradle b/build.gradle index 04324b2c..c0807f5f 100644 --- a/build.gradle +++ b/build.gradle @@ -46,8 +46,7 @@ subprojects { extendsFrom annotationProcessor } } - - tasks.named('test') { - useJUnitPlatform() - } +} +test { + useJUnitPlatform() } diff --git a/common/build.gradle b/common/build.gradle index e7cad7d6..480c97d4 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -10,11 +10,11 @@ repositories { } tasks.bootJar { - enabled = true + enabled = false } tasks.jar { - enabled = false + enabled = true } dependencies { diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index d3fe7c56..d6c2df06 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -62,6 +62,7 @@ public class News extends BaseEntity { @Builder public News( + Long id, String title, String content, String originalContent, @@ -73,6 +74,7 @@ public News( NewsStatus status, LocalDateTime sendAt ) { + this.id = id; this.title = title; this.content = content; this.originalContent = originalContent; diff --git a/schedule/build.gradle b/schedule/build.gradle index 7c31d1de..fa938168 100644 --- a/schedule/build.gradle +++ b/schedule/build.gradle @@ -13,6 +13,11 @@ tasks.bootJar { enabled = false } +tasks.jar { + enabled = true +} + + dependencies { implementation project(':common') @@ -24,8 +29,6 @@ dependencies { implementation 'org.jsoup:jsoup:1.18.3' implementation 'org.json:json:20240303' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' - // monitoring implementation 'org.springframework.boot:spring-boot-starter-actuator' runtimeOnly 'io.micrometer:micrometer-registry-prometheus' diff --git a/schedule/src/main/java/itcast/ai/application/GPTService.java b/schedule/src/main/java/itcast/ai/application/GPTService.java index 181298d3..a700eac9 100644 --- a/schedule/src/main/java/itcast/ai/application/GPTService.java +++ b/schedule/src/main/java/itcast/ai/application/GPTService.java @@ -6,6 +6,7 @@ import itcast.ai.client.GPTClient; import itcast.ai.dto.request.GPTSummaryRequest; import itcast.ai.dto.response.GPTSummaryResponse; +import itcast.blog.repository.BlogRepository; import itcast.domain.blog.Blog; import itcast.domain.blog.enums.BlogStatus; import itcast.domain.news.News; diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index a26161d1..ffd07a0a 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -4,6 +4,7 @@ import itcast.ai.dto.request.GPTSummaryRequest; import itcast.ai.dto.request.Message; import itcast.domain.news.News; +import itcast.exception.ItCastApplicationException; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; import jakarta.transaction.Transactional; @@ -11,7 +12,6 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; @@ -20,7 +20,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import static itcast.exception.ErrorCodes.*; @@ -33,13 +32,13 @@ public class NewsService { private static final int YESTERDAY = 2; private static final int ALARM_HOUR = 7; private static final int ALARM_DAY = 2; - private static final String url = "https://news.naver.com/breakingnews/section/105/283"; + private static final String URL = "https://news.naver.com/breakingnews/section/105/283"; private final NewsRepository newsRepository; private final GPTService gptService; public void newsCrawling() throws IOException { - List links = findLinks(url); + List links = findLinks(URL); links = isValidLinks(links); links.forEach(link -> { @@ -100,10 +99,8 @@ public void newsAlarm() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); - if (createdAlarm.isEmpty()) { - throw new ItCastApplicationException(INVALID_NEWS_CONTENT); - } LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); + createdAlarm.forEach(alarm -> { if (alarm == null) { throw new ItCastApplicationException(INVALID_NEWS_CONTENT); diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index ee26a757..1d67d32c 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -1,5 +1,6 @@ package itcast.news.application; +import itcast.ai.application.GPTService; import itcast.domain.news.News; import itcast.news.repository.NewsRepository; import org.jsoup.Connection; @@ -13,7 +14,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; @@ -26,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; - @ExtendWith(MockitoExtension.class) public class NewsServiceTest { @@ -36,6 +35,9 @@ public class NewsServiceTest { @Mock private NewsRepository newsRepository; + @Mock + private GPTService gptService; + @Test @DisplayName("크롤링 테스트") void SuccessCrawling() throws IOException { @@ -54,17 +56,17 @@ void SuccessCrawling() throws IOException { @DisplayName("알람 보내는 메소드 테스트") void testUpdateNews() { // given - LocalDate yesterday = LocalDate.now().minusDays(1); + LocalDate yesterday = LocalDate.now().minusDays(2); News mockNews = mock(News.class); - Mockito.when(newsRepository.findAllByCreatedAt(yesterday)) + when(newsRepository.findAllByCreatedAt(yesterday)) .thenReturn(Collections.singletonList(mockNews)); // When newsService.newsAlarm(); // Then - verify(mockNews).newsUpdate(Mockito.any(LocalDateTime.class)); + verify(mockNews).newsUpdate(any(LocalDateTime.class)); } @Test @@ -141,7 +143,7 @@ void testDeleteOldNews() throws IOException { // Then // newsRepository.deleteOldNews 호출 여부 검증 - verify(newsRepository, Mockito.times(1)).deleteOldNews(); + verify(newsRepository,times(1)).deleteOldNews(); } @Test @@ -183,9 +185,4 @@ void findLinksTest() throws IOException { } } - @Test - @DisplayName("newsCrawling 메소드 테스트") - void newsCrawlingTest(){ - - } } From 5b71e70366ec7c17d1e93564b495bc0e0960066f Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 09:52:51 +0900 Subject: [PATCH 074/105] =?UTF-8?q?[#69]=20feat:=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EB=8B=A8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/SendNewsService.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 schedule/src/main/java/itcast/news/application/SendNewsService.java diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java new file mode 100644 index 00000000..ac6272f8 --- /dev/null +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -0,0 +1,20 @@ +package itcast.news.application; + +import itcast.news.repository.NewsRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class SendNewsService { + + private final NewsRepository newsRepository; + + public void selectNews() { + + } + + public void sendNews() { + + } +} From cbe14e0b66401c9347915b5072336d3723271c29 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 09:53:05 +0900 Subject: [PATCH 075/105] =?UTF-8?q?[#69]=20feat:=20=ED=81=AC=EB=A1=A0=20?= =?UTF-8?q?=ED=91=9C=ED=98=84=EC=8B=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/resources/application.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index 558445c6..1a899d67 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -25,6 +25,8 @@ scheduler: crawling: "0 0 */3 * * ?" alarm-scheduled: "0 0 1 1/1 * ?" old-delete-data: "0 0 0 1 1,7 ?" + select-news: "0 6 * * * ?" + send-alarm: "0 8 * * * ?" blog: crawling: "0 0 */3 * * ?" selecting: "0 6 * * * *" From 3dabd1a6aeab8ef642ee99b6beea21c3245701ee Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 09:55:28 +0900 Subject: [PATCH 076/105] =?UTF-8?q?[#69]=20feat:=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B0=9C=EC=86=A1=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/common/schedule/AlarmSchedule.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index 4ca91dfd..cdb3539d 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -1,6 +1,7 @@ package itcast.news.common.schedule; import itcast.news.application.NewsService; +import itcast.news.application.SendNewsService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; @@ -11,6 +12,7 @@ @Slf4j public class AlarmSchedule { private final NewsService newsService; + private final SendNewsService sendNewsService; @Scheduled(cron = "${scheduler.news.alarm-scheduled}") public void CreateAlarmSchedule() { @@ -18,4 +20,18 @@ public void CreateAlarmSchedule() { newsService.newsAlarm(); log.info("alarm schedule Finish"); } + + @Scheduled(cron = "${scheduler.news.select-news}") + public void selectNewsSchedule() { + log.info("Selecting schedule...."); + sendNewsService.selectNews(); + log.info("Selecting Finish"); + } + + @Scheduled(cron = "${scheduler.news.send-alarm}") + public void sendAlarmSchedule() { + log.info("Sending schedule...."); + sendNewsService.sendNews(); + log.info("Sending schedule Finish"); + } } From 4f670284902f2ad6cad722b6d5bcf6953275410c Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 11:52:29 +0900 Subject: [PATCH 077/105] =?UTF-8?q?[#69]=20feat:=20repository=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/repository/NewsRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index af07902e..808bfd6b 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -20,4 +20,11 @@ public interface NewsRepository extends JpaRepository { @Modifying @Query("DELETE FROM News n WHERE n.createdAt <= CURRENT_DATE - 6 MONTH") void deleteOldNews(); + + @Query(""" + select n.title, n.link, n.thumbnail, n.content + from News n + where date_format(n.sendAt, '%Y-%m-%d') = CURDATE() + order by n.rating desc limit 3""") + List findTop3ByTodayOrderByRating(); } From 350b869f4864c3efced63381edafb4b63c4e55fd Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 11:52:51 +0900 Subject: [PATCH 078/105] =?UTF-8?q?[#69]=20feat:=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/SendNewsService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index ac6272f8..b0d1682f 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -1,16 +1,29 @@ package itcast.news.application; +import itcast.domain.news.News; +import itcast.exception.ItCastApplicationException; import itcast.news.repository.NewsRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.Arrays; +import java.util.List; + +import static itcast.exception.ErrorCodes.TODAY_NEWS_NOT_FOUND; + @Service @RequiredArgsConstructor public class SendNewsService { + private static final int NEWS_SIZE = 3; private final NewsRepository newsRepository; + private static List NEWS_LIST = Arrays.asList(); public void selectNews() { + NEWS_LIST = newsRepository.findTop3ByTodayOrderByRating(); + if (NEWS_LIST.isEmpty() || NEWS_LIST.size() < NEWS_SIZE) { + throw new ItCastApplicationException(TODAY_NEWS_NOT_FOUND); + } } From 5fd3df988c49fe273be3b41d59b357fb97f5241e Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 11:53:07 +0900 Subject: [PATCH 079/105] =?UTF-8?q?[#69]=20feat:=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=9D=B5=EC=85=89=EC=85=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/exception/ErrorCodes.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/itcast/exception/ErrorCodes.java b/common/src/main/java/itcast/exception/ErrorCodes.java index b473913d..868d00f6 100644 --- a/common/src/main/java/itcast/exception/ErrorCodes.java +++ b/common/src/main/java/itcast/exception/ErrorCodes.java @@ -11,6 +11,7 @@ public enum ErrorCodes { INVALID_NEWS_CONTENT("뉴스의 내용이 없습니다",2002L,HttpStatus.BAD_REQUEST), INVALID_NEWS_DATE("출판 날짜 형식이 아닙니다",2003L ,HttpStatus.BAD_REQUEST), CRAWLING_PARSE_ERROR("크롤링에 실패했습니다",2004L,HttpStatus.BAD_REQUEST), + TODAY_NEWS_NOT_FOUND("뉴스 선택에 실패했습니다",2005L,HttpStatus.NOT_FOUND), BAD_REQUEST("BAD_REQUEST", 9404L, HttpStatus.BAD_REQUEST), From 17d35f8f4b10071b9a5f45c3657e7b0aaa4dba1f Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 11:53:49 +0900 Subject: [PATCH 080/105] =?UTF-8?q?[#69]=20fix:=20=ED=81=AC=EB=A1=A0?= =?UTF-8?q?=ED=91=9C=ED=98=84=EC=8B=9D=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/common/schedule/AlarmSchedule.java | 2 +- schedule/src/main/resources/application.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index cdb3539d..34bc225a 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -25,7 +25,7 @@ public void CreateAlarmSchedule() { public void selectNewsSchedule() { log.info("Selecting schedule...."); sendNewsService.selectNews(); - log.info("Selecting Finish"); + log.info("Selecting schedule Finish"); } @Scheduled(cron = "${scheduler.news.send-alarm}") diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index 1a899d67..c518e89d 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -25,8 +25,8 @@ scheduler: crawling: "0 0 */3 * * ?" alarm-scheduled: "0 0 1 1/1 * ?" old-delete-data: "0 0 0 1 1,7 ?" - select-news: "0 6 * * * ?" - send-alarm: "0 8 * * * ?" + select-news: "0 0 6 * * ?" + send-alarm: "0 0 8 * * ?" blog: crawling: "0 0 */3 * * ?" selecting: "0 6 * * * *" From cb37b7284496a64fd1151e488ec0bcfad608e288 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 14:23:24 +0900 Subject: [PATCH 081/105] =?UTF-8?q?[#69]=20fix:=20=EB=89=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/SendNewsService.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index b0d1682f..ec227c98 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -1,33 +1,41 @@ package itcast.news.application; import itcast.domain.news.News; -import itcast.exception.ItCastApplicationException; import itcast.news.repository.NewsRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.util.Arrays; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; -import static itcast.exception.ErrorCodes.TODAY_NEWS_NOT_FOUND; - @Service @RequiredArgsConstructor public class SendNewsService { - private static final int NEWS_SIZE = 3; + + private static final int YESTERDAY = 1; + private static final int ALARM_HOUR = 2; + private static final int ALARM_DAY = 2; + private final NewsRepository newsRepository; - private static List NEWS_LIST = Arrays.asList(); + @Transactional public void selectNews() { - NEWS_LIST = newsRepository.findTop3ByTodayOrderByRating(); - if (NEWS_LIST.isEmpty() || NEWS_LIST.size() < NEWS_SIZE) { - throw new ItCastApplicationException(TODAY_NEWS_NOT_FOUND); - } + LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); + List newsList = newsRepository.findRatingTot3ByCreatedAtOrdarByRating(yesterday); + LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); + + newsList.forEach(alarm -> { + alarm.newsUpdate(sendAt); + }); } public void sendNews() { + List sendNews = newsRepository.findAllBySendAt(); + } } From 865b767a9c9405486e3d4baf62b9647631870bbb Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 14:23:46 +0900 Subject: [PATCH 082/105] =?UTF-8?q?[#69]=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=81=B4=EB=A0=88=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/SelectNewsServiceTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java new file mode 100644 index 00000000..282b6383 --- /dev/null +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -0,0 +1,8 @@ +package itcast.news.application; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class SelectNewsServiceTest { +} From ffda79dd5289327f28cc201587478f685764ab08 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 14:24:13 +0900 Subject: [PATCH 083/105] =?UTF-8?q?[#69]=20fix:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/application/NewsService.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index ffd07a0a..3a584694 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -29,9 +29,6 @@ public class NewsService { private static final int LINK_SIZE = 10; private static final int HOUR = 12; - private static final int YESTERDAY = 2; - private static final int ALARM_HOUR = 7; - private static final int ALARM_DAY = 2; private static final String URL = "https://news.naver.com/breakingnews/section/105/283"; private final NewsRepository newsRepository; @@ -94,21 +91,6 @@ List isValidLinks(List links) { .toList(); } - @Transactional - public void newsAlarm() { - LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); - List createdAlarm = newsRepository.findAllByCreatedAt(yesterday); - - LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); - - createdAlarm.forEach(alarm -> { - if (alarm == null) { - throw new ItCastApplicationException(INVALID_NEWS_CONTENT); - } - alarm.newsUpdate(sendAt); - }); - } - @Transactional public void deleteOldData() { newsRepository.deleteOldNews(); From c976ed8b6b4ba51c817eabcfd90553bc7f13ac23 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 14:24:22 +0900 Subject: [PATCH 084/105] =?UTF-8?q?[#69]=20fix:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/NewsServiceTest.java | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index 1d67d32c..f0307d18 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -52,23 +52,6 @@ void SuccessCrawling() throws IOException { assertFalse(articles.isEmpty(), "링크가 없음"); } - @Test - @DisplayName("알람 보내는 메소드 테스트") - void testUpdateNews() { - // given - LocalDate yesterday = LocalDate.now().minusDays(2); - News mockNews = mock(News.class); - - when(newsRepository.findAllByCreatedAt(yesterday)) - .thenReturn(Collections.singletonList(mockNews)); - - // When - newsService.newsAlarm(); - - // Then - verify(mockNews).newsUpdate(any(LocalDateTime.class)); - } - @Test @DisplayName("중복 된 메소드 체크 테스트") void isValidLinksTest() { @@ -137,7 +120,7 @@ void cleanContent() { @Test @DisplayName("오레된 데이터 삭제 메소드 테스트") - void testDeleteOldNews() throws IOException { + void testDeleteOldNews() { // When newsService.deleteOldData(); From 11c1aa82a3fd9b625ce74da88d18d19fe4370cce Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 14:24:43 +0900 Subject: [PATCH 085/105] =?UTF-8?q?[#69]=20fix:=20=EB=89=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20sql=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/news/repository/NewsRepository.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index 808bfd6b..d051a330 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -11,20 +11,16 @@ import java.util.List; public interface NewsRepository extends JpaRepository { - @Query("select n.link from News n") + @Query("select n.link from News n") List findAllLinks(); - @Query("select n from News n where function('DATE',n.createdAt) = :yesterday ") - List findAllByCreatedAt(@Param("yesterday") LocalDate yesterday); + @Query("select n from News n where function('DATE',n.createdAt) = :yesterday order by n.rating desc limit 3") + List findRatingTot3ByCreatedAtOrdarByRating(@Param("yesterday") LocalDate yesterday); @Modifying @Query("DELETE FROM News n WHERE n.createdAt <= CURRENT_DATE - 6 MONTH") void deleteOldNews(); - @Query(""" - select n.title, n.link, n.thumbnail, n.content - from News n - where date_format(n.sendAt, '%Y-%m-%d') = CURDATE() - order by n.rating desc limit 3""") - List findTop3ByTodayOrderByRating(); + @Query("select n.title, n.content, n.link, n.thumbnail from News n where n.sendAt is not null") + List findAllBySendAt(); } From 4cfa95bb847b4b21b4b4142d2ed4330d0e647756 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 16:30:36 +0900 Subject: [PATCH 086/105] =?UTF-8?q?[#69]=20feat:=20=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B0=9C=EC=86=A1=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=B0=BE?= =?UTF-8?q?=EB=8A=94=20repository=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/jwt/repository/UserRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/src/main/java/itcast/jwt/repository/UserRepository.java b/common/src/main/java/itcast/jwt/repository/UserRepository.java index 788fb61e..feaaeff5 100644 --- a/common/src/main/java/itcast/jwt/repository/UserRepository.java +++ b/common/src/main/java/itcast/jwt/repository/UserRepository.java @@ -1,7 +1,9 @@ package itcast.jwt.repository; +import java.util.List; import java.util.Optional; +import itcast.domain.user.enums.Interest; import org.springframework.data.jpa.repository.JpaRepository; import itcast.domain.user.User; @@ -11,4 +13,5 @@ public interface UserRepository extends JpaRepository { boolean existsByEmail(String email); boolean existsByNickname(String nickname); Optional findByKakaoEmail(String kakaoEmail); + List findAllByInterest(Interest interest); } From 7baf169706a0bbab69425ba0e3d8994577a1984a Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 16:31:01 +0900 Subject: [PATCH 087/105] =?UTF-8?q?[#69]=20feat:=20=EB=A9=94=EC=9D=BC?= =?UTF-8?q?=EC=97=90=20=EB=84=A3=EB=8A=94=20=EB=82=B4=EC=9A=A9=20=EB=B9=8C?= =?UTF-8?q?=EB=8D=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/mail/dto/request/MailContent.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/schedule/src/main/java/itcast/mail/dto/request/MailContent.java b/schedule/src/main/java/itcast/mail/dto/request/MailContent.java index 375329bf..0c456f9b 100644 --- a/schedule/src/main/java/itcast/mail/dto/request/MailContent.java +++ b/schedule/src/main/java/itcast/mail/dto/request/MailContent.java @@ -1,6 +1,7 @@ package itcast.mail.dto.request; import jakarta.validation.constraints.NotBlank; +import lombok.Builder; public record MailContent( @NotBlank @@ -12,4 +13,15 @@ public record MailContent( @NotBlank String thumbnail ) { + @Builder + public MailContent( + String title, + String summary, + String originalLink, + String thumbnail) { + this.title = title; + this.summary = summary; + this.originalLink = originalLink; + this.thumbnail = thumbnail; + } } From 780b726667e0a1f5681e43b2a0f06e21d9aa339e Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 16:42:19 +0900 Subject: [PATCH 088/105] =?UTF-8?q?[#69]=20feat:=20=EB=B0=9C=EC=86=A1=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EB=A9=94=EC=9D=BC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/SendNewsService.java | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index ec227c98..da5d8798 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -1,6 +1,14 @@ package itcast.news.application; import itcast.domain.news.News; +import itcast.domain.user.User; +import itcast.domain.user.enums.Interest; +import itcast.exception.ErrorCodes; +import itcast.exception.ItCastApplicationException; +import itcast.jwt.repository.UserRepository; +import itcast.mail.application.MailService; +import itcast.mail.dto.request.MailContent; +import itcast.mail.dto.request.SendMailRequest; import itcast.news.repository.NewsRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -20,22 +28,59 @@ public class SendNewsService { private static final int ALARM_DAY = 2; private final NewsRepository newsRepository; + private final UserRepository userRepository; + private final MailService mailService; @Transactional public void selectNews() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); List newsList = newsRepository.findRatingTot3ByCreatedAtOrdarByRating(yesterday); + if (newsList == null || newsList.isEmpty()) { + throw new ItCastApplicationException(ErrorCodes.INVALID_NEWS_CONTENT); + } + LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); - newsList.forEach(alarm -> { - alarm.newsUpdate(sendAt); + newsList.forEach(news -> { + news.newsUpdate(sendAt); }); } public void sendNews() { List sendNews = newsRepository.findAllBySendAt(); + if (sendNews == null || sendNews.isEmpty()) { + throw new ItCastApplicationException(ErrorCodes.NOT_FOUND_SEND_DATA); + } + + List mailContents = sendNews.stream() + .map(news -> + MailContent.builder() + .title(news.getTitle()) + .originalLink(news.getLink()) + .summary(news.getContent()) + .thumbnail(news.getThumbnail()) + .build()) + .toList(); + + List emails = retrieveUserEmails(Interest.NEWS); + + if (emails == null || emails.isEmpty()) { + throw new ItCastApplicationException(ErrorCodes.NOT_FOUND_EMAIL); + } + + SendMailRequest mailRequest = new SendMailRequest(emails, mailContents); + mailService.send(mailRequest); + } + private List retrieveUserEmails(Interest interest) { + if (interest != Interest.NEWS) { + throw new ItCastApplicationException(ErrorCodes.INVALID_INTEREST_TYPE_ERROR); + } + return userRepository.findAllByInterest(interest) + .stream() + .map(User::getEmail) + .toList(); } } From 060a871810ae410cdab439f1f3ad5c4d30a689ab Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 16:42:34 +0900 Subject: [PATCH 089/105] =?UTF-8?q?[#69]=20feat:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/exception/ErrorCodes.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/src/main/java/itcast/exception/ErrorCodes.java b/common/src/main/java/itcast/exception/ErrorCodes.java index 868d00f6..812e37a0 100644 --- a/common/src/main/java/itcast/exception/ErrorCodes.java +++ b/common/src/main/java/itcast/exception/ErrorCodes.java @@ -12,6 +12,9 @@ public enum ErrorCodes { INVALID_NEWS_DATE("출판 날짜 형식이 아닙니다",2003L ,HttpStatus.BAD_REQUEST), CRAWLING_PARSE_ERROR("크롤링에 실패했습니다",2004L,HttpStatus.BAD_REQUEST), TODAY_NEWS_NOT_FOUND("뉴스 선택에 실패했습니다",2005L,HttpStatus.NOT_FOUND), + NOT_FOUND_EMAIL("이메일을 찾을 수 없습니다",2006L,HttpStatus.NOT_FOUND), + NOT_FOUND_SEND_DATA("발송 데이터를 찾을 수 없습니다",2007L,HttpStatus.NOT_FOUND), + INVALID_INTEREST_TYPE_ERROR("invalid타입이 맞지 않습니다",2008L,HttpStatus.BAD_REQUEST), BAD_REQUEST("BAD_REQUEST", 9404L, HttpStatus.BAD_REQUEST), From 9ab0b8cded872039ef371dfe4d6195406f1645e4 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 17:33:18 +0900 Subject: [PATCH 090/105] =?UTF-8?q?[#69]=20docs:=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/news/common/schedule/AlarmSchedule.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index 34bc225a..c3714735 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -14,13 +14,6 @@ public class AlarmSchedule { private final NewsService newsService; private final SendNewsService sendNewsService; - @Scheduled(cron = "${scheduler.news.alarm-scheduled}") - public void CreateAlarmSchedule() { - log.info("alarm schedule...."); - newsService.newsAlarm(); - log.info("alarm schedule Finish"); - } - @Scheduled(cron = "${scheduler.news.select-news}") public void selectNewsSchedule() { log.info("Selecting schedule...."); From f9b643b1f793d44d784bbfad2ca0f300a5f9c329 Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 17:33:56 +0900 Subject: [PATCH 091/105] =?UTF-8?q?[#69]=20test:=20retrieveUserEmails=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/SendNewsService.java | 2 +- .../application/SelectNewsServiceTest.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index da5d8798..4bf7be1b 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -74,7 +74,7 @@ public void sendNews() { mailService.send(mailRequest); } - private List retrieveUserEmails(Interest interest) { + public List retrieveUserEmails(Interest interest) { if (interest != Interest.NEWS) { throw new ItCastApplicationException(ErrorCodes.INVALID_INTEREST_TYPE_ERROR); } diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java index 282b6383..0d67eecc 100644 --- a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -1,8 +1,55 @@ package itcast.news.application; +import itcast.domain.user.User; +import itcast.domain.user.enums.Interest; +import itcast.jwt.repository.UserRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) public class SelectNewsServiceTest { + + @Mock + private UserRepository userRepository; + + @InjectMocks + private SendNewsService sendNewsService; + + @Test + @DisplayName("retrieveUserEmails 메소드 테스트") + public void retrieveUserEmailsTest() { + // give + Interest validInterest = Interest.NEWS; + + User mockUser1 = mock(User.class); + when(mockUser1.getEmail()).thenReturn("user1@example.com"); + + User mockUser2 = mock(User.class); + when(mockUser2.getEmail()).thenReturn("user2@example.com"); + + List users = List.of(mockUser1, mockUser2); + when(userRepository.findAllByInterest(validInterest)).thenReturn(users); + + // when + List result = sendNewsService.retrieveUserEmails(validInterest); + + // then + assertNotNull(result); + assertEquals(2, result.size()); + assertTrue(result.contains("user1@example.com")); + assertTrue(result.contains("user2@example.com")); + verify(userRepository, times(1)).findAllByInterest(validInterest); + verify(mockUser1, times(1)).getEmail(); + verify(mockUser2, times(1)).getEmail(); + } } From c400286c127d087a29ceab0b574c2e9fa24125fd Mon Sep 17 00:00:00 2001 From: jubseok Date: Wed, 18 Dec 2024 17:57:12 +0900 Subject: [PATCH 092/105] =?UTF-8?q?[#69]=20test:=20selectNews=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/SelectNewsServiceTest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java index 0d67eecc..b236019d 100644 --- a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -1,15 +1,21 @@ package itcast.news.application; +import itcast.domain.news.News; import itcast.domain.user.User; import itcast.domain.user.enums.Interest; import itcast.jwt.repository.UserRepository; +import itcast.news.repository.NewsRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -18,10 +24,16 @@ @ExtendWith(MockitoExtension.class) public class SelectNewsServiceTest { + private static final int YESTERDAY = 1; + private static final int ALARM_HOUR = 2; + private static final int ALARM_DAY = 2; @Mock private UserRepository userRepository; + @Mock + private NewsRepository newsRepository; + @InjectMocks private SendNewsService sendNewsService; @@ -52,4 +64,33 @@ public void retrieveUserEmailsTest() { verify(mockUser1, times(1)).getEmail(); verify(mockUser2, times(1)).getEmail(); } + + @Test + @DisplayName("selectNews 메소드 테스트") + public void selectNewsTest() { + // give + LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); + LocalDateTime expectedSendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); + + News mockNews1 = mock(News.class); + News mockNews2 = mock(News.class); + List mockNewsList = List.of(mockNews1, mockNews2); + + when(newsRepository.findRatingTot3ByCreatedAtOrdarByRating(yesterday)).thenReturn(mockNewsList); + + // when + sendNewsService.selectNews(); + + // then + ArgumentCaptor captor = ArgumentCaptor.forClass(LocalDateTime.class); + + verify(mockNews1, times(1)).newsUpdate(captor.capture()); + LocalDateTime actualSendAt1 = captor.getValue(); + assertEquals(expectedSendAt.truncatedTo(ChronoUnit.SECONDS), actualSendAt1.truncatedTo(ChronoUnit.SECONDS)); + + verify(mockNews2, times(1)).newsUpdate(captor.capture()); + LocalDateTime actualSendAt2 = captor.getValue(); + assertEquals(expectedSendAt.truncatedTo(ChronoUnit.SECONDS), actualSendAt2.truncatedTo(ChronoUnit.SECONDS)); + + } } From b6f5a082013df41e699710b4678a2f74df25bf79 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 19 Dec 2024 15:37:32 +0900 Subject: [PATCH 093/105] =?UTF-8?q?[#69]=20fix:=20PR=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/itcast/domain/news/News.java | 11 ++++++----- .../java/itcast/news/application/NewsService.java | 6 +++--- .../itcast/news/application/SendNewsService.java | 14 ++++++-------- .../itcast/news/dto/request/CreateNewsRequest.java | 2 +- .../java/itcast/news/exception/NewsException.java | 0 schedule/src/main/resources/application.yml | 5 ++--- 6 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 schedule/src/main/java/itcast/news/exception/NewsException.java diff --git a/common/src/main/java/itcast/domain/news/News.java b/common/src/main/java/itcast/domain/news/News.java index d6c2df06..2bcd368a 100644 --- a/common/src/main/java/itcast/domain/news/News.java +++ b/common/src/main/java/itcast/domain/news/News.java @@ -14,6 +14,7 @@ import jakarta.persistence.Lob; +import java.time.LocalDate; import java.time.LocalDateTime; import lombok.AccessLevel; @@ -58,7 +59,7 @@ public class News extends BaseEntity { @Enumerated(EnumType.STRING) private NewsStatus status; - private LocalDateTime sendAt; + private LocalDate sendAt; @Builder public News( @@ -72,7 +73,7 @@ public News( String link, String thumbnail, NewsStatus status, - LocalDateTime sendAt + LocalDate sendAt ) { this.id = id; this.title = title; @@ -111,11 +112,11 @@ public void update( String originalContent, Interest interest, LocalDateTime publishedAt, - int rating, + Integer rating, String link, String thumbnail, NewsStatus status, - LocalDateTime sendAt + LocalDate sendAt ) { this.title = title; this.content = content; @@ -129,7 +130,7 @@ public void update( this.sendAt = sendAt; } - public void newsUpdate(LocalDateTime sendAt) { + public void newsUpdate(LocalDate sendAt) { this.sendAt = sendAt; } diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 62fed537..d4e47c29 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -4,6 +4,7 @@ import itcast.ai.dto.request.GPTSummaryRequest; import itcast.ai.dto.request.Message; import itcast.domain.news.News; +import itcast.exception.ItCastApplicationException; import itcast.news.dto.request.CreateNewsRequest; import itcast.news.repository.NewsRepository; import jakarta.transaction.Transactional; @@ -11,16 +12,15 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; + +import static itcast.exception.ErrorCodes.*; @Service @RequiredArgsConstructor diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index 4bf7be1b..8ef2fbc7 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -24,7 +24,6 @@ public class SendNewsService { private static final int YESTERDAY = 1; - private static final int ALARM_HOUR = 2; private static final int ALARM_DAY = 2; private final NewsRepository newsRepository; @@ -40,7 +39,7 @@ public void selectNews() { throw new ItCastApplicationException(ErrorCodes.INVALID_NEWS_CONTENT); } - LocalDateTime sendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); + LocalDate sendAt = LocalDate.now().plusDays(ALARM_DAY); newsList.forEach(news -> { news.newsUpdate(sendAt); @@ -56,12 +55,11 @@ public void sendNews() { List mailContents = sendNews.stream() .map(news -> - MailContent.builder() - .title(news.getTitle()) - .originalLink(news.getLink()) - .summary(news.getContent()) - .thumbnail(news.getThumbnail()) - .build()) + MailContent.of( + news.getTitle(), + news.getContent(), + news.getLink(), + news.getThumbnail())) .toList(); List emails = retrieveUserEmails(Interest.NEWS); diff --git a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java index f6de8289..7bdeed17 100644 --- a/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java +++ b/schedule/src/main/java/itcast/news/dto/request/CreateNewsRequest.java @@ -11,7 +11,7 @@ public record CreateNewsRequest( String originalContent, String link, String thumbnail, - LocalDateTime sendAt + LocalDateTime publishedAt ) { public News toEntity( String title, diff --git a/schedule/src/main/java/itcast/news/exception/NewsException.java b/schedule/src/main/java/itcast/news/exception/NewsException.java deleted file mode 100644 index e69de29b..00000000 diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index c50e95e8..e92af084 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -23,14 +23,13 @@ spring: scheduler: news: crawling: "0 0 */3 * * ?" - alarm-scheduled: "0 0 1 1/1 * ?" old-delete-data: "0 0 0 1 1,7 ?" select-news: "0 0 6 * * ?" send-alarm: "0 0 8 * * ?" blog: crawling: "0 0 */3 * * ?" - selecting: "0 6 * * * *" - sending: "0 9 * * * *" + selecting: "0 0 6 * * *" + sending: "0 0 9 * * *" timeout: connection: 5000 From 5f2426947d01d139244d7aec270a2a6d8cfb0ea8 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 19 Dec 2024 17:50:04 +0900 Subject: [PATCH 094/105] =?UTF-8?q?[#79]=20fix:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EB=B0=8F=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itcast/dto/request/AdminNewsRequest.java | 4 +- .../dto/response/AdminNewsResponse.java | 4 +- .../repository/CustomNewsRepositoryImpl.java | 5 +- .../java/itcast/AdminNewsServiceTest.java | 18 +++--- .../itcast/jwt/repository/UserRepository.java | 9 ++- .../blog/application/BlogSendService.java | 5 +- .../itcast/mail/dto/request/MailContent.java | 2 + .../itcast/news/application/NewsService.java | 19 +++--- .../news/application/SendNewsService.java | 5 +- .../news/application/NewsServiceTest.java | 8 +-- .../application/SelectNewsServiceTest.java | 61 ++++++++++++++----- 11 files changed, 88 insertions(+), 52 deletions(-) diff --git a/admin/src/main/java/itcast/dto/request/AdminNewsRequest.java b/admin/src/main/java/itcast/dto/request/AdminNewsRequest.java index 452afe81..258e429b 100644 --- a/admin/src/main/java/itcast/dto/request/AdminNewsRequest.java +++ b/admin/src/main/java/itcast/dto/request/AdminNewsRequest.java @@ -3,6 +3,8 @@ import itcast.domain.news.News; import itcast.domain.news.enums.NewsStatus; import itcast.domain.user.enums.Interest; + +import java.time.LocalDate; import java.time.LocalDateTime; public record AdminNewsRequest( @@ -15,7 +17,7 @@ public record AdminNewsRequest( String link, String thumbnail, NewsStatus status, - LocalDateTime sendAt + LocalDate sendAt ) { public static News toEntity(AdminNewsRequest adminNewsRequest) { return News.builder() diff --git a/admin/src/main/java/itcast/dto/response/AdminNewsResponse.java b/admin/src/main/java/itcast/dto/response/AdminNewsResponse.java index f81ca362..2180e9ab 100644 --- a/admin/src/main/java/itcast/dto/response/AdminNewsResponse.java +++ b/admin/src/main/java/itcast/dto/response/AdminNewsResponse.java @@ -3,6 +3,8 @@ import itcast.domain.news.News; import itcast.domain.news.enums.NewsStatus; import itcast.domain.user.enums.Interest; + +import java.time.LocalDate; import java.time.LocalDateTime; public record AdminNewsResponse( @@ -16,7 +18,7 @@ public record AdminNewsResponse( String link, String thumbnail, NewsStatus status, - LocalDateTime sendAt + LocalDate sendAt ) { public AdminNewsResponse(News news) { this( diff --git a/admin/src/main/java/itcast/repository/CustomNewsRepositoryImpl.java b/admin/src/main/java/itcast/repository/CustomNewsRepositoryImpl.java index 60b152b8..66782af4 100644 --- a/admin/src/main/java/itcast/repository/CustomNewsRepositoryImpl.java +++ b/admin/src/main/java/itcast/repository/CustomNewsRepositoryImpl.java @@ -72,9 +72,8 @@ private BooleanExpression sendAtEq(LocalDate sendAt) { return null; } - LocalDateTime startAt = sendAt.atStartOfDay(); - LocalDateTime endAt = sendAt.plusDays(1).atStartOfDay(); + LocalDate endAt = sendAt.plusDays(1); - return news.sendAt.between(startAt, endAt); + return news.sendAt.between(sendAt, endAt); } } diff --git a/admin/src/test/java/itcast/AdminNewsServiceTest.java b/admin/src/test/java/itcast/AdminNewsServiceTest.java index a608b8d6..cacd1036 100644 --- a/admin/src/test/java/itcast/AdminNewsServiceTest.java +++ b/admin/src/test/java/itcast/AdminNewsServiceTest.java @@ -47,7 +47,7 @@ public void SuccessNewsCreate() { //given Long userId = 1L; LocalDateTime fixedTime = LocalDateTime.of(2024, 12, 1, 12, 0); - + LocalDate fixedDate2 = LocalDate.of(2024, 12, 1); User user = User.builder() .id(1L) .kakaoEmail("kakao@kakao.com") @@ -63,7 +63,7 @@ public void SuccessNewsCreate() { .link("http://example.com") .thumbnail("http://thumbnail.com") .status(NewsStatus.SUMMARY) - .sendAt(fixedTime) + .sendAt(fixedDate2) .build(); AdminNewsRequest adminNewsRequest = new AdminNewsRequest( "제목", @@ -75,7 +75,7 @@ public void SuccessNewsCreate() { "http://example.com", "http://thumbnail.com", NewsStatus.SUMMARY, - fixedTime + fixedDate2 ); given(userRepository.findById(userId)).willReturn(Optional.of(user)); @@ -118,7 +118,7 @@ public void SuccessNewsRetrieve() { "http://link1.com", "http:thumb1.com", NewsStatus.SUMMARY, - LocalDateTime.of(2024, 12, 1, 13, 0)), + LocalDate.of(2024, 12, 1)), new AdminNewsResponse( 2L, "뉴스2", @@ -130,7 +130,7 @@ public void SuccessNewsRetrieve() { "http://link2.com", "http:thumb2.com", NewsStatus.SUMMARY, - LocalDateTime.of(2024, 12, 1, 13, 0)) + LocalDate.of(2024, 12, 1)) ); Pageable pageable = PageRequest.of(page, size); @@ -159,6 +159,7 @@ public void SuccessNewsUpdate() { Long userId = 1L; Long newsId = 1L; LocalDateTime fixedTime = LocalDateTime.of(2024, 12, 1, 12, 0); + LocalDate fixedDate2 = LocalDate.of(2024, 12, 1); User user = User.builder() .id(userId) @@ -176,7 +177,7 @@ public void SuccessNewsUpdate() { .link("http://example.com") .thumbnail("http://thumbnail.com") .status(NewsStatus.SUMMARY) - .sendAt(fixedTime) + .sendAt(fixedDate2) .build(); AdminNewsRequest adminNewsRequest = new AdminNewsRequest( "제목2", @@ -188,7 +189,7 @@ public void SuccessNewsUpdate() { "http://example2.com", "http://thumbnail2.com", NewsStatus.ORIGINAL, - fixedTime + fixedDate2 ); given(userRepository.findById(userId)).willReturn(Optional.of(user)); @@ -207,6 +208,7 @@ public void SuccessNewsUpdate() { @DisplayName("뉴스 삭제 성공") public void successDeleteNews() { LocalDateTime fixedTime = LocalDateTime.of(2024, 12, 1, 12, 0); + LocalDate sendAt = LocalDate.of(2024, 12, 1); // Given Long userId = 1L; Long newsId = 1L; @@ -225,7 +227,7 @@ public void successDeleteNews() { .link("http://example.com") .thumbnail("http://thumbnail.com") .status(NewsStatus.SUMMARY) - .sendAt(fixedTime) + .sendAt(sendAt) .build(); given(userRepository.findById(userId)).willReturn(Optional.of(adminUser)); diff --git a/common/src/main/java/itcast/jwt/repository/UserRepository.java b/common/src/main/java/itcast/jwt/repository/UserRepository.java index feaaeff5..dd09ffd8 100644 --- a/common/src/main/java/itcast/jwt/repository/UserRepository.java +++ b/common/src/main/java/itcast/jwt/repository/UserRepository.java @@ -7,11 +7,18 @@ import org.springframework.data.jpa.repository.JpaRepository; import itcast.domain.user.User; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface UserRepository extends JpaRepository { Optional findById(Long id); + boolean existsByEmail(String email); + boolean existsByNickname(String nickname); + Optional findByKakaoEmail(String kakaoEmail); - List findAllByInterest(Interest interest); + + @Query("SELECT u FROM User u WHERE u.interest = :interest") + List findAllByInterest(@Param("interest")Interest interest); } diff --git a/schedule/src/main/java/itcast/blog/application/BlogSendService.java b/schedule/src/main/java/itcast/blog/application/BlogSendService.java index fe29fb1e..cd9d4400 100644 --- a/schedule/src/main/java/itcast/blog/application/BlogSendService.java +++ b/schedule/src/main/java/itcast/blog/application/BlogSendService.java @@ -52,9 +52,6 @@ private void sendBlogsByInterest(LocalDateTime sendAt, Interest interest) { } private List retrieveUserEmails(Interest interest) { - return userRepository.findAllByInterest(interest) - .stream() - .map(User::getEmail) - .toList(); + return userRepository.findAllByInterest(interest); } } diff --git a/schedule/src/main/java/itcast/mail/dto/request/MailContent.java b/schedule/src/main/java/itcast/mail/dto/request/MailContent.java index 146f2f4f..97445a50 100644 --- a/schedule/src/main/java/itcast/mail/dto/request/MailContent.java +++ b/schedule/src/main/java/itcast/mail/dto/request/MailContent.java @@ -1,7 +1,9 @@ package itcast.mail.dto.request; import jakarta.validation.constraints.NotBlank; +import lombok.Builder; +@Builder public record MailContent( @NotBlank String title, diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index d4e47c29..f16e8b5a 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -81,7 +81,7 @@ public List findLinks(String url) throws IOException { return links; } - List isValidLinks(List links) { + public List isValidLinks(List links) { List isValidLinks = newsRepository.findAllLinks(); return links .stream() @@ -95,19 +95,16 @@ public void deleteOldData() { newsRepository.deleteOldNews(); } - LocalDateTime convertDateTime(String info) { + public LocalDateTime convertDateTime(String info) { if (info == null || info.trim().isEmpty()) { throw new ItCastApplicationException(INVALID_NEWS_CONTENT); } - String[] parts = info.split(" "); - if (parts.length != 4) { - throw new ItCastApplicationException(INVALID_NEWS_DATE); - } - String date = parts[1]; - String ampm = parts[2]; - String time = parts[3]; + String[] parts = info.replaceAll("입력", "").split(" "); + + String date = parts[0]; + String ampm = parts[1]; + String time = parts[2]; - date = date.replaceAll("입력", ""); String[] timeParts = time.split(":"); int hour = Integer.parseInt(timeParts[0]); @@ -119,7 +116,7 @@ LocalDateTime convertDateTime(String info) { } String timeDate = date + " " + String.format("%02d", hour) + ":" + timeParts[1]; - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd. HH:mm"); return LocalDateTime.parse(timeDate, formatter); } diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index 8ef2fbc7..fad292cd 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -76,9 +76,6 @@ public List retrieveUserEmails(Interest interest) { if (interest != Interest.NEWS) { throw new ItCastApplicationException(ErrorCodes.INVALID_INTEREST_TYPE_ERROR); } - return userRepository.findAllByInterest(interest) - .stream() - .map(User::getEmail) - .toList(); + return userRepository.findAllByInterest(interest); } } diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index f0307d18..92ba26b6 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -83,10 +83,10 @@ void isValidLinksTest() { @DisplayName("convertDateTime 메서드 테스트") void convertDateTimeTest() { // give - String testPmDate = "입력 2020-01-01 오후 01:01"; - String testAmDate = "입력 2020-01-01 오전 01:01"; - String testNoonDate = "입력 2020-01-01 오후 12:01"; - String testMidnighDate = "입력 2020-01-01 오전 12:01"; + String testPmDate = "입력2020.01.01. 오후 01:01"; + String testAmDate = "입력2020.01.01. 오전 01:01"; + String testNoonDate = "입력2020.01.01. 오후 12:01"; + String testMidnighDate = "입력2020.01.01. 오전 12:01"; // when LocalDateTime convertDatePm = newsService.convertDateTime(testPmDate); diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java index b236019d..b2d4b452 100644 --- a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -4,6 +4,8 @@ import itcast.domain.user.User; import itcast.domain.user.enums.Interest; import itcast.jwt.repository.UserRepository; +import itcast.mail.application.MailService; +import itcast.mail.dto.request.SendMailRequest; import itcast.news.repository.NewsRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -15,7 +17,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -37,19 +38,19 @@ public class SelectNewsServiceTest { @InjectMocks private SendNewsService sendNewsService; + @Mock + private MailService mailService; + @Test @DisplayName("retrieveUserEmails 메소드 테스트") public void retrieveUserEmailsTest() { // give Interest validInterest = Interest.NEWS; - User mockUser1 = mock(User.class); - when(mockUser1.getEmail()).thenReturn("user1@example.com"); - - User mockUser2 = mock(User.class); - when(mockUser2.getEmail()).thenReturn("user2@example.com"); + String mockUser1 = "user1@example.com"; + String mockUser2 = "user2@example.com"; - List users = List.of(mockUser1, mockUser2); + List users = List.of(mockUser1, mockUser2); when(userRepository.findAllByInterest(validInterest)).thenReturn(users); // when @@ -61,8 +62,6 @@ public void retrieveUserEmailsTest() { assertTrue(result.contains("user1@example.com")); assertTrue(result.contains("user2@example.com")); verify(userRepository, times(1)).findAllByInterest(validInterest); - verify(mockUser1, times(1)).getEmail(); - verify(mockUser2, times(1)).getEmail(); } @Test @@ -82,15 +81,47 @@ public void selectNewsTest() { sendNewsService.selectNews(); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(LocalDateTime.class); + ArgumentCaptor captor = ArgumentCaptor.forClass(LocalDate.class); verify(mockNews1, times(1)).newsUpdate(captor.capture()); - LocalDateTime actualSendAt1 = captor.getValue(); - assertEquals(expectedSendAt.truncatedTo(ChronoUnit.SECONDS), actualSendAt1.truncatedTo(ChronoUnit.SECONDS)); - verify(mockNews2, times(1)).newsUpdate(captor.capture()); - LocalDateTime actualSendAt2 = captor.getValue(); - assertEquals(expectedSendAt.truncatedTo(ChronoUnit.SECONDS), actualSendAt2.truncatedTo(ChronoUnit.SECONDS)); + } + + @Test + @DisplayName("sendNews 메소드 테스트") + public void sendNewsTest() { + News news1 = News.builder() + .id(1L) + .title("Test Title 1") + .content("Test Content 1") + .link("http://link1.com") + .thumbnail("http://thumbnail1.com") + .sendAt(LocalDate.now()) + .build(); + + News news2 = News.builder() + .id(2L) + .title("Test Title 2") + .content("Test Content 2") + .link("http://link2.com") + .thumbnail("http://thumbnail2.com") + .sendAt(LocalDate.now()) + .build(); + + // 이메일 리스트 Mock 데이터 + List emails = List.of("test1@example.com", "test2@example.com"); + + when(newsRepository.findAllBySendAt()).thenReturn(List.of(news1, news2)); // 뉴스 반환 + when(sendNewsService.retrieveUserEmails(Interest.NEWS)).thenReturn(emails); // 이메일 반환 + + // when: 메소드 실행 + sendNewsService.sendNews(); + + // then: 메일 전송 호출 여부 확인 + verify(mailService, times(1)).send(any(SendMailRequest.class)); + } + + } From 5fd43a209af7a9a9c6dbad51a49fcce77c7f7508 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 19 Dec 2024 19:50:07 +0900 Subject: [PATCH 095/105] =?UTF-8?q?[#69]=20fix:=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/jwt/repository/UserRepository.java | 4 +++- .../java/itcast/blog/application/BlogSendService.java | 5 ++++- .../java/itcast/news/application/SendNewsService.java | 8 +++++++- .../main/java/itcast/news/repository/NewsRepository.java | 2 +- schedule/src/main/resources/application-test.yml | 1 - 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/itcast/jwt/repository/UserRepository.java b/common/src/main/java/itcast/jwt/repository/UserRepository.java index dd09ffd8..632b54d6 100644 --- a/common/src/main/java/itcast/jwt/repository/UserRepository.java +++ b/common/src/main/java/itcast/jwt/repository/UserRepository.java @@ -20,5 +20,7 @@ public interface UserRepository extends JpaRepository { Optional findByKakaoEmail(String kakaoEmail); @Query("SELECT u FROM User u WHERE u.interest = :interest") - List findAllByInterest(@Param("interest")Interest interest); + List findAllByInterest(@Param("interest")Interest interest); + + User findByEmail(String email); } diff --git a/schedule/src/main/java/itcast/blog/application/BlogSendService.java b/schedule/src/main/java/itcast/blog/application/BlogSendService.java index b71051dd..8f2ca32a 100644 --- a/schedule/src/main/java/itcast/blog/application/BlogSendService.java +++ b/schedule/src/main/java/itcast/blog/application/BlogSendService.java @@ -62,7 +62,10 @@ private void sendBlogsByInterestAndCreateHistory(LocalDate sendAt, Interest inte } private List retrieveUserEmails(Interest interest) { - return userRepository.findAllByInterest(interest); + return userRepository.findAllByInterest(interest) + .stream() + .map(User::getEmail) + .toList(); } private void createBlogHistory(List blogs, List userEmails) { diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index fad292cd..ea6dcc90 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -76,6 +76,12 @@ public List retrieveUserEmails(Interest interest) { if (interest != Interest.NEWS) { throw new ItCastApplicationException(ErrorCodes.INVALID_INTEREST_TYPE_ERROR); } - return userRepository.findAllByInterest(interest); + return userRepository.findAllByInterest(interest) + .stream() + .map(User::getEmail) + .toList(); + } + public void createNewsHistory(String email) { + } } diff --git a/schedule/src/main/java/itcast/news/repository/NewsRepository.java b/schedule/src/main/java/itcast/news/repository/NewsRepository.java index 92319e00..08f55d11 100644 --- a/schedule/src/main/java/itcast/news/repository/NewsRepository.java +++ b/schedule/src/main/java/itcast/news/repository/NewsRepository.java @@ -21,6 +21,6 @@ public interface NewsRepository extends JpaRepository { @Query("DELETE FROM News n WHERE n.createdAt <= CURRENT_DATE - 6 MONTH") void deleteOldNews(); - @Query("select n.title, n.content, n.link, n.thumbnail from News n where n.sendAt is not null") + @Query("select n from News n where n.sendAt is not null") List findAllBySendAt(); } diff --git a/schedule/src/main/resources/application-test.yml b/schedule/src/main/resources/application-test.yml index df218e30..15d235da 100644 --- a/schedule/src/main/resources/application-test.yml +++ b/schedule/src/main/resources/application-test.yml @@ -28,7 +28,6 @@ spring: ansi: enabled: always - logging: level: org.hibernate.orm.jdbc.bind: TRACE From 2b723ed0a2f4ce6c6c258da9a37a6be0ecd0fea2 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 20 Dec 2024 14:30:49 +0900 Subject: [PATCH 096/105] =?UTF-8?q?[#79]=20fix:=20=EB=89=B4=EC=8A=A4=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EB=A6=AC=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/news/repository/NewsHistoryRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 schedule/src/main/java/itcast/news/repository/NewsHistoryRepository.java diff --git a/schedule/src/main/java/itcast/news/repository/NewsHistoryRepository.java b/schedule/src/main/java/itcast/news/repository/NewsHistoryRepository.java new file mode 100644 index 00000000..b30610dd --- /dev/null +++ b/schedule/src/main/java/itcast/news/repository/NewsHistoryRepository.java @@ -0,0 +1,7 @@ +package itcast.news.repository; + +import itcast.domain.newsHistory.NewsHistory; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NewsHistoryRepository extends JpaRepository { +} From e18f343b172ba1a5b0124bd46a410603c507c5ce Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 20 Dec 2024 14:31:02 +0900 Subject: [PATCH 097/105] =?UTF-8?q?[#79]=20feat:=20=EB=89=B4=EC=8A=A4=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EB=B9=8C=EB=8D=94=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/domain/newsHistory/NewsHistory.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java index 4a3671cf..020e7ca7 100644 --- a/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java +++ b/common/src/main/java/itcast/domain/newsHistory/NewsHistory.java @@ -11,6 +11,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -30,4 +31,10 @@ public class NewsHistory extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "news_id") private News news; + + @Builder + public NewsHistory(User user, News news) { + this.user = user; + this.news = news; + } } From 8475bcaedfbee0b1d594dafc88172f079abd3cfe Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 20 Dec 2024 14:31:36 +0900 Subject: [PATCH 098/105] =?UTF-8?q?[#79]=20feat:=20=EB=89=B4=EC=8A=A4=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/SendNewsService.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index ea6dcc90..c07ccc2c 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -1,6 +1,7 @@ package itcast.news.application; import itcast.domain.news.News; +import itcast.domain.newsHistory.NewsHistory; import itcast.domain.user.User; import itcast.domain.user.enums.Interest; import itcast.exception.ErrorCodes; @@ -9,6 +10,7 @@ import itcast.mail.application.MailService; import itcast.mail.dto.request.MailContent; import itcast.mail.dto.request.SendMailRequest; +import itcast.news.repository.NewsHistoryRepository; import itcast.news.repository.NewsRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,7 +18,9 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -28,12 +32,14 @@ public class SendNewsService { private final NewsRepository newsRepository; private final UserRepository userRepository; + private final NewsHistoryRepository newsHistoryRepository; private final MailService mailService; + @Transactional public void selectNews() { LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); - List newsList = newsRepository.findRatingTot3ByCreatedAtOrdarByRating(yesterday); + List newsList = newsRepository.findRatingTop3ByCreatedAt(yesterday); if (newsList == null || newsList.isEmpty()) { throw new ItCastApplicationException(ErrorCodes.INVALID_NEWS_CONTENT); @@ -70,6 +76,7 @@ public void sendNews() { SendMailRequest mailRequest = new SendMailRequest(emails, mailContents); mailService.send(mailRequest); + createNewsHistory(sendNews); } public List retrieveUserEmails(Interest interest) { @@ -81,7 +88,20 @@ public List retrieveUserEmails(Interest interest) { .map(User::getEmail) .toList(); } - public void createNewsHistory(String email) { + public void createNewsHistory(List sendNews) { + List users = userRepository.findAllByInterest(Interest.NEWS); + List newsHistories = new ArrayList<>(); + + for (News news : sendNews) { + for (User user : users) { + NewsHistory newsHistory = NewsHistory.builder() + .user(user) + .news(news) + .build(); + newsHistories.add(newsHistory); + } + } + newsHistoryRepository.saveAll(newsHistories); } } From af38317da5b1a3ed717ce7f0135d8cb62c7ed288 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 20 Dec 2024 14:32:40 +0900 Subject: [PATCH 099/105] =?UTF-8?q?[#79]=20fix:=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=9C=20JPA=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/itcast/jwt/repository/UserRepository.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/src/main/java/itcast/jwt/repository/UserRepository.java b/common/src/main/java/itcast/jwt/repository/UserRepository.java index 29891ea1..8709581c 100644 --- a/common/src/main/java/itcast/jwt/repository/UserRepository.java +++ b/common/src/main/java/itcast/jwt/repository/UserRepository.java @@ -19,10 +19,6 @@ public interface UserRepository extends JpaRepository { Optional findByKakaoEmail(String kakaoEmail); - List findAllByInterest(Interest interest); - - User findByEmail(String userEmail); - boolean existsByPhoneNumber(String phoneNumber); @Query("SELECT u FROM User u WHERE u.interest = :interest") From 0746f3be739ca2b7ac845494dec83e949fa0bdd9 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 20 Dec 2024 18:00:34 +0900 Subject: [PATCH 100/105] =?UTF-8?q?[#79]=20fix:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/SelectNewsServiceTest.java | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java index b2d4b452..7d113ee3 100644 --- a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -1,11 +1,12 @@ package itcast.news.application; import itcast.domain.news.News; +import itcast.domain.newsHistory.NewsHistory; import itcast.domain.user.User; import itcast.domain.user.enums.Interest; import itcast.jwt.repository.UserRepository; import itcast.mail.application.MailService; -import itcast.mail.dto.request.SendMailRequest; +import itcast.news.repository.NewsHistoryRepository; import itcast.news.repository.NewsRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -34,6 +35,8 @@ public class SelectNewsServiceTest { @Mock private NewsRepository newsRepository; + @Mock + private NewsHistoryRepository newsHistoryRepository; @InjectMocks private SendNewsService sendNewsService; @@ -47,11 +50,17 @@ public void retrieveUserEmailsTest() { // give Interest validInterest = Interest.NEWS; - String mockUser1 = "user1@example.com"; - String mockUser2 = "user2@example.com"; + User user1 = User.builder() + .email("test1@example.com") + .interest(Interest.NEWS) + .build(); + User user2 = User.builder() + .email("test2@example.com") + .interest(Interest.NEWS) + .build(); - List users = List.of(mockUser1, mockUser2); - when(userRepository.findAllByInterest(validInterest)).thenReturn(users); + List mockUsers = List.of(user1, user2); + when(userRepository.findAllByInterest(validInterest)).thenReturn(mockUsers); // when List result = sendNewsService.retrieveUserEmails(validInterest); @@ -59,8 +68,8 @@ public void retrieveUserEmailsTest() { // then assertNotNull(result); assertEquals(2, result.size()); - assertTrue(result.contains("user1@example.com")); - assertTrue(result.contains("user2@example.com")); + assertTrue(result.contains("test1@example.com")); + assertTrue(result.contains("test2@example.com")); verify(userRepository, times(1)).findAllByInterest(validInterest); } @@ -75,7 +84,7 @@ public void selectNewsTest() { News mockNews2 = mock(News.class); List mockNewsList = List.of(mockNews1, mockNews2); - when(newsRepository.findRatingTot3ByCreatedAtOrdarByRating(yesterday)).thenReturn(mockNewsList); + when(newsRepository.findRatingTop3ByCreatedAt(yesterday)).thenReturn(mockNewsList); // when sendNewsService.selectNews(); @@ -86,41 +95,52 @@ public void selectNewsTest() { verify(mockNews1, times(1)).newsUpdate(captor.capture()); verify(mockNews2, times(1)).newsUpdate(captor.capture()); - } @Test - @DisplayName("sendNews 메소드 테스트") - public void sendNewsTest() { + @DisplayName("createNewsHistory 메소드 테스트") + public void createNewsHistoryTest() { + // give News news1 = News.builder() .id(1L) - .title("Test Title 1") + .title("Test News 1") .content("Test Content 1") - .link("http://link1.com") - .thumbnail("http://thumbnail1.com") .sendAt(LocalDate.now()) .build(); News news2 = News.builder() .id(2L) - .title("Test Title 2") + .title("Test News 2") .content("Test Content 2") - .link("http://link2.com") - .thumbnail("http://thumbnail2.com") .sendAt(LocalDate.now()) .build(); - // 이메일 리스트 Mock 데이터 - List emails = List.of("test1@example.com", "test2@example.com"); + User user1 = User.builder().id(1L).email("test1@example.com").build(); + User user2 = User.builder().id(2L).email("test2@example.com").build(); + + List sendNews = List.of(news1, news2); + List users = List.of(user1, user2); + when(userRepository.findAllByInterest(Interest.NEWS)).thenReturn(users); + + // when + sendNewsService.createNewsHistory(sendNews); + + // then + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(newsHistoryRepository, times(1)).saveAll(captor.capture()); - when(newsRepository.findAllBySendAt()).thenReturn(List.of(news1, news2)); // 뉴스 반환 - when(sendNewsService.retrieveUserEmails(Interest.NEWS)).thenReturn(emails); // 이메일 반환 + List savedNewsHistories = captor.getValue(); - // when: 메소드 실행 - sendNewsService.sendNews(); + assertEquals(4, savedNewsHistories.size()); + assertTrue(savedNewsHistories.stream() + .anyMatch(nh -> nh.getUser().equals(user1) && nh.getNews().equals(news1))); + assertTrue(savedNewsHistories.stream() + .anyMatch(nh -> nh.getUser().equals(user1) && nh.getNews().equals(news2))); + assertTrue(savedNewsHistories.stream() + .anyMatch(nh -> nh.getUser().equals(user2) && nh.getNews().equals(news1))); + assertTrue(savedNewsHistories.stream() + .anyMatch(nh -> nh.getUser().equals(user2) && nh.getNews().equals(news2))); - // then: 메일 전송 호출 여부 확인 - verify(mailService, times(1)).send(any(SendMailRequest.class)); } From 84969110f4d4fd02a4ce61e041a9b83b1c973979 Mon Sep 17 00:00:00 2001 From: jubseok Date: Fri, 20 Dec 2024 18:01:28 +0900 Subject: [PATCH 101/105] =?UTF-8?q?[#79]=20fix:=20yml=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schedule/src/main/resources/application.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index 8f86f691..cbd0b4b6 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -5,6 +5,9 @@ spring: password: 1234 driver-class-name: com.mysql.cj.jdbc.Driver + kakao: + client-id: bc2c718aa374df7f4b52201962968bbd + h2: console: enabled: true From a1804767f3bff3add84380a29e959ba91139a77c Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 24 Dec 2024 17:12:41 +0900 Subject: [PATCH 102/105] =?UTF-8?q?[#69]=20fix:=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/application/SendNewsService.java | 27 ++++++------------- schedule/src/main/resources/application.yml | 3 --- .../application/SelectNewsServiceTest.java | 3 +-- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index c07ccc2c..c0076e73 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -17,17 +17,12 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class SendNewsService { - - private static final int YESTERDAY = 1; private static final int ALARM_DAY = 2; private final NewsRepository newsRepository; @@ -35,10 +30,8 @@ public class SendNewsService { private final NewsHistoryRepository newsHistoryRepository; private final MailService mailService; - @Transactional - public void selectNews() { - LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); + public void selectNews(LocalDate yesterday) { List newsList = newsRepository.findRatingTop3ByCreatedAt(yesterday); if (newsList == null || newsList.isEmpty()) { @@ -91,17 +84,13 @@ public List retrieveUserEmails(Interest interest) { public void createNewsHistory(List sendNews) { List users = userRepository.findAllByInterest(Interest.NEWS); - List newsHistories = new ArrayList<>(); - - for (News news : sendNews) { - for (User user : users) { - NewsHistory newsHistory = NewsHistory.builder() - .user(user) - .news(news) - .build(); - newsHistories.add(newsHistory); - } - } + List newsHistories = sendNews.stream() + .flatMap(news -> users.stream() + .map(user -> NewsHistory.builder() + .user(user) + .news(news) + .build())) + .toList(); newsHistoryRepository.saveAll(newsHistories); } } diff --git a/schedule/src/main/resources/application.yml b/schedule/src/main/resources/application.yml index cbd0b4b6..8f86f691 100644 --- a/schedule/src/main/resources/application.yml +++ b/schedule/src/main/resources/application.yml @@ -5,9 +5,6 @@ spring: password: 1234 driver-class-name: com.mysql.cj.jdbc.Driver - kakao: - client-id: bc2c718aa374df7f4b52201962968bbd - h2: console: enabled: true diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java index 7d113ee3..8d45ba8d 100644 --- a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -78,7 +78,6 @@ public void retrieveUserEmailsTest() { public void selectNewsTest() { // give LocalDate yesterday = LocalDate.now().minusDays(YESTERDAY); - LocalDateTime expectedSendAt = LocalDateTime.now().plusDays(ALARM_DAY).plusHours(ALARM_HOUR); News mockNews1 = mock(News.class); News mockNews2 = mock(News.class); @@ -87,7 +86,7 @@ public void selectNewsTest() { when(newsRepository.findRatingTop3ByCreatedAt(yesterday)).thenReturn(mockNewsList); // when - sendNewsService.selectNews(); + sendNewsService.selectNews(yesterday); // then ArgumentCaptor captor = ArgumentCaptor.forClass(LocalDate.class); From e98808d28f8e8ac786be594c7a12e99dea03f2e0 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 24 Dec 2024 17:12:46 +0900 Subject: [PATCH 103/105] =?UTF-8?q?[#69]=20fix:=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/itcast/news/common/schedule/AlarmSchedule.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java index c3714735..c5a1cfbc 100644 --- a/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java +++ b/schedule/src/main/java/itcast/news/common/schedule/AlarmSchedule.java @@ -7,6 +7,8 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.time.LocalDate; + @Component @RequiredArgsConstructor @Slf4j @@ -17,7 +19,8 @@ public class AlarmSchedule { @Scheduled(cron = "${scheduler.news.select-news}") public void selectNewsSchedule() { log.info("Selecting schedule...."); - sendNewsService.selectNews(); + LocalDate yesterday = LocalDate.now().minusDays(1); + sendNewsService.selectNews(yesterday); log.info("Selecting schedule Finish"); } From 964308a4af5b3044b2dfb8035c9ddd44488a9c01 Mon Sep 17 00:00:00 2001 From: jubseok Date: Tue, 24 Dec 2024 20:59:03 +0900 Subject: [PATCH 104/105] =?UTF-8?q?[#98]=20refactor:=20=EB=89=B4=EC=8A=A4?= =?UTF-8?q?=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EB=B6=80=EB=B6=84=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/itcast/exception/ErrorCodes.java | 1 + .../itcast/news/application/NewsService.java | 84 +++++++++++-------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/common/src/main/java/itcast/exception/ErrorCodes.java b/common/src/main/java/itcast/exception/ErrorCodes.java index d63c12eb..7df33963 100644 --- a/common/src/main/java/itcast/exception/ErrorCodes.java +++ b/common/src/main/java/itcast/exception/ErrorCodes.java @@ -29,6 +29,7 @@ public enum ErrorCodes { NOT_FOUND_EMAIL("이메일을 찾을 수 없습니다",2006L,HttpStatus.NOT_FOUND), NOT_FOUND_SEND_DATA("발송 데이터를 찾을 수 없습니다",2007L,HttpStatus.NOT_FOUND), INVALID_INTEREST_TYPE_ERROR("invalid타입이 맞지 않습니다",2008L,HttpStatus.BAD_REQUEST), + GPT_SERVICE_ERROR("GPT요약 중 오류가 발생했습니다 ",2009L,HttpStatus.BAD_REQUEST), BAD_REQUEST("BAD_REQUEST", 9404L, HttpStatus.BAD_REQUEST), BAD_REQUEST_JSON_PARSE_ERROR("[BAD_REQUEST] JSON_PARSE_ERROR - 올바른 JSON 형식이 아님", 9405L, HttpStatus.BAD_REQUEST), diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index 25e60bfc..c85098ac 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -37,48 +37,63 @@ public void newsCrawling() throws IOException { List links = findLinks(URL); links = isValidLinks(links); + List newsList = new ArrayList<>(); + links.forEach(link -> { - try { - Document url = Jsoup.connect(link).get(); - String titles = url.select("#title_area").text(); - String content = url.select("#dic_area").text(); - String date = - url.select(".media_end_head_info_datestamp_bunch").text(); - String thumbnail = - url.selectFirst("meta[property=og:image]").attr("content"); - - titles = cleanContent(titles); - content = cleanContent(content); - LocalDateTime publishedAt = convertDateTime(date); - - if (thumbnail.isEmpty()) { - throw new ItCastApplicationException(INVALID_NEWS_CONTENT); - } - - CreateNewsRequest newsRequest = new CreateNewsRequest(titles, content, link, thumbnail, publishedAt); - News news = newsRepository.save(newsRequest.toEntity(titles, content, link, thumbnail, publishedAt)); - Message message = new Message("user", content); - GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini", message, 0.7f); - gptService.updateNewsBySummaryContent(request, news.getId()); - } catch (IOException e) { - throw new ItCastApplicationException(CRAWLING_PARSE_ERROR); + News news = processNews(link); + if (news != null) { + newsList.add(news); } }); + + if (!newsList.isEmpty()) { + newsRepository.saveAll(newsList); + } + } + + public News processNews(String link) { + try { + Document url = Jsoup.connect(link).get(); + String titles = url.select("#title_area").text(); + String content = url.select("#dic_area").text(); + String date = url.select(".media_end_head_info_datestamp_bunch").text(); + String thumbnail = url.selectFirst("meta[property=og:image]").attr("content"); + + titles = cleanContent(titles); + content = cleanContent(content); + LocalDateTime publishedAt = convertDateTime(date); + + if (thumbnail.isEmpty()) { + throw new ItCastApplicationException(INVALID_NEWS_CONTENT); + } + + CreateNewsRequest newsRequest = new CreateNewsRequest(titles, content, link, thumbnail, publishedAt); + News news = newsRequest.toEntity(titles, content, link, thumbnail, publishedAt); + updateNewsSummary(news, content); + return news; + } catch (IOException e) { + throw new ItCastApplicationException(CRAWLING_PARSE_ERROR); + } + } + + private void updateNewsSummary(News news, String content) { + try { + Message message = new Message("user", content); + GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini", message, 0.7f); + gptService.updateNewsBySummaryContent(request, news.getId()); + } catch (Exception e) { + throw new ItCastApplicationException(GPT_SERVICE_ERROR); + } } public List findLinks(String url) throws IOException { Document document = Jsoup.connect(url).get(); Elements articles = document.select(".sa_thumb_inner"); - List links = new ArrayList<>(); - articles.forEach(article -> { - if (links.size() >= LINK_SIZE) { - return; - } - String link = article.select("a").attr("href"); - links.add(link); - }); - return links; + return articles.stream() + .map(article -> article.select("a").attr("href")) + .limit(LINK_SIZE) + .toList(); } public List isValidLinks(List links) { @@ -124,9 +139,8 @@ public String cleanContent(String info) { throw new ItCastApplicationException(INVALID_NEWS_CONTENT); } - info = info.replaceAll("\\[.*?\\]", "") + return info.replaceAll("\\[.*?\\]", "") .replaceAll("\\(.*?\\)", "") .trim(); - return info; } } From 7c7d3f2b590f4f0525a9198229b45d9225a000a2 Mon Sep 17 00:00:00 2001 From: jubseok Date: Thu, 26 Dec 2024 11:07:58 +0900 Subject: [PATCH 105/105] =?UTF-8?q?[#98]=20test:=20=EB=82=98=EB=88=A0?= =?UTF-8?q?=EC=A7=84=20=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/dto/request/GPTSummaryRequest.java | 9 ++++++ .../itcast/news/application/NewsService.java | 2 +- .../news/application/SendNewsService.java | 2 -- .../news/application/NewsServiceTest.java | 31 +++++++++++++++++-- .../application/SelectNewsServiceTest.java | 2 -- 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/schedule/src/main/java/itcast/ai/dto/request/GPTSummaryRequest.java b/schedule/src/main/java/itcast/ai/dto/request/GPTSummaryRequest.java index 50e00340..356921b2 100644 --- a/schedule/src/main/java/itcast/ai/dto/request/GPTSummaryRequest.java +++ b/schedule/src/main/java/itcast/ai/dto/request/GPTSummaryRequest.java @@ -1,8 +1,17 @@ package itcast.ai.dto.request; +import lombok.Builder; + + public record GPTSummaryRequest( String model, Message message, float temperature ) { + @Builder + public GPTSummaryRequest(String model, Message message, float temperature) { + this.model = model; + this.message = message; + this.temperature = temperature; + } } diff --git a/schedule/src/main/java/itcast/news/application/NewsService.java b/schedule/src/main/java/itcast/news/application/NewsService.java index c85098ac..a005a023 100644 --- a/schedule/src/main/java/itcast/news/application/NewsService.java +++ b/schedule/src/main/java/itcast/news/application/NewsService.java @@ -76,7 +76,7 @@ public News processNews(String link) { } } - private void updateNewsSummary(News news, String content) { + public void updateNewsSummary(News news, String content) { try { Message message = new Message("user", content); GPTSummaryRequest request = new GPTSummaryRequest("gpt-4o-mini", message, 0.7f); diff --git a/schedule/src/main/java/itcast/news/application/SendNewsService.java b/schedule/src/main/java/itcast/news/application/SendNewsService.java index c0076e73..9996bbd4 100644 --- a/schedule/src/main/java/itcast/news/application/SendNewsService.java +++ b/schedule/src/main/java/itcast/news/application/SendNewsService.java @@ -39,7 +39,6 @@ public void selectNews(LocalDate yesterday) { } LocalDate sendAt = LocalDate.now().plusDays(ALARM_DAY); - newsList.forEach(news -> { news.newsUpdate(sendAt); }); @@ -60,7 +59,6 @@ public void sendNews() { news.getLink(), news.getThumbnail())) .toList(); - List emails = retrieveUserEmails(Interest.NEWS); if (emails == null || emails.isEmpty()) { diff --git a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java index 92ba26b6..3ca2edbd 100644 --- a/schedule/src/test/java/itcast/news/application/NewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/NewsServiceTest.java @@ -1,6 +1,7 @@ package itcast.news.application; import itcast.ai.application.GPTService; +import itcast.ai.dto.request.GPTSummaryRequest; import itcast.domain.news.News; import itcast.news.repository.NewsRepository; import org.jsoup.Connection; @@ -11,16 +12,15 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Arrays; -import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -168,4 +168,31 @@ void findLinksTest() throws IOException { } } + @Test + @DisplayName("updateNewsSummary 메소드 테스트") + void updateNewsSummaryTest() { + // give + News news = News.builder() + .id(1L) + .title("Sample News") + .content("Original Content") + .build(); + String content = "Updated Summary Content"; + + doNothing().when(gptService).updateNewsBySummaryContent(any(GPTSummaryRequest.class), eq(news.getId())); + + // when + assertDoesNotThrow(() -> newsService.updateNewsSummary(news, content)); + + ArgumentCaptor captor = ArgumentCaptor.forClass(GPTSummaryRequest.class); + verify(gptService, times(1)).updateNewsBySummaryContent(captor.capture(), eq(news.getId())); + + // then + GPTSummaryRequest capturedRequest = captor.getValue(); + assertEquals("gpt-4o-mini", capturedRequest.model()); + assertEquals("user", capturedRequest.message().getRole()); + assertEquals(content, capturedRequest.message().getContent()); + assertEquals(0.7f, capturedRequest.temperature()); + } + } diff --git a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java index 8d45ba8d..440fa17f 100644 --- a/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java +++ b/schedule/src/test/java/itcast/news/application/SelectNewsServiceTest.java @@ -27,8 +27,6 @@ @ExtendWith(MockitoExtension.class) public class SelectNewsServiceTest { private static final int YESTERDAY = 1; - private static final int ALARM_HOUR = 2; - private static final int ALARM_DAY = 2; @Mock private UserRepository userRepository;