第3章Spring Boot进阶,开发社区核心功能【仿牛客网社区论坛项目】
前言
2023-7-28 21:18:08
以下内容源自【Java面试项目】
仅供学习交流使用
推荐
项目总结
第3章Spring Boot进阶,开发社区核心功能
1.过滤敏感词
一:构建敏感词的前缀树
二:过滤文本
SensitiveFilter
java
package com.jsss.community.util;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
@Component
public class SensitiveFilter {
private static final Logger logger= LoggerFactory.getLogger(SensitiveFilter.class);
// 替换符
private static final String REPLACEMENT="***";
//根节点
private TrieNode rootNode=new TrieNode();
@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());
}
}
//将一个敏感词添加到前缀树中
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);
}
}
}
/**
* 过滤敏感词
* @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;
}
}
//将最后一批字符计入结果,当指针3提前结束
sb.append(text.substring(begin));
return sb.toString();
}
//判断是否为符号
private boolean isSymbol(Character c){
//0x2E80~0x9FFF 东亚文字范围
return !CharUtils.isAsciiAlphanumeric(c) &&(c<0x2E80||c>0x9FFF);
}
//前缀树
private class TrieNode{
//关键词结束标识
private boolean keyWordEnd =false;
//子节点(key是下级字符,value是下级结点)
private Map<Character,TrieNode> subNodes =new HashMap<>();
public TrieNode() {
}
public void setKeyWordEnd(boolean keyWordEnd) {
this.keyWordEnd = keyWordEnd;
}
public boolean isKeyWordEnd() {
return keyWordEnd;
}
//添加子节点
public void addSubNode(Character c,TrieNode node){
subNodes.put(c,node);
}
//获取子节点
public TrieNode getSubNode(Character c){
return subNodes.get(c);
}
}
}
测试:
SensitiveTests
java
package com.jsss.community;
import com.jsss.community.util.SensitiveFilter;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class SensitiveTests {
@Autowired
private SensitiveFilter sensitiveFilter;
@Test
public void testSensitiveFilter(){
String text="这里可以唱,可以☆跳☆,可以☆rap☆,可☆以☆篮☆球☆,哈哈哈!";
String filter = sensitiveFilter.filter(text);
System.out.println(filter);
// 这里可以***,可以☆***☆,可以☆***☆,可☆以☆***☆,哈哈哈!
}
}
sensitive-words.txt
java
唱
跳
rap
篮球
2.发布帖子
java
@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,"发布成功!");
}
3.帖子详情
java
@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);
//点赞数量
long likeCount =likeService.findEntityLikeCount(ENTITY_TYPE_POST,discussPostId);
model.addAttribute("likeCount",likeCount);
//点赞状态
int likeStatus=hostHolder.getUser()==null?0:
likeService.findEntityLikeStatus(hostHolder.getUser().getId(),ENTITY_TYPE_POST,discussPostId);
model.addAttribute("likeStatus",likeStatus);
//评论的分页信息
page.setLimit(5);
page.setPath("/discuss/detail/"+discussPostId);
page.setRows(post.getCommentCount());
//评论:给帖子的评论
//回复:给评论的评论
//评论列表
List<Comment> commentList = commentService.findCommentsByEntity(
CommunityConstant.ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
//评论VO列表
List<Map<String,Object>> commentVoList =new ArrayList<>();
if (commentVoList !=null){
for(Comment comment:commentList){
//评论VO
Map<String,Object> commentVo =new HashMap<>();
//评论
commentVo.put("comment",comment);
//作者
commentVo.put("user",userService.findUserById(comment.getUserId()));
//点赞数量
likeCount =likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT,comment.getId());
commentVo.put("likeCount",likeCount);
//点赞状态
likeStatus=hostHolder.getUser()==null?0:
likeService.findEntityLikeStatus(hostHolder.getUser().getId(),ENTITY_TYPE_COMMENT,comment.getId());
commentVo.put("likeStatus",likeStatus);
//回复列表
List<Comment> replyList = commentService.findCommentsByEntity(
CommunityConstant.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);
//点赞数量
likeCount =likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT,reply.getId());
replyVo.put("likeCount",likeCount);
//点赞状态
likeStatus=hostHolder.getUser()==null?0:
likeService.findEntityLikeStatus(hostHolder.getUser().getId(),ENTITY_TYPE_COMMENT,reply.getId());
replyVo.put("likeStatus",likeStatus);
replyVoList.add(replyVo);
}
}
commentVo.put("replys",replyVoList);
//回复的数量
int replyCount = commentService.findCommentCount(CommunityConstant.ENTITY_TYPE_COMMENT, comment.getId());
commentVo.put("replyCount",replyCount);
commentVoList.add(commentVo);
}
}
model.addAttribute("comments",commentVoList);
return "site/discuss-detail";
}
4.事务管理
5.显示评论
6.添加评论
java
@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;
}
7.私信列表
java
// 私信列表
@RequestMapping(path = "/letter/list", method = RequestMethod.GET)
public String getLetterList(Model model, Page page) {
User user = hostHolder.getUser();
// 分页信息
page.setLimit(5);
page.setPath("/letter/list");
page.setRows(messageService.findConversationCount(user.getId()));
// 会话列表
List<Message> conversationList = messageService.findConversations(
user.getId(), page.getOffset(), page.getLimit());
List<Map<String, Object>> conversations = new ArrayList<>();
if (conversationList != null) {
for (Message message : conversationList) {
Map<String, Object> map = new HashMap<>();
map.put("conversation", message);
map.put("letterCount", messageService.findLetterCount(message.getConversationId()));
map.put("unreadCount", messageService.findLetterUnreadCount(user.getId(), message.getConversationId()));
int targetId = user.getId() == message.getFromId() ? message.getToId() : message.getFromId();
map.put("target", userService.findUserById(targetId));
conversations.add(map);
}
}
model.addAttribute("conversations", conversations);
// 查询未读消息数量
int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null);
model.addAttribute("letterUnreadCount", letterUnreadCount);
int noticeUnreadCount = messageService.findNoticeUnreadCount(user.getId(), null);
model.addAttribute("noticeUnreadCount", noticeUnreadCount);
return "site/letter";
}
8.发送私信
java
@RequestMapping(path = "/letter/send", method = RequestMethod.POST)
@ResponseBody
public String sendLetter(String toName, String content) {
User target = userService.findUserByName(toName);
if (target == null) {
return CommunityUtil.getJSONString(1, "目标用户不存在!");
}
Message message = new Message();
message.setFromId(hostHolder.getUser().getId());
message.setToId(target.getId());
if (message.getFromId() < message.getToId()) {
message.setConversationId(message.getFromId() + "_" + message.getToId());
} else {
message.setConversationId(message.getToId() + "_" + message.getFromId());
}
message.setContent(content);
message.setCreateTime(new Date());
messageService.addMessage(message);
return CommunityUtil.getJSONString(0);
}
9.统一处理异常
java
package com.jsss.community.controller.advice;
import com.jsss.community.util.CommunityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {
private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
@ExceptionHandler({Exception.class})
public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.error("服务器发生异常: " + e.getMessage());
for (StackTraceElement element : e.getStackTrace()) {
logger.error(element.toString());
}
String xRequestedWith = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequestedWith)) {
response.setContentType("application/plain;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
} else {
response.sendRedirect(request.getContextPath() + "/error");
}
}
}
10.统一记录日志
java
package com.jsss.community.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
//@Component
//@Aspect
public class ServiceLogAspect {
private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
@Pointcut("execution(* com.jsss.community.service.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
// 用户[1.2.3.4],在[xxx],访问了[com.jsss.community.service.xxx()].
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return;
}
HttpServletRequest request = attributes.getRequest();
String ip = request.getRemoteHost();
String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
}
}
最后
这篇博客能写好的原因是:站在巨人的肩膀上
这篇博客要写好的目的是:做别人的肩膀
开源:为爱发电
学习:为我而行