CISP 全称 Campus Information Sharing Platform -- 校内信息共享平台
这是本科毕业设计,一个类似于论坛的信息发布平台
后端基于SpringBoot开发
前端使用LayUI框架 + freemarker动态模板生成
数据库使用MySQL
MVC三层架构
CISP 全称 Campus Information Sharing Platform -- 校内信息共享平台
这是本科毕业设计,一个类似于论坛的信息发布平台
后端基于SpringBoot开发
前端使用LayUI框架 + freemarker动态模板生成
数据库使用MySQL
MVC三层架构
注册
登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
Figure 2:
显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
Figure 3:
article表
Figure 4:
category表
Figure 5:
comment表
Figure 6:
user表
Figure 7:
Figure 8:
version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
<relativePath/>
parent>
<groupId>com.zbingroupId>
<artifactId>cispartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>cispname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.0.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.26version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mindrotgroupId>
<artifactId>jbcryptartifactId>
<version>0.4version>
dependency>
<dependency>
<groupId>com.qiniugroupId>
<artifactId>qiniu-java-sdkartifactId>
<version>7.2.18version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.56version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<mainClass>com.zbin.cisp.CispApplicationmainClass>
configuration>
plugin>
plugins>
build>
project>
# 配置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
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 {
}
}
文件处理
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);
}
}
加密工具类
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;
}
}
}
@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, "置顶失败");
}
}
}
主页
Figure 9:
登录页
Figure 10:
主页
Figure 11:
个人中心
Figure 12:
Figure 13:
主页文章分类
Figure 14:
文章详情
Figure 15:
Figure 16:
文章评论
Figure 17:
发表文章
Figure 18:
登录
Figure 19:
主页
Figure 20:
文章管理
Figure 21:
修改文章
Figure 22:
查看文章
Figure 23:
删除文章
Figure 24:
分类管理
Figure 25:
用户管理
Figure 26:
禁言处理
Figure 27:
添加用户
Figure 28:
模糊查询
Figure 29:
数据统计
Figure 30:
注册
登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
Figure 2:
显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
Figure 3:
article表
Figure 4:
category表
Figure 5:
comment表
Figure 6:
user表
Figure 7:
Figure 8:
version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
<relativePath/>
parent>
<groupId>com.zbingroupId>
<artifactId>cispartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>cispname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.0.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.26version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mindrotgroupId>
<artifactId>jbcryptartifactId>
<version>0.4version>
dependency>
<dependency>
<groupId>com.qiniugroupId>
<artifactId>qiniu-java-sdkartifactId>
<version>7.2.18version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.56version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<mainClass>com.zbin.cisp.CispApplicationmainClass>
configuration>
plugin>
plugins>
build>
project>
# 配置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
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 {
}
}
文件处理
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);
}
}
加密工具类
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;
}
}
}
@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, "置顶失败");
}
}
}
主页
Figure 9:
登录页
Figure 10:
主页
Figure 11:
个人中心
Figure 12:
Figure 13:
主页文章分类
Figure 14:
文章详情
Figure 15:
Figure 16:
文章评论
Figure 17:
发表文章
Figure 18:
登录
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 全称 Campus Information Sharing Platform -- 校内信息共享平台
这是本科毕业设计,一个类似于论坛的信息发布平台
后端基于SpringBoot开发
前端使用LayUI框架 + freemarker动态模板生成
数据库使用MySQL
MVC三层架构
注册
登录 | 登出
登录认证模块跳过了 Spring Secuity 自带的认证机制。主要逻辑如下:
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
Figure 2:
显示评论及相关信息
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决 ~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
Figure 3:
article表
Figure 4:
category表
Figure 5:
comment表
Figure 6:
user表
Figure 7:
Figure 8:
version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
<relativePath/>
parent>
<groupId>com.zbingroupId>
<artifactId>cispartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>cispname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.0.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.26version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mindrotgroupId>
<artifactId>jbcryptartifactId>
<version>0.4version>
dependency>
<dependency>
<groupId>com.qiniugroupId>
<artifactId>qiniu-java-sdkartifactId>
<version>7.2.18version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.56version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<mainClass>com.zbin.cisp.CispApplicationmainClass>
configuration>
plugin>
plugins>
build>
project>
# 配置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
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 {
}
}
文件处理
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);
}
}
加密工具类
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;
}
}
}
@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, "置顶失败");
}
}
}
主页
Figure 9:
登录页
Figure 10:
主页
Figure 11:
个人中心
Figure 12:
Figure 13:
主页文章分类
Figure 14:
文章详情
Figure 15:
Figure 16:
文章评论
Figure 17:
发表文章
Figure 18:
登录
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的校园信息共享平台,分为前后端管理,前后用户查看,登陆注册账户,后端管理用户发布文章等内容。