diff --git a/src/main/java/com/openisle/config/AsyncConfig.java b/src/main/java/com/openisle/config/AsyncConfig.java new file mode 100644 index 000000000..a8806f64d --- /dev/null +++ b/src/main/java/com/openisle/config/AsyncConfig.java @@ -0,0 +1,23 @@ +package com.openisle.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +public class AsyncConfig { + @Bean(name = "notificationExecutor") + public Executor notificationExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(2); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("notification-"); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/com/openisle/service/NotificationService.java b/src/main/java/com/openisle/service/NotificationService.java index ccfbecbbd..43dc956cb 100644 --- a/src/main/java/com/openisle/service/NotificationService.java +++ b/src/main/java/com/openisle/service/NotificationService.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import java.util.List; +import java.util.concurrent.Executor; /** Service for creating and retrieving notifications. */ @Service @@ -22,6 +23,7 @@ public class NotificationService { private final EmailSender emailSender; private final PushNotificationService pushNotificationService; private final ReactionRepository reactionRepository; + private final Executor notificationExecutor; @Value("${app.website-url}") private String websiteUrl; @@ -60,30 +62,32 @@ public class NotificationService { n.setContent(content); n = notificationRepository.save(n); - if (type == NotificationType.COMMENT_REPLY && user.getEmail() != null && post != null && comment != null) { - String url = String.format("%s/posts/%d#comment-%d", websiteUrl, post.getId(), comment.getId()); - String pushContent = comment.getAuthor() + "回复了你: \"" + comment.getContent() + "\""; - emailSender.sendEmail(user.getEmail(), "【OpenIsle】您有新的回复", pushContent + ", 点击以查看: " + url); - sendCustomPush(user, pushContent, url); - } else if (type == NotificationType.REACTION && comment != null) { - long count = reactionRepository.countReceived(comment.getAuthor().getUsername()); - if (count % 5 == 0) { - String url = websiteUrl + "/messages"; - sendCustomPush(comment.getAuthor(), "你有新的互动", url); - if (comment.getAuthor().getEmail() != null) { - emailSender.sendEmail(comment.getAuthor().getEmail(), "【OpenIsle】你有新的互动", "你有新的互动, 点击以查看: " + url); + notificationExecutor.execute(() -> { + if (type == NotificationType.COMMENT_REPLY && user.getEmail() != null && post != null && comment != null) { + String url = String.format("%s/posts/%d#comment-%d", websiteUrl, post.getId(), comment.getId()); + String pushContent = comment.getAuthor() + "回复了你: \"" + comment.getContent() + "\""; + emailSender.sendEmail(user.getEmail(), "【OpenIsle】您有新的回复", pushContent + ", 点击以查看: " + url); + sendCustomPush(user, pushContent, url); + } else if (type == NotificationType.REACTION && comment != null) { + long count = reactionRepository.countReceived(comment.getAuthor().getUsername()); + if (count % 5 == 0) { + String url = websiteUrl + "/messages"; + sendCustomPush(comment.getAuthor(), "你有新的互动", url); + if (comment.getAuthor().getEmail() != null) { + emailSender.sendEmail(comment.getAuthor().getEmail(), "【OpenIsle】你有新的互动", "你有新的互动, 点击以查看: " + url); + } + } + } else if (type == NotificationType.REACTION && post != null) { + long count = reactionRepository.countReceived(post.getAuthor().getUsername()); + if (count % 5 == 0) { + String url = websiteUrl + "/messages"; + sendCustomPush(post.getAuthor(), "你有新的互动", url); + if (post.getAuthor().getEmail() != null) { + emailSender.sendEmail(post.getAuthor().getEmail(), "【OpenIsle】你有新的互动", "你有新的互动, 点击以查看: " + url); + } } } - } else if (type == NotificationType.REACTION && post != null) { - long count = reactionRepository.countReceived(post.getAuthor().getUsername()); - if (count % 5 == 0) { - String url = websiteUrl + "/messages"; - sendCustomPush(post.getAuthor(), "你有新的互动", url); - if (post.getAuthor().getEmail() != null) { - emailSender.sendEmail(post.getAuthor().getEmail(), "【OpenIsle】你有新的互动", "你有新的互动, 点击以查看: " + url); - } - } - } + }); return n; } diff --git a/src/main/java/com/openisle/service/ResendEmailSender.java b/src/main/java/com/openisle/service/ResendEmailSender.java index 21db847e0..fbc4663c5 100644 --- a/src/main/java/com/openisle/service/ResendEmailSender.java +++ b/src/main/java/com/openisle/service/ResendEmailSender.java @@ -6,6 +6,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import org.springframework.scheduling.annotation.Async; import org.springframework.web.client.RestTemplate; import java.util.HashMap; @@ -20,6 +21,7 @@ public class ResendEmailSender extends EmailSender { private final RestTemplate restTemplate = new RestTemplate(); @Override + @Async("notificationExecutor") public void sendEmail(String to, String subject, String text) { String url = "https://api.resend.com/emails"; // hypothetical endpoint diff --git a/src/test/java/com/openisle/service/NotificationServiceTest.java b/src/test/java/com/openisle/service/NotificationServiceTest.java index 1dca76b33..3c4389c19 100644 --- a/src/test/java/com/openisle/service/NotificationServiceTest.java +++ b/src/test/java/com/openisle/service/NotificationServiceTest.java @@ -4,6 +4,8 @@ import com.openisle.model.*; import com.openisle.repository.NotificationRepository; import com.openisle.repository.UserRepository; import com.openisle.service.PushNotificationService; +import com.openisle.repository.ReactionRepository; +import java.util.concurrent.Executor; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -21,7 +23,9 @@ class NotificationServiceTest { UserRepository uRepo = mock(UserRepository.class); EmailSender email = mock(EmailSender.class); PushNotificationService push = mock(PushNotificationService.class); - NotificationService service = new NotificationService(nRepo, uRepo, email, push); + ReactionRepository rRepo = mock(ReactionRepository.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); User user = new User(); @@ -50,7 +54,9 @@ class NotificationServiceTest { UserRepository uRepo = mock(UserRepository.class); EmailSender email = mock(EmailSender.class); PushNotificationService push = mock(PushNotificationService.class); - NotificationService service = new NotificationService(nRepo, uRepo, email, push); + ReactionRepository rRepo = mock(ReactionRepository.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); User user = new User(); @@ -73,7 +79,9 @@ class NotificationServiceTest { UserRepository uRepo = mock(UserRepository.class); EmailSender email = mock(EmailSender.class); PushNotificationService push = mock(PushNotificationService.class); - NotificationService service = new NotificationService(nRepo, uRepo, email, push); + ReactionRepository rRepo = mock(ReactionRepository.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); User user = new User(); @@ -94,7 +102,9 @@ class NotificationServiceTest { UserRepository uRepo = mock(UserRepository.class); EmailSender email = mock(EmailSender.class); PushNotificationService push = mock(PushNotificationService.class); - NotificationService service = new NotificationService(nRepo, uRepo, email, push); + ReactionRepository rRepo = mock(ReactionRepository.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); User admin = new User(); @@ -116,7 +126,9 @@ class NotificationServiceTest { UserRepository uRepo = mock(UserRepository.class); EmailSender email = mock(EmailSender.class); PushNotificationService push = mock(PushNotificationService.class); - NotificationService service = new NotificationService(nRepo, uRepo, email, push); + ReactionRepository rRepo = mock(ReactionRepository.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); User admin = new User(); @@ -138,7 +150,9 @@ class NotificationServiceTest { UserRepository uRepo = mock(UserRepository.class); EmailSender email = mock(EmailSender.class); PushNotificationService push = mock(PushNotificationService.class); - NotificationService service = new NotificationService(nRepo, uRepo, email, push); + ReactionRepository rRepo = mock(ReactionRepository.class); + Executor executor = Runnable::run; + NotificationService service = new NotificationService(nRepo, uRepo, email, push, rRepo, executor); org.springframework.test.util.ReflectionTestUtils.setField(service, "websiteUrl", "https://ex.com"); User user = new User();