diff --git a/src/main/java/com/youlai/boot/common/enums/MessageTypeEnum.java b/src/main/java/com/youlai/boot/common/enums/MessageTypeEnum.java new file mode 100644 index 00000000..d642f757 --- /dev/null +++ b/src/main/java/com/youlai/boot/common/enums/MessageTypeEnum.java @@ -0,0 +1,19 @@ +package com.youlai.boot.common.enums; + +/** + * 消息类型枚举 + * @author Theo + * @since 2024-9-2 14:32:58 + */ +public enum MessageTypeEnum { + WEBSOCKET("webScoket", "websocket消息"); + + private String value; + + private String label; + + MessageTypeEnum(String value, String label) { + this.value = value; + this.label = label; + } +} diff --git a/src/main/java/com/youlai/boot/common/util/CommonUtil.java b/src/main/java/com/youlai/boot/common/util/CommonUtil.java index 055da5ae..f039ed48 100644 --- a/src/main/java/com/youlai/boot/common/util/CommonUtil.java +++ b/src/main/java/com/youlai/boot/common/util/CommonUtil.java @@ -60,4 +60,8 @@ public class CommonUtil { return List.of(str.split(separator)); } + + public static String delHtmlTags(String htmlStr) { + return htmlStr.replaceAll("<[^>]+>", ""); + } } diff --git a/src/main/java/com/youlai/boot/platform/websocket/service/MessageService.java b/src/main/java/com/youlai/boot/platform/websocket/service/MessageService.java new file mode 100644 index 00000000..0f6fcc14 --- /dev/null +++ b/src/main/java/com/youlai/boot/platform/websocket/service/MessageService.java @@ -0,0 +1,29 @@ +package com.youlai.boot.platform.websocket.service; + +import com.youlai.boot.common.enums.MessageTypeEnum; +import com.youlai.boot.system.model.dto.MessageDTO; + +/** + * 消息服务接口 + * + * @author Theo + * @since 2024-9-2 14:32:58 + */ +public interface MessageService { + + + /** + * 检查消息类型 + * + * @param messageType 消息类型 + * @return 是否支持 + */ + boolean check(MessageTypeEnum messageType); + + /** + * 发送消息 + * + * @param message 消息 + */ + void sendMessage(MessageDTO message); +} diff --git a/src/main/java/com/youlai/boot/platform/websocket/service/WebsocketService.java b/src/main/java/com/youlai/boot/platform/websocket/service/WebsocketService.java deleted file mode 100644 index 4e23d583..00000000 --- a/src/main/java/com/youlai/boot/platform/websocket/service/WebsocketService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.youlai.boot.platform.websocket.service; - -import java.util.Set; - -public interface WebsocketService { - - void addUser(String username); - - void removeUser(String username) ; - - Set getUsers(); - /** - * 发送消息到前端 - * @param message - */ - void sendStringToFrontend(String sender,String message); -} diff --git a/src/main/java/com/youlai/boot/platform/websocket/service/impl/WebsocketServiceImpl.java b/src/main/java/com/youlai/boot/platform/websocket/service/impl/WebsocketServiceImpl.java index 5ab04a31..1ed4a900 100644 --- a/src/main/java/com/youlai/boot/platform/websocket/service/impl/WebsocketServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/websocket/service/impl/WebsocketServiceImpl.java @@ -1,42 +1,47 @@ package com.youlai.boot.platform.websocket.service.impl; -import com.youlai.boot.platform.websocket.service.WebsocketService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.youlai.boot.common.enums.MessageTypeEnum; +import com.youlai.boot.platform.websocket.service.MessageService; import com.youlai.boot.system.event.UserConnectionEvent; import com.youlai.boot.system.model.dto.ChatMessage; +import com.youlai.boot.system.model.dto.MessageDTO; +import com.youlai.boot.system.model.entity.User; +import com.youlai.boot.system.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.security.SecurityUtil; import org.springframework.context.event.EventListener; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +/** + * WebSocket消息服务实现类 + * + * @author ray + * @since 2024-9-2 14:32:58 + */ @Service @Slf4j @RequiredArgsConstructor -public class WebsocketServiceImpl implements WebsocketService { +public class WebsocketServiceImpl implements MessageService { private final SimpMessagingTemplate messagingTemplate; private final Set onlineUsers = ConcurrentHashMap.newKeySet(); - @Override - public void addUser(String username) { - onlineUsers.add(username); - } - - @Override - public void removeUser(String username) { - onlineUsers.remove(username); - } - - @Override - public Set getUsers() { - return onlineUsers; - } - + /** + * 用户连接事件处理 + * + * @param event 用户连接事件 + */ @EventListener public void handleUserConnectionEvent(UserConnectionEvent event) { String username = event.getUsername(); @@ -51,16 +56,44 @@ public class WebsocketServiceImpl implements WebsocketService { messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUsers.size()); } + /** + * 定时推送在线用户人数 + */ @Scheduled(fixedRate = 5000) public void sendOnlineUserCount() { messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUsers.size()); } + + /** + * 策略模式检查 + * + * @param messageType 消息类型 + * @return boolean + */ @Override - public void sendStringToFrontend(String sender, String message) { - ChatMessage chatMessage = new ChatMessage(sender, message); - onlineUsers.forEach(receiver -> { - messagingTemplate.convertAndSendToUser(receiver, "/topic/chat", chatMessage); + public boolean check(MessageTypeEnum messageType) { + return messageType.equals(MessageTypeEnum.WEBSOCKET); + } + + /** + * 发送消息 + * + * @param message 消息 + */ + @Override + public void sendMessage(MessageDTO message) { + List users = null; + if(message.getReceiver() == null || message.getReceiver().isEmpty()){ + // 发送给所有在线用户 离线用户不发送,因为离线用户下次登录会直接查询未读消息 + users = new ArrayList<>(onlineUsers); + }else{ + users = message.getReceiver().stream().filter(onlineUsers::contains).collect(Collectors.toList()); + } + //获取当前用户 + ChatMessage chatMessage = new ChatMessage(message.getSender(), message.getContent()); + users.forEach(receiver -> { + messagingTemplate.convertAndSendToUser(receiver, "/queue/message", chatMessage); }); } } diff --git a/src/main/java/com/youlai/boot/system/handler/MessageHandler.java b/src/main/java/com/youlai/boot/system/handler/MessageHandler.java new file mode 100644 index 00000000..e66ca7db --- /dev/null +++ b/src/main/java/com/youlai/boot/system/handler/MessageHandler.java @@ -0,0 +1,35 @@ +package com.youlai.boot.system.handler; + +import com.youlai.boot.platform.websocket.service.MessageService; +import com.youlai.boot.system.model.dto.MessageDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 消息处理器 + * + * @author Theo + * @since 2024-9-2 14:32:58 + */ +@Component +@RequiredArgsConstructor +public class MessageHandler { + + private final List messageServices; + + + /** + * 发送消息 + * + * @param messageDTO 消息载体 + */ + public void sendMessage(MessageDTO messageDTO) { + messageServices.forEach(messageService -> { + if (messageService.check(messageDTO.getMessageType())) { + messageService.sendMessage(messageDTO); + } + }); + } +} diff --git a/src/main/java/com/youlai/boot/system/model/dto/MessageDTO.java b/src/main/java/com/youlai/boot/system/model/dto/MessageDTO.java new file mode 100644 index 00000000..4f7e7f7e --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/dto/MessageDTO.java @@ -0,0 +1,30 @@ +package com.youlai.boot.system.model.dto; + +import com.youlai.boot.common.enums.MessageTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 消息载体 + * + * @author Theo + * @since 2024-9-2 14:32:58 + * @version 1.0.0 + */ +@Data +public class MessageDTO { + + @Schema(description = "消息内容") + private String content; + + @Schema(description = "发送者") + private String sender; + + @Schema(description = "接收者") + private List receiver; + + @Schema(description = "消息类型") + private MessageTypeEnum messageType; +} diff --git a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java index f13e1f33..120f1fac 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java @@ -2,17 +2,20 @@ package com.youlai.boot.system.service.impl; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.google.gson.*; import com.youlai.boot.common.constant.SymbolConstant; +import com.youlai.boot.common.enums.MessageTypeEnum; +import com.youlai.boot.common.util.CommonUtil; import com.youlai.boot.core.security.util.SecurityUtils; -import com.youlai.boot.platform.websocket.service.WebsocketService; import com.youlai.boot.system.converter.NoticeConverter; +import com.youlai.boot.system.handler.MessageHandler; import com.youlai.boot.system.mapper.NoticeMapper; import com.youlai.boot.system.model.bo.NoticeBO; +import com.youlai.boot.system.model.dto.MessageDTO; import com.youlai.boot.system.model.entity.Notice; import com.youlai.boot.system.model.entity.NoticeStatus; import com.youlai.boot.system.model.entity.User; @@ -30,6 +33,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; /** * 通知公告服务实现类 @@ -43,24 +47,12 @@ public class NoticeServiceImpl extends ServiceImpl impleme private final NoticeConverter noticeConverter; - private final WebsocketService websocketService; + private final MessageHandler messageHandler; private final NoticeStatusService noticeStatusService; private final UserService userService; - private final Gson gson = new GsonBuilder() - .registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (localDateTime, type, jsonSerializationContext) -> - new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))) - .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> - LocalDateTime.parse(jsonElement.getAsString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME)) - .create(); - - private void sendWebSocketMsg(Notice notice) { - String jsonNotice = gson.toJson(noticeConverter.toVO(notice)); - websocketService.sendStringToFrontend(SecurityUtils.getUsername(), jsonNotice); - } - /** * 获取通知公告分页列表 * @@ -181,10 +173,37 @@ public class NoticeServiceImpl extends ServiceImpl impleme noticeStatusService.saveBatch(needSaveList); } //最后,给当前在线的用户发送websocket消息 - //TODO: 通知公告的websocket消息发送 + List usernameList = null; + if(notice.getTarType() == 1){ + List collect = needSaveList.stream().map(NoticeStatus::getUserId).collect(Collectors.toList()); + List userList = userService.list(new LambdaQueryWrapper().in(User::getId, collect).select(User::getUsername)); + usernameList = userList.stream().map(User::getUsername).collect(Collectors.toList()); + } + MessageDTO message = new MessageDTO(); + message.setMessageType(MessageTypeEnum.WEBSOCKET); + message.setReceiver(usernameList); + message.setContent(getNoticeContent(notice)); + message.setSender(SecurityUtils.getUsername()); + messageHandler.sendMessage(message); return this.updateById(notice); } + /** + * 自定义组合公告内容 + * + * @param notice 通知公告 + * @return 自定义组合通知公告内容 + */ + private String getNoticeContent(Notice notice) { + JSONObject jsonObject = new JSONObject(); + jsonObject.set("id", notice.getId()); + jsonObject.set("title", notice.getTitle()); + jsonObject.set("messageType", notice.getNoticeType()); + jsonObject.set("releaseTime", notice.getReleaseTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + jsonObject.set("type", "release"); + return jsonObject.toString(); + } + /** * 撤回通知公告 *