基于SpringBoot和MySQL实现的论坛信息发布平台

基于SpringBoot和MySQL实现的论坛信息发布平台 1,项目简介 CISP 全称 Campus Information Sharing Platform(校内信息共享平台)

本文包含相关资料包-----> 点击直达获取<-------

基于SpringBoot和MySQL实现的论坛信息发布平台

1.项目简介

CISP 全称 Campus Information Sharing Platform(校内信息共享平台),这是本科毕业设计,一个类似于论坛的信息发布平台。

  • 后端:SpringBoot

  • 前端:LayUI框架 + freemarker动态模板

  • 数据库使用MySQL

  • MVC三层架构

1.1 功能逻辑

1.1.1 注册

  • 用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活

  • 向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)

1.1.2 登录 | 登出

登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:

  • 进入登录界面,随机生成一个字符串来标识这个将要登录的用户,将这个字符串短暂的存入 Cookie(60 秒)

  • 动态生成验证码,并将验证码及标识该用户的字符串短暂存入 Redis(60 秒)

  • 为登录成功(验证用户名、密码、验证码)的用户随机生成登录凭证且设置状态为有效,并将登录凭证及其状态等信息永久存入 Redis,再在 Cookie 中存一份登录凭证

  • 使用拦截器在所有的请求执行之前,从 Cookie 中获取登录凭证,只要 Redis 中该凭证有效并在有效期内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息,保证多台服务器上用户的登录状态同步)

  • 勾选记住我,则延长 Cookie 中登录凭证的有效时间

  • 用户登出,将凭证状态设为无效,并更新 Redis 中该登录凭证的相关信息

下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全):

1.1.3 显示评论及相关信息

评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~

关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。

评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的。

1.2 开发环境

  • 操作系统 :Windows 10

  • 构建工具 :Apache Maven

  • 集成开发工具 :Intellij IDEA

  • 应用服务器 :Apache Tomcat

  • 接口测试工具 :Postman

  • 压力测试工具 :Apache JMeter

  • 版本控制工具 :Git

  • Java 版本 :8

2.数据库设计

2.1 表结构

article表

category表

comment表

user表

2.2 E-R图

3.项目开发

3.1 项目配置

```yaml

配置freemarker

spring: freemarker: # 设置模板后缀名 suffix: .html # 设置文档类型 content-type: text/html # 设置页面编码格式 charset: UTF-8 # 设置页面缓存 cache: false # 设置ftl文件路径 template-loader-path: - classpath:/templates # 设置静态文件路径,js,css等 mvc: static-path-pattern: /static/ resources: static-locations: ["/templates/","/static/"] http: encoding: charset: utf-8 force: true enabled: true #配置数据源 datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath :mapper/ .xml type-aliases-package: com.zbin.cisp.domain configuration: map-underscore-to-camel-case: true server: port: 8080 ```

3.2 拦截器实现

java public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { boolean flag; if (request.getRequestURI().startsWith("/admin") && request.getSession().getAttribute("adminUser") == null) { response.sendRedirect("/admin"); flag = false; } else if (request.getSession().getAttribute("user") == null && request.getSession().getAttribute("adminUser") == null) { response.sendRedirect("/login"); flag = false; } else { flag = true; } return flag; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }

3.3 工具类

文件处理

java public class FileUtil { private static final String ACCESS_KEY = "pl7KvcAWGCe1eI2RPKKyrp7zxU_o8PM6rGAb7SG7"; private static final String SECRET_KEY = "aQtmNi3Zvo_qDJ-8tBQ1tObNxJ-M95Bkr2ndIpDK"; private static final String PREFIX_URL = "http://cdn.iwzb.top/"; private static final String BUCKET = "cisp"; public static String upload(MultipartFile originFile) { try { String filename = ""; if (originFile.getOriginalFilename() != null) { filename = originFile.getOriginalFilename(); } File file = new File(filename); FileUtils.copyInputStreamToFile(originFile.getInputStream(), file); UploadManager uploadManager = getUploadManager(); String token = getToken(); Response response = uploadManager.put(file.getAbsolutePath(), newName(file.getName()), token); //解析上传成功的结果 DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class); if (file.delete()) { return PREFIX_URL + putRet.key; } return null; } catch (Exception e) { e.printStackTrace(); return null; } } public static boolean delete(String key) { try { BucketManager bkm = getBucketManager(); bkm.delete(BUCKET, key); return true; } catch (Exception e) { return false; } } private static UploadManager getUploadManager() { Configuration cfg = new Configuration(Zone.zone0()); return new UploadManager(cfg); } private static BucketManager getBucketManager() { Configuration cfg = new Configuration(Zone.zone0()); Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY); return new BucketManager(auth, cfg); } private static String newName(String oldName) { String[] datas = oldName.split("\\."); String type = datas[datas.length - 1]; return UUID.randomUUID().toString() + "." + type; } private static String getToken() { Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY); return auth.uploadToken(BUCKET); } }

加密工具类

java public class PasswordUtil { /** * 加密密码 */ public static String bryptPwd(String pwd) { return BCrypt.hashpw(pwd, BCrypt.gensalt()); } /** * 校验密码 */ public static boolean validPwd(String pwd, String hashed) { try { return BCrypt.checkpw(pwd, hashed); } catch (Exception e) { e.printStackTrace(); return false; } } }

3.4 主要功能实现

java @Controller @RequestMapping("/article") public class ArticleController { @Resource ArticleService articleService; @Resource CategoryService categoryService; @Resource CommentService commentService; @Resource UserService userService; @RequestMapping("/uploadImg") @ResponseBody public ReturnJson upload(HttpServletRequest request, MultipartFile file) { String imgUrl = FileUtil.upload(file); Map<String, String> imgMap = new HashMap<>(); imgMap.put("src", imgUrl); imgMap.put("title", file.getOriginalFilename()); return new ReturnJson("上传成功", imgMap); } @RequestMapping("/add") @ResponseBody public ReturnJson add(HttpServletRequest request, @RequestBody Article article) { try { User user; if (request.getSession().getAttribute("adminUser") != null) { user = (User) request.getSession().getAttribute("adminUser"); } else { user = (User) request.getSession().getAttribute("user"); } if ("禁言".equals(user.getStatus())) { return new ReturnJson(1, "您被禁言,无法发布文章!"); } article.setUserId(user.getId()); if (article.getId() == null) { articleService.create(article); } else { articleService.update(article); } return new ReturnJson(0, "发布成功"); } catch (Exception e) { return new ReturnJson(1, "发布失败"); } } @RequestMapping("/addCategory") @ResponseBody public ReturnJson addCategory(HttpServletRequest request, @RequestBody Category category) { try { if (category.getName() != null) { categoryService.create(category); return new ReturnJson(0, "新增分类成功"); } else { return new ReturnJson(1, "分类名不能为空"); } } catch (Exception e) { return new ReturnJson(1, "新增分类失败"); } } @RequestMapping("/delCategory") @ResponseBody public ReturnJson delCategory(HttpServletRequest request, @RequestBody Category category) { try { if (category.getId() != null) { categoryService.deleteById(category.getId()); return new ReturnJson(0, "删除分类成功"); } else { return new ReturnJson(1, "删除分类失败"); } } catch (Exception e) { return new ReturnJson(1, "新增分类失败"); } } @RequestMapping("/updateCategory") @ResponseBody public ReturnJson updateCategory(HttpServletRequest request, @RequestBody Category category) { try { if (category.getId() != null && category.getName() != null) { categoryService.updateById(category); return new ReturnJson(0, "修改分类成功"); } else { return new ReturnJson(1, "修改分类失败"); } } catch (Exception e) { return new ReturnJson(1, "修改分类失败"); } } @RequestMapping("/addComment") @ResponseBody public ReturnJson addComment(@RequestBody Comment comment) { try { User user = userService.getUserById(comment.getUserId()); if ("禁言".equals(user.getStatus())) { return new ReturnJson(1, "您被禁言,无法发表评论!"); } commentService.create(comment); return new ReturnJson("评论成功"); } catch (Exception e) { return new ReturnJson(1, "评论失败"); } } @RequestMapping("/delComment") @ResponseBody public ReturnJson delComment(@RequestBody Comment comment) { try { commentService.delete(comment); return new ReturnJson("删除评论成功"); } catch (Exception e) { return new ReturnJson(1, "删除评论失败"); } } @RequestMapping("/delete") @ResponseBody public ReturnJson deleteArticle(@RequestBody String param) { try { JSONObject json = JSON.parseObject(param); Integer id = json.getInteger("id"); articleService.delete(id); return new ReturnJson("删除成功"); } catch (Exception e) { return new ReturnJson(1, "删除失败"); } } @RequestMapping("/setTop") @ResponseBody public ReturnJson setTopArticle(@RequestBody String param) { try { JSONObject json = JSON.parseObject(param); Integer articleId = json.getInteger("value"); articleService.setTopStatus(articleId); return new ReturnJson("置顶成功"); } catch (Exception e) { return new ReturnJson(1, "置顶失败"); } } }

4.项目展示

4.1 普通用户

主页

登录页

主页

个人中心

主页文章分类

文章详情

文章评论

发表文章

4.2 管理员

登录

主页

文章管理

修改文章

查看文章

删除文章

分类管理

用户管理

禁言处理

添加用户

模糊查询

数据统计

参考文献

  • 商务网站广告发布模块的设计与实现(吉林大学·金兰)
  • 一个通用论坛系统的设计与实现(山东大学·张正)
  • 基于PHP的BBS论坛管理系统的设计与实现(吉林大学·齐越)
  • 基于Web技术的校园论坛设计与实现(内蒙古科技大学·李鹏飞)
  • 基于JavaEE的ITPUB软件开发技术论坛的设计与实现(吉林大学·孙睿)
  • 基于J2EE的汽车零部件信息发布平台(内蒙古科技大学·王峰)
  • 一个通用论坛系统的设计与实现(山东大学·张正)
  • 基于SSH框架的博客用户分享平台的设计与实现(河北工业大学·刘磊)
  • 网上论坛系统设计与实现(西安电子科技大学·胡秉玺)
  • 基于ASP.NET MVC和实体框架的农业论坛的设计与实现(辽宁科技大学·高起跃)
  • 基于J2EE的汽车零部件信息发布平台(内蒙古科技大学·王峰)
  • 基于SSH框架的博客用户分享平台的设计与实现(河北工业大学·刘磊)
  • 基于B/S结构的新闻发布系统(吉林大学·郭蕊)
  • 基于J2EE的汽车零部件信息发布平台(内蒙古科技大学·王峰)
  • 基于JavaEE的ITPUB软件开发技术论坛的设计与实现(吉林大学·孙睿)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码工厂 ,原文地址:https://m.bishedaima.com/yuanma/35477.html

相关推荐

发表回复

登录后才能评论