Myblog
码云:link
需求
-
界面简洁,好看
-
展示模块(展示我们的blog)
-
登录模块(认证用户)
-
后台模块(管理我们的blog)
- 文章
- 写文章
- 看文章
- 草稿箱,回收站,已发布
- 修改文章
- 文件上传(附件可供整个blog使用)
- 日志(记录我们的操作)
- 统计
- 文章
实现
使用工具
- Maven3.8.1
- java1.8.0
- idea2020.1.3
- mysql5.7.34
页面展示
博客展示
首页
归档–文章
友链
搜索
登录
后台
swagger界面–可在线测试,记录,同步更新我们的接口,
1.设计数据库
文章表(包含文章的所有信息)
分类关联表(将文章和文章分类关联在一起)
标签关联表(将文章和文章标签关联在一起)
分类表和标签表
附件表–我们上传的附件
友链表–友情链接
日志表–记录我们的操作
菜单表–博客展示页面的菜单
设置表
用户表-我们的管理人员
主题表–我们的主题
补充
- 文章表可通过文章分类,标签关联表将分类表,标签表连在一起–我们的文章有对应分类和标签
2.创建实体类对象(与数据库中中的表做映射)–resultMap
分类和标签实体类对象继承文章类–文章的所有信息–与我们查询数据库(连表查询得到的数据做映射)
3.导入依赖
<name>mayday</name>
<description>
学不在入,而在于出,分享热爱
</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<druid.version>1.1.10</druid.version>
<fastjson.version>1.2.47</fastjson.version>
<pagehelper.version>5.1.6</pagehelper.version>
<hutool-all.version>4.1.14</hutool-all.version>
<thumbnailator.version>0.4.8</thumbnailator.version>
<ehcache.version>3.6.3</ehcache.version>
<MDTool.version>1.2.1</MDTool.version>
<rome.version>1.0</rome.version>
</properties>
<dependencies>
<!--支持切面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--前端模板-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--连接数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--springboot测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<!-- druid数据库连接池,数据源 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--json工具-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--mybatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-autoconfigure -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-autoconfigure</artifactId>
<version>1.2.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.7</version>
</dependency>
<!-- Java工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<!-- 此包一般在Servlet容器中都有提供 -->
<scope>provided</scope>
</dependency>
<!--缩略图生成库-压缩图片工具类 -->
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>${thumbnailator.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<!--Java的进程内缓存框架-->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>${ehcache.version}</version>
</dependency>
<!--将markdown转换为html的工具-->
<dependency>
<groupId>com.youbenzi</groupId>
<artifactId>MDTool</artifactId>
<version>${MDTool.version}</version>
</dependency>
<!-- rss -->
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>${rome.version}</version>
</dependency>
<!--在线API框架-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
4.配置(yaml)
server:
port: 8888
logging:
level:
#可以设置要输出内容的等级并输出到控制台
org.springframework: info
com.songhaozhi.mayday.mapper: debug
com.songhaozhi.mayday.web: debug
#配置文件所在地(定义输出到文件,输出到控制台,设置输出什么等级的日志)
config: classpath:logback-spring.xml
spring:
mvc:
#静态资源路劲--/--静态文件夹static
static-path-pattern: /**
thymeleaf:
servlet:
content-type: text/html
encoding: UTF-8
cache: false
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
mybatis:
typeAliasesPackage: com.songhaozhi.mayday.model.domain
mapperLocations: classpath:mapper/*/*.xml
#是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN映射 到经典 Java 属性名 aColumn
configuration:
map-underscore-to-camel-case: true
pagehelper:
#分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言
helper-dialect: mysql
#分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页
reasonable: true
support-methods-arguments: true
#为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值,
params: count=countSql
private Integer userId;
5.自定义配置(拦截器,资源处理,视图解析)
- 日志
- 缓存
- 初始化(启动程序就会执行)
- 加载菜单
- 设置选项
- 主题
- 拦截器
- 登录拦截–session判断
- 首页拦截–非AJAX请求,就把我们初始化的东西给前端
- 注册拦截–判断我们是否注册blog(不会拦截安装请求和静态资源)
- 未注册:进行注册–初始化我们的菜单,设置,主题,用户,文章到数据库(初始化)
- swagger配置
- 注意点:配置资源处理,注意我们的请求(带参数的请求)占有swagger-ui.html,会把我们的swagger-ui.html看成参数
6.项目结构
实体类对象
-
日志接口–存放我们的日志关键
public interface LogConstant { String LOGIN = "登录后台"; String SUCCESS = "操作成功"; String ERROR = "操作失败"; String UPLOAD_ATTACHMENT = "上传附件"; String UPLOAD_SUCCESS = "上传成功"; String UPDATE_PWD="修改密码"; String DELETE_ATTACHMENT = "删除附件"; String DELETE_SUCCESS = "删除成功"; String LOGIN_SUCCES = "登录成功"; String LOGIN_ERROR = "登录失败"; String PUBLISH_AN_ARTICLE = "发表文章"; String UPDATE_AN_ARTICLE = "更新文章"; String REMOVE_AN_ARTICLE = "删除文章"; String PUBLISH_AN_PAGE = "发布页面"; String UPDATE_AN_PAGE = "更新页面"; String REMOVE_AN_PAGE = "删除页面"; String PUBLISH_AN_THEME = "添加主题"; String REMOVE_AN_THEME = "删除主题"; String INSTALL_SUCCESS = "安装MAYDAY"; }
我们可以直接取
-
消息实体类–用于返回信息给前端
-
public class JsonResult { private boolean flag; private String msg; private Object data; public JsonResult() { super(); } public JsonResult(boolean flag, String msg) { super(); this.flag = flag; this.msg = msg; } public JsonResult(boolean flag, String msg, Object data) { super(); this.flag = flag; this.msg = msg; this.data = data; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
通过@ResponseBody将方法的返回值直接输出到前端页面–返回对象(前端会自动解析为json数据)
也可以将对象转换为json格式
-
-
通用变量类–可以所有类使用
package com.songhaozhi.mayday.model.dto; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.songhaozhi.mayday.model.domain.Menu; public class MaydayConst { /** * user_session */ public static final String USER_SESSION_KEY = "user_session"; /** * 最大页码 */ public static final int MAX_PAGE = 100; /** * 所有设置选项 */ public static Map<String, String> OPTIONS = new HashMap<String, String>(); /** * 所有菜单 */ public static List<Menu> MENUS = new ArrayList<Menu>(); /** * 主题 */ public static String THEME_NAME; /** * 同一IP十分钟以内重复访问同一篇文章只算一次 */ public static final Integer IP_REPEAT_TIME=600; /** * 点击次数超过多少更新到数据库 */ public static final int CLICK_EXCEED = 10; }
-
枚举类–(很多对象–存放着值)
文章功能
查看文章
-
可编辑,操作,删除文章
-
三个模块–回收站,草稿,已发布
-
设计数据库
- 文章表–包含文章的信息—状态
- 分类表–包含分类信息
- 标签表–包含标签信息
- 文章–标签
- 文章–分类
-
创建实体类对象
方法一:
-
创建文章实体类对象–对应以上三表联合的表
-
创建分类实体类
-
创建标签实体类
方法二
-
创建文章实体类对象–对应文章
- 创建一个子类(包含分类信息和标签信息)
-
创建分类实体类
-
创建标签实体类
-
-
构建SQL语句
- 通过状态(0,1,2–区别回收站,草稿,已发布)和请求方式(post–安全)获取相应的文章(多表查询获取文章的所有信息)–通过文章id和标签id,分类id的绑定表进行获取数据
- 通过状态获取个数
- 通过文章url进行查找文章—id主键
- 修改
- 传入文章信息----对象–修改文章
- 传入标签id–插入到表(文章–标签)
- 传入分类id----插入到表(文章–分类)
- 改变状态移动
- 通过id进行删除
-
MVC的操作
-
写文章
前端提供一个页面(富文本编辑)—发送请求-保存(1)发布(0)-表单的信息发送给服务器–服务器的控制层接收请求,接收到所有有信息(分类ID,标签ID,文章信息)–将文章信息插入到数据库(生成文章ID)—构建文章ID和标,分ID绑在一起的实体类对象—插入数据库—相应到文章页面
用户功能
重点:了解MVC三层模型,设计数据库,设计Dao层(Mapper-xml)CRUD
-
登录
- Ajax发送请求,接收数据,跳转或刷新----通过设置Session验证并跳转
-
查看个人资料,修改个人资料
- 发送请求—处理请求—查询或修改数据库—响应数据—前端刷新(window.location.reload();)或跳转(window.location.href = “/admin/login”;)
-
密码修改
- 发送请求—处理请求—查询或修改数据库—响应数据—前端刷新或跳转
-
未完待续
统计功能
发布的文章
友链
附件
-
上传附件
/** * 上传功能 * @param file * @param request * @return */ public JsonResult uploadAttachment(@RequestParam(value = "file") MultipartFile file, HttpServletRequest request) { //接收前端传来的文件 if (!file.isEmpty()) { try { 1.处理文件---为文件创建目录 // 获取用户目录 String userPath = System.getProperties().getProperty("user.home") + "/mayday/"; // 保存目录 StringBuffer hold = new StringBuffer("upload/"); // 获取时间,以年月创建目录 Date date = DateUtil.date(); hold.append(DateUtil.thisYear()).append("/").append(DateUtil.thisMonth() + 1).append("/"); File mediaPath = new File(userPath, hold.toString()); // 如果没有该目录则创建 if (!mediaPath.exists()) { mediaPath.mkdirs(); } System.out.println("路径++++++" + mediaPath); SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 生成文件名称 2.处理文件---为文件创建名字 String nameSuffix = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf(".")) .replaceAll(" ", "_").replaceAll(",", "") + format.format(DateUtil.date()) + new Random().nextInt(1000); // 文件后缀 String fileSuffix = file.getOriginalFilename() .substring(file.getOriginalFilename().lastIndexOf(".") + 1); // 上传文件名加后缀 String fileName = nameSuffix + "." + fileSuffix; 3.处理文件---将文件放入指定的文件里(路径+文件名) // 转存文件 file.transferTo(new File(mediaPath.toString(), fileName)); 4.获取图片和压缩图片路径 // 原图片路径 StringBuffer originalPath = new StringBuffer(); originalPath.append(mediaPath.getAbsolutePath()).append("/").append(fileName); // 压缩图片路径 StringBuffer compressPath = new StringBuffer(); compressPath.append(mediaPath.getAbsolutePath()).append("/").append(nameSuffix).append("_small.") .append(fileSuffix); 5.压缩图片 // 压缩图片 Thumbnails.of(originalPath.toString()).size(256, 256).keepAspectRatio(false).toFile(compressPath.toString()); 6.获取数据库路劲 // 原图数据库路径 StringBuffer originalDataPath = new StringBuffer(); originalDataPath.append("/").append(hold).append(fileName); // 压缩图数据库路径 StringBuffer compressDataPath = new StringBuffer(); compressDataPath.append("/").append(hold).append(nameSuffix).append("_small.").append(fileSuffix); 7.将附件放入数据库 // 添加数据库 Attachment attachment = new Attachment(); attachment.setPictureName(fileName); attachment.setPicturePath(originalDataPath.toString()); attachment.setPictureType(file.getContentType()); attachment.setPictureCreateDate(date); attachment.setPictureSuffix(new StringBuffer().append(".").append(fileSuffix).toString()); attachment.setPictureSmallPath(compressDataPath.toString()); attachment.setPictureWh(MaydayUtil.getImageWh(new File(mediaPath.toString() + "/" + fileName))); attachment.setPictureSize(MaydayUtil.parseSize(new File(mediaPath.toString() + "/" + fileName).length())); attachmentService.save(attachment); // 添加日志 logService.save(new Log(LogConstant.UPLOAD_ATTACHMENT, LogConstant.UPLOAD_SUCCESS, ServletUtil.getClientIP(request), DateUtil.date())); } catch (Exception e) { log.error("上传附件错误" + e.getMessage()); return new JsonResult(false, "系统未知错误"); } } else { return new JsonResult(false, "文件不能为空"); } return new JsonResult(true, "上传成功"); }
-
查看附件详情
-
前端点击图片触发事件,发送请求—通过Id获得当前附件—响应详情页面
<!--显示图片以及图片的事件--> <div class="row"> <th:block th:each="attachment : ${info.list}"> <div class="col-lg-2 col-md-3 col-sm-6 col-6 div-thumbnail" th:onclick="'javascript:viewDetails('+${attachment.id}+')'"> <a href="#"><img class="img-thumbnail img-fluid img-responsive" th:src="${attachment.pictureSmallPath}"></a> </div> </th:block> </div> <script type="text/javascript"> function viewDetails(id) { layer.open({ type : 2, title : '附件详情', anim : 2, area : [ '90%', '90%' ], maxmin : true, content : "attachment/viewDetails/" + id }); }
-
提供相应操作–删除—通过ID删除当前附件—响应打开这个窗口的页面(parent.location.reload())
-
-
分页展示附件
- 通过条件进行查询,获得所有附件–使用分页工具,展示分页的附件给前端–前端通过图片路路径展示图片
日志功能
<div class="col-lg-6 col-md-12">
<div class="tile">
<div>
<h3 class="tile-title" style="float: left;">最新日志</h3>
<div class="dropdown" style="float: right;">
<button type="button" class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-bars"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" onclick="viewLogs()">查看所有</a>
<a class="dropdown-item" href="/admin/logs/clear">清空日志</a>
</div>
</div>
</div>
<table class="table">
<tr>
<td>事件</td>
<td>结果</td>
<td>IP</td>
<td>时间</td>
</tr>
<tr th:each="log : ${logs}">
<td th:text="${log.logTitle}"></td>
<td th:text="${log.logContent}"></td>
<td th:text="${log.logIp}"></td>
<td th:text="${#dates.format(log.logDate,'yyyy/MM/dd HH:mm:ss')}"></td>
</tr>
</table>
</div>
</div>
前端提供一个日志界面,前端向后端发送请求
后端提供一个接口,相应数据返回
创建日志常量类(封装我们的日志常量)
创建日志类(封装我们的日志信息)
问题以及解决方法
1.无法解析Mybatis的XML文件
- 那么一定是xml文件问题,检查代码
原博客地址
link:https://github.com/gujiniCY
对原作者的Blog的知识结构进行了总结,修改,打造成属于自己的Blog