From 0bc2c85d113fc84f1754bba0b2ca9641f8123945 Mon Sep 17 00:00:00 2001 From: rabeaM <rabea.mueller@sva.de> Date: Tue, 10 Dec 2024 14:47:01 +0100 Subject: [PATCH] Add the functions to set a user message is read --- .../numportal/domain/model/Message.java | 14 +++++++ .../domain/repository/MessageRepository.java | 8 ++++ .../numportal/service/MessageService.java | 37 +++++++++++++++++++ .../web/controller/MessageController.java | 20 ++++++++++ .../num-portal/V08__user_messages.sql | 25 +++++++++---- 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/highmed/numportal/domain/model/Message.java b/src/main/java/org/highmed/numportal/domain/model/Message.java index 431e00a0..a3b19c9e 100644 --- a/src/main/java/org/highmed/numportal/domain/model/Message.java +++ b/src/main/java/org/highmed/numportal/domain/model/Message.java @@ -1,9 +1,15 @@ package org.highmed.numportal.domain.model; +import org.highmed.numportal.domain.model.admin.User; +import org.highmed.numportal.domain.model.admin.UserDetails; + import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.OneToMany; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,6 +18,7 @@ import java.io.Serializable; import java.time.LocalDateTime; +import java.util.List; @Entity @Builder @@ -39,4 +46,11 @@ public class Message implements Serializable { private boolean markAsDeleted; + @OneToMany + @JoinTable( + name = "read_message_by_users", + joinColumns = @JoinColumn(name = "message_id"), + inverseJoinColumns = @JoinColumn(name = "user_details_id")) + private List<UserDetails> readByUsers; + } diff --git a/src/main/java/org/highmed/numportal/domain/repository/MessageRepository.java b/src/main/java/org/highmed/numportal/domain/repository/MessageRepository.java index e132e476..4da497cd 100644 --- a/src/main/java/org/highmed/numportal/domain/repository/MessageRepository.java +++ b/src/main/java/org/highmed/numportal/domain/repository/MessageRepository.java @@ -1,12 +1,20 @@ package org.highmed.numportal.domain.repository; import org.highmed.numportal.domain.model.Message; +import org.highmed.numportal.domain.model.admin.UserDetails; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + @Repository public interface MessageRepository extends JpaRepository<Message, Long>, PagingAndSortingRepository<Message, Long> { + @Query("SELECT m FROM Message m WHERE :userDetails NOT MEMBER OF m.readByUsers AND m.startDate <= :now AND m.endDate >= :now") + List<Message> findAllActiveMessagesNotReadByUser(@Param("userDetails") UserDetails userDetails, @Param("now")LocalDateTime now); } diff --git a/src/main/java/org/highmed/numportal/service/MessageService.java b/src/main/java/org/highmed/numportal/service/MessageService.java index 9ee2b410..143c5cc2 100644 --- a/src/main/java/org/highmed/numportal/service/MessageService.java +++ b/src/main/java/org/highmed/numportal/service/MessageService.java @@ -2,9 +2,12 @@ import org.highmed.numportal.domain.dto.MessageDto; import org.highmed.numportal.domain.model.Message; +import org.highmed.numportal.domain.model.admin.UserDetails; import org.highmed.numportal.domain.repository.MessageRepository; +import org.highmed.numportal.domain.repository.UserDetailsRepository; import org.highmed.numportal.mapper.MessageMapper; import org.highmed.numportal.service.exception.BadRequestException; +import org.highmed.numportal.service.exception.ForbiddenException; import org.highmed.numportal.service.exception.ResourceNotFound; import lombok.AllArgsConstructor; @@ -16,11 +19,16 @@ import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import static org.highmed.numportal.domain.templates.ExceptionsTemplate.CANNOT_ACCESS_THIS_RESOURCE; import static org.highmed.numportal.domain.templates.ExceptionsTemplate.CANNOT_DELETE_MESSAGE; import static org.highmed.numportal.domain.templates.ExceptionsTemplate.CANNOT_HANDLE_DATE; import static org.highmed.numportal.domain.templates.ExceptionsTemplate.CANNOT_UPDATE_MESSAGE_INVALID; import static org.highmed.numportal.domain.templates.ExceptionsTemplate.MESSAGE_NOT_FOUND; +import static org.highmed.numportal.domain.templates.ExceptionsTemplate.USER_NOT_FOUND; @Slf4j @@ -29,6 +37,7 @@ public class MessageService { private static final Safelist safeList = Safelist.simpleText().addTags("br"); + private final UserDetailsRepository userDetailsRepository; private MessageMapper messageMapper; private UserDetailsService userDetailsService; @@ -111,6 +120,34 @@ public void deleteUserMessage(Long id, String userId) { } } + public void markUserMessageAsRead(Long id, String userId) { + LocalDateTime now = LocalDateTime.now(); + Message readMessage = messageRepository.findById(id) + .orElseThrow(() -> new ResourceNotFound(MessageService.class, MESSAGE_NOT_FOUND, + String.format(MESSAGE_NOT_FOUND, id))); + if (readMessage.getStartDate().isBefore(now) && readMessage.getEndDate().isAfter(now) && !readMessage.isSessionBased()) { + UserDetails userDetails = userDetailsRepository.findByUserId(userId) + .orElseThrow(() -> new ResourceNotFound(MessageService.class, USER_NOT_FOUND, + String.format(USER_NOT_FOUND, userId))); + readMessage.getReadByUsers().add(userDetails); + } else { + throw new ForbiddenException(MessageService.class, CANNOT_ACCESS_THIS_RESOURCE); + } + } + + public List<MessageDto> getAllDisplayedUserMessages(String userId) { + UserDetails userDetails = userDetailsRepository.findByUserId(userId) + .orElseThrow(() -> new ResourceNotFound(MessageService.class, USER_NOT_FOUND, + String.format(USER_NOT_FOUND, userId))); + List<Message> notReadByUserMessages = messageRepository.findAllActiveMessagesNotReadByUser(userDetails, LocalDateTime.now()); + List<MessageDto> notReadByUserMessagesDto = new ArrayList<>(); + for (Message message : notReadByUserMessages) { + MessageDto messageDto = messageMapper.convertToDto(message); + notReadByUserMessagesDto.add(messageDto); + } + return notReadByUserMessagesDto; + } + private static boolean isInactiveMessage(Message messageToUpdate, LocalDateTime now) { return messageToUpdate.getEndDate().isBefore(now); } diff --git a/src/main/java/org/highmed/numportal/web/controller/MessageController.java b/src/main/java/org/highmed/numportal/web/controller/MessageController.java index 79b2b2bd..2b412949 100644 --- a/src/main/java/org/highmed/numportal/web/controller/MessageController.java +++ b/src/main/java/org/highmed/numportal/web/controller/MessageController.java @@ -27,6 +27,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @AllArgsConstructor @RequestMapping(value = "/message", produces = "application/json") @@ -91,6 +93,24 @@ public ResponseEntity<Void> deleteUserMessage( messageService.deleteUserMessage(id, principal.getSubject()); return ResponseEntity.ok().build(); } + + @PostMapping(value = "/read/{id}") + @Operation( + description = "Marked a user message as read by logged in user") + public ResponseEntity<Void> markUserMessageAsRead( + @PathVariable("id") Long id, + @AuthenticationPrincipal @NotNull Jwt principal) { + messageService.markUserMessageAsRead(id, principal.getSubject()); + return ResponseEntity.ok().build(); + } + + @GetMapping(value = "/read") + @Operation( + description = "Get a list of all not marked and session based user messages that are currently active") + public ResponseEntity<List<MessageDto>> getAllDisplayedUserMessages( + @AuthenticationPrincipal @NotNull Jwt principal) { + return ResponseEntity.ok(messageService.getAllDisplayedUserMessages(principal.getSubject())); + } } diff --git a/src/main/resources/db/migration/num-portal/V08__user_messages.sql b/src/main/resources/db/migration/num-portal/V08__user_messages.sql index a73ec133..758a3170 100644 --- a/src/main/resources/db/migration/num-portal/V08__user_messages.sql +++ b/src/main/resources/db/migration/num-portal/V08__user_messages.sql @@ -1,11 +1,20 @@ DROP TABLE IF EXISTS message; -CREATE TABLE message( - id BIGSERIAL PRIMARY KEY, - title VARCHAR(255) NOT NULL, - text text, - start_date timestamp NOT NULL, - end_date timestamp NOT NULL, - type varchar(125) NOT NULL , - mark_as_deleted boolean +CREATE TABLE message +( + id serial PRIMARY KEY, + title VARCHAR(255) NOT NULL, + text text, + start_date timestamp NOT NULL, + end_date timestamp NOT NULL, + type varchar(125) NOT NULL, + mark_as_deleted boolean, + sessionBased boolean +); + +CREATE TABLE read_message_by_users +( + message_id int REFERENCES message (id) ON UPDATE CASCADE, + user_details_id varchar(250) REFERENCES user_details (user_id) ON UPDATE CASCADE, + CONSTRAINT message_template_pkey PRIMARY KEY (message_id, user_details_id) ); \ No newline at end of file