第3章Spring Boot进阶,开发社区核心功能【仿牛客网社区论坛项目】
- 前言
- 推荐
- 项目总结
- 第3章Spring Boot进阶,开发社区核心功能
- 1.过滤敏感词
- 2.发布帖子
- 3.帖子详情
- 4.事务管理
- 5.显示评论
- 6.添加评论
- 7.私信列表
- 8.发送私信
- 9.统一处理异常
- 10.统一记录日志
- 最后
前言
2023-7-28 21:18:08
以下内容源自【Java面试项目】
仅供学习交流使用
推荐
仿牛客网项目【面试】
项目总结
第3章Spring Boot进阶,开发社区核心功能
1.过滤敏感词
一:构建敏感词的前缀树
二:过滤文本
SensitiveFilter
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();@PostConstructpublic 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;}//指针1TrieNode tempNode = rootNode;//指针2int begin=0;//指针3int 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
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 {@Autowiredprivate SensitiveFilter sensitiveFilter;@Testpublic void testSensitiveFilter(){String text="这里可以唱,可以☆跳☆,可以☆rap☆,可☆以☆篮☆球☆,哈哈哈!";String filter = sensitiveFilter.filter(text);System.out.println(filter);// 这里可以***,可以☆***☆,可以☆***☆,可☆以☆***☆,哈哈哈!}
}
sensitive-words.txt
唱
跳
rap
篮球
2.发布帖子
@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,"发布成功!");}
3.帖子详情
@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){//评论VOMap<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.添加评论
@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.私信列表
// 私信列表@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.发送私信
@RequestMapping(path = "/letter/send", method = RequestMethod.POST)@ResponseBodypublic 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.统一处理异常
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.统一记录日志
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));}}
最后
这篇博客能写好的原因是:站在巨人的肩膀上
这篇博客要写好的目的是:做别人的肩膀
开源:为爱发电
学习:为我而行