Day22:过滤敏感词、开发发布帖子、帖子详情

2024-03-22 03:04

本文主要是介绍Day22:过滤敏感词、开发发布帖子、帖子详情,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

过滤敏感词

前缀树

- 名称:Trie、字典树、查找树
- 特点:查找效率高,消耗内存大
- 应用:字符串检索、词频统计、字符串排序等

在这里插入图片描述

敏感词过滤器的步骤

  • 根节点不包含任何字符;
  • 其余每个节点只有一个字符;
  • 连接起来一条路就是字符串,每条路的字符串都不同;

image

(怎么感觉有点像KMP算法)

  1. 在resources文件夹下创建敏感词txt:
赌博
嫖娼
吸毒
开票

定义前缀树

  1. 在utils下创建工具类SensitiveFilter,创建内部类定义前缀树的结构:
@Component
public class SensitiveFilter {private class TrieNode {private boolean isKeywordEnd = false;//是否是敏感词的结尾private Map<Character, TrieNode> subNodes = new HashMap<>();//key是下级字符,value是下级节点public void addSubNode(Character c, TrieNode node) {subNodes.put(c, node);}public TrieNode getSubNode(Character c) {return subNodes.get(c);}public boolean isKeywordEnd() {return isKeywordEnd;}public void setKeywordEnd(boolean keywordEnd) {isKeywordEnd = keywordEnd;}}
}

根据敏感词,初始化前缀树

//注解的意思是在在Spring创建Bean的实例,设置完所有属性,解析并完成所有的Bean的依赖注入之后调用
@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) {logger.error("加载敏感词文件失败:" + e.getMessage());}
}
  • 这里使用PostConstruct注解,方便在Spring创建依赖Bean的时候就创建好前缀树;
  • InputStream is = this.getClass().getClassLoader().getResourceAsStream(“sensitive-words.txt”),这个的意思是从classpath下读取txt文件,为什么不用相对路径?

在Web应用中,你通常不能控制当前工作目录。Web服务器可能在任何位置启动你的应用,这使得使用相对路径来访问资源文件变得不可靠。使用上面的路径也就是访问构建后classes底下的文件(如果构建后没有出现,需要maven clean之后重新构建):

image

  • addKeyword方法,将String添加到前缀树:
private void addKeyword(String keyword){TrieNode tempNode = rootNode;//相当于指针从root开始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);}}}

编写过滤敏感词的方法

/*过滤敏感词参数:待过滤的文本返回:过滤后的文本*/public String filter(String text){if(text == null){return null;}//指针1:指向树TrieNode tempNode = rootNode;//指针2:指向文本开始int begin = 0;//指针3:指向文本末尾int position = 0;//结果StringBuilder sb = new StringBuilder();//利用指针3遍历文本(整个文本都要遍历)while(position < text.length()){char c = text.charAt(position);//跳过符号(有的敏感词中间有符号以规避)if(isSymbol(c)){//若指针1处于根节点,将此符号计入结果(直接跳过特殊符号),让指针2向下走一步if(tempNode == rootNode){sb.append(c);begin++;}//无论符号在开头还是中间,指针3都向下走一步position++;//指针3是整体遍历的,不管都要走continue;//进入下一轮循环}//检查下级节点tempNode = tempNode.getSubNode(c);if(tempNode == null) {//以begin开头的字符串不是敏感词sb.append(text.charAt(begin));//进入下一个位置position = ++begin;//先加后赋值//重新指向根节点tempNode = rootNode;//指针3重新到跟节点}else if (tempNode.isKeywordEnd()) {//发现敏感词,将begin-position字符串替换掉sb.append(REPLACEMENT);//进入下一个位置(end的下一个位置),两者重合begin = ++position;//重新指向根节点tempNode = rootNode;}else {//检查下一个字符position++;}}//将最后一批字符计入结果sb.append(text.substring(begin));return sb.toString();}
  • 防止有人用特殊符号隔开敏感词。判断是否是符号。
private boolean isSymbol(char c) {// 0x2E80-0x9FFF 东亚文字范围return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class FilterTests {@Autowiredprivate SensitiveFilter sensitiveFilter;@Testpublic void testSensitiveFilter() {String text = "这里可以*赌*博*,可以|嫖|娼|,可以|吸|毒|,可以*开*票*,哈哈哈";String text1 = sensitiveFilter.filter(text);System.out.println(text1);}
}

输出:

这里可以*****,可以|***|,可以|***|,可以*****,哈哈哈

发布帖子

AJAX

  • Asynchronous JavaScript and XML
  • 异步的JavaScript与XML,不是一门新技术,只是一个新的术语。
  • 使用AJAX,网页能够将增量更新呈现在页面上,而不需要刷新整个页面(异步的意思)。
  • 虽然X代表XML,但目前JSON的使用比XML更加普遍。
  • https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX

例子:使用jQuery发送AJAX请求

  1. 编写生成json字符串的工具类(CommunityUtils中重载三个方法):
public static String getJsonString(int code, String msg, Map<String, Object> map) {JSONObject json = new JSONObject();json.put("code", code);json.put("msg", msg);if(map != null) {
//            for(Map.Entry<String, Object> entry : map.entrySet()) {
//                json.put(entry.getKey(), entry.getValue());
//            }json.putAll(map);}return json.toJSONString();}//重载public static String getJsonString(int code, String msg) {return getJsonString(code, msg, null);}//重载public static String getJsonString(int code) {return getJsonString(code, null, null);}
  1. 编写controller接受异步请求:(AlphaController中)
    @RequestMapping(path = "/ajax", method = RequestMethod.POST)@ResponseBodypublic String testAjax(String name, int age) {System.out.println(name);System.out.println(age);return CommunityUtil.getJsonString(0, "操作成功");}
  • 因为返回的是json字符串而不是页面 ,所以使用@ResponseBody注解;
  1. 测试,编写一个静态html,在其中点击按钮就提交相应的json数据;
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>ajax</title>
</head>
<body>
<p><input type="button" value="发送" onclick="send();">
</p><scriptsrc="https://code.jquery.com/jquery-3.7.1.min.js"integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous">
</script>
<script>function send() {$.ajax({url: "/community/alpha/ajax",type: "post",data: {name: "zhangsan",age: 18},success: function (data) {console.log(typeof (data))console.log(data);// 将json字符串转换为json对象data = $.parseJSON(data);console.log(typeof (data));console.log(data.code)}});}
</script>
</body>
</html>

开发发布帖子功能

  1. DAO层添加insert接口:
@Mapper
public interface DiscussPostMapper {//userId为0时,表示查询所有用户的帖子,如果不为0,表示查询指定用户的帖子//offset表示起始行号,limit表示每页最多显示的行数List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);//查询帖子的行数//userId为0时,表示查询所有用户的帖子int selectDiscussPostRows(@Param("userId") int userId);//@param注解用于给参数取别名,拼到sql语句中,如果只有一个参数,并且在<if>标签里,则必须加别名int insertDiscussPost(DiscussPost discussPost);}

修改Mapper-xml:

<sql id = "insertFields">user_id, title, content, type, status, create_time, comment_count, score
</sql><insert id="insertDiscussPost" parameterType="DiscussPost">insert into discuss_post (<include refid="insertFields"></include>)values (#{userId}, #{title}, #{content}, #{type}, #{status}, #{createTime}, #{commentCount}, #{score})
</insert>
  1. Service层对内容进行敏感词过滤等:DiscussPostService
public int addDiscussPost(DiscussPost discussPost) {if(discussPost == null) {throw new IllegalArgumentException("参数不能为空");}//转义HTML标记discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));//过滤敏感词discussPost.setTitle(sensitiveFilter.filter(discussPost.getTitle()));discussPost.setContent(sensitiveFilter.filter(discussPost.getContent()));return discussPostMapper.insertDiscussPost(discussPost);}
  1. Controller层addpost:
@Controller
@RequestMapping("/discuss")
public class DiscussPostController {@Autowiredprivate DiscussPostService discussPostService;@Autowiredprivate HostHolder hostHolder;@RequestMapping(path = "/add", method = RequestMethod.POST)@ResponseBodypublic String addDiscussPost(String title, String content) {User user = hostHolder.getUser();if(user == null) {return CommunityUtil.getJsonString(403, "你还没有登录!");}DiscussPost post = new DiscussPost();post.setUserId(user.getId());post.setTitle(title);post.setContent(content);post.setCreateTime(new Date());discussPostService.addDiscussPost(post);//报错的情况将来统一处理return CommunityUtil.getJsonString(0, "发布成功!");}}
  1. 测试访问index发现报错:
com.mysql.cj.exceptions.UnableToConnectException: Public Key Retrieval is not allowed at java.bas

解决方法,修改properties文件:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong&allowPublicKeyRetrieval=true

添加allowPublicKeyRetrieval=true

  1. 修改index.html

image

image

这里修改成不登录发布按钮不显示

  1. 修改index.js
$(function(){$("#publishBtn").click(publish);
});function publish() {$("#publishModal").modal("hide");//获取标题和内容var title = $("#recipient-name").val();var content = $("#message-text").val();//发送异步请求(POST)$.post(CONTEXT_PATH + "/discuss/add",{"title":title, "content":content},function(data){data = $.parseJSON(data);//将字符串转换为json对象//在提示框中显示返回的消息$("#hintBody").text(data.msg);//显示提示框$("#hintModal").modal("show");//2秒后自动隐藏提示框setTimeout(function(){$("#hintModal").modal("hide");//刷新页面if(data.code == 0){//发布成功window.location.reload();//刷新页面}}, 2000);});$("#hintModal").modal("show");setTimeout(function(){$("#hintModal").modal("hide");}, 2000);
}

这个就是上面那个id。

  1. 测试

image

bug解决

  • 这里终于解决了bug,疑似是js文件版本不对的问题,导致点击下拉框和发布框的时候都弹不出来啊,改成了原来的index.html的js,就恢复正常了:
	<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script>

image

开发帖子详情

Dao层:DiscussPostMapper

  1. 添加一个方法:
    DiscussPost selectDiscussPostById(int id);
  1. 修改Mapper.xml
    <select id="selectDiscussPostById" resultType="DiscussPost">select<include refid="selectFields"></include>from discuss_postwhere id = #{id}</select>

Service层:DiscussPostService

 public DiscussPost findDiscussPostById(int id) {return discussPostMapper.selectDiscussPostById(id);}

Controller层:DiscussPostController

  @RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)public String getDiscussPost(@PathVariable(name="discussPostId") int discussPostId, Model model) {DiscussPost post = discussPostService.findDiscussPostById(discussPostId);model.addAttribute("post", post);//帖子的作者User user = userService.findUserById(post.getUserId());model.addAttribute("user", user);return "/site/discuss-detail";//返回模版路径}
  • 需要接收帖子id,一般都使用url中带着,所以用@PathVariable从注解中取;
  • 需要通过帖子id查帖子,再用帖子差用户id,最后查到用户名;

修改index.html

<!-- 帖子列表 -->
<ul class="list-unstyled"><li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}"><a href="site/profile.html"><img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;"></a><div class="media-body"><h6 class="mt-0 mb-3"><a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a><span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span><span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span></h6><div class="text-muted font-size-12"><u class="mr-3" th:utext="${map.user.username}">寒江雪</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b><ul class="d-inline float-right"><li class="d-inline ml-2">11</li><li class="d-inline ml-2">|</li><li class="d-inline ml-2">回帖 7</li></ul></div></div>						</li>
</ul>
  • 这里把原来静态的都改成动态,注意变量要用${}括起来。
  • 这里我突然想起来一个问题,在实体类中,这些属性都是private为什么能直接用.运算符获取?

在你的代码中,post.title就是一个表达式,它表示post对象的title属性。虽然title属性在post类中被声明为private,但是Thymeleaf可以通过post类的getTitle方法来获取title属性的值。这是Java的标准Bean规范,即对于一个名为foo的属性,应该有一个名为getFoo的方法来获取它的值,有一个名为setFoo的方法来设置它的值。

注意这就是java的bean,不是受Spring托管的@Bean注解:

在Java中,一个类并不需要使用@Bean注解或其他任何注解就能成为一个Java Bean。Java Bean是遵循特定命名规则的Java类,主要包含私有属性和对应的公有getter和setter方法。 在你的DiscussPost类中,所有的属性都是私有的,并且每个属性都有对应的公有getter和setter方法,所以它就是一个Java Bean。 @Bean注解通常用在Spring框架中,用于声明一个方法返回的对象应该被Spring管理。但是,并不是所有的Java Bean都需要被Spring管理,所以并不是所有的Java Bean都需要使用@Bean注解。

修改discuss-detail.html

<div class="main"><!-- 帖子详情 --><div class="container"><!-- 标题 --><h6 class="mb-4"><img src="http://static.nowcoder.com/images/img/icons/ico-discuss.png"/><span th:utext="${post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</span><div class="float-right"><button type="button" class="btn btn-danger btn-sm">置顶</button><button type="button" class="btn btn-danger btn-sm">加精</button><button type="button" class="btn btn-danger btn-sm">删除</button></div></h6><!-- 作者 --><div class="media pb-3 border-bottom"><a href="profile.html"><img th:src="${user.headerUrl}" class="align-self-start mr-4 rounded-circle user-header" alt="用户头像" ></a><div class="media-body"><div class="mt-0 text-warning" th:utext="${user.username}">寒江雪</div><div class="text-muted mt-3">发布于 <b th:text="${#dates.format(post.createTime,'yyyy-mm-dd-hh-mm-ss')}">2019-04-15 15:32:18</b><ul class="d-inline float-right"><li class="d-inline ml-2"><a href="#" class="text-primary">11</a></li><li class="d-inline ml-2">|</li><li class="d-inline ml-2"><a href="#replyform" class="text-primary">回帖 7</a></li></ul></div></div></div>	<!-- 正文 --><div class="mt-4 mb-3 content" th:utext="${post.content}">金三银四的金三已经到了,你还沉浸在过年的喜悦中吗?如果是,那我要让你清醒一下了:目前大部分公司已经开启了内推,正式网申也将在3月份陆续开始,金三银四,春招的求职黄金时期已经来啦!!!再不准备,作为19应届生的你可能就找不到工作了。。。作为20届实习生的你可能就找不到实习了。。。现阶段时间紧,任务重,能做到短时间内快速提升的也就只有算法了,那么算法要怎么复习?重点在哪里?常见笔试面试算法题型和解题思路以及最优代码是怎样的?跟左程云老师学算法,不仅能解决以上所有问题,还能在短时间内得到最大程度的提升!!!</div></div>

最终效果:

image

这篇关于Day22:过滤敏感词、开发发布帖子、帖子详情的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/834412

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

pandas数据过滤

Pandas 数据过滤方法 Pandas 提供了多种方法来过滤数据,可以根据不同的条件进行筛选。以下是一些常见的 Pandas 数据过滤方法,结合实例进行讲解,希望能帮你快速理解。 1. 基于条件筛选行 可以使用布尔索引来根据条件过滤行。 import pandas as pd# 创建示例数据data = {'Name': ['Alice', 'Bob', 'Charlie', 'Dav

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。