feat: 通知公告临时提交

通知公告临时提交
This commit is contained in:
胡少翔
2024-09-02 16:23:48 +08:00
parent 2ec2eb91b5
commit b649eacba5
8 changed files with 206 additions and 54 deletions

View File

@@ -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;
}
}

View File

@@ -60,4 +60,8 @@ public class CommonUtil {
return List.of(str.split(separator));
}
public static String delHtmlTags(String htmlStr) {
return htmlStr.replaceAll("<[^>]+>", "");
}
}

View File

@@ -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);
}

View File

@@ -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<String> getUsers();
/**
* 发送消息到前端
* @param message
*/
void sendStringToFrontend(String sender,String message);
}

View File

@@ -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<String> onlineUsers = ConcurrentHashMap.newKeySet();
@Override
public void addUser(String username) {
onlineUsers.add(username);
}
@Override
public void removeUser(String username) {
onlineUsers.remove(username);
}
@Override
public Set<String> 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<String> 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);
});
}
}

View File

@@ -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<MessageService> messageServices;
/**
* 发送消息
*
* @param messageDTO 消息载体
*/
public void sendMessage(MessageDTO messageDTO) {
messageServices.forEach(messageService -> {
if (messageService.check(messageDTO.getMessageType())) {
messageService.sendMessage(messageDTO);
}
});
}
}

View File

@@ -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<String> receiver;
@Schema(description = "消息类型")
private MessageTypeEnum messageType;
}

View File

@@ -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<NoticeMapper, Notice> 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>) (localDateTime, type, jsonSerializationContext) ->
new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)))
.registerTypeAdapter(LocalDateTime.class, (JsonDeserializer<LocalDateTime>) (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<NoticeMapper, Notice> impleme
noticeStatusService.saveBatch(needSaveList);
}
//最后给当前在线的用户发送websocket消息
//TODO: 通知公告的websocket消息发送
List<String> usernameList = null;
if(notice.getTarType() == 1){
List<Long> collect = needSaveList.stream().map(NoticeStatus::getUserId).collect(Collectors.toList());
List<User> userList = userService.list(new LambdaQueryWrapper<User>().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();
}
/**
* 撤回通知公告
*