代码展示
1.项目简介
CISP 全称 Campus Information Sharing Platform -- 校内信息共享平台
这是本科毕业设计,一个类似于论坛的信息发布平台
后端基于SpringBoot开发
前端使用LayUI框架 + freemarker动态模板生成
数据库使用MySQL
MVC三层架构
cisp
1.项目简介
CISP 全称 Campus Information Sharing Platform -- 校内信息共享平台
这是本科毕业设计,一个类似于论坛的信息发布平台
后端基于SpringBoot开发
前端使用LayUI框架 + freemarker动态模板生成
数据库使用MySQL
MVC三层架构
1.1功能逻辑注册
- 用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活
- 向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)
- 1:
登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
- 进入登录界面,随机生成一个字符串来标识这个将要登录的用户,将这个字符串短暂的存入 cookie(60 秒);
- 动态生成验证码,并将验证码及标识该用户的字符串短暂存入 Redis(60 秒);
- 为登录成功(验证用户名、密码、验证码)的用户随机生成登录凭证且设置状态为有效,并将登录凭证及其状态等信息永久存入 Redis,再在 cookie 中存一份登录凭证;
- 使用拦截器在所有的请求执行之前,从 cookie 中获取登录凭证,只要 Redis 中该凭证有效并在有效期内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息,保证多台服务器上用户的登录状态同步);
- 勾选记住我,则延长 cookie 中登录凭证的有效时间;
- 用户登出,将凭证状态设为无效,并更新 Redis 中该登录凭证的相关信息。
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
Figure 2:
显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
Figure 3:
2.开发环境- 操作系统:Windows 10
- 构建工具:Apache Maven
- 集成开发工具:Intellij IDEA
- 应用服务器:Apache Tomcat
- 接口测试工具:Postman
- 压力测试工具:Apache JMeter
- 版本控制工具:Git
- Java 版本:8
3.数据库设计 3.1表结构
article表
Figure 4:
category表
Figure 5:
comment表
Figure 6:
user表
Figure 7:
3.2ER图Figure 8:
4.项目开发 4.1配置maven依赖
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
# 配置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
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;
}
}
}
@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
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, "置顶失败");
}
}
}
5.项目展示 5.1普通用户
主页
Figure 9:
登录页
Figure 10:
主页
Figure 11:
个人中心
Figure 12:
Figure 13:
主页文章分类
Figure 14:
文章详情
Figure 15:
Figure 16:
文章评论
Figure 17:
发表文章
Figure 18:
5.2管理员登录
Figure 19:
主页
Figure 20:
文章管理
Figure 21:
修改文章
Figure 22:
查看文章
Figure 23:
删除文章
Figure 24:
分类管理
Figure 25:
用户管理
Figure 26:
禁言处理
Figure 27:
添加用户
Figure 28:
模糊查询
Figure 29:
数据统计
Figure 30:
1.1功能逻辑注册
- 用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活
- 向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)
- 1:
登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
- 进入登录界面,随机生成一个字符串来标识这个将要登录的用户,将这个字符串短暂的存入 cookie(60 秒);
- 动态生成验证码,并将验证码及标识该用户的字符串短暂存入 Redis(60 秒);
- 为登录成功(验证用户名、密码、验证码)的用户随机生成登录凭证且设置状态为有效,并将登录凭证及其状态等信息永久存入 Redis,再在 cookie 中存一份登录凭证;
- 使用拦截器在所有的请求执行之前,从 cookie 中获取登录凭证,只要 Redis 中该凭证有效并在有效期内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息,保证多台服务器上用户的登录状态同步);
- 勾选记住我,则延长 cookie 中登录凭证的有效时间;
- 用户登出,将凭证状态设为无效,并更新 Redis 中该登录凭证的相关信息。
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
Figure 2:
显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
Figure 3:
2.开发环境- 操作系统:Windows 10
- 构建工具:Apache Maven
- 集成开发工具:Intellij IDEA
- 应用服务器:Apache Tomcat
- 接口测试工具:Postman
- 压力测试工具:Apache JMeter
- 版本控制工具:Git
- Java 版本:8
3.数据库设计 3.1表结构
article表
Figure 4:
category表
Figure 5:
comment表
Figure 6:
user表
Figure 7:
3.2ER图Figure 8:
4.项目开发 4.1配置maven依赖
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
# 配置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
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;
}
}
}
@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
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, "置顶失败");
}
}
}
5.项目展示 5.1普通用户
主页
Figure 9:
登录页
Figure 10:
主页
Figure 11:
个人中心
Figure 12:
Figure 13:
主页文章分类
Figure 14:
文章详情
Figure 15:
Figure 16:
文章评论
Figure 17:
发表文章
Figure 18:
5.2管理员登录
Figure 19:
主页
Figure 20:
文章管理
Figure 21:
修改文章
Figure 22:
查看文章
Figure 23:
删除文章
Figure 24:
分类管理
Figure 25:
用户管理
Figure 26:
禁言处理
Figure 27:
添加用户
Figure 28:
模糊查询
Figure 29:
数据统计
Figure 30:
cisp
1.项目简介
CISP 全称 Campus Information Sharing Platform -- 校内信息共享平台
这是本科毕业设计,一个类似于论坛的信息发布平台
后端基于SpringBoot开发
前端使用LayUI框架 + freemarker动态模板生成
数据库使用MySQL
MVC三层架构
1.1功能逻辑注册
- 用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活
- 向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)
- 1:
登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
- 进入登录界面,随机生成一个字符串来标识这个将要登录的用户,将这个字符串短暂的存入 cookie(60 秒);
- 动态生成验证码,并将验证码及标识该用户的字符串短暂存入 Redis(60 秒);
- 为登录成功(验证用户名、密码、验证码)的用户随机生成登录凭证且设置状态为有效,并将登录凭证及其状态等信息永久存入 Redis,再在 cookie 中存一份登录凭证;
- 使用拦截器在所有的请求执行之前,从 cookie 中获取登录凭证,只要 Redis 中该凭证有效并在有效期内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息,保证多台服务器上用户的登录状态同步);
- 勾选记住我,则延长 cookie 中登录凭证的有效时间;
- 用户登出,将凭证状态设为无效,并更新 Redis 中该登录凭证的相关信息。
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
Figure 2:
显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
Figure 3:
2.开发环境- 操作系统:Windows 10
- 构建工具:Apache Maven
- 集成开发工具:Intellij IDEA
- 应用服务器:Apache Tomcat
- 接口测试工具:Postman
- 压力测试工具:Apache JMeter
- 版本控制工具:Git
- Java 版本:8
3.数据库设计 3.1表结构
article表
Figure 4:
category表
Figure 5:
comment表
Figure 6:
user表
Figure 7:
3.2ER图Figure 8:
4.项目开发 4.1配置maven依赖
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
# 配置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
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;
}
}
}
@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
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, "置顶失败");
}
}
}
5.项目展示 5.1普通用户
主页
Figure 9:
登录页
Figure 10:
主页
Figure 11:
个人中心
Figure 12:
Figure 13:
主页文章分类
Figure 14:
文章详情
Figure 15:
Figure 16:
文章评论
Figure 17:
发表文章
Figure 18:
5.2管理员登录
Figure 19:
主页
Figure 20:
文章管理
Figure 21:
修改文章
Figure 22:
查看文章
Figure 23:
删除文章
Figure 24:
分类管理
Figure 25:
用户管理
Figure 26:
禁言处理
Figure 27:
添加用户
Figure 28:
模糊查询
Figure 29:
数据统计
Figure 30:
项目运行截图
资料说明
基于SSM+SpringBoot+MySql+Layui的校园信息共享平台,分为前后端管理,前后用户查看,登陆注册账户,后端管理用户发布文章等内容。



