diff --git a/build.gradle b/build.gradle index 415a8a7..2929ca8 100644 --- a/build.gradle +++ b/build.gradle @@ -47,4 +47,9 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +tasks.named('compileJava') { + inputs.files(tasks.named('processResources')) +} + targetCompatibility = JavaVersion.VERSION_17 diff --git a/src/main/java/site/easystartup/web/config/AppConfig.java b/src/main/java/site/easystartup/web/config/AppConfig.java index 3316413..36cffe1 100644 --- a/src/main/java/site/easystartup/web/config/AppConfig.java +++ b/src/main/java/site/easystartup/web/config/AppConfig.java @@ -1,11 +1,13 @@ package site.easystartup.web.config; import org.modelmapper.ModelMapper; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; @Configuration @ComponentScan(basePackages = {"site.easystartup.web"}) diff --git a/src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java b/src/main/java/site/easystartup/web/config/WebSocketConfig.java similarity index 95% rename from src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java rename to src/main/java/site/easystartup/web/config/WebSocketConfig.java index ea82539..775f36f 100644 --- a/src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java +++ b/src/main/java/site/easystartup/web/config/WebSocketConfig.java @@ -1,4 +1,4 @@ -package site.easystartup.web.chat.config; +package site.easystartup.web.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Configuration; @@ -19,7 +19,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { - config.enableSimpleBroker( "/user"); + config.enableSimpleBroker( "/user", "/queue"); config.setApplicationDestinationPrefixes("/app"); config.setUserDestinationPrefix("/user"); } diff --git a/src/main/java/site/easystartup/web/notification/controller/NotificationController.java b/src/main/java/site/easystartup/web/notification/controller/NotificationController.java new file mode 100644 index 0000000..6e4df17 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/controller/NotificationController.java @@ -0,0 +1,28 @@ +package site.easystartup.web.notification.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import site.easystartup.web.notification.domian.Notification; + +import java.security.Principal; +import java.util.logging.Logger; + +@Controller +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class NotificationController { + private final SimpMessagingTemplate messagingTemplate; + Logger logger = Logger.getGlobal(); + + @GetMapping("/notify") + public void notification(Principal principal, @Payload Notification notification) { + logger.info("Web socket sent notification to " + principal.getName()); + messagingTemplate.convertAndSendToUser(principal.getName(), "queue/notification", notification); + } + + + +} diff --git a/src/main/java/site/easystartup/web/notification/domian/Notification.java b/src/main/java/site/easystartup/web/notification/domian/Notification.java new file mode 100644 index 0000000..6be660b --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/domian/Notification.java @@ -0,0 +1,10 @@ +package site.easystartup.web.notification.domian; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Notification { + private String text; +} diff --git a/src/main/java/site/easystartup/web/notification/event/EventHandler.java b/src/main/java/site/easystartup/web/notification/event/EventHandler.java new file mode 100644 index 0000000..955dd9d --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/EventHandler.java @@ -0,0 +1,12 @@ +package site.easystartup.web.notification.event; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) + +@Target(ElementType.METHOD) +public @interface EventHandler { +} diff --git a/src/main/java/site/easystartup/web/notification/event/NotificationEvent.java b/src/main/java/site/easystartup/web/notification/event/NotificationEvent.java new file mode 100644 index 0000000..c0a6a6a --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/NotificationEvent.java @@ -0,0 +1,9 @@ +package site.easystartup.web.notification.event; + +public interface NotificationEvent { + void invoke(); + + String getRecipient(); + + String getMessage(); +} diff --git a/src/main/java/site/easystartup/web/notification/event/NotificationEventManager.java b/src/main/java/site/easystartup/web/notification/event/NotificationEventManager.java new file mode 100644 index 0000000..ae549e4 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/NotificationEventManager.java @@ -0,0 +1,36 @@ +package site.easystartup.web.notification.event; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Component +@Scope("singleton") +public class NotificationEventManager { + private List listeners = new ArrayList<>(); + + public void registerListener(NotificationListener listener) { + listeners.add(listener); + } + + public void removeListener(NotificationListener listener) { + listeners.remove(listener); + } + + public void notifyListeners(Class event) { + listeners.forEach(listener -> Arrays.stream(listener.getClass().getMethods()). + filter(m -> m.isAnnotationPresent(EventHandler.class)).forEach(method -> { + if (method.getParameterTypes()[0].equals(event.getClass())) { + try { + method.invoke(listener, event); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + })); + } +} diff --git a/src/main/java/site/easystartup/web/notification/event/NotificationListener.java b/src/main/java/site/easystartup/web/notification/event/NotificationListener.java new file mode 100644 index 0000000..d6392c9 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/NotificationListener.java @@ -0,0 +1,4 @@ +package site.easystartup.web.notification.event; + +public interface NotificationListener { +} diff --git a/src/main/java/site/easystartup/web/notification/event/realtime/RealTimeListener.java b/src/main/java/site/easystartup/web/notification/event/realtime/RealTimeListener.java new file mode 100644 index 0000000..cfc06f1 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/realtime/RealTimeListener.java @@ -0,0 +1,22 @@ +package site.easystartup.web.notification.event.realtime; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import site.easystartup.web.notification.domian.Notification; +import site.easystartup.web.notification.event.EventHandler; +import site.easystartup.web.notification.event.NotificationEvent; +import site.easystartup.web.notification.event.NotificationListener; +import site.easystartup.web.notification.service.NotificationService; +import site.easystartup.web.post.domain.event.RepostEvent; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class RealTimeListener implements NotificationListener { + private final NotificationService notificationService; + + //Обрабатывает все ивенты + @EventHandler + public void onEvent(NotificationEvent event) { + notificationService.sendNotification( + new Notification(event.getMessage())); + } +} diff --git a/src/main/java/site/easystartup/web/notification/service/NotificationService.java b/src/main/java/site/easystartup/web/notification/service/NotificationService.java new file mode 100644 index 0000000..8236010 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/service/NotificationService.java @@ -0,0 +1,17 @@ +package site.easystartup.web.notification.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; +import site.easystartup.web.notification.domian.Notification; + +@Component +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class NotificationService { + private final SimpMessagingTemplate messagingTemplate; + + public void sendNotification(Notification notification) { + messagingTemplate.convertAndSendToUser("server", "queue/notification", notification); + } +} diff --git a/src/main/java/site/easystartup/web/post/domain/event/RepostEvent.java b/src/main/java/site/easystartup/web/post/domain/event/RepostEvent.java new file mode 100644 index 0000000..776448a --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/event/RepostEvent.java @@ -0,0 +1,27 @@ +package site.easystartup.web.post.domain.event; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import site.easystartup.web.notification.event.NotificationEvent; +import site.easystartup.web.notification.event.NotificationEventManager; + +@RequiredArgsConstructor +public class RepostEvent implements NotificationEvent { + @Autowired + private NotificationEventManager notificationEventManager; + + @Getter + private final String recipient; + + @Getter + private final String reportedUser; + + @Getter + private final String message; + + @Override + public void invoke() { + notificationEventManager.notifyListeners(getClass()); + } +} diff --git a/src/main/java/site/easystartup/web/post/service/PostService.java b/src/main/java/site/easystartup/web/post/service/PostService.java index 16c3bd4..a6f235b 100644 --- a/src/main/java/site/easystartup/web/post/service/PostService.java +++ b/src/main/java/site/easystartup/web/post/service/PostService.java @@ -3,6 +3,7 @@ package site.easystartup.web.post.service; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import site.easystartup.web.post.domain.event.RepostEvent; import site.easystartup.web.post.domain.exception.AlreadyLikedException; import site.easystartup.web.post.domain.exception.PostNotFoundException; import site.easystartup.web.post.domain.model.Post; @@ -61,6 +62,9 @@ public class PostService { List answers = answered.getAnswers() != null ? answered.getAnswers() : new ArrayList<>(); answers.add(post.getId()); answered.setAnswers(answers); + new RepostEvent(answered.getSender(), + post.getSender(), + String.format("Пользователь %s ответил на вашу запись!", answered.getSender())).invoke(); postRepo.save(answered); } } diff --git a/src/main/resources/static/js/notification.js b/src/main/resources/static/js/notification.js new file mode 100644 index 0000000..e69de29