diff --git a/src/main/java/ru/scarletredman/gd2spring/config/TestConfig.java b/src/main/java/ru/scarletredman/gd2spring/config/TestConfig.java index 25eac42..1fd9281 100644 --- a/src/main/java/ru/scarletredman/gd2spring/config/TestConfig.java +++ b/src/main/java/ru/scarletredman/gd2spring/config/TestConfig.java @@ -3,13 +3,16 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import ru.scarletredman.gd2spring.model.Level; import ru.scarletredman.gd2spring.model.User; import ru.scarletredman.gd2spring.model.UserComment; import ru.scarletredman.gd2spring.service.LevelService; +import ru.scarletredman.gd2spring.service.MessageService; import ru.scarletredman.gd2spring.service.UserCommentService; import ru.scarletredman.gd2spring.service.UserService; +@Profile("test") @Configuration @RequiredArgsConstructor public class TestConfig { @@ -17,6 +20,7 @@ public class TestConfig { private final UserService userService; private final UserCommentService userCommentService; private final LevelService levelService; + private final MessageService messageService; @Autowired void createTestUser(boolean debugMode) { @@ -42,6 +46,12 @@ void createTestUser(boolean debugMode) { var level = createTestLevel(user, "Test level"); levelService.uploadLevel(level); + + var user2 = userService.findUserById(2).get(); + for (int i = 0; i < 30; i++) { + messageService.sendMessage(user, user2, "Sent " + i, "Hello my dear friend, Test2!"); + messageService.sendMessage(user2, user, "Received " + i, "Hello my dear friend, Test!"); + } } Level createTestLevel(User owner, String name) { diff --git a/src/main/java/ru/scarletredman/gd2spring/controller/LevelController.java b/src/main/java/ru/scarletredman/gd2spring/controller/LevelController.java index c5f0280..cbbf78f 100644 --- a/src/main/java/ru/scarletredman/gd2spring/controller/LevelController.java +++ b/src/main/java/ru/scarletredman/gd2spring/controller/LevelController.java @@ -68,8 +68,10 @@ GetLevelsResponse getLevels( @RequestParam(name = "song", required = false, defaultValue = "0") int song, @RequestParam(name = "customSong", required = false, defaultValue = "0") int customSong) { - var levels = levelService.getLevels(new LevelListPage.Filters( - "", null, null, 0, false, false, false, false, false, false, false, false, 0, 0, 0)); + var filters = new LevelListPage.Filters( + levelName, null, null, page, false, false, false, false, false, false, false, false, 0, 0, 0); + + var levels = levelService.getLevels(filters); return responseLogger.result(new GetLevelsResponse(levels)); } diff --git a/src/main/java/ru/scarletredman/gd2spring/controller/MessageController.java b/src/main/java/ru/scarletredman/gd2spring/controller/MessageController.java new file mode 100644 index 0000000..5abc9b8 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/controller/MessageController.java @@ -0,0 +1,126 @@ +package ru.scarletredman.gd2spring.controller; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.apache.commons.codec.binary.Base64; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.scarletredman.gd2spring.controller.annotation.GeometryDashAPI; +import ru.scarletredman.gd2spring.controller.response.GetMessagesResponse; +import ru.scarletredman.gd2spring.controller.response.MessageResponse; +import ru.scarletredman.gd2spring.controller.response.RemoveMessageResponse; +import ru.scarletredman.gd2spring.controller.response.UploadMessageResponse; +import ru.scarletredman.gd2spring.security.annotation.GDAuthorizedOnly; +import ru.scarletredman.gd2spring.service.MessageService; +import ru.scarletredman.gd2spring.service.UserService; +import ru.scarletredman.gd2spring.util.ResponseLogger; + +@GeometryDashAPI +@RestController +@RequiredArgsConstructor +public class MessageController { + + private final MessageService messageService; + private final UserService userService; + private final ResponseLogger responseLogger; + + @GDAuthorizedOnly + @PostMapping("/getGJMessages20.php") + GetMessagesResponse messages( + @RequestParam(name = "page") int page, + @RequestParam(name = "total") long total, + @RequestParam(name = "getSent", required = false, defaultValue = "0") int isSent) { + + if (page < 0) return responseLogger.result(GetMessagesResponse.error()); + + var user = UserService.getCurrentUserFromSecurityContextHolder(); + var sent = isSent == 1; + var messages = messageService.getMessages(user, isSent == 1, page, 10); + + return responseLogger.result( + new GetMessagesResponse(messages.messages(), sent, messages.total(), messages.page())); + } + + @GDAuthorizedOnly + @PostMapping("/downloadGJMessage20.php") + MessageResponse download( + @RequestParam(name = "messageID") long messageId, @RequestParam(name = "isSender") int isSender) { + + var user = UserService.getCurrentUserFromSecurityContextHolder(); + var message = messageService.getMessageById(messageId); + + if (message.isEmpty()) { + return responseLogger.result(MessageResponse.error()); + } + var sender = isSender == 1; + var msg = message.get(); + + if ((sender && !Objects.equals(msg.getSender().getId(), user.getId())) + || (!sender && !Objects.equals(msg.getReceiver().getId(), user.getId()))) { + return responseLogger.result(MessageResponse.error()); + } + + return responseLogger.result(new MessageResponse(msg, sender)); + } + + @GDAuthorizedOnly + @PostMapping("/uploadGJMessage20.php") + UploadMessageResponse upload( + @RequestParam(name = "toAccountID") long receiverUserId, + @RequestParam(name = "subject") String subject, + @RequestParam(name = "body") String body) { + + var user = UserService.getCurrentUserFromSecurityContextHolder(); + var receiver = userService.findUserById(receiverUserId); + + if (receiver.isEmpty()) { + return responseLogger.result(UploadMessageResponse.error()); + } + + var decodedSubject = new String(Base64.decodeBase64(subject), StandardCharsets.UTF_8).replace('\0', ' '); + var decodedText = new String(Base64.decodeBase64(body), StandardCharsets.UTF_8).replace('\0', ' '); + + messageService.sendMessage(user, receiver.get(), decodedSubject, decodedText); + return responseLogger.result(UploadMessageResponse.success()); + } + + @GDAuthorizedOnly + @PostMapping("/deleteGJMessages20.php") + RemoveMessageResponse delete( + @RequestParam(name = "messageID", defaultValue = "-1", required = false) long messageId, + @RequestParam(name = "messages", defaultValue = "", required = false) String messagesList) { + + var user = UserService.getCurrentUserFromSecurityContextHolder(); + if (messageId == -1 && messagesList.isEmpty()) return responseLogger.result(RemoveMessageResponse.success()); + + try { + if (messageId > 0) { + var message = messageService.getMessageById(messageId); + message.ifPresent(msg -> messageService.deleteMessage(user, msg)); + } + + if (!messagesList.isEmpty()) { + var ids = Arrays.stream(messagesList.split(",")) + .map(num -> { + try { + return Long.parseLong(num); + } catch (NumberFormatException ex) { + return -1L; + } + }) + .filter(num -> num != -1) + .toList(); + + messageService.deleteMessagesById(user, ids); + } + } catch (AccessDeniedException ex) { + return responseLogger.result(RemoveMessageResponse.error()); + } + + return responseLogger.result(RemoveMessageResponse.success()); + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/controller/response/GetMessagesResponse.java b/src/main/java/ru/scarletredman/gd2spring/controller/response/GetMessagesResponse.java new file mode 100644 index 0000000..88b8a3b --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/controller/response/GetMessagesResponse.java @@ -0,0 +1,106 @@ +package ru.scarletredman.gd2spring.controller.response; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.apache.commons.codec.binary.Base64; +import ru.scarletredman.gd2spring.controller.response.json.ResponseSerializer; +import ru.scarletredman.gd2spring.model.Message; +import ru.scarletredman.gd2spring.util.JoinResponseUtil; +import ru.scarletredman.gd2spring.util.TimeFormatUtil; + +@Getter +@Setter +@JsonSerialize(using = ResponseSerializer.class) +public final class GetMessagesResponse implements ResponseSerializer.Response { + + private long messagesCount; + private long offset; + private final List messages = new LinkedList<>(); + private final boolean error; + + private GetMessagesResponse() { + error = true; + } + + public GetMessagesResponse(List messages, boolean sent, long messagesCount, long offset) { + this.messagesCount = messagesCount; + this.offset = offset; + init(messages, sent); + error = false; + } + + public static GetMessagesResponse error() { + return new GetMessagesResponse(); + } + + private void init(List messages, boolean sent) { + this.messages.addAll(messages.stream() + .map(message -> new MessageResponse(message, sent)) + .toList()); + } + + @Override + public String getResponse() { + if (error) return "-1"; + if (messages.isEmpty()) return "-2"; + + var msgList = String.join( + "|", messages.stream().map(MessageResponse::getResponse).toList()); + return msgList + "#" + messagesCount + ":" + (offset * 10) + ":10"; + } + + public static class MessageResponse implements ResponseSerializer.Response { + + private final Map elements = new EnumMap<>(MessageResponse.Key.class); + + public MessageResponse(Message message, boolean isSender) { + init(message, isSender); + } + + private void init(Message message, boolean isSender) { + var target = isSender ? message.getReceiver() : message.getSender(); + var targetId = target.getId(); + + elements.put(MessageResponse.Key.MESSAGE_ID, message.getId()); + elements.put(MessageResponse.Key.TARGET_USER_ID1, targetId); + elements.put(MessageResponse.Key.TARGET_USER_ID2, targetId); + setSubject(message.getSubject()); + elements.put(Key.TARGET_USERNAME, target.getUsername()); + elements.put(Key.UPLOAD_TIME, TimeFormatUtil.formatBetween(message.getTime())); + elements.put(Key.IS_NEW, message.isNew() ? 1 : 0); + elements.put(Key.IS_SENDER, isSender ? 1 : 0); + } + + public void setSubject(String subject) { + elements.put(Key.SUBJECT, Base64.encodeBase64String(subject.getBytes(StandardCharsets.UTF_8))); + } + + @Override + public String getResponse() { + return JoinResponseUtil.join(elements, ":"); + } + + @Getter + @RequiredArgsConstructor + public enum Key implements JoinResponseUtil.Key { + MESSAGE_ID("1"), + TARGET_USER_ID1("2"), + TARGET_USER_ID2("3"), + SUBJECT("4"), + TARGET_USERNAME("6"), + UPLOAD_TIME("7"), + IS_NEW("8"), + IS_SENDER("9"), + ; + + private final String code; + } + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/controller/response/MessageResponse.java b/src/main/java/ru/scarletredman/gd2spring/controller/response/MessageResponse.java new file mode 100644 index 0000000..3e1bbbb --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/controller/response/MessageResponse.java @@ -0,0 +1,82 @@ +package ru.scarletredman.gd2spring.controller.response; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.Map; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.apache.commons.codec.binary.Base64; +import ru.scarletredman.gd2spring.controller.response.json.ResponseSerializer; +import ru.scarletredman.gd2spring.model.Message; +import ru.scarletredman.gd2spring.util.JoinResponseUtil; +import ru.scarletredman.gd2spring.util.TimeFormatUtil; + +@Getter +@Setter +@JsonSerialize(using = ResponseSerializer.class) +public final class MessageResponse implements ResponseSerializer.Response { + + private final Map elements = new EnumMap<>(Key.class); + private final boolean error; + + private MessageResponse() { + error = true; + } + + public MessageResponse(Message message, boolean isSender) { + init(message, isSender); + error = false; + } + + public static MessageResponse error() { + return new MessageResponse(); + } + + private void init(Message message, boolean isSender) { + var target = isSender ? message.getReceiver() : message.getSender(); + var targetId = target.getId(); + + elements.put(Key.MESSAGE_ID, message.getId()); + elements.put(Key.TARGET_USER_ID1, targetId); + elements.put(Key.TARGET_USER_ID2, targetId); + setSubject(message.getSubject()); + setText(message.getText()); + elements.put(Key.TARGET_USERNAME, target.getUsername()); + elements.put(Key.UPLOAD_TIME, TimeFormatUtil.formatBetween(message.getTime())); + elements.put(Key.IS_SENDER, isSender ? 1 : 0); + elements.put(Key.IS_NEW, message.isNew() ? 1 : 0); + } + + public void setSubject(String subject) { + elements.put(Key.SUBJECT, Base64.encodeBase64String(subject.getBytes(StandardCharsets.UTF_8))); + } + + public void setText(String message) { + elements.put(Key.BODY, Base64.encodeBase64String(message.getBytes(StandardCharsets.UTF_8))); + } + + @Override + public String getResponse() { + if (error) return "-1"; + return JoinResponseUtil.join(elements, ":"); + } + + @Getter + @RequiredArgsConstructor + public enum Key implements JoinResponseUtil.Key { + MESSAGE_ID("1"), + TARGET_USER_ID1("2"), + TARGET_USER_ID2("3"), + SUBJECT("4"), + BODY("5"), + TARGET_USERNAME("6"), + UPLOAD_TIME("7"), + IS_NEW("8"), + IS_SENDER("9"), + ; + + private final String code; + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/controller/response/RemoveMessageResponse.java b/src/main/java/ru/scarletredman/gd2spring/controller/response/RemoveMessageResponse.java new file mode 100644 index 0000000..a2bca97 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/controller/response/RemoveMessageResponse.java @@ -0,0 +1,27 @@ +package ru.scarletredman.gd2spring.controller.response; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import ru.scarletredman.gd2spring.controller.response.json.ResponseSerializer; + +@JsonSerialize(using = ResponseSerializer.class) +public final class RemoveMessageResponse implements ResponseSerializer.Response { + + private final boolean error; + + private RemoveMessageResponse(boolean error) { + this.error = error; + } + + public static RemoveMessageResponse success() { + return new RemoveMessageResponse(false); + } + + public static RemoveMessageResponse error() { + return new RemoveMessageResponse(true); + } + + @Override + public String getResponse() { + return error ? "-1" : "1"; + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/controller/response/UploadMessageResponse.java b/src/main/java/ru/scarletredman/gd2spring/controller/response/UploadMessageResponse.java new file mode 100644 index 0000000..1a1382e --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/controller/response/UploadMessageResponse.java @@ -0,0 +1,27 @@ +package ru.scarletredman.gd2spring.controller.response; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import ru.scarletredman.gd2spring.controller.response.json.ResponseSerializer; + +@JsonSerialize(using = ResponseSerializer.class) +public class UploadMessageResponse implements ResponseSerializer.Response { + + private final boolean error; + + private UploadMessageResponse(boolean error) { + this.error = error; + } + + public static UploadMessageResponse success() { + return new UploadMessageResponse(false); + } + + public static UploadMessageResponse error() { + return new UploadMessageResponse(true); + } + + @Override + public String getResponse() { + return error ? "-1" : "1"; + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/model/Message.java b/src/main/java/ru/scarletredman/gd2spring/model/Message.java new file mode 100644 index 0000000..d4e9662 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/model/Message.java @@ -0,0 +1,49 @@ +package ru.scarletredman.gd2spring.model; + +import jakarta.persistence.*; +import java.sql.Timestamp; +import java.time.Instant; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.validator.constraints.Length; + +@Getter +@Setter +@Entity +@Table(name = "messages") +public class Message { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @ManyToOne(optional = false, fetch = FetchType.EAGER) + @JoinColumn(name = "sender", nullable = false) + private User sender; + + @ManyToOne(optional = false, fetch = FetchType.EAGER) + @JoinColumn(name = "receiver", nullable = false) + private User receiver; + + @Length(max = 35) @Column(name = "subject", nullable = false) + private String subject; + + @Length(max = 200) @Column(name = "text", nullable = false) + private String text; + + @Column(name = "time", nullable = false) + private Timestamp time = Timestamp.from(Instant.now()); + + @Column(name = "is_new") + private boolean isNew = true; + + public Message() {} + + public Message(User sender, User receiver, String subject, String text) { + this.sender = sender; + this.receiver = receiver; + this.subject = subject; + this.text = text; + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/rabbit/response/message/MessageSendMQResponse.java b/src/main/java/ru/scarletredman/gd2spring/rabbit/response/message/MessageSendMQResponse.java new file mode 100644 index 0000000..ff4127a --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/rabbit/response/message/MessageSendMQResponse.java @@ -0,0 +1,35 @@ +package ru.scarletredman.gd2spring.rabbit.response.message; + +import java.sql.Timestamp; +import lombok.Getter; +import ru.scarletredman.gd2spring.model.Message; +import ru.scarletredman.gd2spring.rabbit.response.EventMQResponse; + +@Getter +public class MessageSendMQResponse extends EventMQResponse { + + private final long id; + private final long senderUserId; + private final String senderUsername; + private final long receiverUserId; + private final String receiverUsername; + private final String subject; + private final String body; + private final Timestamp timestamp; + + public MessageSendMQResponse(Message message) { + super("message.send"); + id = message.getId(); + subject = message.getSubject(); + body = message.getText(); + timestamp = message.getTime(); + + var sender = message.getSender(); + senderUserId = sender.getId(); + senderUsername = sender.getUsername(); + + var receiver = message.getReceiver(); + receiverUserId = receiver.getId(); + receiverUsername = receiver.getUsername(); + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/repository/MessageRepository.java b/src/main/java/ru/scarletredman/gd2spring/repository/MessageRepository.java new file mode 100644 index 0000000..28046f8 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/repository/MessageRepository.java @@ -0,0 +1,20 @@ +package ru.scarletredman.gd2spring.repository; + +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.scarletredman.gd2spring.model.Message; +import ru.scarletredman.gd2spring.model.User; + +@Repository +public interface MessageRepository extends JpaRepository { + + List findBySenderOrderByIdDesc(User sender, Pageable pageable); + + List findByReceiverOrderByIdDesc(User receiver, Pageable pageable); + + long countBySender(User sender); + + long countByReceiver(User receiver); +} diff --git a/src/main/java/ru/scarletredman/gd2spring/repository/impl/CustomLevelRepositoryImpl.java b/src/main/java/ru/scarletredman/gd2spring/repository/impl/CustomLevelRepositoryImpl.java index 4399e5d..9229b8f 100644 --- a/src/main/java/ru/scarletredman/gd2spring/repository/impl/CustomLevelRepositoryImpl.java +++ b/src/main/java/ru/scarletredman/gd2spring/repository/impl/CustomLevelRepositoryImpl.java @@ -78,8 +78,9 @@ private Predicate[] applyFilters( @Nullable String levelName, LevelListPage.Filters filters) { var criteriaFilters = new ArrayList(); + criteriaFilters.add(criteria.isFalse(rootLevel.get("unlisted"))); if (levelName != null) { - criteriaFilters.add(criteria.like(rootLevel.get("name"), levelName + "%")); + criteriaFilters.add(criteria.like(criteria.lower(rootLevel.get("name")), levelName.toLowerCase() + "%")); } // todo: implement filters // todo: fix filters diff --git a/src/main/java/ru/scarletredman/gd2spring/service/MessageService.java b/src/main/java/ru/scarletredman/gd2spring/service/MessageService.java new file mode 100644 index 0000000..f3b4036 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/service/MessageService.java @@ -0,0 +1,23 @@ +package ru.scarletredman.gd2spring.service; + +import java.util.Collection; +import java.util.Optional; +import org.springframework.security.access.AccessDeniedException; +import ru.scarletredman.gd2spring.model.Message; +import ru.scarletredman.gd2spring.model.User; +import ru.scarletredman.gd2spring.service.type.MessageListPage; + +public interface MessageService { + + Optional getMessageById(long id); + + MessageListPage getMessages(User user, boolean sent, int page, int limit); + + Message sendMessage(User sender, User receiver, String subject, String text); + + void deleteMessage(User user, Message message) throws AccessDeniedException; + + void deleteMessagesById(User user, Collection messageIds) throws AccessDeniedException; + + void deleteMessages(User user, Collection messages) throws AccessDeniedException; +} diff --git a/src/main/java/ru/scarletredman/gd2spring/service/exception/MessageError.java b/src/main/java/ru/scarletredman/gd2spring/service/exception/MessageError.java new file mode 100644 index 0000000..b8d85d1 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/service/exception/MessageError.java @@ -0,0 +1,6 @@ +package ru.scarletredman.gd2spring.service.exception; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class MessageError extends Error {} diff --git a/src/main/java/ru/scarletredman/gd2spring/service/impl/MessageServiceImpl.java b/src/main/java/ru/scarletredman/gd2spring/service/impl/MessageServiceImpl.java new file mode 100644 index 0000000..1f90eeb --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/service/impl/MessageServiceImpl.java @@ -0,0 +1,90 @@ +package ru.scarletredman.gd2spring.service.impl; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import ru.scarletredman.gd2spring.model.Message; +import ru.scarletredman.gd2spring.model.User; +import ru.scarletredman.gd2spring.rabbit.MQEventPublisher; +import ru.scarletredman.gd2spring.rabbit.response.message.MessageSendMQResponse; +import ru.scarletredman.gd2spring.repository.MessageRepository; +import ru.scarletredman.gd2spring.service.MessageService; +import ru.scarletredman.gd2spring.service.type.MessageListPage; + +@RequiredArgsConstructor +@Service +public class MessageServiceImpl implements MessageService { + + private final MessageRepository repository; + private final MQEventPublisher eventPublisher; + + @Transactional(readOnly = true, isolation = Isolation.READ_COMMITTED) + @Override + public Optional getMessageById(long id) { + return repository.findById(id); + } + + @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ) + @Override + public MessageListPage getMessages(User user, boolean sent, int page, int limit) { + List messages; + long total; + + if (sent) { + messages = repository.findBySenderOrderByIdDesc(user, PageRequest.of(page, limit)); + total = repository.countBySender(user); + } else { + messages = repository.findByReceiverOrderByIdDesc(user, PageRequest.of(page, limit)); + total = repository.countByReceiver(user); + } + + return new MessageListPage(messages, page, total); + } + + @Transactional + @Override + public Message sendMessage(User sender, User receiver, String subject, String text) { + var message = repository.save(new Message(sender, receiver, subject, text)); + eventPublisher.publish(new MessageSendMQResponse(message)); + return message; + } + + @Transactional(rollbackFor = AccessDeniedException.class) + @Override + public void deleteMessage(User user, Message message) throws AccessDeniedException { + if (!Objects.equals(message.getReceiver().getId(), user.getId()) + && !Objects.equals(message.getSender().getId(), user.getId())) { + throw new AccessDeniedException("User is not sender or receiver"); + } + + repository.delete(message); + } + + @Transactional(rollbackFor = AccessDeniedException.class) + @Override + public void deleteMessagesById(User user, Collection messageIds) throws AccessDeniedException { + deleteMessages(user, repository.findAllById(messageIds)); + } + + @Transactional(rollbackFor = AccessDeniedException.class) + @Override + public void deleteMessages(User user, Collection messages) throws AccessDeniedException { + var newList = messages.stream() + .peek(message -> { + if (!Objects.equals(message.getReceiver().getId(), user.getId()) + && !Objects.equals(message.getSender().getId(), user.getId())) { + throw new AccessDeniedException("User is not sender or receiver"); + } + }) + .toList(); + + repository.deleteAll(newList); + } +} diff --git a/src/main/java/ru/scarletredman/gd2spring/service/type/MessageListPage.java b/src/main/java/ru/scarletredman/gd2spring/service/type/MessageListPage.java new file mode 100644 index 0000000..a90c3b7 --- /dev/null +++ b/src/main/java/ru/scarletredman/gd2spring/service/type/MessageListPage.java @@ -0,0 +1,6 @@ +package ru.scarletredman.gd2spring.service.type; + +import java.util.List; +import ru.scarletredman.gd2spring.model.Message; + +public record MessageListPage(List messages, int page, long total) {}