From 97490e46c1d785fc481109f6bead9607bea3c3f7 Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Mon, 18 Oct 2021 17:43:52 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=EC=83=88=20=EA=B3=B5=EC=A7=80?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=ED=81=AC=EB=A1=A4=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=84=9C=20=EC=95=8C=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 + .gitignore | 1 + .../controller/NotificationController.java | 15 ++--- .../data/dto/AnnouncementAuthorFollowDto.java | 19 ------ .../khumu/alimi/data/dto/AnnouncementDto.java | 5 +- .../data/dto/NewAnnouncementCrawledDto.java | 33 ++++++++++ .../external/listener/SqsMessageListener.java | 7 +- .../AnnouncementAuthorFollowRepository.java | 10 --- ...nnouncementAuthorFollowRepositoryImpl.java | 19 ------ .../CustomPushDeviceRepository.java | 4 ++ .../AnnouncementEventService.java | 64 +++++++++++++++++++ .../notification/ArticleEventService.java | 1 - .../HaksaScheduleEventService.java | 1 + .../NotifyNewAnnouncementService.java | 56 ---------------- 14 files changed, 117 insertions(+), 119 deletions(-) delete mode 100644 src/main/java/com/khumu/alimi/data/dto/AnnouncementAuthorFollowDto.java create mode 100644 src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java delete mode 100644 src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepository.java delete mode 100644 src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepositoryImpl.java create mode 100644 src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java delete mode 100644 src/main/java/com/khumu/alimi/service/notification/NotifyNewAnnouncementService.java diff --git a/.dockerignore b/.dockerignore index 9067895..55cdd29 100644 --- a/.dockerignore +++ b/.dockerignore @@ -39,3 +39,4 @@ build/ # resources에는 credential이 위치할 수도 있음. /src/main/resources !/src/main/resources/application.properties +testdata.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9c5987e..e4c5f00 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ build/ # resources에는 credential이 위치할 수도 있음. **/src/main/resources !**/src/main/resources/application.properties +testdata.sh \ No newline at end of file diff --git a/src/main/java/com/khumu/alimi/controller/NotificationController.java b/src/main/java/com/khumu/alimi/controller/NotificationController.java index 76e8b4a..a2201ec 100644 --- a/src/main/java/com/khumu/alimi/controller/NotificationController.java +++ b/src/main/java/com/khumu/alimi/controller/NotificationController.java @@ -2,15 +2,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.khumu.alimi.data.ResourceKind; -import com.khumu.alimi.data.dto.AnnouncementDto; -import com.khumu.alimi.data.dto.NotificationDto; -import com.khumu.alimi.data.dto.ResourceNotificationSubscriptionDto; -import com.khumu.alimi.data.dto.SimpleKhumuUserDto; +import com.khumu.alimi.data.dto.*; import com.khumu.alimi.data.entity.Notification; import com.khumu.alimi.data.entity.ResourceNotificationSubscription; import com.khumu.alimi.service.KhumuException; +import com.khumu.alimi.service.notification.AnnouncementEventService; import com.khumu.alimi.service.notification.NotificationService; -import com.khumu.alimi.service.notification.NotifyNewAnnouncementService; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.PageRequest; @@ -32,8 +29,8 @@ @Slf4j @RestController public class NotificationController { - final NotificationService notificationService; - private final NotifyNewAnnouncementService notifyNewAnnouncementService; + private final NotificationService notificationService; + private final AnnouncementEventService announcementEventService; @RequestMapping(value="/ping", method=RequestMethod.GET) public String ping() { @@ -98,8 +95,8 @@ public DefaultResponse delete(@AuthenticationPrincipal SimpleKhumuUserDt @ResponseStatus(code = HttpStatus.OK) // TODO: 지금은 편의상 그냥 Notification을 return // 근데 얘는 영속화된 Notification이 아니라 푸시알림만 보냄. - public List notifyNewAnnouncement(@RequestBody AnnouncementDto body) throws Exception { - return notifyNewAnnouncementService.notify(body); + public void notifyNewAnnouncement(@RequestBody NewAnnouncementCrawledDto body) { + announcementEventService.notifyNewAnnouncementCrawled(body); } @PostMapping(value = "/api/subscriptions") diff --git a/src/main/java/com/khumu/alimi/data/dto/AnnouncementAuthorFollowDto.java b/src/main/java/com/khumu/alimi/data/dto/AnnouncementAuthorFollowDto.java deleted file mode 100644 index ef34977..0000000 --- a/src/main/java/com/khumu/alimi/data/dto/AnnouncementAuthorFollowDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.khumu.alimi.data.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - - -/** - * 공지사항 마이크로서비스에서 조회한 Announcement DTO - */ -@AllArgsConstructor -@NoArgsConstructor -@Data -@Builder -public class AnnouncementAuthorFollowDto { - String authorName; - String followerName; -} \ No newline at end of file diff --git a/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java b/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java index 7ffea7c..dcf6162 100644 --- a/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java +++ b/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java @@ -8,6 +8,7 @@ import java.util.List; /** + * notice crawler가 새로운 공지사항을 crawl한 경우 매시지를 publish { "id": 184, "title": "수강신청 안내", @@ -22,9 +23,7 @@ public class AnnouncementDto { Long id; String title; String authorName; - String link; - // follower들의 ID - List followers; + String subLink; // 왜 필드 이름이 sub_link일까.. } diff --git a/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java b/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java new file mode 100644 index 0000000..5adf5db --- /dev/null +++ b/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java @@ -0,0 +1,33 @@ +package com.khumu.alimi.data.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * notice crawler가 새로운 공지사항을 crawl한 경우 매시지를 publish +{ + "announcement": { + "id": 184, + "title": "수강신청 안내", + "author_name": "컴퓨터공학과" + }, + "followers": [ + "jinsu", "bo314" + ] +} + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class NewAnnouncementCrawledDto { + AnnouncementDto announcement; + // 알림을 받을 후보인 user들. 해당 author을 follow 중이다 + List followers; +} + + diff --git a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java index c97ded1..950f4fc 100644 --- a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java +++ b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java @@ -5,6 +5,7 @@ import com.khumu.alimi.data.ResourceKind; import com.khumu.alimi.data.dto.*; import com.khumu.alimi.data.resource.ArticleResource; +import com.khumu.alimi.service.notification.AnnouncementEventService; import com.khumu.alimi.service.notification.ArticleEventService; import com.khumu.alimi.service.notification.CommentEventService; import com.khumu.alimi.service.notification.HaksaScheduleEventService; @@ -17,8 +18,9 @@ @Slf4j @RequiredArgsConstructor public class SqsMessageListener { - final CommentEventService commentEventMessageService; + final AnnouncementEventService announcementEventService; final ArticleEventService articleEventMessageService; + final CommentEventService commentEventMessageService; final HaksaScheduleEventService haksaScheduleEventService; final ObjectMapper objectMapper; @@ -56,9 +58,10 @@ public void receiveMessage(SqsMessageBodyDto body) { } } break; case announcement:{ - AnnouncementDto announcementDto = objectMapper.readValue(body.getMessage(), AnnouncementDto.class); + NewAnnouncementCrawledDto event = objectMapper.readValue(body.getMessage(), NewAnnouncementCrawledDto.class); switch (eventKind) { case create:{ + announcementEventService.notifyNewAnnouncementCrawled(event); } break; } } break; diff --git a/src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepository.java b/src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepository.java deleted file mode 100644 index 08a099f..0000000 --- a/src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.khumu.alimi.repository; - -import com.khumu.alimi.data.dto.AnnouncementAuthorFollowDto; - -import java.util.List; - -public interface AnnouncementAuthorFollowRepository { - // 특정 공지사항 작성자에 대한 팔로우 정보들을 조회합니다. - List findAllByAuthorName(String authorName); -} diff --git a/src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepositoryImpl.java b/src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepositoryImpl.java deleted file mode 100644 index 8f7b4a6..0000000 --- a/src/main/java/com/khumu/alimi/repository/AnnouncementAuthorFollowRepositoryImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.khumu.alimi.repository; - -import com.khumu.alimi.data.dto.AnnouncementAuthorFollowDto; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public class AnnouncementAuthorFollowRepositoryImpl implements AnnouncementAuthorFollowRepository { - @Override - public List findAllByAuthorName(String authorName) { - return List.of( - AnnouncementAuthorFollowDto.builder().authorName("컴퓨터공학과").followerName("bo314").build(), - AnnouncementAuthorFollowDto.builder().authorName("컴퓨터공학과").followerName("jinsu").build(), - AnnouncementAuthorFollowDto.builder().authorName("컴퓨터공학과").followerName("dizzi").build(), - AnnouncementAuthorFollowDto.builder().authorName("컴퓨터공학과").followerName("gusrl4025").build() - ); - } -} diff --git a/src/main/java/com/khumu/alimi/repository/CustomPushDeviceRepository.java b/src/main/java/com/khumu/alimi/repository/CustomPushDeviceRepository.java index c4aced6..4ca79f4 100644 --- a/src/main/java/com/khumu/alimi/repository/CustomPushDeviceRepository.java +++ b/src/main/java/com/khumu/alimi/repository/CustomPushDeviceRepository.java @@ -50,4 +50,8 @@ public List findAll(){ public PushDevice save(PushDevice subscription) { return pushDeviceRepository.save(subscription); } + + public void delete(PushDevice device) { + pushDeviceRepository.delete(device); + } } diff --git a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java new file mode 100644 index 0000000..8979c6f --- /dev/null +++ b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java @@ -0,0 +1,64 @@ +package com.khumu.alimi.service.notification; + +import com.khumu.alimi.data.ResourceKind; +import com.khumu.alimi.data.dto.NewAnnouncementCrawledDto; +import com.khumu.alimi.data.dto.SimpleKhumuUserDto; +import com.khumu.alimi.data.entity.*; +import com.khumu.alimi.data.resource.ArticleResource; +import com.khumu.alimi.external.push.PushManager; +import com.khumu.alimi.repository.CustomPushDeviceRepository; +import com.khumu.alimi.repository.CustomPushOptionRepository; +import com.khumu.alimi.repository.NotificationRepository; +import com.khumu.alimi.repository.PushDeviceRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 공지사항 관련된 이벤트가 발생했을 때 무슨 작업을 수행할 것인지. + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class AnnouncementEventService { + final NotificationRepository notificationRepository; + final NotificationService notificationService; + final CustomPushDeviceRepository pushDeviceRepository; + final CustomPushOptionRepository pushOptionRepository; + final PushManager pushManager; + + // 새로운 공지사항이 작성되었다는 이벤트를 바탕으로 알림 + public void notifyNewAnnouncementCrawled(NewAnnouncementCrawledDto event) { + for (String recipientId:event.getFollowers()){ + Notification tmp = Notification.builder() + .recipient(recipientId) + .title(event.getAnnouncement().getAuthorName() + "이 새로운 공지사항을 등록했습니다!") + .content(event.getAnnouncement().getTitle()) + .kind("공지사항") + .reference(null) + .link(event.getAnnouncement().getSubLink()) + .build(); + + Notification n = notificationRepository.save(tmp); + + List devices = pushDeviceRepository.findAllByUser(recipientId); + for (PushDevice device : devices) { + try { + pushManager.notify(n, device.getDeviceToken()); + log.info("푸시를 보냅니다. " + device.getUser()); + } catch (PushManager.PushException e) { + if (e.getMessage().contains("Requested entity was not found.")) { + log.warn("더 이상 존재하지 않는 device tokne이므로 삭제합니다." + device.getDeviceToken()); + pushDeviceRepository.delete(device); + } else{ + e.printStackTrace(); + } + } + + } + } + + } +} \ No newline at end of file diff --git a/src/main/java/com/khumu/alimi/service/notification/ArticleEventService.java b/src/main/java/com/khumu/alimi/service/notification/ArticleEventService.java index cb5476d..7317e16 100644 --- a/src/main/java/com/khumu/alimi/service/notification/ArticleEventService.java +++ b/src/main/java/com/khumu/alimi/service/notification/ArticleEventService.java @@ -38,7 +38,6 @@ public void subscribeByNewArticle(ArticleResource article) { } catch (Exception e) { log.error("article에 대한 알림 구독 생성이 실패했습니다. " + article); e.printStackTrace(); - } } diff --git a/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java b/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java index 0ee6d20..948bf81 100644 --- a/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java +++ b/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java @@ -83,6 +83,7 @@ public List createNotificationForHaksaScheduleStarts(HaksaSchedule } catch (PushManager.PushException e) { if (e.getMessage().contains("Requested entity was not found.")) { log.warn("더 이상 존재하지 않는 device tokne이므로 삭제합니다." + device.getDeviceToken()); + pushDeviceRepository.delete(device); } else{ e.printStackTrace(); } diff --git a/src/main/java/com/khumu/alimi/service/notification/NotifyNewAnnouncementService.java b/src/main/java/com/khumu/alimi/service/notification/NotifyNewAnnouncementService.java deleted file mode 100644 index 06915f4..0000000 --- a/src/main/java/com/khumu/alimi/service/notification/NotifyNewAnnouncementService.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.khumu.alimi.service.notification; - -import com.khumu.alimi.data.dto.AnnouncementAuthorFollowDto; -import com.khumu.alimi.data.dto.AnnouncementDto; -import com.khumu.alimi.data.entity.Notification; -import com.khumu.alimi.data.entity.PushDevice; -import com.khumu.alimi.external.push.PushManager; -import com.khumu.alimi.repository.AnnouncementAuthorFollowRepository; -import com.khumu.alimi.repository.CustomPushDeviceRepository; -import com.khumu.alimi.repository.CustomPushOptionRepository; -import com.khumu.alimi.repository.NotificationRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 새 공지사항이 생성되었을 때 알림 - */ -@Service -@RequiredArgsConstructor -@Slf4j -public class NotifyNewAnnouncementService { - final CustomPushDeviceRepository pushDeviceRepository; - private final NotificationRepository notificationRepository; - final PushManager pushManager; - - @Transactional - public List notify(AnnouncementDto announcementDto) throws PushManager.PushException { - List results = new ArrayList<>(); - log.info("새로운 공지사항 " + announcementDto.getId() + "에 대한 알림 전송 시작"); - - for (String recipientId : announcementDto.getFollowers()) { - Notification n = notificationRepository.save(Notification.builder() - .recipient(recipientId) - .title(announcementDto.getAuthorName() + "의 새로운 공지사항이 작성되었어요!") - .content(announcementDto.getTitle()) - .kind("공지사항") - .reference("announcements/" + announcementDto.getId()) - .link(announcementDto.getLink()) - .build()); - - // TODO: 푸시 알림 조건 반영 - List subscriptions = pushDeviceRepository.findAllByUser(recipientId); - for (PushDevice subscription : subscriptions) { - pushManager.notify(n, subscription.getDeviceToken()); - } - results.add(n); - } - return results; - } -} \ No newline at end of file From 11423785a442b24e49bbce3ebe91174144245bfb Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Mon, 25 Oct 2021 02:14:43 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD?= =?UTF-8?q?=20=ED=81=AC=EB=A1=A4=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=A2=80=20=EB=8D=94=20=EA=B3=B5=EC=A7=80=20=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=EC=97=90=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../khumu/alimi/data/dto/AnnouncementDto.java | 28 +++++++++++++++---- .../data/dto/NewAnnouncementCrawledDto.java | 21 ++++++++------ .../AnnouncementEventService.java | 2 +- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java b/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java index dcf6162..804eaf9 100644 --- a/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java +++ b/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java @@ -5,14 +5,20 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; import java.util.List; /** * notice crawler가 새로운 공지사항을 crawl한 경우 매시지를 publish { - "id": 184, - "title": "수강신청 안내", - "author_name": "컴퓨터공학과" + "id": 1, + "title": "소방시설 보완공사 시행 안내 ", + "sub_link": "http://ce.khu.ac.kr/index.php?hCode=BOARD&page=view&idx=2330&bo_idx=1", + "date": "2021-08-06 11:15:38", + "author": { + "id": 1, + "author_name": "컴퓨터공학과", + "followed": false } */ @AllArgsConstructor @@ -22,8 +28,20 @@ public class AnnouncementDto { Long id; String title; - String authorName; - String subLink; // 왜 필드 이름이 sub_link일까.. + String subLink; + LocalDateTime date; + AuthorDto author; + + @AllArgsConstructor + @NoArgsConstructor + @Data + @Builder + public static class AuthorDto { + Long id; + String authorName; + Boolean followed; + } + } diff --git a/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java b/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java index 5adf5db..a76042e 100644 --- a/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java +++ b/src/main/java/com/khumu/alimi/data/dto/NewAnnouncementCrawledDto.java @@ -10,14 +10,19 @@ /** * notice crawler가 새로운 공지사항을 crawl한 경우 매시지를 publish { - "announcement": { - "id": 184, - "title": "수강신청 안내", - "author_name": "컴퓨터공학과" - }, - "followers": [ - "jinsu", "bo314" - ] + "announcement": { + "id": 1, + "title": "소방시설 보완공사 시행 안내 ", + "sub_link": "http://ce.khu.ac.kr/index.php?hCode=BOARD&page=view&idx=2330&bo_idx=1", + "date": "2021-08-06 11:15:38", + "author": { + "id": 1, + "author_name": "컴퓨터공학과", + "followed": false + }, + "followers": [ + "jinsu", "bo314" + ] } */ @AllArgsConstructor diff --git a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java index 8979c6f..6624b45 100644 --- a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java +++ b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java @@ -34,7 +34,7 @@ public void notifyNewAnnouncementCrawled(NewAnnouncementCrawledDto event) { for (String recipientId:event.getFollowers()){ Notification tmp = Notification.builder() .recipient(recipientId) - .title(event.getAnnouncement().getAuthorName() + "이 새로운 공지사항을 등록했습니다!") + .title(event.getAnnouncement().getAuthor().getAuthorName() + "이 새로운 공지사항을 등록했습니다!") .content(event.getAnnouncement().getTitle()) .kind("공지사항") .reference(null) From 85d7a038bf7e83333f25b263232c736a7e0ff291 Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Wed, 10 Nov 2021 20:03:33 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20notification=EC=97=90=20link=20?= =?UTF-8?q?=ED=95=84=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +--- .../com/khumu/alimi/data/dto/NotificationDto.java | 3 ++- .../khumu/alimi/mapper/NotificationMapper.java | 15 ++++++++++++++- src/main/resources/application.properties | 3 ++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 7672553..5d8306d 100644 --- a/build.gradle +++ b/build.gradle @@ -26,9 +26,7 @@ dependencies { // gson implementation('com.google.code.gson:gson:2.8.0') - // jpa mysql driver - // 참고 https://victorydntmd.tistory.com/321 - runtimeOnly 'mysql:mysql-connector-java' + implementation 'mysql:mysql-connector-java:8.0.25' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // logger diff --git a/src/main/java/com/khumu/alimi/data/dto/NotificationDto.java b/src/main/java/com/khumu/alimi/data/dto/NotificationDto.java index cd3a62f..ea73cc4 100644 --- a/src/main/java/com/khumu/alimi/data/dto/NotificationDto.java +++ b/src/main/java/com/khumu/alimi/data/dto/NotificationDto.java @@ -22,7 +22,8 @@ public class NotificationDto { String content; String kind; String recipient; - String reference; // 참조 링크 + String reference; // 참조할 수 있는 경로 + String link; // 실제 링크 // @JsonProperty("is_read")// Jackson이 is라는 prefix를 삭제해버림. // boolean인 경우 발생하던 문젠데 Boolean으로 변경함으로써 해결 diff --git a/src/main/java/com/khumu/alimi/mapper/NotificationMapper.java b/src/main/java/com/khumu/alimi/mapper/NotificationMapper.java index bfe84ea..ef09155 100644 --- a/src/main/java/com/khumu/alimi/mapper/NotificationMapper.java +++ b/src/main/java/com/khumu/alimi/mapper/NotificationMapper.java @@ -4,12 +4,25 @@ import com.khumu.alimi.data.dto.ResourceNotificationSubscriptionDto; import com.khumu.alimi.data.entity.Notification; import com.khumu.alimi.data.entity.ResourceNotificationSubscription; + import org.mapstruct.AfterMapping; +import org.mapstruct.Builder; import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; +import org.springframework.beans.factory.annotation.Value; -@Mapper +@Mapper(builder = @Builder(disableBuilder = true)) public abstract class NotificationMapper { + @Value("${khumu.notification.rootLink}") + String NOTIFICATION_ROOT_LINK; + + @AfterMapping + protected void afterToDto(Notification src, @MappingTarget NotificationDto dest) { + if (src.getLink() == null && src.getReference() != null) { + dest.setLink(NOTIFICATION_ROOT_LINK + "/" + src.getReference()); + } + } + public abstract ResourceNotificationSubscriptionDto toDto(ResourceNotificationSubscription subscription); public abstract NotificationDto toDto(Notification notification); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1df14f9..29943b3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,13 +5,14 @@ server.port=9002 spring.redis.host=${KHUMU_REDIS_HOST} spring.redis.port=6379 # MySQL 을 사용할 것. +spring.datasource.hikari. spring.datasource.url=jdbc:mysql://${KHUMU_DATABASE_HOST}/${KHUMU_DATABASE_NAME}?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.username=${KHUMU_DATABASE_USERNAME} spring.datasource.password=${KHUMU_DATABASE_PASSWORD} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # MySQL 상세 지정 spring.jpa.database=mysql -spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.open-in-view=false spring.jpa.show-sql=true spring.jpa.hibernate.format_sql=true From 45f37e4cb0b383483f920a5fe824fa3de813e852 Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Wed, 10 Nov 2021 21:24:05 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=EC=83=88=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B0=9B=EC=95=84=EC=84=9C=20=EC=8A=AC?= =?UTF-8?q?=EB=9E=99=EC=97=90=20=EC=95=8C=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../khumu/alimi/data/dto/AnnouncementDto.java | 2 +- .../external/listener/SqsMessageListener.java | 3 + .../alimi/external/slack/SlackNotifier.java | 97 +++++++++++++++++++ .../AnnouncementEventService.java | 2 - src/main/resources/application.properties | 7 ++ 6 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/khumu/alimi/external/slack/SlackNotifier.java diff --git a/build.gradle b/build.gradle index 5d8306d..898642a 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation("com.slack.api:slack-api-client:1.12.1") + // gson implementation('com.google.code.gson:gson:2.8.0') diff --git a/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java b/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java index 804eaf9..5ee2415 100644 --- a/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java +++ b/src/main/java/com/khumu/alimi/data/dto/AnnouncementDto.java @@ -29,7 +29,7 @@ public class AnnouncementDto { Long id; String title; String subLink; - LocalDateTime date; +// LocalDateTime date; AuthorDto author; @AllArgsConstructor diff --git a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java index 950f4fc..1f28e6a 100644 --- a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java +++ b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java @@ -5,6 +5,7 @@ import com.khumu.alimi.data.ResourceKind; import com.khumu.alimi.data.dto.*; import com.khumu.alimi.data.resource.ArticleResource; +import com.khumu.alimi.external.slack.SlackNotifier; import com.khumu.alimi.service.notification.AnnouncementEventService; import com.khumu.alimi.service.notification.ArticleEventService; import com.khumu.alimi.service.notification.CommentEventService; @@ -23,6 +24,7 @@ public class SqsMessageListener { final CommentEventService commentEventMessageService; final HaksaScheduleEventService haksaScheduleEventService; final ObjectMapper objectMapper; + final SlackNotifier slackNotifier; @SqsListener(value = "${sqs.notificationQueue.name}") public void receiveMessage(SqsMessageBodyDto body) { @@ -61,6 +63,7 @@ public void receiveMessage(SqsMessageBodyDto body) { NewAnnouncementCrawledDto event = objectMapper.readValue(body.getMessage(), NewAnnouncementCrawledDto.class); switch (eventKind) { case create:{ + slackNotifier.sendSlack("새로운 공지사항에 대한 알림을 보냅니다.", event.getAnnouncement().getTitle()); announcementEventService.notifyNewAnnouncementCrawled(event); } break; } diff --git a/src/main/java/com/khumu/alimi/external/slack/SlackNotifier.java b/src/main/java/com/khumu/alimi/external/slack/SlackNotifier.java new file mode 100644 index 0000000..967cf8f --- /dev/null +++ b/src/main/java/com/khumu/alimi/external/slack/SlackNotifier.java @@ -0,0 +1,97 @@ +package com.khumu.alimi.external.slack; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.util.List; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SlackNotifier { + @Value("${notification.slack.enabled}") + private boolean slackEnabled; + @Value("${notification.slack.webhook.url}") + private String webhookUrl; + @Value("${notification.slack.channel}") + private String channel; + @Value("${notification.slack.botName}") private String botName; + @Value("${notification.slack.icon.emoji}") private String iconEmoji; + @Value("${notification.slack.icon.url}") private String iconUrl; + + @Autowired + Environment env; + @PostConstruct + public void postConstructor(){ +// slack = Slack.getInstance(); + } + public void sendSlack(String title, String content) { + if (slackEnabled) { + try { // create slack + SlackMessage slackMessage = SlackMessage.builder().attachments(List.of(SlackMessage.Attachment.builder() + .title(title) + .text(content) + .footer("from " + env.getActiveProfiles()[0]) + .build() + )).channel(channel).build(); + String payload = new Gson().toJson(slackMessage); + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", MediaType.APPLICATION_JSON_VALUE); // send the post request + HttpEntity entity = new HttpEntity<>(payload, headers); + restTemplate.postForEntity(webhookUrl, entity, String.class); + } catch (Exception e) { + log.error("Unhandled Exception occurred while send slack. [Reason] : ", e); + } + } + } + + @Builder + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class SlackMessage { + @SerializedName("text") + private String text; + @SerializedName("channel") + private String channel; + @SerializedName("username") + private String botName; + @SerializedName("icon_emoji") + private String iconEmoji; + @SerializedName("icon_url") + private String iconUrl; + @SerializedName("attachments") + private List attachments; + + @Builder + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Attachment { + @SerializedName("title") + private String title; + @SerializedName("text") + private String text; + @SerializedName("color") + @Builder.Default + private String color = "#2222BB"; + @SerializedName("footer") + private String footer; + } + } +} diff --git a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java index 6624b45..f64208d 100644 --- a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java +++ b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java @@ -56,9 +56,7 @@ public void notifyNewAnnouncementCrawled(NewAnnouncementCrawledDto event) { e.printStackTrace(); } } - } } - } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 29943b3..94d5fb4 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -43,3 +43,10 @@ firebase.credential.absolutePath=some absolute path sqs.notificationQueue.name=. khumu.notification.rootLink=http://khumu + +notification.slack.enabled=false +notification.slack.webhook.url=https://hooks.slack.com/services/T02XXXXX/B159XXXXX/W5CDXXXXXXXXpZ1Iv0GZygpT2 +notification.slack.channel=#channel +notification.slack.botName=test-bot +notification.slack.icon.emoji=:something +notification.slack.icon.url=. From 281cc017d81ea08a13c09fabf528d88e4c69190c Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Wed, 10 Nov 2021 22:09:35 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix:=20timezone=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EC=B6=B0=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/notification/HaksaScheduleEventService.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java b/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java index 948bf81..ea010c6 100644 --- a/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java +++ b/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java @@ -54,13 +54,12 @@ public List createNotificationForHaksaScheduleStarts(HaksaSchedule // dto에는 UTC + 9 string가 전달되고 // 그걸 해석을 잘 못해서 Timezone 정보가 누락됨. // 그리고 9시간 더해서 실제 한국시로 변환함 - LocalDateTime startDate = haksaScheduleDto.getStartsAt().plusHours(9); - LocalDateTime endDate = haksaScheduleDto.getEndsAt().plusHours(9); + LocalDateTime startDate = haksaScheduleDto.getStartsAt(); + LocalDateTime endDate = haksaScheduleDto.getEndsAt(); String content = "[" + startDate.getYear() + "/" + startDate.getMonthValue() + "/" + startDate.getDayOfMonth();; - if (startDate.getDayOfYear() == endDate.getDayOfYear() && - startDate.getDayOfMonth() == endDate.getDayOfMonth() && - startDate.getDayOfWeek() == endDate.getDayOfWeek() + if (startDate.getDayOfMonth() != endDate.getDayOfMonth() || + startDate.getDayOfWeek() != endDate.getDayOfWeek() ) { content += "~" + endDate.getYear() + "/" + endDate.getMonthValue() + "/" + endDate.getDayOfMonth();; } From e1d294b62251b056a60a97ae8e75f9a724bebd85 Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Fri, 12 Nov 2021 19:08:28 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=ED=95=99=EC=82=AC=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=ED=81=AC=EB=A1=A4=20=EC=8B=9C=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=EC=97=90=EC=84=9C=20=EB=81=84=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20=EC=9C=A0=EC=A0=80=EC=97=90=EA=B2=8C?= =?UTF-8?q?=EB=A7=8C=20=EC=95=8C=EB=A6=BC=20=EB=B3=B4=EB=83=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 한 유저의 디바이스가 여러개면 각각 푸시를 보내되 알림 리소스는 한 번만 생성한다. --- .../controller/NotificationController.java | 11 +--- .../alimi/data/entity/PushOptionKind.java | 4 +- .../external/listener/SqsMessageListener.java | 23 +++---- .../NotifyAnnouncementCrawledService.java | 65 +++++++++++++++++++ .../AnnouncementEventService.java | 62 ------------------ .../HaksaScheduleEventService.java | 25 ++++--- 6 files changed, 94 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java delete mode 100644 src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java diff --git a/src/main/java/com/khumu/alimi/controller/NotificationController.java b/src/main/java/com/khumu/alimi/controller/NotificationController.java index a2201ec..32ac023 100644 --- a/src/main/java/com/khumu/alimi/controller/NotificationController.java +++ b/src/main/java/com/khumu/alimi/controller/NotificationController.java @@ -3,25 +3,18 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.khumu.alimi.data.ResourceKind; import com.khumu.alimi.data.dto.*; -import com.khumu.alimi.data.entity.Notification; import com.khumu.alimi.data.entity.ResourceNotificationSubscription; -import com.khumu.alimi.service.KhumuException; -import com.khumu.alimi.service.notification.AnnouncementEventService; +import com.khumu.alimi.service.NotifyAnnouncementCrawledService; import com.khumu.alimi.service.notification.NotificationService; import lombok.*; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; -import org.springframework.security.access.method.P; -import org.springframework.security.access.prepost.PostAuthorize; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; -import java.util.Map; import static com.khumu.alimi.service.KhumuException.*; @@ -30,7 +23,7 @@ @RestController public class NotificationController { private final NotificationService notificationService; - private final AnnouncementEventService announcementEventService; + private final NotifyAnnouncementCrawledService announcementEventService; @RequestMapping(value="/ping", method=RequestMethod.GET) public String ping() { diff --git a/src/main/java/com/khumu/alimi/data/entity/PushOptionKind.java b/src/main/java/com/khumu/alimi/data/entity/PushOptionKind.java index abe31bc..7be34d0 100644 --- a/src/main/java/com/khumu/alimi/data/entity/PushOptionKind.java +++ b/src/main/java/com/khumu/alimi/data/entity/PushOptionKind.java @@ -11,8 +11,8 @@ public enum PushOptionKind { NEW_HOT_ARTICLE, // 자신이 팔로우 중인 작성자의 새 공지사항이 작성됐을 때 - @JsonProperty("new_announcement_of_following_author") - NEW_ANNOUNCEMENT_OF_FOLLOWING_AUTHOR, + @JsonProperty("announcement_crawled") + ANNOUNCEMENT_CRAWLED, // 학사일정 관련 알림 @JsonProperty("haksa_schedule") diff --git a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java index 1f28e6a..2ab633f 100644 --- a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java +++ b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java @@ -6,7 +6,7 @@ import com.khumu.alimi.data.dto.*; import com.khumu.alimi.data.resource.ArticleResource; import com.khumu.alimi.external.slack.SlackNotifier; -import com.khumu.alimi.service.notification.AnnouncementEventService; +import com.khumu.alimi.service.NotifyAnnouncementCrawledService; import com.khumu.alimi.service.notification.ArticleEventService; import com.khumu.alimi.service.notification.CommentEventService; import com.khumu.alimi.service.notification.HaksaScheduleEventService; @@ -19,7 +19,7 @@ @Slf4j @RequiredArgsConstructor public class SqsMessageListener { - final AnnouncementEventService announcementEventService; + final NotifyAnnouncementCrawledService announcementEventService; final ArticleEventService articleEventMessageService; final CommentEventService commentEventMessageService; final HaksaScheduleEventService haksaScheduleEventService; @@ -69,10 +69,13 @@ public void receiveMessage(SqsMessageBodyDto body) { } } break; case haksa_schedule:{ - System.out.println("새로운 학사일정 전달됨!"); - HaksaScheduleDto haksaScheduleDto = objectMapper.readValue(body.getMessage(), HaksaScheduleDto.class); - System.out.println(1); - haksaScheduleEventService.createNotificationForHaksaScheduleStarts(haksaScheduleDto); + switch (eventKind) { + case start:{ + System.out.println("새로운 학사일정 전달됨!"); + HaksaScheduleDto haksaScheduleDto = objectMapper.readValue(body.getMessage(), HaksaScheduleDto.class); + haksaScheduleEventService.createNotificationForHaksaScheduleStarts(haksaScheduleDto); + } break; + } } break; default: System.out.println("default"); @@ -81,13 +84,7 @@ public void receiveMessage(SqsMessageBodyDto body) { } catch (Exception e) { e.printStackTrace(); log.error("SQS 메시지 처리 도중 오류 발생!"); + slackNotifier.sendSlack("SQS 메시지 처리 도중 오류 발생!", e.getStackTrace().toString()); } -// System.out.println("SQS 비용을 줄이기 위한 Dummy wait 시작"); -// try { -// Thread.sleep(1500); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } -// System.out.println("SQS 비용을 줄이기 위한 Dummy wait 마무리"); } } diff --git a/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java b/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java new file mode 100644 index 0000000..4351a15 --- /dev/null +++ b/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java @@ -0,0 +1,65 @@ +package com.khumu.alimi.service; + +import com.khumu.alimi.data.ResourceKind; +import com.khumu.alimi.data.dto.NewAnnouncementCrawledDto; +import com.khumu.alimi.data.dto.SimpleKhumuUserDto; +import com.khumu.alimi.data.entity.*; +import com.khumu.alimi.data.resource.ArticleResource; +import com.khumu.alimi.external.push.PushManager; +import com.khumu.alimi.repository.CustomPushDeviceRepository; +import com.khumu.alimi.repository.CustomPushOptionRepository; +import com.khumu.alimi.repository.NotificationRepository; +import com.khumu.alimi.repository.PushDeviceRepository; +import com.khumu.alimi.service.notification.NotificationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 공지사항 관련된 이벤트가 발생했을 때 무슨 작업을 수행할 것인지. + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class NotifyAnnouncementCrawledService { + private final NotificationRepository notificationRepository; + private final CustomPushDeviceRepository pushDeviceRepository; + private final CustomPushOptionRepository pushOptionRepository; + private final PushManager pushManager; + + // 새로운 공지사항이 작성되었다는 이벤트를 바탕으로 알림 + public void notifyNewAnnouncementCrawled(NewAnnouncementCrawledDto event) { + for (String recipientId:event.getFollowers()){ + PushOption option = pushOptionRepository.getOrCreate(PushOption.builder().pushOptionKind(PushOptionKind.ANNOUNCEMENT_CRAWLED).username(recipientId).isActivated(true).build()); + if (option.getIsActivated()) { + Notification tmp = Notification.builder() + .recipient(recipientId) + .title(event.getAnnouncement().getAuthor().getAuthorName() + "이 새로운 공지사항을 등록했습니다!") + .content(event.getAnnouncement().getTitle()) + .kind("공지사항") + .reference(null) + .link(event.getAnnouncement().getSubLink()) + .build(); + + Notification n = notificationRepository.save(tmp); + + List devices = pushDeviceRepository.findAllByUser(recipientId); + for (PushDevice device : devices) { + try { + pushManager.notify(n, device.getDeviceToken()); + log.info("푸시를 보냅니다. " + device.getUser()); + } catch (PushManager.PushException e) { + if (e.getMessage().contains("Requested entity was not found.")) { + log.warn("더 이상 존재하지 않는 device tokne이므로 삭제합니다." + device.getDeviceToken()); + pushDeviceRepository.delete(device); + } else{ + e.printStackTrace(); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java b/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java deleted file mode 100644 index f64208d..0000000 --- a/src/main/java/com/khumu/alimi/service/notification/AnnouncementEventService.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.khumu.alimi.service.notification; - -import com.khumu.alimi.data.ResourceKind; -import com.khumu.alimi.data.dto.NewAnnouncementCrawledDto; -import com.khumu.alimi.data.dto.SimpleKhumuUserDto; -import com.khumu.alimi.data.entity.*; -import com.khumu.alimi.data.resource.ArticleResource; -import com.khumu.alimi.external.push.PushManager; -import com.khumu.alimi.repository.CustomPushDeviceRepository; -import com.khumu.alimi.repository.CustomPushOptionRepository; -import com.khumu.alimi.repository.NotificationRepository; -import com.khumu.alimi.repository.PushDeviceRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 공지사항 관련된 이벤트가 발생했을 때 무슨 작업을 수행할 것인지. - */ -@Slf4j -@RequiredArgsConstructor -@Service -public class AnnouncementEventService { - final NotificationRepository notificationRepository; - final NotificationService notificationService; - final CustomPushDeviceRepository pushDeviceRepository; - final CustomPushOptionRepository pushOptionRepository; - final PushManager pushManager; - - // 새로운 공지사항이 작성되었다는 이벤트를 바탕으로 알림 - public void notifyNewAnnouncementCrawled(NewAnnouncementCrawledDto event) { - for (String recipientId:event.getFollowers()){ - Notification tmp = Notification.builder() - .recipient(recipientId) - .title(event.getAnnouncement().getAuthor().getAuthorName() + "이 새로운 공지사항을 등록했습니다!") - .content(event.getAnnouncement().getTitle()) - .kind("공지사항") - .reference(null) - .link(event.getAnnouncement().getSubLink()) - .build(); - - Notification n = notificationRepository.save(tmp); - - List devices = pushDeviceRepository.findAllByUser(recipientId); - for (PushDevice device : devices) { - try { - pushManager.notify(n, device.getDeviceToken()); - log.info("푸시를 보냅니다. " + device.getUser()); - } catch (PushManager.PushException e) { - if (e.getMessage().contains("Requested entity was not found.")) { - log.warn("더 이상 존재하지 않는 device tokne이므로 삭제합니다." + device.getDeviceToken()); - pushDeviceRepository.delete(device); - } else{ - e.printStackTrace(); - } - } - } - } - } -} \ No newline at end of file diff --git a/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java b/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java index ea010c6..043ca23 100644 --- a/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java +++ b/src/main/java/com/khumu/alimi/service/notification/HaksaScheduleEventService.java @@ -21,9 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.*; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import static com.khumu.alimi.service.KhumuException.WrongResourceKindException; @@ -65,17 +63,24 @@ public List createNotificationForHaksaScheduleStarts(HaksaSchedule } content += "] " + haksaScheduleDto.getTitle(); + // ㅇr... 이건 수신자를 전달을 안해줘서... 한 사람에게 Notification은 하나만 생성하고 + // device마다 푸시를 보내는 게 쉽지는 않네... + // Hash맵을 이용해서 한 번만 보내야겠다... + Map notifications = new HashMap<>(); for (PushDevice device : devices) { if (!usersIgnored.contains(device.getUser())) { - Notification tmp = Notification.builder() - .recipient(device.getUser()) - .title("새로운 학사일정이 있어요!") - .content(content) - .kind("학사일정") - .build(); + if (!notifications.containsKey(device.getUser())) { + notifications.put(device.getUser(), notificationRepository.save(Notification.builder() + .recipient(device.getUser()) + .title("새로운 학사일정이 있어요!") + .content(content) + .kind("학사일정") + .build() + )); + } - Notification n = notificationRepository.save(tmp); try { + Notification n = notifications.get(device.getUser()); pushManager.notify(n, device.getDeviceToken()); log.info("푸시를 보냈습니다. " + device.getUser()); results.add(n); From 96ce20390f4fee8e619a2089f269b85efc475ee0 Mon Sep 17 00:00:00 2001 From: Jinsu Park Date: Thu, 18 Nov 2021 01:13:43 +0900 Subject: [PATCH 7/7] =?UTF-8?q?chores:=20=EA=B3=B5=EC=A7=80=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=82=B4=EC=A7=9D=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 --- .../com/khumu/alimi/external/listener/SqsMessageListener.java | 2 +- .../khumu/alimi/service/NotifyAnnouncementCrawledService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java index 2ab633f..7a0b749 100644 --- a/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java +++ b/src/main/java/com/khumu/alimi/external/listener/SqsMessageListener.java @@ -84,7 +84,7 @@ public void receiveMessage(SqsMessageBodyDto body) { } catch (Exception e) { e.printStackTrace(); log.error("SQS 메시지 처리 도중 오류 발생!"); - slackNotifier.sendSlack("SQS 메시지 처리 도중 오류 발생!", e.getStackTrace().toString()); + slackNotifier.sendSlack("SQS 메시지 처리 도중 오류 발생!", e.getMessage()); } } } diff --git a/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java b/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java index 4351a15..894d72d 100644 --- a/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java +++ b/src/main/java/com/khumu/alimi/service/NotifyAnnouncementCrawledService.java @@ -36,7 +36,7 @@ public void notifyNewAnnouncementCrawled(NewAnnouncementCrawledDto event) { if (option.getIsActivated()) { Notification tmp = Notification.builder() .recipient(recipientId) - .title(event.getAnnouncement().getAuthor().getAuthorName() + "이 새로운 공지사항을 등록했습니다!") + .title(event.getAnnouncement().getAuthor().getAuthorName() + "의 새로운 공지사항을 올라왔어요!") .content(event.getAnnouncement().getTitle()) .kind("공지사항") .reference(null)