diff --git a/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java b/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java index 0dae722..d07e0f4 100644 --- a/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java +++ b/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java @@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit; * @author Andrews * @date 2023/11/30 11:12 */ +@Slf4j public final class ThreadPoolExecutorFactory { /** * 机器的CPU核数:Runtime.getRuntime().availableProcessors() diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java index 0e8d8eb..e3ecbaa 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java @@ -1,6 +1,5 @@ package com.bx.implatform.service.impl; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -27,12 +26,13 @@ import com.bx.implatform.service.IGroupService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; import com.bx.implatform.util.BeanUtils; -import com.bx.implatform.util.DateTimeUtils; +import com.bx.implatform.util.SensitiveFilterUtil; import com.bx.implatform.vo.GroupMessageVO; import com.google.common.base.Splitter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -47,6 +47,7 @@ public class GroupMessageServiceImpl extends ServiceImpl redisTemplate; private final IMClient imClient; + private final SensitiveFilterUtil sensitiveFilterUtil; @Override public Long sendMessage(GroupMessageDTO dto) { @@ -55,12 +56,12 @@ public class GroupMessageServiceImpl extends ServiceImpl(); } // 只能拉取最近1个月的 - Date minDate = DateTimeUtils.addMonths(new Date(), -1); + Date minDate = DateUtils.addMonths(new Date(), -1); LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.gt(GroupMessage::getId, minId).gt(GroupMessage::getSendTime, minDate).in(GroupMessage::getGroupId, ids) .ne(GroupMessage::getStatus, MessageStatus.RECALL.code()).orderByAsc(GroupMessage::getId).last("limit 100"); @@ -220,7 +224,7 @@ public class GroupMessageServiceImpl extends ServiceImpl sendMessage = new IMPrivateMessage<>(); @@ -70,7 +74,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl queryWrapper = Wrappers.lambdaQuery(); // 只能拉取最近1个月的 - Date minDate = DateTimeUtils.addMonths(new Date(), -1); + Date minDate = DateUtils.addMonths(new Date(), -1); queryWrapper.gt(PrivateMessage::getId, minId) .ge(PrivateMessage::getSendTime, minDate) .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code()) diff --git a/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java b/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java index e000fd4..91935d9 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java @@ -40,6 +40,8 @@ public class FileService { private String imagePath; @Value("${minio.filePath}") private String filePath; + @Value("${minio.videoPath}") + private String videoPath; @PostConstruct @@ -109,13 +111,13 @@ public class FileService { String url = minIoServer + "/" + bucketName; switch (fileTypeEnum) { case FILE: - url += "/file/"; + url += "/" + filePath + "/"; break; case IMAGE: - url += "/image/"; + url += "/" + imagePath + "/"; break; case VIDEO: - url += "/video/"; + url += "/" + videoPath + "/"; break; default: break; diff --git a/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java b/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java new file mode 100644 index 0000000..78158c2 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java @@ -0,0 +1,204 @@ +package com.bx.implatform.util; + +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.CharUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +/** + * 敏感词过滤器——SensitiveFilter + * + * @author Andrews + * @date 2023/12/4 11:12 + * @return null + */ +@Slf4j +@Component +@NoArgsConstructor +public final class SensitiveFilterUtil { + + /** + * 替换符 + */ + private static final String REPLACE_MENT = "***"; + + /** + * 根节点 + */ + private static final TrieNode ROOT_NODE = new TrieNode(); + + /** + * 1、 前缀树 前缀树某一个节点 + * + * @author NXY + * @date 2023/12/4 11:17 + * @return null + */ + private static class TrieNode { + // 关键词结束标识 + private boolean isKeywordEnd = false; + + // 子节点(key是下级字符,value是下级节点) + // 当前节点的子节点 + private final Map subNodes = new HashMap<>(); + + public boolean isKeywordEnd() { + return isKeywordEnd; + } + + public void setKeywordEnd(boolean keywordEnd) { + isKeywordEnd = keywordEnd; + } + + // 添加子节点 + public void addSubNode(Character c, TrieNode node) { + subNodes.put(c, node); + } + + // 获取子节点 + public TrieNode getSubNode(Character c) { + return subNodes.get(c); + } + + } + + /** + * 2、初始化方法,服务器启动时初始化 + * + * @author NXY + * @date 2023/12/4 11:18 + */ + @PostConstruct + public void init() { + try ( + // 类加载器 + InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt"); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + ) { + String keyword; + while ((keyword = reader.readLine()) != null) { + // 添加到前缀树 + this.addKeyword(keyword); + } + } catch (IOException e) { + log.error("加载敏感词文件失败: " + e.getMessage()); + } + } + + /** + * 3、将一个敏感词添加到前缀树中 + * + * @param keyword + * @author NXY + * @date 2023/12/4 11:15 + */ + private void addKeyword(String keyword) { + TrieNode tempNode = ROOT_NODE; + for (int i = 0; i < keyword.length(); i++) { + char c = keyword.charAt(i); + TrieNode subNode = tempNode.getSubNode(c); + if (subNode == null) { + // 初始化子节点 + subNode = new TrieNode(); + tempNode.addSubNode(c, subNode); + } + // 指向子节点,进入下一轮循环 + tempNode = subNode; + // 设置结束标识 + if (i == keyword.length() - 1) { + tempNode.setKeywordEnd(true); + } + } + } + + /** + * 过滤敏感词 + * + * @param text 待过滤的文本 + * @return 过滤后的文本 + */ + public String filter(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + // 结果 + StringBuilder sb = new StringBuilder(); + try { + // 指针1 + TrieNode tempNode = ROOT_NODE; + // 指针2 + int begin = 0; + // 指针3 + int position = 0; + while (begin < text.length()) { + if (position < text.length()) { + char c = text.charAt(position); + // 跳过符号 + if (isSymbol(c)) { + // 若指针1处于根节点,将此符号计入结果,让指针2向下走一步 + if (tempNode == ROOT_NODE) { + sb.append(c); + begin++; + } + // 无论符号在开头或中间,指针3都向下走一步 + position++; + continue; + } + // 检查下级节点 + tempNode = tempNode.getSubNode(c); + if (tempNode == null) { + // 以begin开头的字符串不是敏感词 + sb.append(text.charAt(begin)); + // 进入下一个位置 + position = ++begin; + // 重新指向根节点 + tempNode = ROOT_NODE; + } else if (tempNode.isKeywordEnd()) { + // 发现敏感词,将begin~position字符串替换掉 + sb.append(REPLACE_MENT); + // 进入下一个位置 + begin = ++position; + // 重新指向根节点 + tempNode = ROOT_NODE; + } else { + // 检查下一个字符 + position++; + } + } + // position遍历越界仍未匹配到敏感词 + else { + sb.append(text.charAt(begin)); + position = ++begin; + tempNode = ROOT_NODE; + } + } + } catch (Exception e) { + sb = new StringBuilder(text); + } + return sb.toString(); + } + + /** + * 判断是否为符号 ——特殊符号 + * + * @param c + * @return boolean + * @author NXY + * @date 2023/12/4 11:17 + */ + private boolean isSymbol(Character c) { + // 0x2E80~0x9FFF 是东亚文字范围 + return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF); + } +} + + diff --git a/im-platform/src/main/resources/application.yml b/im-platform/src/main/resources/application.yml index 41e22c6..a682483 100644 --- a/im-platform/src/main/resources/application.yml +++ b/im-platform/src/main/resources/application.yml @@ -39,6 +39,7 @@ minio: bucketName: box-im imagePath: image filePath: file + videoPath: video webrtc: iceServers: diff --git a/im-platform/src/main/resources/sensitive-words.txt b/im-platform/src/main/resources/sensitive-words.txt new file mode 100644 index 0000000..59cac85 --- /dev/null +++ b/im-platform/src/main/resources/sensitive-words.txt @@ -0,0 +1,3 @@ +杀了你 +傻逼 +去死 \ No newline at end of file