[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-pinglunzangciguolujiyudfasuanfadegaoxingnengdongtaiguolufangan":3},{"id":4,"title":5,"slug":6,"summary":7,"content":8,"contentHtml":9,"wordCount":10,"readingTime":11,"categoryId":12,"tags":13,"coverImage":13,"thumbnail":13,"status":14,"isTop":15,"isRecommended":15,"allowComments":15,"password":13,"viewCount":16,"likeCount":15,"commentCount":15,"seoTitle":5,"seoKeywords":17,"seoDescription":18,"source":13,"sourceUrl":13,"publishTime":19},"2010880124117905409","评论脏词过滤：基于DFA算法的高性能动态过滤方案","pinglunzangciguolujiyudfasuanfadegaoxingnengdongtaiguolufangan","评论系统是内容平台和社交应用的核心功能之一，而脏词过滤是保障内容安全、维护社区氛围的关键技术。本文将详细介绍如何在SpringBoot中实现一个**高性能、支持动态更新**的脏词过滤系统。 ## 一、背景与需求分析 随着Web应用的发展，用户生成内容（UGC）面临着严峻的内容安全挑战。脏词过滤系统需要满足以下核心需求： - **高性能**：处理海量用户评论时不能成为系统瓶颈 - **动态更新**：脏词库需要支持实时更新，无需重启服务 - **准确性**：准确识别敏感词，同时控制误判率 - **灵活性**：支持多种处理策略（替换、拒绝、审核） ## 二、核心技术选型：为何选择DFA算法？ 在脏词过滤场景下，**DFA（确定有限状态自动机）算法**相比传统方法有显著优势。 ### 2.1 传统方法的瓶颈 - **暴力匹配**：遍历每个脏词检查文本是否包含，时间复杂度O(n*m)，随脏词数量增加...","评论系统是内容平台和社交应用的核心功能之一，而脏词过滤是保障内容安全、维护社区氛围的关键技术。本文将详细介绍如何在SpringBoot中实现一个**高性能、支持动态更新**的脏词过滤系统。\n\n## 一、背景与需求分析\n\n随着Web应用的发展，用户生成内容（UGC）面临着严峻的内容安全挑战。脏词过滤系统需要满足以下核心需求：\n\n- **高性能**：处理海量用户评论时不能成为系统瓶颈\n- **动态更新**：脏词库需要支持实时更新，无需重启服务\n- **准确性**：准确识别敏感词，同时控制误判率\n- **灵活性**：支持多种处理策略（替换、拒绝、审核）\n\n## 二、核心技术选型：为何选择DFA算法？\n\n在脏词过滤场景下，**DFA（确定有限状态自动机）算法**相比传统方法有显著优势。\n\n### 2.1 传统方法的瓶颈\n\n- **暴力匹配**：遍历每个脏词检查文本是否包含，时间复杂度O(n*m)，随脏词数量增加性能急剧下降\n- **正则表达式**：编译开销大，动态更新困难，复杂匹配可能产生回溯问题\n\n### 2.2 DFA算法的优势\n\nDFA算法通过**前缀树（Trie树）** 结构实现高效匹配：\n- **时间复杂度O(n)**：只需对输入文本扫描一次，性能不受脏词库大小影响\n- **内存效率**：共享相同前缀的敏感词共享树节点，节省内存空间\n- **匹配准确**：支持完整词匹配，避免误匹配\n\n以下是DFA算法的核心原理状态转移图：\n\n```mermaid\nflowchart TD\n    A[开始匹配] --> B[从文本当前位置i及根节点开始]\n    B --> C{当前字符是否匹配状态转移?}\n    C -- 是 --> D[转移到下一个状态]\n    C -- 否 --> E[回溯\u003Cbr>从位置i+1重新开始]\n    D --> F{是否到达接受状态?}\n    F -- 是 --> G[标记敏感词\u003Cbr>记录位置]\n    G --> H[回溯到词结束后继续匹配]\n    F -- 否 --> C\n    H --> B\n    E --> I[i++]\n    I --> B\n```\n\n## 三、系统设计与实现\n\n### 3.1 整体架构设计\n\n系统采用分层架构，保证各模块职责单一：\n\n```\n脏词过滤系统架构\n├── 数据存储层 (MySQL + Redis)\n├── 算法核心层 (DFA引擎)\n├── 业务服务层 (过滤服务、管理服务)\n└── 应用接入层 (AOP切面、注解)\n```\n\n### 3.2 核心数据结构：Trie树节点\n\nTrie树节点是DFA算法的基石，每个节点代表一个字符状态：\n\n```java\npublic class TrieNode {\n    \u002F\u002F 子节点映射：Key为字符，Value为对应的子节点\n    private Map\u003CCharacter, TrieNode> children = new HashMap\u003C>();\n    \u002F\u002F 标记当前节点是否为某个脏词的结尾\n    private boolean isEnd = false;\n    \u002F\u002F 可选：用于记录到达此节点的完整脏词\n    private String keyword;\n    \n    \u002F\u002F Getter和Setter方法\n    public TrieNode getChild(char c) {\n        return children.get(c);\n    }\n    \n    public TrieNode addChild(char c) {\n        return children.computeIfAbsent(c, k -> new TrieNode());\n    }\n    \n    public boolean isEnd() {\n        return isEnd;\n    }\n}\n```\n\n### 3.3 过滤器核心引擎\n\n这个类负责构建Trie树并执行过滤逻辑：\n\n```java\n@Component\npublic class SensitiveWordFilter {\n    private volatile TrieNode root; \u002F\u002F 使用volatile保证多线程可见性\n    \n    \u002F**\n     * 构建Trie树\n     *\u002F\n    private TrieNode buildTrie(Set\u003CString> sensitiveWords) {\n        TrieNode root = new TrieNode();\n        for (String word : sensitiveWords) {\n            if (word == null || word.trim().isEmpty()) continue;\n            TrieNode currentNode = root;\n            for (char c : word.toCharArray()) {\n                currentNode = currentNode.addChild(c);\n            }\n            currentNode.setEnd(true);\n            currentNode.setKeyword(word);\n        }\n        return root;\n    }\n    \n    \u002F**\n     * 核心DFA匹配：判断是否包含脏词\n     *\u002F\n    public boolean containsSensitiveWord(String text) {\n        if (text == null || text.length() \u003C minWordLength) return false;\n        \n        char[] chars = text.toCharArray();\n        for (int i = 0; i \u003C chars.length; i++) {\n            TrieNode node = root;\n            for (int j = i; j \u003C chars.length; j++) {\n                node = node.getChild(chars[j]);\n                if (node == null) break; \u002F\u002F 转移失败，从i+1开始下一轮\n                if (node.isEnd()) return true; \u002F\u002F 匹配到一个脏词\n            }\n        }\n        return false;\n    }\n    \n    \u002F**\n     * 替换文本中的脏词\n     *\u002F\n    public String filter(String text, String replacement) {\n        if (text == null) return null;\n        \n        StringBuilder result = new StringBuilder(text);\n        List\u003CSensitiveWordResult> foundWords = findAllWords(text);\n        \n        \u002F\u002F 从后往前替换，避免影响索引\n        for (int i = foundWords.size() - 1; i >= 0; i--) {\n            SensitiveWordResult word = foundWords.get(i);\n            String replaceStr = String.valueOf(replacement)\n                .repeat(word.getEnd() - word.getStart() + 1);\n            result.replace(word.getStart(), word.getEnd() + 1, replaceStr);\n        }\n        return result.toString();\n    }\n}\n```\n\n### 3.4 实现脏词动态更新\n\n动态性是系统弹性的关键。通过结合数据库和缓存，可以实现在不重启服务的情况下更新脏词库。\n\n#### 数据库设计\n\n建议设计两个表：\n- `sensitive_word`：存储脏词和基础配置\n- `sensitive_word_config`：存储全局配置（如是否启用数字忽略等）\n\n#### 动态词库管理服务\n\n```java\n@Service\npublic class SensitiveWordManager {\n    @Autowired\n    private SensitiveWordRepository wordRepository;\n    \n    private volatile SensitiveWordFilter currentFilter;\n    private ScheduledExecutorService scheduler = \n        Executors.newSingleThreadScheduledExecutor();\n    \n    @PostConstruct\n    public void init() {\n        loadWords();\n        \u002F\u002F 每隔1小时自动刷新一次\n        scheduler.scheduleAtFixedRate(this::loadWords, 1, 1, TimeUnit.HOURS);\n    }\n    \n    public void loadWords() {\n        try {\n            Set\u003CString> newWordSet = wordRepository.findAllActiveWords();\n            SensitiveWordFilter newFilter = new SensitiveWordFilter();\n            newFilter.init(newWordSet);\n            this.currentFilter = newFilter; \u002F\u002F 原子性切换引用\n        } catch (Exception e) {\n            \u002F\u002F 记录日志，继续使用旧版本词库\n        }\n    }\n    \n    \u002F\u002F 提供手动刷新接口\n    @EventListener\n    public void handleRefreshEvent(RefreshEvent event) {\n        loadWords();\n    }\n}\n```\n\n### 3.5 与SpringBoot集成：使用AOP实现无缝过滤\n\n利用Spring AOP，我们可以优雅地将过滤逻辑织入到业务代码中：\n\n```java\n@Aspect\n@Component\npublic class SensitiveWordAspect {\n    @Autowired\n    private SensitiveWordFilter sensitiveWordFilter;\n    \n    \u002F\u002F 拦截所有带有@SensitiveFilter注解的方法\n    @Around(\"@annotation(sensitiveFilter)\")\n    public Object filterSensitiveWords(ProceedingJoinPoint joinPoint, \n                                     SensitiveFilter sensitiveFilter) throws Throwable {\n        Object[] args = joinPoint.getArgs();\n        \n        \u002F\u002F 遍历方法参数，处理String类型参数\n        for (int i = 0; i \u003C args.length; i++) {\n            if (args[i] instanceof String) {\n                String originalText = (String) args[i];\n                \n                if (sensitiveFilter.action() == Action.REPLACE) {\n                    args[i] = sensitiveWordFilter.filter(originalText, \n                        sensitiveFilter.replacement());\n                } else if (sensitiveFilter.action() == Action.REJECT) {\n                    if (sensitiveWordFilter.containsSensitiveWord(originalText)) {\n                        throw new IllegalArgumentException(\"内容包含违规词汇\");\n                    }\n                }\n            }\n        }\n        \n        return joinPoint.proceed(args);\n    }\n}\n\n\u002F\u002F 自定义注解\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface SensitiveFilter {\n    Action action() default Action.REPLACE;\n    String replacement() default \"***\";\n}\n```\n\n### 3.6 评论审核状态管理\n\n结合脏词过滤，实现完整的评论审核流程：\n\n```java\n@Entity\npublic class Comment {\n    @Id\n    private Long id;\n    private String content;\n    private String author;\n    private LocalDateTime createTime;\n    private String status; \u002F\u002F PENDING, APPROVED, REJECTED\n    \n    \u002F\u002F 审核后可见内容\n    public String getVisibleContent() {\n        return \"APPROVED\".equals(status) ? content : \"该评论正在审核中\";\n    }\n}\n```\n\n## 四、性能优化进阶策略\n\n当系统面临极高并发或海量脏词时，可以考虑以下高级优化：\n\n### 4.1 双缓冲策略\n使用双Trie树结构，实现**零停机更新**：一个用于当前查询，一个用于后台更新，更新完成后原子切换。\n\n### 4.2 布隆过滤器预筛选\n使用布隆过滤器快速判断一段文本\"绝对不包含\"脏词，避免不必要的DFA匹配。\n\n### 4.3 多级缓存架构\n- **L1缓存**：本地内存Trie树\n- **L2缓存**：Redis集群共享词库状态\n- **L3存储**：MySQL持久化脏词数据\n\n### 4.4 算法优化选项\n- **双数组Trie**：更节省内存的Trie树实现\n- **AC自动机**：支持多模式匹配，可以同时匹配所有模式串\n\n## 五、实践建议与注意事项\n\n### 5.1 脏词库建设\n- **分类管理**：按业务场景分类（政治、辱骂、广告等）\n- **权重机制**：不同类别脏词设置不同处理严格程度\n- **定期更新**：建立脏词库定期更新机制\n\n### 5.2 异常处理与降级策略\n- **故障降级**：过滤服务异常时，可降级为人工审核\n- **超时控制**：设置过滤操作超时时间，避免影响主流程\n- **监控告警**：监控过滤成功率、耗时等关键指标\n\n### 5.3 用户体验优化\n- **智能提示**：非恶意用户提示具体违规内容，引导修改\n- **多语言支持**：考虑不同语言字符集特点\n- **上下文理解**：结合上下文降低误判率（如学术讨论场景）\n\n## 六、总结\n\n本文介绍了基于SpringBoot和DFA算法的高性能脏词过滤系统完整实现方案。该方案具有以下特点：\n\n| 特性 | 实现方案 | 优势 |\n|------|---------|------|\n| **高性能** | DFA算法+Trie树 | O(n)时间复杂度，性能稳定 |\n| **动态更新** | 数据库+定时任务+原子切换 | 支持热更新，无需重启 |\n| **灵活集成** | 注解+AOP切面 | 非侵入式，业务代码无感知 |\n| **可扩展性** | 多级缓存+分布式架构 | 支持水平扩展 |\n\n实际项目中，建议根据具体业务场景选择合适的实现复杂度。对于中小型项目，可以从基础DFA实现开始；对于高并发大型平台，可以考虑引入更高级的优化策略。\n\n此方案已在多个内容平台实践中验证，能有效平衡性能需求与内容安全要求，为社区健康氛围提供技术保障。","\u003Cp data-line=\"0\">评论系统是内容平台和社交应用的核心功能之一，而脏词过滤是保障内容安全、维护社区氛围的关键技术。本文将详细介绍如何在SpringBoot中实现一个\u003Cstrong>高性能、支持动态更新\u003C\u002Fstrong>的脏词过滤系统。\u003C\u002Fp>\n\u003Ch2 data-line=\"2\" id=\"一、背景与需求分析\">一、背景与需求分析\u003C\u002Fh2>\n\u003Cp data-line=\"4\">随着Web应用的发展，用户生成内容（UGC）面临着严峻的内容安全挑战。脏词过滤系统需要满足以下核心需求：\u003C\u002Fp>\n\u003Cul data-line=\"6\">\n\u003Cli data-line=\"6\">\u003Cstrong>高性能\u003C\u002Fstrong>：处理海量用户评论时不能成为系统瓶颈\u003C\u002Fli>\n\u003Cli data-line=\"7\">\u003Cstrong>动态更新\u003C\u002Fstrong>：脏词库需要支持实时更新，无需重启服务\u003C\u002Fli>\n\u003Cli data-line=\"8\">\u003Cstrong>准确性\u003C\u002Fstrong>：准确识别敏感词，同时控制误判率\u003C\u002Fli>\n\u003Cli data-line=\"9\">\u003Cstrong>灵活性\u003C\u002Fstrong>：支持多种处理策略（替换、拒绝、审核）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 data-line=\"11\" id=\"二、核心技术选型：为何选择DFA算法？\">二、核心技术选型：为何选择DFA算法？\u003C\u002Fh2>\n\u003Cp data-line=\"13\">在脏词过滤场景下，\u003Cstrong>DFA（确定有限状态自动机）算法\u003C\u002Fstrong>相比传统方法有显著优势。\u003C\u002Fp>\n\u003Ch3 data-line=\"15\" id=\"2.1 传统方法的瓶颈\">2.1 传统方法的瓶颈\u003C\u002Fh3>\n\u003Cul data-line=\"17\">\n\u003Cli data-line=\"17\">\u003Cstrong>暴力匹配\u003C\u002Fstrong>：遍历每个脏词检查文本是否包含，时间复杂度O(n*m)，随脏词数量增加性能急剧下降\u003C\u002Fli>\n\u003Cli data-line=\"18\">\u003Cstrong>正则表达式\u003C\u002Fstrong>：编译开销大，动态更新困难，复杂匹配可能产生回溯问题\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 data-line=\"20\" id=\"2.2 DFA算法的优势\">2.2 DFA算法的优势\u003C\u002Fh3>\n\u003Cp data-line=\"22\">DFA算法通过\u003Cstrong>前缀树（Trie树）\u003C\u002Fstrong> 结构实现高效匹配：\u003C\u002Fp>\n\u003Cul data-line=\"23\">\n\u003Cli data-line=\"23\">\u003Cstrong>时间复杂度O(n)\u003C\u002Fstrong>：只需对输入文本扫描一次，性能不受脏词库大小影响\u003C\u002Fli>\n\u003Cli data-line=\"24\">\u003Cstrong>内存效率\u003C\u002Fstrong>：共享相同前缀的敏感词共享树节点，节省内存空间\u003C\u002Fli>\n\u003Cli data-line=\"25\">\u003Cstrong>匹配准确\u003C\u002Fstrong>：支持完整词匹配，避免误匹配\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp data-line=\"27\">以下是DFA算法的核心原理状态转移图：\u003C\u002Fp>\n\u003Cp  data-line=\"29\" class=\"md-editor-mermaid\" data-mermaid-theme=\"light\" data-closed=\"true\" data-processed=\"\" data-content=\"flowchart TD\n    A[开始匹配] --&gt; B[从文本当前位置i及根节点开始]\n    B --&gt; C{当前字符是否匹配状态转移?}\n    C -- 是 --&gt; D[转移到下一个状态]\n    C -- 否 --&gt; E[回溯&lt;br&gt;从位置i+1重新开始]\n    D --&gt; F{是否到达接受状态?}\n    F -- 是 --&gt; G[标记敏感词&lt;br&gt;记录位置]\n    G --&gt; H[回溯到词结束后继续匹配]\n    F -- 否 --&gt; C\n    H --&gt; B\n    E --&gt; I[i++]\n    I --&gt; B\">\u003Csvg aria-roledescription=\"flowchart-v2\" role=\"graphics-document document\" viewBox=\"0 0 648.34375 1145.75\" style=\"max-width: 648.34375px;\" class=\"flowchart\" xmlns:xlink=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxlink\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"100%\" id=\"mkbw052dj7bsbp9p46\">\u003Cstyle>#mkbw052dj7bsbp9p46{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mkbw052dj7bsbp9p46 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mkbw052dj7bsbp9p46 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mkbw052dj7bsbp9p46 .error-icon{fill:#552222;}#mkbw052dj7bsbp9p46 .error-text{fill:#552222;stroke:#552222;}#mkbw052dj7bsbp9p46 .edge-thickness-normal{stroke-width:1px;}#mkbw052dj7bsbp9p46 .edge-thickness-thick{stroke-width:3.5px;}#mkbw052dj7bsbp9p46 .edge-pattern-solid{stroke-dasharray:0;}#mkbw052dj7bsbp9p46 .edge-thickness-invisible{stroke-width:0;fill:none;}#mkbw052dj7bsbp9p46 .edge-pattern-dashed{stroke-dasharray:3;}#mkbw052dj7bsbp9p46 .edge-pattern-dotted{stroke-dasharray:2;}#mkbw052dj7bsbp9p46 .marker{fill:#333333;stroke:#333333;}#mkbw052dj7bsbp9p46 .marker.cross{stroke:#333333;}#mkbw052dj7bsbp9p46 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mkbw052dj7bsbp9p46 p{margin:0;}#mkbw052dj7bsbp9p46 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mkbw052dj7bsbp9p46 .cluster-label text{fill:#333;}#mkbw052dj7bsbp9p46 .cluster-label span{color:#333;}#mkbw052dj7bsbp9p46 .cluster-label span p{background-color:transparent;}#mkbw052dj7bsbp9p46 .label text,#mkbw052dj7bsbp9p46 span{fill:#333;color:#333;}#mkbw052dj7bsbp9p46 .node rect,#mkbw052dj7bsbp9p46 .node circle,#mkbw052dj7bsbp9p46 .node ellipse,#mkbw052dj7bsbp9p46 .node polygon,#mkbw052dj7bsbp9p46 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mkbw052dj7bsbp9p46 .rough-node .label text,#mkbw052dj7bsbp9p46 .node .label text,#mkbw052dj7bsbp9p46 .image-shape .label,#mkbw052dj7bsbp9p46 .icon-shape .label{text-anchor:middle;}#mkbw052dj7bsbp9p46 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mkbw052dj7bsbp9p46 .rough-node .label,#mkbw052dj7bsbp9p46 .node .label,#mkbw052dj7bsbp9p46 .image-shape .label,#mkbw052dj7bsbp9p46 .icon-shape .label{text-align:center;}#mkbw052dj7bsbp9p46 .node.clickable{cursor:pointer;}#mkbw052dj7bsbp9p46 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mkbw052dj7bsbp9p46 .arrowheadPath{fill:#333333;}#mkbw052dj7bsbp9p46 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mkbw052dj7bsbp9p46 .flowchart-link{stroke:#333333;fill:none;}#mkbw052dj7bsbp9p46 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mkbw052dj7bsbp9p46 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mkbw052dj7bsbp9p46 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mkbw052dj7bsbp9p46 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mkbw052dj7bsbp9p46 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mkbw052dj7bsbp9p46 .cluster text{fill:#333;}#mkbw052dj7bsbp9p46 .cluster span{color:#333;}#mkbw052dj7bsbp9p46 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mkbw052dj7bsbp9p46 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mkbw052dj7bsbp9p46 rect.text{fill:none;stroke-width:0;}#mkbw052dj7bsbp9p46 .icon-shape,#mkbw052dj7bsbp9p46 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mkbw052dj7bsbp9p46 .icon-shape p,#mkbw052dj7bsbp9p46 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mkbw052dj7bsbp9p46 .icon-shape rect,#mkbw052dj7bsbp9p46 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mkbw052dj7bsbp9p46 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mkbw052dj7bsbp9p46 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mkbw052dj7bsbp9p46 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}\u003C\u002Fstyle>\u003Cg>\u003Cmarker orient=\"auto\" markerHeight=\"8\" markerWidth=\"8\" markerUnits=\"userSpaceOnUse\" refY=\"5\" refX=\"5\" viewBox=\"0 0 10 10\" class=\"marker flowchart-v2\" id=\"mkbw052dj7bsbp9p46_flowchart-v2-pointEnd\">\u003Cpath style=\"stroke-width: 1; stroke-dasharray: 1, 0;\" class=\"arrowMarkerPath\" d=\"M 0 0 L 10 5 L 0 10 z\">\u003C\u002Fpath>\u003C\u002Fmarker>\u003Cmarker orient=\"auto\" markerHeight=\"8\" markerWidth=\"8\" markerUnits=\"userSpaceOnUse\" refY=\"5\" refX=\"4.5\" viewBox=\"0 0 10 10\" class=\"marker flowchart-v2\" id=\"mkbw052dj7bsbp9p46_flowchart-v2-pointStart\">\u003Cpath style=\"stroke-width: 1; stroke-dasharray: 1, 0;\" class=\"arrowMarkerPath\" d=\"M 0 5 L 10 10 L 10 0 z\">\u003C\u002Fpath>\u003C\u002Fmarker>\u003Cmarker orient=\"auto\" markerHeight=\"11\" markerWidth=\"11\" markerUnits=\"userSpaceOnUse\" refY=\"5\" refX=\"11\" viewBox=\"0 0 10 10\" class=\"marker flowchart-v2\" id=\"mkbw052dj7bsbp9p46_flowchart-v2-circleEnd\">\u003Ccircle style=\"stroke-width: 1; stroke-dasharray: 1, 0;\" class=\"arrowMarkerPath\" r=\"5\" cy=\"5\" cx=\"5\">\u003C\u002Fcircle>\u003C\u002Fmarker>\u003Cmarker orient=\"auto\" markerHeight=\"11\" markerWidth=\"11\" markerUnits=\"userSpaceOnUse\" refY=\"5\" refX=\"-1\" viewBox=\"0 0 10 10\" class=\"marker flowchart-v2\" id=\"mkbw052dj7bsbp9p46_flowchart-v2-circleStart\">\u003Ccircle style=\"stroke-width: 1; stroke-dasharray: 1, 0;\" class=\"arrowMarkerPath\" r=\"5\" cy=\"5\" cx=\"5\">\u003C\u002Fcircle>\u003C\u002Fmarker>\u003Cmarker orient=\"auto\" markerHeight=\"11\" markerWidth=\"11\" markerUnits=\"userSpaceOnUse\" refY=\"5.2\" refX=\"12\" viewBox=\"0 0 11 11\" class=\"marker cross flowchart-v2\" id=\"mkbw052dj7bsbp9p46_flowchart-v2-crossEnd\">\u003Cpath style=\"stroke-width: 2; stroke-dasharray: 1, 0;\" class=\"arrowMarkerPath\" d=\"M 1,1 l 9,9 M 10,1 l -9,9\">\u003C\u002Fpath>\u003C\u002Fmarker>\u003Cmarker orient=\"auto\" markerHeight=\"11\" markerWidth=\"11\" markerUnits=\"userSpaceOnUse\" refY=\"5.2\" refX=\"-1\" viewBox=\"0 0 11 11\" class=\"marker cross flowchart-v2\" id=\"mkbw052dj7bsbp9p46_flowchart-v2-crossStart\">\u003Cpath style=\"stroke-width: 2; stroke-dasharray: 1, 0;\" class=\"arrowMarkerPath\" d=\"M 1,1 l 9,9 M 10,1 l -9,9\">\u003C\u002Fpath>\u003C\u002Fmarker>\u003Cg class=\"root\">\u003Cg class=\"clusters\">\u003C\u002Fg>\u003Cg class=\"edgePaths\">\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_A_B_0\" d=\"M510.344,62L510.344,66.167C510.344,70.333,510.344,78.667,510.344,86.333C510.344,94,510.344,101,510.344,104.5L510.344,108\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_B_C_0\" d=\"M380.344,181.662L356.786,187.219C333.229,192.775,286.115,203.887,262.628,213.027C239.141,222.167,239.281,229.334,239.351,232.917L239.422,236.501\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_C_D_0\" d=\"M181.977,434.852L168.647,450.522C155.318,466.193,128.659,497.534,115.329,520.704C102,543.875,102,558.875,102,566.375L102,573.875\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_C_E_0\" d=\"M297.628,434.247L311.135,450.019C324.642,465.79,351.657,497.332,365.164,518.604C378.672,539.875,378.672,550.875,378.672,556.375L378.672,561.875\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_D_F_0\" d=\"M102,631.875L102,638.042C102,644.208,102,656.542,105.903,671.233C109.806,685.924,117.613,702.972,121.516,711.496L125.419,720.021\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_F_G_0\" d=\"M156.367,882.25L156.284,888.333C156.201,894.417,156.034,906.583,155.951,918.167C155.867,929.75,155.867,940.75,155.867,946.25L155.867,951.75\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_G_H_0\" d=\"M155.867,1033.75L155.867,1037.917C155.867,1042.083,155.867,1050.417,170.3,1058.572C184.733,1066.728,213.598,1074.706,228.031,1078.695L242.464,1082.684\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_F_C_0\" d=\"M195.014,733.021L202.345,722.33C209.676,711.639,224.338,690.257,231.669,668.899C239,647.542,239,626.208,239,602.875C239,579.542,239,554.208,239.074,536.125C239.148,518.042,239.297,507.208,239.371,501.791L239.445,496.375\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_H_B_0\" d=\"M455.212,1083.75L472.374,1079.583C489.535,1075.417,523.857,1067.083,541.018,1052.25C558.18,1037.417,558.18,1016.083,558.18,992.75C558.18,969.417,558.18,944.083,558.18,909.594C558.18,875.104,558.18,831.458,558.18,789.813C558.18,748.167,558.18,708.521,558.18,678.031C558.18,647.542,558.18,626.208,558.18,602.875C558.18,579.542,558.18,554.208,558.18,514.385C558.18,474.563,558.18,420.25,558.18,367.938C558.18,315.625,558.18,265.313,555.464,236.524C552.749,207.735,547.319,200.469,544.604,196.837L541.889,193.204\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_E_I_0\" d=\"M378.672,643.875L378.672,648.042C378.672,652.208,378.672,660.542,386.831,679.448C394.99,698.354,411.307,727.834,419.466,742.573L427.625,757.313\">\u003C\u002Fpath>\u003Cpath marker-end=\"url(#mkbw052dj7bsbp9p46_flowchart-v2-pointEnd)\" style=\"\" class=\"edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link\" id=\"L_I_B_0\" d=\"M459.453,760.813L467.935,745.49C476.417,730.167,493.38,699.521,501.862,673.531C510.344,647.542,510.344,626.208,510.344,602.875C510.344,579.542,510.344,554.208,510.344,514.385C510.344,474.563,510.344,420.25,510.344,367.938C510.344,315.625,510.344,265.313,510.344,236.656C510.344,208,510.344,201,510.344,197.5L510.344,194\">\u003C\u002Fpath>\u003C\u002Fg>\u003Cg class=\"edgeLabels\">\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(102, 528.875)\" class=\"edgeLabel\">\u003Cg transform=\"translate(-8, -12)\" class=\"label\">\u003CforeignObject height=\"24\" width=\"16\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003Cp>是\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(378.671875, 528.875)\" class=\"edgeLabel\">\u003Cg transform=\"translate(-8, -12)\" class=\"label\">\u003CforeignObject height=\"24\" width=\"16\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003Cp>否\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(155.8671875, 918.75)\" class=\"edgeLabel\">\u003Cg transform=\"translate(-8, -12)\" class=\"label\">\u003CforeignObject height=\"24\" width=\"16\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003Cp>是\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(239, 604.875)\" class=\"edgeLabel\">\u003Cg transform=\"translate(-8, -12)\" class=\"label\">\u003CforeignObject height=\"24\" width=\"16\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003Cp>否\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"edgeLabel\">\u003Cg transform=\"translate(0, 0)\" class=\"label\">\u003CforeignObject height=\"0\" width=\"0\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" class=\"labelBkg\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"edgeLabel\">\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003C\u002Fg>\u003Cg class=\"nodes\">\u003Cg transform=\"translate(510.34375, 35)\" id=\"flowchart-A-0\" class=\"node default\">\u003Crect height=\"54\" width=\"124\" y=\"-27\" x=\"-62\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-32, -12)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"24\" width=\"64\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>开始匹配\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(510.34375, 151)\" id=\"flowchart-B-1\" class=\"node default\">\u003Crect height=\"78\" width=\"260\" y=\"-39\" x=\"-130\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-100, -24)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"48\" width=\"200\">\u003Cdiv style=\"display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>从文本当前位置i及根节点开始\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(239, 365.9375)\" id=\"flowchart-C-3\" class=\"node default\">\u003Cpolygon transform=\"translate(-125.9375,125.9375)\" class=\"label-container\" points=\"125.9375,0 251.875,-125.9375 125.9375,-251.875 0,-125.9375\">\u003C\u002Fpolygon>\u003Cg transform=\"translate(-98.9375, -12)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"24\" width=\"197.875\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>当前字符是否匹配状态转移?\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(102, 604.875)\" id=\"flowchart-D-5\" class=\"node default\">\u003Crect height=\"54\" width=\"188\" y=\"-27\" x=\"-94\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-64, -12)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"24\" width=\"128\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>转移到下一个状态\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(378.671875, 604.875)\" id=\"flowchart-E-7\" class=\"node default\">\u003Crect height=\"78\" width=\"193.34375\" y=\"-39\" x=\"-96.671875\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-66.671875, -24)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"48\" width=\"133.34375\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>回溯\u003Cbr>从位置i+1重新开始\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(155.8671875, 787.8125)\" id=\"flowchart-F-9\" class=\"node default\">\u003Cpolygon transform=\"translate(-93.9375,93.9375)\" class=\"label-container\" points=\"93.9375,0 187.875,-93.9375 93.9375,-187.875 0,-93.9375\">\u003C\u002Fpolygon>\u003Cg transform=\"translate(-66.9375, -12)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"24\" width=\"133.875\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>是否到达接受状态?\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(155.8671875, 994.75)\" id=\"flowchart-G-11\" class=\"node default\">\u003Crect height=\"78\" width=\"140\" y=\"-39\" x=\"-70\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-40, -24)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"48\" width=\"80\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>标记敏感词\u003Cbr>记录位置\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(344.0078125, 1110.75)\" id=\"flowchart-H-13\" class=\"node default\">\u003Crect height=\"54\" width=\"236\" y=\"-27\" x=\"-118\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-88, -12)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"24\" width=\"176\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>回溯到词结束后继续匹配\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003Cg transform=\"translate(444.5078125, 787.8125)\" id=\"flowchart-I-19\" class=\"node default\">\u003Crect height=\"54\" width=\"81.34375\" y=\"-27\" x=\"-40.671875\" style=\"\" class=\"basic label-container\">\u003C\u002Frect>\u003Cg transform=\"translate(-10.671875, -12)\" style=\"\" class=\"label\">\u003Crect>\u003C\u002Frect>\u003CforeignObject height=\"24\" width=\"21.34375\">\u003Cdiv style=\"display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\u003Cspan class=\"nodeLabel\">\u003Cp>i++\u003C\u002Fp>\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002FforeignObject>\u003C\u002Fg>\u003C\u002Fg>\u003C\u002Fg>\u003C\u002Fg>\u003C\u002Fg>\u003C\u002Fsvg>\u003C\u002Fp>\u003Ch2 data-line=\"44\" id=\"三、系统设计与实现\">三、系统设计与实现\u003C\u002Fh2>\n\u003Ch3 data-line=\"46\" id=\"3.1 整体架构设计\">3.1 整体架构设计\u003C\u002Fh3>\n\u003Cp data-line=\"48\">系统采用分层架构，保证各模块职责单一：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"50\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">脏词过滤系统架构\n├── 数据存储层 (MySQL + Redis)\n├── 算法核心层 (DFA引擎)\n├── 业务服务层 (过滤服务、管理服务)\n└── 应用接入层 (AOP切面、注解)\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch3 data-line=\"58\" id=\"3.2 核心数据结构：Trie树节点\">3.2 核心数据结构：Trie树节点\u003C\u002Fh3>\n\u003Cp data-line=\"60\">Trie树节点是DFA算法的基石，每个节点代表一个字符状态：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"62\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">TrieNode\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 子节点映射：Key为字符，Value为对应的子节点\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> Map&lt;Character, TrieNode&gt; children = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">HashMap\u003C\u002Fspan>&lt;&gt;();\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 标记当前节点是否为某个脏词的结尾\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">boolean\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">isEnd\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>;\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 可选：用于记录到达此节点的完整脏词\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> String keyword;\n    \n    \u003Cspan class=\"hljs-comment\">\u002F\u002F Getter和Setter方法\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> TrieNode \u003Cspan class=\"hljs-title function_\">getChild\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(\u003Cspan class=\"hljs-type\">char\u003C\u002Fspan> c)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> children.get(c);\n    }\n    \n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> TrieNode \u003Cspan class=\"hljs-title function_\">addChild\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(\u003Cspan class=\"hljs-type\">char\u003C\u002Fspan> c)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> children.computeIfAbsent(c, k -&gt; \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">TrieNode\u003C\u002Fspan>());\n    }\n    \n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">boolean\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">isEnd\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">()\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> isEnd;\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch3 data-line=\"86\" id=\"3.3 过滤器核心引擎\">3.3 过滤器核心引擎\u003C\u002Fh3>\n\u003Cp data-line=\"88\">这个类负责构建Trie树并执行过滤逻辑：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"90\" class=\"md-editor-code\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-meta\">@Component\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">SensitiveWordFilter\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">volatile\u003C\u002Fspan> TrieNode root; \u003Cspan class=\"hljs-comment\">\u002F\u002F 使用volatile保证多线程可见性\u003C\u002Fspan>\n    \n    \u003Cspan class=\"hljs-comment\">\u002F**\n     * 构建Trie树\n     *\u002F\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> TrieNode \u003Cspan class=\"hljs-title function_\">buildTrie\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(Set&lt;String&gt; sensitiveWords)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-type\">TrieNode\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">root\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">TrieNode\u003C\u002Fspan>();\n        \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (String word : sensitiveWords) {\n            \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (word == \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan> || word.trim().isEmpty()) \u003Cspan class=\"hljs-keyword\">continue\u003C\u002Fspan>;\n            \u003Cspan class=\"hljs-type\">TrieNode\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">currentNode\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> root;\n            \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-type\">char\u003C\u002Fspan> c : word.toCharArray()) {\n                currentNode = currentNode.addChild(c);\n            }\n            currentNode.setEnd(\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>);\n            currentNode.setKeyword(word);\n        }\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> root;\n    }\n    \n    \u003Cspan class=\"hljs-comment\">\u002F**\n     * 核心DFA匹配：判断是否包含脏词\n     *\u002F\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">boolean\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">containsSensitiveWord\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(String text)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (text == \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan> || text.length() &lt; minWordLength) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>;\n        \n        \u003Cspan class=\"hljs-type\">char\u003C\u002Fspan>[] chars = text.toCharArray();\n        \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-type\">int\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">i\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>; i &lt; chars.length; i++) {\n            \u003Cspan class=\"hljs-type\">TrieNode\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">node\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> root;\n            \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-type\">int\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">j\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> i; j &lt; chars.length; j++) {\n                node = node.getChild(chars[j]);\n                \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (node == \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">break\u003C\u002Fspan>; \u003Cspan class=\"hljs-comment\">\u002F\u002F 转移失败，从i+1开始下一轮\u003C\u002Fspan>\n                \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (node.isEnd()) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>; \u003Cspan class=\"hljs-comment\">\u002F\u002F 匹配到一个脏词\u003C\u002Fspan>\n            }\n        }\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>;\n    }\n    \n    \u003Cspan class=\"hljs-comment\">\u002F**\n     * 替换文本中的脏词\n     *\u002F\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> String \u003Cspan class=\"hljs-title function_\">filter\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(String text, String replacement)\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (text == \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>;\n        \n        \u003Cspan class=\"hljs-type\">StringBuilder\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">result\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">StringBuilder\u003C\u002Fspan>(text);\n        List&lt;SensitiveWordResult&gt; foundWords = findAllWords(text);\n        \n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 从后往前替换，避免影响索引\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-type\">int\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">i\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> foundWords.size() - \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>; i &gt;= \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>; i--) {\n            \u003Cspan class=\"hljs-type\">SensitiveWordResult\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">word\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> foundWords.get(i);\n            \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">replaceStr\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> String.valueOf(replacement)\n                .repeat(word.getEnd() - word.getStart() + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>);\n            result.replace(word.getStart(), word.getEnd() + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, replaceStr);\n        }\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> result.toString();\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch3 data-line=\"151\" id=\"3.4 实现脏词动态更新\">3.4 实现脏词动态更新\u003C\u002Fh3>\n\u003Cp data-line=\"153\">动态性是系统弹性的关键。通过结合数据库和缓存，可以实现在不重启服务的情况下更新脏词库。\u003C\u002Fp>\n\u003Ch4 data-line=\"155\" id=\"数据库设计\">数据库设计\u003C\u002Fh4>\n\u003Cp data-line=\"157\">建议设计两个表：\u003C\u002Fp>\n\u003Cul data-line=\"158\">\n\u003Cli data-line=\"158\">\u003Ccode>sensitive_word\u003C\u002Fcode>：存储脏词和基础配置\u003C\u002Fli>\n\u003Cli data-line=\"159\">\u003Ccode>sensitive_word_config\u003C\u002Fcode>：存储全局配置（如是否启用数字忽略等）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch4 data-line=\"161\" id=\"动态词库管理服务\">动态词库管理服务\u003C\u002Fh4>\n\n        \u003Cdetails  data-line=\"163\" class=\"md-editor-code\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-meta\">@Service\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">SensitiveWordManager\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-meta\">@Autowired\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> SensitiveWordRepository wordRepository;\n    \n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">volatile\u003C\u002Fspan> SensitiveWordFilter currentFilter;\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">ScheduledExecutorService\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">scheduler\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \n        Executors.newSingleThreadScheduledExecutor();\n    \n    \u003Cspan class=\"hljs-meta\">@PostConstruct\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">init\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">()\u003C\u002Fspan> {\n        loadWords();\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 每隔1小时自动刷新一次\u003C\u002Fspan>\n        scheduler.scheduleAtFixedRate(\u003Cspan class=\"hljs-built_in\">this\u003C\u002Fspan>::loadWords, \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, TimeUnit.HOURS);\n    }\n    \n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">loadWords\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">()\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n            Set&lt;String&gt; newWordSet = wordRepository.findAllActiveWords();\n            \u003Cspan class=\"hljs-type\">SensitiveWordFilter\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">newFilter\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">SensitiveWordFilter\u003C\u002Fspan>();\n            newFilter.init(newWordSet);\n            \u003Cspan class=\"hljs-built_in\">this\u003C\u002Fspan>.currentFilter = newFilter; \u003Cspan class=\"hljs-comment\">\u002F\u002F 原子性切换引用\u003C\u002Fspan>\n        } \u003Cspan class=\"hljs-keyword\">catch\u003C\u002Fspan> (Exception e) {\n            \u003Cspan class=\"hljs-comment\">\u002F\u002F 记录日志，继续使用旧版本词库\u003C\u002Fspan>\n        }\n    }\n    \n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 提供手动刷新接口\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-meta\">@EventListener\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">handleRefreshEvent\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(RefreshEvent event)\u003C\u002Fspan> {\n        loadWords();\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch3 data-line=\"199\" id=\"3.5 与SpringBoot集成：使用AOP实现无缝过滤\">3.5 与SpringBoot集成：使用AOP实现无缝过滤\u003C\u002Fh3>\n\u003Cp data-line=\"201\">利用Spring AOP，我们可以优雅地将过滤逻辑织入到业务代码中：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"203\" class=\"md-editor-code\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-meta\">@Aspect\u003C\u002Fspan>\n\u003Cspan class=\"hljs-meta\">@Component\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">SensitiveWordAspect\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-meta\">@Autowired\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> SensitiveWordFilter sensitiveWordFilter;\n    \n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 拦截所有带有@SensitiveFilter注解的方法\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-meta\">@Around(&quot;@annotation(sensitiveFilter)&quot;)\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> Object \u003Cspan class=\"hljs-title function_\">filterSensitiveWords\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">(ProceedingJoinPoint joinPoint, \n                                     SensitiveFilter sensitiveFilter)\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">throws\u003C\u002Fspan> Throwable {\n        Object[] args = joinPoint.getArgs();\n        \n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 遍历方法参数，处理String类型参数\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-type\">int\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">i\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>; i &lt; args.length; i++) {\n            \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (args[i] \u003Cspan class=\"hljs-keyword\">instanceof\u003C\u002Fspan> String) {\n                \u003Cspan class=\"hljs-type\">String\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">originalText\u003C\u002Fspan> \u003Cspan class=\"hljs-operator\">=\u003C\u002Fspan> (String) args[i];\n                \n                \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (sensitiveFilter.action() == Action.REPLACE) {\n                    args[i] = sensitiveWordFilter.filter(originalText, \n                        sensitiveFilter.replacement());\n                } \u003Cspan class=\"hljs-keyword\">else\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (sensitiveFilter.action() == Action.REJECT) {\n                    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (sensitiveWordFilter.containsSensitiveWord(originalText)) {\n                        \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">IllegalArgumentException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&quot;内容包含违规词汇&quot;\u003C\u002Fspan>);\n                    }\n                }\n            }\n        }\n        \n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> joinPoint.proceed(args);\n    }\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F 自定义注解\u003C\u002Fspan>\n\u003Cspan class=\"hljs-meta\">@Target(ElementType.METHOD)\u003C\u002Fspan>\n\u003Cspan class=\"hljs-meta\">@Retention(RetentionPolicy.RUNTIME)\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-meta\">@interface\u003C\u002Fspan> SensitiveFilter {\n    Action \u003Cspan class=\"hljs-title function_\">action\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">()\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">default\u003C\u002Fspan> Action.REPLACE;\n    String \u003Cspan class=\"hljs-title function_\">replacement\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">()\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">default\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;***&quot;\u003C\u002Fspan>;\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch3 data-line=\"245\" id=\"3.6 评论审核状态管理\">3.6 评论审核状态管理\u003C\u002Fh3>\n\u003Cp data-line=\"247\">结合脏词过滤，实现完整的评论审核流程：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"249\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">java\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-java\" language=java>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-meta\">@Entity\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Comment\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-meta\">@Id\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> Long id;\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> String content;\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> String author;\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> LocalDateTime createTime;\n    \u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> String status; \u003Cspan class=\"hljs-comment\">\u002F\u002F PENDING, APPROVED, REJECTED\u003C\u002Fspan>\n    \n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 审核后可见内容\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> String \u003Cspan class=\"hljs-title function_\">getVisibleContent\u003C\u002Fspan>\u003Cspan class=\"hljs-params\">()\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;APPROVED&quot;\u003C\u002Fspan>.equals(status) ? content : \u003Cspan class=\"hljs-string\">&quot;该评论正在审核中&quot;\u003C\u002Fspan>;\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch2 data-line=\"266\" id=\"四、性能优化进阶策略\">四、性能优化进阶策略\u003C\u002Fh2>\n\u003Cp data-line=\"268\">当系统面临极高并发或海量脏词时，可以考虑以下高级优化：\u003C\u002Fp>\n\u003Ch3 data-line=\"270\" id=\"4.1 双缓冲策略\">4.1 双缓冲策略\u003C\u002Fh3>\n\u003Cp data-line=\"271\">使用双Trie树结构，实现\u003Cstrong>零停机更新\u003C\u002Fstrong>：一个用于当前查询，一个用于后台更新，更新完成后原子切换。\u003C\u002Fp>\n\u003Ch3 data-line=\"273\" id=\"4.2 布隆过滤器预筛选\">4.2 布隆过滤器预筛选\u003C\u002Fh3>\n\u003Cp data-line=\"274\">使用布隆过滤器快速判断一段文本&quot;绝对不包含&quot;脏词，避免不必要的DFA匹配。\u003C\u002Fp>\n\u003Ch3 data-line=\"276\" id=\"4.3 多级缓存架构\">4.3 多级缓存架构\u003C\u002Fh3>\n\u003Cul data-line=\"277\">\n\u003Cli data-line=\"277\">\u003Cstrong>L1缓存\u003C\u002Fstrong>：本地内存Trie树\u003C\u002Fli>\n\u003Cli data-line=\"278\">\u003Cstrong>L2缓存\u003C\u002Fstrong>：Redis集群共享词库状态\u003C\u002Fli>\n\u003Cli data-line=\"279\">\u003Cstrong>L3存储\u003C\u002Fstrong>：MySQL持久化脏词数据\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 data-line=\"281\" id=\"4.4 算法优化选项\">4.4 算法优化选项\u003C\u002Fh3>\n\u003Cul data-line=\"282\">\n\u003Cli data-line=\"282\">\u003Cstrong>双数组Trie\u003C\u002Fstrong>：更节省内存的Trie树实现\u003C\u002Fli>\n\u003Cli data-line=\"283\">\u003Cstrong>AC自动机\u003C\u002Fstrong>：支持多模式匹配，可以同时匹配所有模式串\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 data-line=\"285\" id=\"五、实践建议与注意事项\">五、实践建议与注意事项\u003C\u002Fh2>\n\u003Ch3 data-line=\"287\" id=\"5.1 脏词库建设\">5.1 脏词库建设\u003C\u002Fh3>\n\u003Cul data-line=\"288\">\n\u003Cli data-line=\"288\">\u003Cstrong>分类管理\u003C\u002Fstrong>：按业务场景分类（政治、辱骂、广告等）\u003C\u002Fli>\n\u003Cli data-line=\"289\">\u003Cstrong>权重机制\u003C\u002Fstrong>：不同类别脏词设置不同处理严格程度\u003C\u002Fli>\n\u003Cli data-line=\"290\">\u003Cstrong>定期更新\u003C\u002Fstrong>：建立脏词库定期更新机制\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 data-line=\"292\" id=\"5.2 异常处理与降级策略\">5.2 异常处理与降级策略\u003C\u002Fh3>\n\u003Cul data-line=\"293\">\n\u003Cli data-line=\"293\">\u003Cstrong>故障降级\u003C\u002Fstrong>：过滤服务异常时，可降级为人工审核\u003C\u002Fli>\n\u003Cli data-line=\"294\">\u003Cstrong>超时控制\u003C\u002Fstrong>：设置过滤操作超时时间，避免影响主流程\u003C\u002Fli>\n\u003Cli data-line=\"295\">\u003Cstrong>监控告警\u003C\u002Fstrong>：监控过滤成功率、耗时等关键指标\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 data-line=\"297\" id=\"5.3 用户体验优化\">5.3 用户体验优化\u003C\u002Fh3>\n\u003Cul data-line=\"298\">\n\u003Cli data-line=\"298\">\u003Cstrong>智能提示\u003C\u002Fstrong>：非恶意用户提示具体违规内容，引导修改\u003C\u002Fli>\n\u003Cli data-line=\"299\">\u003Cstrong>多语言支持\u003C\u002Fstrong>：考虑不同语言字符集特点\u003C\u002Fli>\n\u003Cli data-line=\"300\">\u003Cstrong>上下文理解\u003C\u002Fstrong>：结合上下文降低误判率（如学术讨论场景）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 data-line=\"302\" id=\"六、总结\">六、总结\u003C\u002Fh2>\n\u003Cp data-line=\"304\">本文介绍了基于SpringBoot和DFA算法的高性能脏词过滤系统完整实现方案。该方案具有以下特点：\u003C\u002Fp>\n\u003Ctable data-line=\"306\">\n\u003Cthead data-line=\"306\">\n\u003Ctr data-line=\"306\">\n\u003Cth>特性\u003C\u002Fth>\n\u003Cth>实现方案\u003C\u002Fth>\n\u003Cth>优势\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody data-line=\"308\">\n\u003Ctr data-line=\"308\">\n\u003Ctd>\u003Cstrong>高性能\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>DFA算法+Trie树\u003C\u002Ftd>\n\u003Ctd>O(n)时间复杂度，性能稳定\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr data-line=\"309\">\n\u003Ctd>\u003Cstrong>动态更新\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>数据库+定时任务+原子切换\u003C\u002Ftd>\n\u003Ctd>支持热更新，无需重启\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr data-line=\"310\">\n\u003Ctd>\u003Cstrong>灵活集成\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>注解+AOP切面\u003C\u002Ftd>\n\u003Ctd>非侵入式，业务代码无感知\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr data-line=\"311\">\n\u003Ctd>\u003Cstrong>可扩展性\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>多级缓存+分布式架构\u003C\u002Ftd>\n\u003Ctd>支持水平扩展\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp data-line=\"313\">实际项目中，建议根据具体业务场景选择合适的实现复杂度。对于中小型项目，可以从基础DFA实现开始；对于高并发大型平台，可以考虑引入更高级的优化策略。\u003C\u002Fp>\n\u003Cp data-line=\"315\">此方案已在多个内容平台实践中验证，能有效平衡性能需求与内容安全要求，为社区健康氛围提供技术保障。\u003C\u002Fp>\n",7520,25,"0","",2,0,296,"脏,词,#mkbw,bsbp,dj","评论系统是内容平台和社交应用的核心功能之一，而脏词过滤是保障内容安全、维护社区氛围的关键技术。本文将详细介绍如何在SpringBoot中实现一个高性能、支持动态更新的脏词过滤系统。\n一、背景与需求分析\n随着Web应用的发展，用户生成内容（UGC）面临着严峻的内容安全挑战。","2026-01-13 09:02:35"]