From ffb291100f084e789080df0c0fea55606797a8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BC=93=E5=8A=B1=E5=B8=88?= Date: Thu, 26 Oct 2023 16:03:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E5=B7=A5=E5=85=B7=E9=9D=A2=E6=9D=BF=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=B9=E9=87=8F=E8=89=BE=E7=89=B9=E3=80=81?= =?UTF-8?q?=E7=A7=81=E8=81=8A=E3=80=81=E5=B1=8F=E8=94=BD=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E3=80=81=E5=9C=A8=E7=BA=BF=E7=BB=9F=E8=AE=A1=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/entity/StatisticsMsgDTO.java | 25 +++ .../xeblog/commons/entity/SystemStateDTO.java | 30 ++++ .../cn/xeblog/commons/entity/UserMsgDTO.java | 1 + .../java/cn/xeblog/commons/enums/Action.java | 6 +- .../cn/xeblog/commons/enums/CountEnum.java | 37 +++++ .../xeblog/plugin/action/ConsoleAction.java | 7 + .../cn/xeblog/plugin/action/InputAction.java | 97 ++++++++---- .../handler/message/SystemMessageHandler.java | 22 ++- .../handler/message/UserMessageHandler.java | 9 ++ .../cn/xeblog/plugin/cache/DataCache.java | 8 +- .../java/cn/xeblog/plugin/tools/Tools.java | 3 +- .../plugin/tools/encourage/Encourage.java | 103 ++++++++++++ .../tools/encourage/cache/EncourageCache.java | 54 +++++++ .../plugin/tools/encourage/ui/AbstractUI.java | 133 ++++++++++++++++ .../plugin/tools/encourage/ui/AtUsersUI.java | 116 ++++++++++++++ .../tools/encourage/ui/BlockMessageUI.java | 108 +++++++++++++ .../tools/encourage/ui/StatisticsUI.java | 124 +++++++++++++++ .../java/cn/xeblog/server/XEChatServer.java | 4 + .../action/handler/ChatActionHandler.java | 25 ++- .../handler/StatisticsActionHandler.java | 96 +++++++++++ .../action/handler/WeatherActionHandler.java | 5 +- .../server/builder/ResponseBuilder.java | 3 +- .../server/util/CountOnlineUserUtil.java | 149 ++++++++++++++++++ 23 files changed, 1116 insertions(+), 49 deletions(-) create mode 100644 xechat-commons/src/main/java/cn/xeblog/commons/entity/StatisticsMsgDTO.java create mode 100644 xechat-commons/src/main/java/cn/xeblog/commons/entity/SystemStateDTO.java create mode 100644 xechat-commons/src/main/java/cn/xeblog/commons/enums/CountEnum.java create mode 100644 xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/Encourage.java create mode 100644 xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/cache/EncourageCache.java create mode 100644 xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AbstractUI.java create mode 100644 xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AtUsersUI.java create mode 100644 xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/BlockMessageUI.java create mode 100644 xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/StatisticsUI.java create mode 100644 xechat-server/src/main/java/cn/xeblog/server/action/handler/StatisticsActionHandler.java create mode 100644 xechat-server/src/main/java/cn/xeblog/server/util/CountOnlineUserUtil.java diff --git a/xechat-commons/src/main/java/cn/xeblog/commons/entity/StatisticsMsgDTO.java b/xechat-commons/src/main/java/cn/xeblog/commons/entity/StatisticsMsgDTO.java new file mode 100644 index 00000000..553f9e46 --- /dev/null +++ b/xechat-commons/src/main/java/cn/xeblog/commons/entity/StatisticsMsgDTO.java @@ -0,0 +1,25 @@ +package cn.xeblog.commons.entity; + +import cn.xeblog.commons.enums.CountEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author 鼓励师 + * @date 2023/11/8 17:24 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class StatisticsMsgDTO implements Serializable { + + private CountEnum countEnum; + + private boolean toAll; + private boolean showDetail; + private int showSize; + +} diff --git a/xechat-commons/src/main/java/cn/xeblog/commons/entity/SystemStateDTO.java b/xechat-commons/src/main/java/cn/xeblog/commons/entity/SystemStateDTO.java new file mode 100644 index 00000000..23c598b7 --- /dev/null +++ b/xechat-commons/src/main/java/cn/xeblog/commons/entity/SystemStateDTO.java @@ -0,0 +1,30 @@ +package cn.xeblog.commons.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 系统状态 + * + * @author 鼓励师 + * @date 2023/11/1 10:56 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SystemStateDTO implements Serializable { + + private String message; + /** + * 下面两个参数可以做成配置 如果有必要 + */ + private Boolean supportStatistics = true; + private Boolean privateChat = true; + + public SystemStateDTO(String message) { + this.message = message; + } +} diff --git a/xechat-commons/src/main/java/cn/xeblog/commons/entity/UserMsgDTO.java b/xechat-commons/src/main/java/cn/xeblog/commons/entity/UserMsgDTO.java index 923d4c65..e69419dd 100644 --- a/xechat-commons/src/main/java/cn/xeblog/commons/entity/UserMsgDTO.java +++ b/xechat-commons/src/main/java/cn/xeblog/commons/entity/UserMsgDTO.java @@ -39,6 +39,7 @@ public UserMsgDTO(Object content, MsgType msgType, String[] toUsers) { public enum MsgType { TEXT, + PRIVATE, IMAGE } diff --git a/xechat-commons/src/main/java/cn/xeblog/commons/enums/Action.java b/xechat-commons/src/main/java/cn/xeblog/commons/enums/Action.java index fce0f4eb..7d7e5157 100644 --- a/xechat-commons/src/main/java/cn/xeblog/commons/enums/Action.java +++ b/xechat-commons/src/main/java/cn/xeblog/commons/enums/Action.java @@ -59,5 +59,9 @@ public enum Action { /** * react */ - REACT; + REACT, + /** + * 统计在线用户 + */ + STATISTICS; } diff --git a/xechat-commons/src/main/java/cn/xeblog/commons/enums/CountEnum.java b/xechat-commons/src/main/java/cn/xeblog/commons/enums/CountEnum.java new file mode 100644 index 00000000..8abc012c --- /dev/null +++ b/xechat-commons/src/main/java/cn/xeblog/commons/enums/CountEnum.java @@ -0,0 +1,37 @@ +package cn.xeblog.commons.enums; + +import lombok.Getter; + +/** + * @author 鼓励师 + * @date 2023/8/30 11:01 + */ +@Getter +public enum CountEnum { + + COUNTRY("country", "国家"), + PROVINCE("province", "省份"), + CITY("city", "城市"), + ISP("isp", "运营商"), + USER_STATUS("status", "用户状态"), + ; + + CountEnum(String countType, String desc) { + this.countType = countType; + this.desc = desc; + } + + private final String countType; + private final String desc; + + public static CountEnum getByIndex(int index) { + for (int i = 0; i < values().length; i++) { + if (index == i) { + return values()[i]; + } + } + return null; + } + +} + diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/ConsoleAction.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/ConsoleAction.java index 9ba02383..cc36c504 100644 --- a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/ConsoleAction.java +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/ConsoleAction.java @@ -17,6 +17,8 @@ import java.awt.datatransfer.StringSelection; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -219,6 +221,11 @@ private static void bindPopupMenu() { })); } + public static void showSystemMsg(String msg) { + String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("MM/dd HH:mm")); + showSystemMsg(time, msg); + } + public static void showSystemMsg(String time, String msg) { ConsoleAction.renderText(String.format("[%s] 系统消息:%s\n", time, msg), Style.SYSTEM_MSG); } diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/InputAction.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/InputAction.java index 4e41e15e..a9131db9 100644 --- a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/InputAction.java +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/InputAction.java @@ -11,6 +11,7 @@ import cn.xeblog.plugin.cache.DataCache; import cn.xeblog.plugin.enums.Command; import cn.xeblog.plugin.listener.MainWindowInitializedEventListener; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; import cn.xeblog.plugin.ui.MainWindow; import cn.xeblog.plugin.util.CommandHistoryUtils; import cn.xeblog.plugin.util.UploadUtils; @@ -28,8 +29,9 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; -import java.util.*; import java.util.List; +import java.util.*; +import java.util.stream.Collectors; /** * @author anlingyi @@ -300,46 +302,25 @@ private static void sendMsg() { Command.handle(content); } else { if (DataCache.isOnline) { - if (sendCounter == 0 && System.currentTimeMillis() - sendCounterStartTime < INTERVAL_TIME) { - sendCounterStartTime = 0; - freezeEndTime = System.currentTimeMillis() + FREEZE_TIME; - } - long endTime = freezeEndTime - System.currentTimeMillis(); - if (endTime > 0) { - ConsoleAction.showSimpleMsg("消息发送过于频繁,请于" + endTime / 1000 + "s后再发..."); + if (checkFreeze()) { return; } - String[] toUsers = null; - List toUserList = ReUtil.findAll("(@)([^\\s]+)([\\s]*)", content, 2); - if (CollectionUtil.isNotEmpty(toUserList)) { - List removeList = new ArrayList<>(); - for (String toUser : toUserList) { - if (DataCache.getUser(toUser) == null) { - removeList.add(toUser); - } - } - if (!removeList.isEmpty()) { - toUserList.removeAll(removeList); - } - if (!toUserList.isEmpty()) { - toUserList.add(DataCache.username); - toUsers = ArrayUtil.toArray(new HashSet<>(toUserList), String.class); - } - } - - if (sendCounter == -1) { - sendCounter = 0; - } - if (++sendCounter >= 6) { - sendCounter = 0; - } - if (sendCounter == 1) { - sendCounterStartTime = System.currentTimeMillis(); + UserMsgDTO.MsgType msgType = UserMsgDTO.MsgType.TEXT; + String[] toUsers; + if (EncourageCache.supportPrivateChat && EncourageCache.privateChatUser != null) { + toUsers = new String[]{EncourageCache.privateChatUser.getUsername()}; + msgType = UserMsgDTO.MsgType.PRIVATE; + } else if (!EncourageCache.atUsers.isEmpty()) { + // 艾特勾选的用户 + toUsers = getToUsers(content, EncourageCache.atUsers.stream().map(User::getUsername).collect(Collectors.toList())); + content += " @批量艾特[" + EncourageCache.atUsers.size() + "]人"; + } else { + toUsers = getToUsers(content, Collections.emptyList()); } - MessageAction.send(new UserMsgDTO(content, toUsers), Action.CHAT); + MessageAction.send(new UserMsgDTO(content, msgType, toUsers), Action.CHAT); } else { ConsoleAction.showLoginMsg(); } @@ -350,6 +331,52 @@ private static void sendMsg() { ConsoleAction.gotoConsoleLow(); } + private static boolean checkFreeze() { + if (sendCounter == 0 && System.currentTimeMillis() - sendCounterStartTime < INTERVAL_TIME) { + sendCounterStartTime = 0; + freezeEndTime = System.currentTimeMillis() + FREEZE_TIME; + } + + long endTime = freezeEndTime - System.currentTimeMillis(); + if (endTime > 0) { + ConsoleAction.showSimpleMsg("消息发送过于频繁,请于" + endTime / 1000 + "s后再发..."); + return true; + } + + if (sendCounter == -1) { + sendCounter = 0; + } + if (++sendCounter >= 6) { + sendCounter = 0; + } + if (sendCounter == 1) { + sendCounterStartTime = System.currentTimeMillis(); + } + return false; + } + + public static String[] getToUsers(String content, List extraList) { + String[] toUsers = null; + List toUserList = new ArrayList<>(ReUtil.findAll("(@)([^\\s]+)([\\s]*)", content, 2)); + toUserList.addAll(extraList); + if (CollectionUtil.isNotEmpty(toUserList)) { + List removeList = new ArrayList<>(); + for (String toUser : toUserList) { + if (DataCache.getUser(toUser) == null) { + removeList.add(toUser); + } + } + if (!removeList.isEmpty()) { + toUserList.removeAll(removeList); + } + if (!toUserList.isEmpty()) { + toUserList.add(DataCache.username); + toUsers = ArrayUtil.toArray(new HashSet<>(toUserList), String.class); + } + } + return toUsers; + } + public static void clean() { contentArea.setText(""); } diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/SystemMessageHandler.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/SystemMessageHandler.java index 2f3071c1..42aa115e 100644 --- a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/SystemMessageHandler.java +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/SystemMessageHandler.java @@ -1,20 +1,34 @@ package cn.xeblog.plugin.action.handler.message; +import cn.hutool.core.util.StrUtil; +import cn.xeblog.commons.entity.Response; +import cn.xeblog.commons.entity.SystemStateDTO; import cn.xeblog.commons.enums.MessageType; import cn.xeblog.plugin.action.ConsoleAction; -import cn.xeblog.commons.entity.Response; import cn.xeblog.plugin.annotation.DoMessage; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; /** * @author anlingyi * @date 2020/8/19 */ @DoMessage(MessageType.SYSTEM) -public class SystemMessageHandler extends AbstractMessageHandler { +public class SystemMessageHandler extends AbstractMessageHandler { @Override - protected void process(Response response) { - ConsoleAction.showSystemMsg(response.getTime(), response.getBody()); + protected void process(Response response) { + Object responseBody = response.getBody(); + if (responseBody instanceof String) { + ConsoleAction.showSystemMsg(response.getTime(), String.valueOf(responseBody)); + } else if (responseBody instanceof SystemStateDTO) { + SystemStateDTO systemStateDTO = (SystemStateDTO) responseBody; + String message = systemStateDTO.getMessage(); + if (StrUtil.isNotEmpty(message)) { + ConsoleAction.showSystemMsg(response.getTime(), message); + } + EncourageCache.supportStatistics = systemStateDTO.getSupportStatistics(); + EncourageCache.supportPrivateChat = systemStateDTO.getPrivateChat(); + } } } diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/UserMessageHandler.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/UserMessageHandler.java index d4544d5a..c41cef44 100644 --- a/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/UserMessageHandler.java +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/action/handler/message/UserMessageHandler.java @@ -16,6 +16,7 @@ import cn.xeblog.plugin.annotation.DoMessage; import cn.xeblog.plugin.cache.DataCache; import cn.xeblog.plugin.enums.Style; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; import cn.xeblog.plugin.util.NotifyUtils; import com.intellij.ide.actions.OpenFileAction; import com.intellij.openapi.application.ApplicationManager; @@ -43,6 +44,14 @@ protected void process(Response response) { User user = response.getUser(); UserMsgDTO body = response.getBody(); boolean isImage = body.getMsgType() == UserMsgDTO.MsgType.IMAGE; + + if (EncourageCache.checkBlock(user)) { + if (EncourageCache.showTips) { + ConsoleAction.showSystemMsg(response.getTime(), String.format("用户[%s]发了[%s]", user.getUsername(), isImage ? "一张图片" : "一条消息")); + } + return; + } + if (isImage) { renderImage(response); } else { diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/cache/DataCache.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/cache/DataCache.java index 5e43de9b..e6990317 100644 --- a/xechat-plugin/src/main/java/cn/xeblog/plugin/cache/DataCache.java +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/cache/DataCache.java @@ -6,6 +6,7 @@ import cn.xeblog.commons.enums.UserStatus; import cn.xeblog.plugin.action.ConnectionAction; import cn.xeblog.plugin.tools.browser.config.BrowserConfig; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; import cn.xeblog.plugin.tools.read.ReadConfig; import com.intellij.openapi.project.Project; import io.netty.channel.Channel; @@ -76,7 +77,6 @@ public class DataCache { public static List serverList; /** - * 阅读配置 */ public static ReadConfig readConfig = new ReadConfig(); @@ -94,7 +94,7 @@ public class DataCache { */ public static User getUser(String username) { if (userMap == null) { - return null; + return null; } return userMap.get(username); @@ -138,6 +138,10 @@ public static void removeUser(User user) { if (StrUtil.equals(origin.getId(), user.getId())) { userMap.remove(user.getUsername()); } + + if (EncourageCache.privateChatUser != null && StrUtil.equals(origin.getId(), user.getId())) { + EncourageCache.privateChatUser = null; + } } public static int getOnlineUserTotal() { diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/Tools.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/Tools.java index b888d88b..d1faea47 100644 --- a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/Tools.java +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/Tools.java @@ -11,7 +11,8 @@ @AllArgsConstructor public enum Tools { READ("阅读", false), - BROWSER("浏览器", false); + BROWSER("浏览器", false), + ENCOURAGE("附加工具", true); /** * 工具名称 diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/Encourage.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/Encourage.java new file mode 100644 index 00000000..f0a676eb --- /dev/null +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/Encourage.java @@ -0,0 +1,103 @@ +package cn.xeblog.plugin.tools.encourage; + +import cn.xeblog.plugin.annotation.DoTool; +import cn.xeblog.plugin.cache.DataCache; +import cn.xeblog.plugin.tools.AbstractTool; +import cn.xeblog.plugin.tools.Tools; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; +import cn.xeblog.plugin.tools.encourage.ui.AtUsersUI; +import cn.xeblog.plugin.tools.encourage.ui.BlockMessageUI; +import cn.xeblog.plugin.tools.encourage.ui.StatisticsUI; + +import javax.swing.*; +import java.awt.*; + +/** + * 附加工具操作面板 + * + * @author 鼓励师 + * @date 2023/10/20 11:38 + */ +@DoTool(Tools.ENCOURAGE) +public class Encourage extends AbstractTool { + + // 设置按钮统一属性 + private final Dimension buttonDimension = new Dimension(100, 30); + private final Dimension mainDimension = new Dimension(310, 250); + + private JPanel mainPanel; + + @Override + protected void init() { + initPanel(); + + JLabel title = new JLabel("附加工具面板"); + title.setFont(new Font("", Font.BOLD, 14)); + + Box vBox = Box.createVerticalBox(); + vBox.add(Box.createVerticalStrut(20)); + vBox.add(title); + vBox.add(Box.createVerticalStrut(20)); + vBox.add(newJButton("消息屏蔽", new BlockMessageUI(getMenuJButton()))); + vBox.add(Box.createVerticalStrut(5)); + vBox.add(newJButton("艾特+私聊", new AtUsersUI(getMenuJButton()))); + + if (DataCache.getCurrentUser().isAdmin() && EncourageCache.supportStatistics) { + vBox.add(Box.createVerticalStrut(5)); + vBox.add(newJButton("在线统计", new StatisticsUI(getMenuJButton()))); + } + + vBox.add(Box.createVerticalStrut(5)); + vBox.add(getExitButton()); + + JPanel panel = new JPanel(); + panel.setBounds(10, 10, 100, 250); + panel.add(vBox); + + mainPanel.add(panel, BorderLayout.CENTER); + mainPanel.updateUI(); + } + + @Override + protected JComponent getComponent() { + return mainPanel; + } + + private void initPanel() { + if (mainPanel == null) { + mainPanel = new JPanel(); + } + + mainPanel.removeAll(); + mainPanel.setLayout(new BorderLayout()); + mainPanel.setMaximumSize(mainDimension); + mainPanel.setMinimumSize(mainDimension); + mainPanel.setPreferredSize(mainDimension); + mainPanel.setEnabled(true); + mainPanel.setVisible(true); + } + + + /** + * 统一按钮格式 + */ + private JButton newJButton(String text, JPanel newjPanel) { + JButton jButton = new JButton(text); + jButton.setPreferredSize(buttonDimension); + + jButton.addActionListener(j -> { + initPanel(); + mainPanel.add(newjPanel, BorderLayout.CENTER); + mainPanel.updateUI(); + }); + return jButton; + } + + public JButton getMenuJButton() { + JButton menu = new JButton("主菜单"); + menu.setPreferredSize(buttonDimension); + menu.addActionListener(e -> init()); + return menu; + } + +} diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/cache/EncourageCache.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/cache/EncourageCache.java new file mode 100644 index 00000000..8e5dbd01 --- /dev/null +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/cache/EncourageCache.java @@ -0,0 +1,54 @@ +package cn.xeblog.plugin.tools.encourage.cache; + +import cn.xeblog.commons.entity.User; + +import java.util.ArrayList; +import java.util.List; + +/** + * 缓存区 + * + * @author 鼓励师 + * @date 2023/10/20 16:21 + */ +public class EncourageCache { + + /** + * 艾特用户缓存 + */ + public static final List atUsers = new ArrayList<>(); + + /** + * 屏蔽用户缓存 里面存了userName和uuid + */ + public static final List BLOCK_USER_CACHE = new ArrayList<>(32); + + /** + * 显示提示消息 + */ + public static boolean showTips = false; + /** + * 私聊用户 + */ + public static User privateChatUser = null; + + /** + * 服务端是否支持在线统计 + */ + public static boolean supportStatistics = false; + /** + * 服务端是否支持私聊 + */ + public static boolean supportPrivateChat = false; + + public static boolean checkBlock(User user) { + if (user == null) { + return false; + } + + String username = user.getUsername(); + String uuid = user.getUuid(); + return EncourageCache.BLOCK_USER_CACHE.contains(username) || EncourageCache.BLOCK_USER_CACHE.contains(uuid); + } + +} diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AbstractUI.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AbstractUI.java new file mode 100644 index 00000000..2ca1ee6e --- /dev/null +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AbstractUI.java @@ -0,0 +1,133 @@ +package cn.xeblog.plugin.tools.encourage.ui; + +import cn.hutool.core.util.StrUtil; +import cn.xeblog.commons.entity.User; +import cn.xeblog.plugin.cache.DataCache; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.ui.components.JBScrollPane; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public abstract class AbstractUI extends JPanel { + + // 设置统一属性 + final Dimension mainDimension = new Dimension(300, 250); + final Dimension buttonDimension = new Dimension(80, 30); + final Dimension selectDimension = new Dimension(80, 30); + + protected JLabel buildNewJLabel(String text) { + JLabel jLabel = new JLabel(text); + jLabel.setFont(new Font("", Font.BOLD, 13)); + jLabel.setHorizontalAlignment(JLabel.LEFT); + return jLabel; + } + + protected ComboBox buildYesOrNoComboBox() { + ComboBox comboBox = new ComboBox<>(); + comboBox.setMaximumSize(selectDimension); + comboBox.setPreferredSize(selectDimension); + comboBox.addItem("是"); + comboBox.addItem("否"); + comboBox.setSelectedItem("否"); + return comboBox; + } + + private final JTextField searchUserField = new JTextField(); + private final JPanel userListPanel = new JPanel(); + final Box userBox = Box.createVerticalBox(); + final JButton flushButton = new JButton("刷新数据"); + + public AbstractUI() { + super(); + + // 搜索框监听事件 + searchUserField.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + flushUserList(); + } + }); + // 刷新按钮点击事件 + flushButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + flushUserList(); + } + }); + flushButton.setMaximumSize(buttonDimension); + + Box searchBox = Box.createHorizontalBox(); + searchBox.add(new JLabel("搜索用户:")); + searchBox.add(searchUserField); + + JPanel userListMainPanel = new JPanel(new BorderLayout()); + JBScrollPane onlineUserScrollBar = new JBScrollPane(userListPanel); + onlineUserScrollBar.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + userListMainPanel.setPreferredSize(new Dimension(300, 200)); + userListMainPanel.add(onlineUserScrollBar); + + userBox.add(searchBox); + userBox.add(Box.createVerticalStrut(5)); + userBox.add(userListMainPanel); + } + + protected void flushUserList() { + userListPanel.removeAll(); + Box vBox = Box.createVerticalBox(); + List userList = searchUserList(); + userList.forEach(user -> vBox.add(generateJPanelByUser(user))); + userListPanel.add(vBox); + userListPanel.updateUI(); + } + + /** + * 根据搜索框筛选用户 + * + * @author 鼓励师 + * @date 2023/10/23 10:15 + */ + protected List searchUserList() { + String search = searchUserField != null ? searchUserField.getText() : ""; + Map map = DataCache.userMap == null ? new HashMap<>() : DataCache.userMap; + return map.values().stream().filter(u -> StrUtil.isBlank(search) || u.getUsername().toLowerCase().contains(search.toLowerCase())).collect(Collectors.toList()); + } + + /** + * 生成一个横向的选择面板 用户名 □ □ + * + * @author 鼓励师 + * @date 2023/10/23 10:14 + */ + protected JPanel generateJPanelByUser(User user) { + JPanel userPanel = new JPanel(); + + JLabel label = new JLabel(user.getUsername()); + label.setForeground(new Color(238, 146, 146)); + label.setFont(new Font("", Font.PLAIN, 13)); + + userPanel.add(label); + userPanel.add(Box.createVerticalStrut(10)); + buildJCheckBoxList(user).forEach(userPanel::add); + + return userPanel; + } + + /** + * 构建 N个 选择框 + * + * @return 选择框集合 + */ + protected List buildJCheckBoxList(User user) { + return new ArrayList<>(); + } +} diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AtUsersUI.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AtUsersUI.java new file mode 100644 index 00000000..ee30f628 --- /dev/null +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/AtUsersUI.java @@ -0,0 +1,116 @@ +package cn.xeblog.plugin.tools.encourage.ui; + +import cn.hutool.core.util.StrUtil; +import cn.xeblog.commons.entity.User; +import cn.xeblog.plugin.action.ConsoleAction; +import cn.xeblog.plugin.cache.DataCache; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +/** + * 批量艾特用户面板 + * + * @author 鼓励师 + * @date 2023/10/20 13:37 + */ +public class AtUsersUI extends AbstractUI { + + public AtUsersUI(JButton backMenu) { + super(); + + Box hBox = Box.createHorizontalBox(); + hBox.add(buildNewJLabel("批量艾特用户")); + + Box mainVBox = Box.createVerticalBox(); + mainVBox.add(hBox); + mainVBox.add(Box.createVerticalStrut(10)); + mainVBox.add(userBox); + mainVBox.add(Box.createVerticalStrut(10)); + + JPanel panel = new JPanel(); + panel.setBounds(10, 10, 280, 250); + panel.add(mainVBox); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setPreferredSize(new Dimension(200, 35)); + buttonPanel.add(flushButton); + buttonPanel.add(getClearJButton()); + buttonPanel.add(backMenu); + + flushUserList(); + + this.setMaximumSize(mainDimension); + this.setLayout(new BorderLayout()); + this.add(panel, BorderLayout.CENTER); + this.add(buttonPanel, BorderLayout.SOUTH); + this.updateUI(); + } + + @Override + protected List buildJCheckBoxList(User user) { + List jCheckBoxList = new ArrayList<>(); + + boolean anyMatch = EncourageCache.atUsers.stream().anyMatch(u -> u.equals(user)); + JCheckBox blockCheckBox = new JCheckBox("@TA", anyMatch); + blockCheckBox.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + EncourageCache.atUsers.add(user); + ConsoleAction.showSystemMsg(StrUtil.format("增加艾特用户:【{}】", user.getUsername())); + } else { + EncourageCache.atUsers.remove(user); + ConsoleAction.showSystemMsg(StrUtil.format("移除艾特用户:【{}】", user.getUsername())); + } + }); + jCheckBoxList.add(blockCheckBox); + + if (EncourageCache.supportPrivateChat) { + boolean privateCheck = EncourageCache.privateChatUser != null && EncourageCache.privateChatUser.equals(user); + JCheckBox privateChatCheckBox = new JCheckBox("私聊", privateCheck); + privateChatCheckBox.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (DataCache.username.equals(user.getUsername())) { + ConsoleAction.showSystemMsg(StrUtil.format("可不兴选择自己,换个人吧!")); + flushUserList(); + return; + } + + if (EncourageCache.privateChatUser == null) { + ConsoleAction.showSystemMsg(StrUtil.format("设定私聊用户:【{}】", user.getUsername())); + } else { + ConsoleAction.showSystemMsg(StrUtil.format("切换私聊用户:【{}】->【{}】", EncourageCache.privateChatUser.getUsername(), user.getUsername())); + } + EncourageCache.privateChatUser = user; + } else { + ConsoleAction.showSystemMsg(StrUtil.format("取消私聊用户:【{}】", user.getUsername())); + EncourageCache.privateChatUser = null; + } + flushUserList(); + }); + jCheckBoxList.add(privateChatCheckBox); + } + + return jCheckBoxList; + } + + public JButton getClearJButton() { + JButton flushButton = new JButton("清空"); + flushButton.setMaximumSize(buttonDimension); + flushButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + EncourageCache.atUsers.clear(); + EncourageCache.privateChatUser = null; + flushUserList(); + ConsoleAction.showSystemMsg("已清空所有艾特用户和私聊用户!"); + } + }); + return flushButton; + } +} diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/BlockMessageUI.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/BlockMessageUI.java new file mode 100644 index 00000000..be621654 --- /dev/null +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/BlockMessageUI.java @@ -0,0 +1,108 @@ +package cn.xeblog.plugin.tools.encourage.ui; + +import cn.xeblog.commons.entity.User; +import cn.xeblog.plugin.action.ConsoleAction; +import cn.xeblog.plugin.cache.DataCache; +import cn.xeblog.plugin.tools.encourage.cache.EncourageCache; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.List; + +/** + * 消息屏蔽面板 + * + * @author 鼓励师 + * @date 2023/10/20 13:37 + */ +public class BlockMessageUI extends AbstractUI { + + final JCheckBox tipsCheckBox = new JCheckBox("显示用户消息提示"); + + public BlockMessageUI(JButton backMenu) { + super(); + + Box hBox = Box.createHorizontalBox(); + hBox.add(buildNewJLabel("用户消息屏蔽")); + + Box tipsBox = Box.createHorizontalBox(); + tipsBox.add(tipsCheckBox); + tipsCheckBox.addItemListener(e -> { + EncourageCache.showTips = e.getStateChange() == ItemEvent.SELECTED; + ConsoleAction.showSystemMsg(String.format("[%s]屏蔽用户的消息提示", EncourageCache.showTips ? "开启" : "关闭")); + flushUserList(); + }); + + Box mainVBox = Box.createVerticalBox(); + mainVBox.add(Box.createVerticalStrut(10)); + mainVBox.add(hBox); + mainVBox.add(Box.createVerticalStrut(5)); + mainVBox.add(tipsBox); + mainVBox.add(Box.createVerticalStrut(5)); + mainVBox.add(userBox); + + JPanel panel = new JPanel(); + panel.setBounds(10, 10, 200, 250); + panel.add(mainVBox); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setPreferredSize(new Dimension(200, 35)); + buttonPanel.add(getClearJButton()); + buttonPanel.add(flushButton); + buttonPanel.add(backMenu); + + flushUserList(); + + this.setMaximumSize(mainDimension); + this.setLayout(new BorderLayout()); + this.add(panel, BorderLayout.CENTER); + this.add(buttonPanel, BorderLayout.SOUTH); + this.updateUI(); + } + + public JButton getClearJButton() { + JButton flushButton = new JButton("清空所有"); + flushButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + EncourageCache.BLOCK_USER_CACHE.clear(); + flushUserList(); + ConsoleAction.showSystemMsg("已清空所有屏蔽的用户信息!"); + } + }); + return flushButton; + } + + private void doBlock(User user, boolean selected) { + String username = user.getUsername(); + String uuid = user.getUuid(); + + if (DataCache.username.equals(username) && DataCache.uuid.equals(uuid)) { + ConsoleAction.showSystemMsg("这么想不开要屏蔽自己?不批!"); + flushUserList(); + return; + } + + if (selected) { + EncourageCache.BLOCK_USER_CACHE.add(username); + EncourageCache.BLOCK_USER_CACHE.add(uuid); + ConsoleAction.showSystemMsg(String.format("已屏蔽用户[%s]的消息", username)); + } else { + EncourageCache.BLOCK_USER_CACHE.remove(username); + EncourageCache.BLOCK_USER_CACHE.remove(uuid); + ConsoleAction.showSystemMsg(String.format("已解除屏蔽用户[%s]的消息", username)); + } + } + + @Override + protected List buildJCheckBoxList(User user) { + boolean anyMatch = EncourageCache.BLOCK_USER_CACHE.stream().anyMatch(b -> b.equals(user.getUsername()) || b.equals(user.getUuid())); + JCheckBox blockCheckBox = new JCheckBox("屏蔽", anyMatch); + blockCheckBox.addItemListener(e -> doBlock(user, e.getStateChange() == ItemEvent.SELECTED)); + return Collections.singletonList(blockCheckBox); + } +} diff --git a/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/StatisticsUI.java b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/StatisticsUI.java new file mode 100644 index 00000000..a2f82b23 --- /dev/null +++ b/xechat-plugin/src/main/java/cn/xeblog/plugin/tools/encourage/ui/StatisticsUI.java @@ -0,0 +1,124 @@ +package cn.xeblog.plugin.tools.encourage.ui; + +import cn.xeblog.commons.entity.StatisticsMsgDTO; +import cn.xeblog.commons.enums.Action; +import cn.xeblog.commons.enums.CountEnum; +import cn.xeblog.plugin.action.MessageAction; +import com.intellij.openapi.ui.ComboBox; + +import javax.swing.*; +import java.awt.*; + +/** + * 在线统计面板 + * + * @author 鼓励师 + * @date 2023/10/20 13:37 + */ +public class StatisticsUI extends AbstractUI { + + private CountEnum selectCountEnum; + private boolean toAll = false; + private boolean showDetail = false; + private int showSize = 6; + private final ComboBox showSizeComboBox = new ComboBox<>(); + + public StatisticsUI(JButton backMenu) { + + buildShowSizeComboBox(); + + Box hBox = Box.createHorizontalBox(); + hBox.add(buildNewJLabel("在线统计")); + + Box selectBox1 = Box.createHorizontalBox(); + selectBox1.add(buildNewJLabel("统计分类:")); + selectBox1.add(buildCountComboBox()); + + ComboBox yesOrNoComboBox1 = buildYesOrNoComboBox(); + yesOrNoComboBox1.addActionListener(l -> toAll = "是".equals(yesOrNoComboBox1.getSelectedItem())); + Box selectBox2 = Box.createHorizontalBox(); + selectBox2.add(buildNewJLabel("所有人可见:")); + selectBox2.add(yesOrNoComboBox1); + + ComboBox yesOrNoComboBox2 = buildYesOrNoComboBox(); + yesOrNoComboBox2.addActionListener(l -> { + showDetail = "是".equals(yesOrNoComboBox2.getSelectedItem()); + showSizeComboBox.setEnabled(showDetail); + showSizeComboBox.repaint(); + }); + Box selectBox3 = Box.createHorizontalBox(); + selectBox3.add(buildNewJLabel("是否展示详情:")); + selectBox3.add(yesOrNoComboBox2); + + Box selectBox4 = Box.createHorizontalBox(); + selectBox4.add(buildNewJLabel("表格展示列:")); + selectBox4.add(showSizeComboBox); + + Box mainVBox = Box.createVerticalBox(); + mainVBox.add(hBox); + mainVBox.add(Box.createVerticalStrut(10)); + mainVBox.add(selectBox1); + mainVBox.add(Box.createVerticalStrut(10)); + mainVBox.add(selectBox2); + mainVBox.add(Box.createVerticalStrut(10)); + mainVBox.add(selectBox3); + mainVBox.add(Box.createVerticalStrut(10)); + mainVBox.add(selectBox4); + mainVBox.add(Box.createVerticalStrut(10)); + + JPanel panel = new JPanel(); + panel.setBounds(10, 10, 200, 250); + panel.add(mainVBox); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setPreferredSize(new Dimension(150, 70)); + buttonPanel.add(bulidSendJButton()); + buttonPanel.add(backMenu); + + this.setMaximumSize(mainDimension); + this.setLayout(new BorderLayout()); + this.add(panel, BorderLayout.CENTER); + this.add(buttonPanel, BorderLayout.SOUTH); + this.updateUI(); + } + + private ComboBox buildCountComboBox() { + ComboBox comboBox = new ComboBox<>(); + comboBox.setMaximumSize(selectDimension); + comboBox.setPreferredSize(selectDimension); + for (CountEnum countEnum : CountEnum.values()) { + comboBox.addItem(countEnum.getDesc()); + } + comboBox.addActionListener(l -> selectCountEnum = CountEnum.getByIndex(comboBox.getSelectedIndex())); + comboBox.setSelectedItem(CountEnum.CITY.getDesc()); + return comboBox; + } + + private void buildShowSizeComboBox() { + showSizeComboBox.setMaximumSize(selectDimension); + showSizeComboBox.setPreferredSize(selectDimension); + for (int i = 5; i <= 9; i++) { + showSizeComboBox.addItem(String.valueOf(i)); + } + showSizeComboBox.setSelectedItem(String.valueOf(showSize)); + showSizeComboBox.addActionListener(l -> showSize = Integer.parseInt(String.valueOf(showSizeComboBox.getSelectedItem()))); + showSizeComboBox.setEnabled(showDetail); + } + + private JButton bulidSendJButton() { + JButton menu = new JButton("发送"); + menu.setMaximumSize(buttonDimension); + menu.addActionListener(e -> send()); + return menu; + } + + private void send() { + StatisticsMsgDTO statisticsMsgDTO = new StatisticsMsgDTO(); + statisticsMsgDTO.setCountEnum(selectCountEnum); + statisticsMsgDTO.setToAll(toAll); + statisticsMsgDTO.setShowDetail(showDetail); + statisticsMsgDTO.setShowSize(showSize); + MessageAction.send(statisticsMsgDTO, Action.STATISTICS); + } + +} diff --git a/xechat-server/src/main/java/cn/xeblog/server/XEChatServer.java b/xechat-server/src/main/java/cn/xeblog/server/XEChatServer.java index 31ac25b4..44a8e353 100644 --- a/xechat-server/src/main/java/cn/xeblog/server/XEChatServer.java +++ b/xechat-server/src/main/java/cn/xeblog/server/XEChatServer.java @@ -2,6 +2,7 @@ import cn.hutool.core.lang.Singleton; import cn.hutool.core.util.StrUtil; +import cn.xeblog.server.action.handler.StatisticsActionHandler; import cn.xeblog.server.config.IpRegionProperties; import cn.xeblog.server.config.ServerConfig; import cn.xeblog.server.handler.DefaultChannelInitializer; @@ -147,5 +148,8 @@ public static void main(String[] args) { XEChatServer server = new XEChatServer(serverConfig.getPort()); server.enableWS = serverConfig.getEnableWS(); server.run(); + + // 计时器 定时播报在线统计消息 + StatisticsActionHandler.startTimerTask(); } } diff --git a/xechat-server/src/main/java/cn/xeblog/server/action/handler/ChatActionHandler.java b/xechat-server/src/main/java/cn/xeblog/server/action/handler/ChatActionHandler.java index 66a2f3cb..8874c1e4 100644 --- a/xechat-server/src/main/java/cn/xeblog/server/action/handler/ChatActionHandler.java +++ b/xechat-server/src/main/java/cn/xeblog/server/action/handler/ChatActionHandler.java @@ -11,6 +11,7 @@ import cn.xeblog.server.action.ChannelAction; import cn.xeblog.server.annotation.DoAction; import cn.xeblog.server.builder.ResponseBuilder; +import cn.xeblog.server.cache.UserCache; import cn.xeblog.server.config.GlobalConfig; import cn.xeblog.server.util.BaiDuFyUtil; import cn.xeblog.server.util.SensitiveWordUtils; @@ -42,12 +43,32 @@ protected void process(User user, UserMsgDTO body) { BaiDuFyUtil baiDuFyUtil = Singleton.get(BaiDuFyUtil.class.getName(), () -> new BaiDuFyUtil("", "")); body.setContent(baiDuFyUtil.translate(SensitiveWordUtils.loveChina(msg))); + ChannelAction.send(user, body, MessageType.USER); + } else if (body.getMsgType() == UserMsgDTO.MsgType.PRIVATE) { + String toUserName = body.getToUsers()[0]; + String msg = Convert.toStr(body.getContent()); + + // 消息原封不动发给自己 不然看不到自己发了啥 + user.send(ResponseBuilder.build(user, body, MessageType.USER)); + if (user.getUsername().equals(toUserName)) { + user.send(ResponseBuilder.system("发给自己?是不是很无聊?")); + return; + } + + // 发给对方 + User toUser = UserCache.getUserByUsername(toUserName); + if (toUser != null) { + UserMsgDTO msgDTO = new UserMsgDTO(StrUtil.format("私聊消息::{}", msg), new String[]{toUserName}); + toUser.send(ResponseBuilder.build(user, msgDTO, MessageType.USER)); + } else { + user.send(ResponseBuilder.system(StrUtil.format("用户[{}]不存在!", toUserName))); + } + } else { // 暂时不支持这种形式的消息,全部转为文本消息 body.setMsgType(UserMsgDTO.MsgType.TEXT); + ChannelAction.send(user, body, MessageType.USER); } - - ChannelAction.send(user, body, MessageType.USER); } } diff --git a/xechat-server/src/main/java/cn/xeblog/server/action/handler/StatisticsActionHandler.java b/xechat-server/src/main/java/cn/xeblog/server/action/handler/StatisticsActionHandler.java new file mode 100644 index 00000000..777590dd --- /dev/null +++ b/xechat-server/src/main/java/cn/xeblog/server/action/handler/StatisticsActionHandler.java @@ -0,0 +1,96 @@ +package cn.xeblog.server.action.handler; + +import cn.hutool.core.util.StrUtil; +import cn.xeblog.commons.entity.StatisticsMsgDTO; +import cn.xeblog.commons.entity.User; +import cn.xeblog.commons.enums.Action; +import cn.xeblog.commons.enums.CountEnum; +import cn.xeblog.commons.enums.MessageType; +import cn.xeblog.server.action.ChannelAction; +import cn.xeblog.server.annotation.DoAction; +import cn.xeblog.server.builder.ResponseBuilder; +import cn.xeblog.server.cache.UserCache; +import cn.xeblog.server.util.CountOnlineUserUtil; +import lombok.extern.slf4j.Slf4j; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; + + +/** + * 在线用户统计 + * + * @author 鼓励师 + * @date 2023/11/8 17:20 + */ +@DoAction(Action.STATISTICS) +@Slf4j +public class StatisticsActionHandler extends AbstractActionHandler { + + @Override + protected void process(User user, StatisticsMsgDTO dto) { + + if (!user.isAdmin()) { + user.send(ResponseBuilder.build(user, "你不是管理员,无权操作!", MessageType.USER)); + return; + } + + CountEnum countEnum = dto.getCountEnum(); + if (countEnum == null) { + user.send(ResponseBuilder.system("统计类型有误!")); + return; + } + + List users = UserCache.listUser(); + String consoleTableStr = dto.isShowDetail() ? CountOnlineUserUtil.showAllUsers(users, countEnum, dto.getShowSize()) + : CountOnlineUserUtil.getConsoleTableStr(users, countEnum); + + // 表格消息 + String dateMsg = StrUtil.format("现在时间是:{}\n{}", dateFormat.format(new Date()), consoleTableStr); + if (dto.isToAll()) { + ChannelAction.send(ResponseBuilder.system(dateMsg)); + } else { + user.send(ResponseBuilder.system(dateMsg)); + } + } + + static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + static final Timer timer = new Timer(); + + public static void startTimerTask() { + TimerTask task = new TimerTask() { + @Override + public void run() { + Date now = new Date(); + Calendar instance = Calendar.getInstance(); + instance.setTime(now); + int hour = instance.get(Calendar.HOUR_OF_DAY); + if (hour >= 7 && hour <= 22) { + String consoleTableStr = CountOnlineUserUtil.getConsoleTableStr(UserCache.listUser(), CountEnum.PROVINCE); + String dateMsg = StrUtil.format("现在时间是:{}\n{}", dateFormat.format(now), consoleTableStr); + ChannelAction.send(ResponseBuilder.system(dateMsg)); + } + } + }; + Date firstTime = getNextHour(); // 设置定时器第1次执行的开始时间 + long period = TimeUnit.HOURS.toMillis(1); // 设置每隔1小时执行1次 + timer.scheduleAtFixedRate(task, firstTime, period); + } + + /** + * 获取当前时间的下一个小时的整点时间 + */ + public static Date getNextHour() { + Calendar calendar = Calendar.getInstance(); + log.info("==> 计时器:当前时间 = " + dateFormat.format(calendar.getTime())); + calendar.set(Calendar.MILLISECOND, 0); // 清空毫秒 + calendar.set(Calendar.SECOND, 0); // 清空秒 + calendar.set(Calendar.MINUTE, 0); // 清空分 + calendar.add(Calendar.HOUR, 1); // 添加1小时 + Date nextHour = calendar.getTime(); + log.info("==> 计时器:开始时间 = " + dateFormat.format(nextHour)); + return nextHour; + } + +} diff --git a/xechat-server/src/main/java/cn/xeblog/server/action/handler/WeatherActionHandler.java b/xechat-server/src/main/java/cn/xeblog/server/action/handler/WeatherActionHandler.java index 4d5e2896..6645de78 100644 --- a/xechat-server/src/main/java/cn/xeblog/server/action/handler/WeatherActionHandler.java +++ b/xechat-server/src/main/java/cn/xeblog/server/action/handler/WeatherActionHandler.java @@ -11,7 +11,6 @@ import cn.xeblog.commons.entity.weather.CurrentWeather; import cn.xeblog.commons.entity.weather.FutureWeather; import cn.xeblog.commons.enums.Action; -import cn.xeblog.commons.enums.MessageType; import cn.xeblog.commons.enums.WeatherType; import cn.xeblog.server.annotation.DoAction; import cn.xeblog.server.builder.ResponseBuilder; @@ -59,7 +58,7 @@ protected void process(User user, WeatherDTO body) { } } catch (Exception e) { log.error("出现异常:", e); - user.send(ResponseBuilder.build(null, "天气查询异常,请联系管理员!", MessageType.SYSTEM)); + user.send(ResponseBuilder.system("天气查询异常,请联系管理员!")); return; } @@ -83,7 +82,7 @@ protected void process(User user, WeatherDTO body) { } final String msg = StrUtil.CRLF + cityInfo.getLocationName() + " 天气预报" + StrUtil.CRLF + consoleTable; - user.send(ResponseBuilder.build(null, msg, MessageType.SYSTEM)); + user.send(ResponseBuilder.system(msg)); } } diff --git a/xechat-server/src/main/java/cn/xeblog/server/builder/ResponseBuilder.java b/xechat-server/src/main/java/cn/xeblog/server/builder/ResponseBuilder.java index 7c55ca62..d0f581ab 100644 --- a/xechat-server/src/main/java/cn/xeblog/server/builder/ResponseBuilder.java +++ b/xechat-server/src/main/java/cn/xeblog/server/builder/ResponseBuilder.java @@ -1,6 +1,7 @@ package cn.xeblog.server.builder; import cn.xeblog.commons.entity.Response; +import cn.xeblog.commons.entity.SystemStateDTO; import cn.xeblog.commons.entity.User; import cn.xeblog.commons.entity.react.result.ReactResult; import cn.xeblog.commons.enums.MessageType; @@ -16,7 +17,7 @@ public static Response react(ReactResult result) { } public static Response system(String msg) { - return build(null, msg, MessageType.SYSTEM); + return build(null, new SystemStateDTO(msg), MessageType.SYSTEM); } public static Response build(User user, Object msg, MessageType messageType) { diff --git a/xechat-server/src/main/java/cn/xeblog/server/util/CountOnlineUserUtil.java b/xechat-server/src/main/java/cn/xeblog/server/util/CountOnlineUserUtil.java new file mode 100644 index 00000000..30f863ed --- /dev/null +++ b/xechat-server/src/main/java/cn/xeblog/server/util/CountOnlineUserUtil.java @@ -0,0 +1,149 @@ +package cn.xeblog.server.util; + +import cn.hutool.core.lang.ConsoleTable; +import cn.xeblog.commons.entity.IpRegion; +import cn.xeblog.commons.entity.User; +import cn.xeblog.commons.enums.CountEnum; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author 鼓励师 + * @date 2023/8/29 16:52 + */ +public class CountOnlineUserUtil { + + /** + * 统计在线人数 控制台打印 + * + * @author 鼓励师 + * @date 2023/8/29 17:11 + */ + public static String getConsoleTableStr(List onlineUsers, CountEnum countEnum) { + return getConsoleTableStr(groupUsers(onlineUsers, countEnum), countEnum); + } + + /** + * 统计在线人数 控制台打印 + * + * @author 鼓励师 + * @date 2023/8/29 17:11 + */ + public static String getConsoleTableStr(Map> groupUserMap, CountEnum countEnum) { + Map countMap = groupUserMap.keySet().stream().collect(Collectors.toMap(k -> k, k -> groupUserMap.get(k).size())); + ConsoleTable consoleTable = ConsoleTable.create(); + consoleTable.addHeader(countEnum.getDesc(), "在线人数"); + countMap.forEach((key, value) -> consoleTable.addBody(key, value.toString())); + return consoleTable.toString(); + } + + /** + * 统计在线人数分类 + * + * @author 鼓励师 + * @date 2023/8/31 17:18 + */ + public static Map> groupUsers(List onlineUsers, CountEnum countEnum) { + Map> countMap = new HashMap<>(); + onlineUsers.forEach(user -> { + String type = null; + + IpRegion region = user.getRegion(); + if (region != null) { + switch (countEnum) { + case COUNTRY: + type = region.getCountry(); + break; + case PROVINCE: + type = region.getProvince(); + break; + case CITY: + type = region.getCity(); + break; + case ISP: + type = region.getIsp(); + break; + case USER_STATUS: + type = user.getStatus().alias(); + break; + } + } + + if (type == null || "0".equals(type.trim())) { + type = "未知"; + } + + List userList = countMap.computeIfAbsent(type, k -> new ArrayList<>()); + userList.add(user); + }); + + return countMap; + } + + /** + * 分类展示所有人 + * + * @author 鼓励师 + * @date 2023/9/1 10:20 + */ + public static String showAllUsers(List onlineUsers, CountEnum countEnum, int size) { + Map> groupUsers = groupUsers(onlineUsers, countEnum); + ConsoleTable consoleTable = ConsoleTable.create(); + + String[] headers = new String[size + 1]; + headers[0] = countEnum.getDesc(); + for (int i = 1; i < size + 1; i++) { + headers[i] = "用户"; + } + consoleTable.addHeader(headers); + + groupUsers.forEach((key, userList) -> { + List stringsList = queryTableString(userList, key + "(" + userList.size() + ")", size); + stringsList.forEach(consoleTable::addBody); + }); + return consoleTable.toString(); + } + + /** + * 统计在线人数分类 + * + * @author 鼓励师 + * @date 2023/8/31 17:18 + */ + private static List queryTableString(List onlineUsers, String title, int size) { + List list = new ArrayList<>(); + + int merchant = onlineUsers.size() / size; // 商 + int remainder = onlineUsers.size() % size; // 余数 + + int f = 0; + if (merchant == 0 && remainder > 0) { + f = 1; + } else if (merchant > 0 && remainder == 0) { + f = merchant; + } else if (remainder > 0) { + f = merchant + 1; + } + + for (int i = 0; i < f; i++) { + String[] names = new String[size + 1]; + names[0] = title; + for (int j = i * size; j < i * size + size; j++) { + int index = j % size + 1; + if (onlineUsers.size() > j) { + String username = onlineUsers.get(j).getUsername(); + names[index] = username; + } else { + names[index] = " "; + } + } + list.add(names); + } + + return list; + } +}