赞
踩
在resources下新建sensitive-word.txt,并在util下实现 SensitiveFilter
// 前缀树 private class TrieNode { // 关键词结束标识 private boolean isKeywordEnd = false; // 子节点(key是下级字符,value是下级节点) private Map<Character, TrieNode> 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); } }
// 将一个敏感词添加到前缀树中 private void addKeyword(String keyword) { TrieNode tempNode = rootNode; 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); } } }
@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());
}
}
/** * 过滤敏感词 * * @param text 待过滤的文本 * @return 过滤后的文本 */ public String filter(String text) { if (StringUtils.isBlank(text)) { return null; } // 指针1 TrieNode tempNode = rootNode; // 指针2 int begin = 0; // 指针3 int position = 0; // 结果 StringBuilder sb = new StringBuilder(); while (position < text.length()) { char c = text.charAt(position); // 跳过符号 if (isSymbol(c)) { // 若指针1处于根节点,将此符号计入结果,让指针2向下走一步 if (tempNode == rootNode) { sb.append(c); begin++; } // 无论符号在开头或中间,指针3都向下走一步 position++; continue; } // 检查下级节点 tempNode = tempNode.getSubNode(c); if (tempNode == null) { // 以begin开头的字符串不是敏感词 sb.append(text.charAt(begin)); // 进入下一个位置 position = ++begin; // 重新指向根节点 tempNode = rootNode; } else if (tempNode.isKeywordEnd()) { // 发现敏感词,将begin~position字符串替换掉 sb.append(REPLACEMENT); // 进入下一个位置 begin = ++position; // 重新指向根节点 tempNode = rootNode; } else { // 检查下一个字符 position++; } } // 将最后一批字符计入结果 sb.append(text.substring(begin)); return sb.toString(); }
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
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 (String key : map.keySet()) {
json.put(key, map.get(key));
}
}
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);
}
⑤ 写个main方法简单测一下,不需要容器管理
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("name", "zhangsan");
map.put("age", 25);
System.out.println(getJSONString(0, "ok", map));
}
// ajax示例
@RequestMapping(path = "/ajax", method = RequestMethod.POST)
@ResponseBody
public String testAjax(String name, int age) {
System.out.println(name);
System.out.println(age);
return CommunityUtil.getJSONString(0, "操作成功!");
}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX</title> </head> <body> <p> <input type="button" value="发送" onclick="send();"> </p> <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script> <script> function send() { $.post( "/community/alpha/ajax", {"name":"张三","age":23}, function(data) { console.log(typeof(data)); console.log(data); data = $.parseJSON(data); console.log(typeof(data)); console.log(data.code); console.log(data.msg); } ); } </script> </body> </html>
点击发送后
int insertDiscussPost(DiscussPost discussPost);
<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>
@Autowired private DiscussPostMapper discussPostMapper; @Autowired private SensitiveFilter sensitiveFilter; public int addDiscussPost(DiscussPost post) { if (post == null) { throw new IllegalArgumentException("参数不能为空!"); } // 转义HTML标记 post.setTitle(HtmlUtils.htmlEscape(post.getTitle())); post.setContent(HtmlUtils.htmlEscape(post.getContent())); // 过滤敏感词 post.setTitle(sensitiveFilter.filter(post.getTitle())); post.setContent(sensitiveFilter.filter(post.getContent())); return discussPostMapper.insertDiscussPost(post); }
@RequestMapping(path = "/add", method = RequestMethod.POST) @ResponseBody public 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, "发布成功!"); }
$(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); // 在提示框中显示返回消息 $("#hintBody").text(data.msg); // 显示提示框 $("#hintModal").modal("show"); // 2秒后,自动隐藏提示框 setTimeout(function(){ $("#hintModal").modal("hide"); // 刷新页面 if(data.code == 0) { window.location.reload(); } }, 2000); } ); }
② 不登陆不显示发布的按钮 index.html,判断当前是否有用户登录即可
<button type="button" class="btn btn-primary btn-sm position-absolute rt-0" data-toggle="modal" data-target="#publishModal" th:if="${loginUser!=null}">我要发布</button>
DiscussPost selectDiscussPostById(int id);
<select id="selectDiscussPostById" resultType="DiscussPost">
select <include refid="selectFields"></include>
from discuss_post
where id = #{id}
</select>
public DiscussPost findDiscussPostById(int id) {
return discussPostMapper.selectDiscussPostById(id);
}
在DisscussPostController下添加方法getDiscussPost
@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) {
// 帖子
DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
model.addAttribute("post", post);
// 作者
User user = userService.findUserById(post.getUserId());
model.addAttribute("user", user);
return "/site/discuss-detail";
}
<a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a>
<span th:utext="${post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</span>
⑥ 作者部分:头像和姓名
<img th:src="${user.headerUrl}" class="align-self-start mr-4 rounded-circle user-header" alt="用户头像" >
<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>
</div>
⑧ 正文部分
<div class="mt-4 mb-3 content" th:utext="${post.content}">
金三银四的金三已经到了,你还沉浸在过年的喜悦中吗?
如果是,那我要让你清醒一下了:目前大部分公司已经开启了内推,正式网申也将在3月份陆续开始,金三银四,春招的求职黄金时期已经来啦!!!
再不准备,作为19应届生的你可能就找不到工作了。。。作为20届实习生的你可能就找不到实习了。。。
现阶段时间紧,任务重,能做到短时间内快速提升的也就只有算法了,
那么算法要怎么复习?重点在哪里?常见笔试面试算法题型和解题思路以及最优代码是怎样的?
跟左程云老师学算法,不仅能解决以上所有问题,还能在短时间内得到最大程度的提升!!!
</div>
entity_type:评论目标的类别(如:1代表帖子,2代表评论等)
entity_id:具体目标(帖子id 228,或者帖子 id 229)
target_id:指向某个人的评论
属性,getset方法,tostring
package com.nowcoder.community.entity; import java.util.Date; public class Comment { private int id; private int userId; private int entityType; private int entityId; private int targetId; private String content; private int status; private Date createTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public int getEntityType() { return entityType; } public void setEntityType(int entityType) { this.entityType = entityType; } public int getEntityId() { return entityId; } public void setEntityId(int entityId) { this.entityId = entityId; } public int getTargetId() { return targetId; } public void setTargetId(int targetId) { this.targetId = targetId; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { return "Comment{" + "id=" + id + ", userId=" + userId + ", entityType=" + entityType + ", entityId=" + entityId + ", targetId=" + targetId + ", content='" + content + '\'' + ", status=" + status + ", createTime=" + createTime + '}'; } }
package com.nowcoder.community.dao;
import com.nowcoder.community.entity.Comment;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CommentMapper {
List<Comment> selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
int selectCountByEntity(int entityType, int entityId);
}
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.nowcoder.community.dao.CommentMapper"> <sql id="selectFields"> id, user_id, entity_type, entity_id, target_id, content, status, create_time </sql> <select id="selectCommentsByEntity" resultType="Comment"> select <include refid="selectFields"></include> from comment where status = 0 and entity_type = #{entityType} and entity_id = #{entityId} order by create_time asc limit #{offset}, #{limit} </select> <select id="selectCountByEntity" resultType="int"> select count(id) from comment where status = 0 and entity_type = #{entityType} and entity_id = #{entityId} </select> </mapper>
service下实现 CommentService
package com.nowcoder.community.service; import com.nowcoder.community.dao.CommentMapper; import com.nowcoder.community.entity.Comment; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class CommentService implements CommunityConstant { @Autowired private CommentMapper commentMapper; public List<Comment> findCommentsByEntity(int entityType, int entityId, int offset, int limit) { return commentMapper.selectCommentsByEntity(entityType, entityId, offset, limit); } public int findCommentCount(int entityType, int entityId) { return commentMapper.selectCountByEntity(entityType, entityId); } }
page.setLimit(5); page.setPath("/discuss/detail/" + discussPostId); page.setRows(post.getCommentCount());
List<Comment> commentList
commentVo.put("comment", comment);
commentVo.put("user", userService.findUserById(comment.getUserId()));
List<Comment> replyList
List<Map<String, Object>> replyVoList
replyVo.put("reply", reply);
replyVo.put("user", userService.findUserById(reply.getUserId()));
replyVo.put("target", target);
commentVo.put("replys", replyVoList);
commentVo.put("replyCount", replyCount);
commentVoList.add(commentVo);
model.addAttribute("comments", commentVoList);
@Autowired private CommentService commentService; @RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET) public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) { // 帖子 DiscussPost post = discussPostService.findDiscussPostById(discussPostId); model.addAttribute("post", post); // 作者 User user = userService.findUserById(post.getUserId()); model.addAttribute("user", user); // 评论分页信息 page.setLimit(5); page.setPath("/discuss/detail/" + discussPostId); page.setRows(post.getCommentCount()); // 评论: 给帖子的评论 // 回复: 给评论的评论 // 评论列表 List<Comment> commentList = commentService.findCommentsByEntity( ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit()); // 评论VO列表 List<Map<String, Object>> commentVoList = new ArrayList<>(); if (commentList != null) { for (Comment comment : commentList) { // 评论VO Map<String, Object> commentVo = new HashMap<>(); // 评论 commentVo.put("comment", comment); // 作者 commentVo.put("user", userService.findUserById(comment.getUserId())); // 回复列表 List<Comment> replyList = commentService.findCommentsByEntity( ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE); // 回复VO列表 List<Map<String, Object>> replyVoList = new ArrayList<>(); if (replyList != null) { for (Comment reply : replyList) { Map<String, Object> replyVo = new HashMap<>(); // 回复 replyVo.put("reply", reply); // 作者 replyVo.put("user", userService.findUserById(reply.getUserId())); // 回复目标 User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId()); replyVo.put("target", target); replyVoList.add(replyVo); } } commentVo.put("replys", replyVoList); // 回复数量 int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId()); commentVo.put("replyCount", replyCount); commentVoList.add(commentVo); } } model.addAttribute("comments", commentVoList); return "/site/discuss-detail"; }
回帖数量
<li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span></li>
<h6><b class="square"></b> <i th:text="${post.commentCount}">30</i>条回帖</h6>
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="cvo:${comments}">
<img th:src="${cvo.user.headerUrl}" class="align-self-start mr-4 rounded-circle user-header" alt="用户头像" >
<span class="font-size-12 text-success" th:utext="${cvo.user.username}">掉脑袋切切</span>
<span class="badge badge-secondary float-right floor">
<i th:text="${page.offset + cvoStat.count}">1</i>#
</span>
<li class="pb-3 pt-3 mb-3 border-bottom" th:each="rvo:${cvo.replys}">
<span th:if="${rvo.target==null}">
<b class="text-info" th:text="${rvo.user.username}">寒江雪</b>:
</span>
<span th:if="${rvo.target!=null}">
<i class="text-info" th:text="${rvo.user.username}">Sissi</i> 回复
<b class="text-info" th:text="${rvo.target.username}">寒江雪</b>:
</span>
<span th:text="${#dates.format(rvo.reply.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</span>
<li class="d-inline ml-2"><a th:href="|#huifu-${rvoStat.count}|" data-toggle="collapse" class="text-primary">回复</a></li>
<nav class="mt-5" th:if="${page.rows>0}" th:fragment="pagination">
……
</nav>
当前页面的
<nav class="mt-5" th:replace="index::pagination">
……
</nav>
int insertComment(Comment comment);
<sql id="insertFields">
user_id, entity_type, entity_id, target_id, content, status, create_time
</sql>
<insert id="insertComment" parameterType="Comment">
insert into comment(<include refid="insertFields"></include>)
values(#{userId},#{entityType},#{entityId},#{targetId},#{content},#{status},#{createTime})
</insert>
int insertComment(Comment comment);
<sql id="insertFields">
user_id, entity_type, entity_id, target_id, content, status, create_time
</sql>
<insert id="insertComment" parameterType="Comment">
insert into comment(<include refid="insertFields"></include>)
values(#{userId},#{entityType},#{entityId},#{targetId},#{content},#{status},#{createTime})
</insert>
int updateCommentCount(int id, int commentCount);
<update id="updateCommentCount">
update discuss_post set comment_count = #{commentCount} where id = #{id}
</update>
public int updateCommentCount(int id, int commentCount) {
return discussPostMapper.updateCommentCount(id, commentCount);
}
@Autowired private DiscussPostService discussPostService; @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public int addComment(Comment comment) { if (comment == null) { throw new IllegalArgumentException("参数不能为空!"); } // 添加评论 comment.setContent(HtmlUtils.htmlEscape(comment.getContent())); comment.setContent(sensitiveFilter.filter(comment.getContent())); int rows = commentMapper.insertComment(comment); // 更新帖子评论数量 if (comment.getEntityType() == ENTITY_TYPE_POST) { int count = commentMapper.selectCountByEntity(comment.getEntityType(), comment.getEntityId()); discussPostService.updateCommentCount(comment.getEntityId(), count); } return rows; }
package com.nowcoder.community.controller; import com.nowcoder.community.entity.Comment; import com.nowcoder.community.service.CommentService; import com.nowcoder.community.util.HostHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.Date; @Controller @RequestMapping("/comment") public class CommentController { @Autowired private CommentService commentService; @Autowired private HostHolder hostHolder; @RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST) public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) { comment.setUserId(hostHolder.getUser().getId()); comment.setStatus(0); comment.setCreateTime(new Date()); commentService.addComment(comment); return "redirect:/discuss/detail/" + discussPostId; } }
<form class="replyform" method="post" th:action="@{|/comment/add/${post.id}|}">
② 处理文本域,取名为content和实体属性对应
<textarea placeholder="在这里畅所欲言你的看法吧!" name="content"></textarea>
③ 两个隐含条件entityType和entityId
<input type="hidden" name="entityType" value="1">
<input type="hidden" name="entityId" th:value="${post.id}">
<form method="post" th:action="@{|/comment/add/${post.id}|}">
……
</form>
② 处理文本域,取名为content和实体属性对应
<input type="text" class="input-size" name="content" placeholder="请输入你的观点"/>
③ 两个隐含条件entityType和entityId
<input type="hidden" name="entityType" value="2">
<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
<form method="post" th:action="@{|/comment/add/${post.id}|}">
……
</form>
② 处理文本域,取名为content和实体属性对应
<input type="text" class="input-size" name="content" th:placeholder="|回复${rvo.user.username}|"/>
③ 两个隐含条件entityType和entityId和targetId
<input type="hidden" name="entityType" value="2">
<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
<input type="hidden" name="targetId" th:value="${rvo.user.id}">
④ 显示回复给哪个人
<input type="text" class="input-size" name="content" th:placeholder="|回复${rvo.user.username}|"/>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。