文章目录
- 博客系统
- 前端代码 :
- 1. add.html
- 2. blog_detail.html
- 3. blog_edit.html
- 4. blog_list.html
- 5. login.html
- css 文件
- 1. blog_detail.css
- 2. blog_edit.css
- 3. blog_list.css
- 4. common.css
- 5. login.css
- 6. 分页器使用的 css
- 后端代码
- 1.config 包
- 1.1 AppConfig类
- 1.2 LoginIntercept 类
- 1.3 MyExceptionAdvice 类
- 1.4 MyResponseAdvice 类
- 2. controller 包
- 2.1 UserController 类
- 2.2 BlogController类
- 3. mapper 包
- 3.1 UserMapper 接口
- 3.2 BlogMapper 接口
- 4. model 包
- 4.1 User类
- 4.2 Blog 类
- 5. service 包
- 5.1 UserService 类
- 5.2 BlogService 类
- 6. util 包
- 6.1 Constant 类</font>
- 6.2 PasswordUtil类
- 6.3 ResponseBodyMessage 类
- 7. Mybatis 包
- 7.1 UserMapper.xml
- 7.2 BlogMapper.xml
博客系统
前端代码 :
1. add.html
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
<!-- 指定字符集 -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>添加用户</title>
<link href="./css/bootstrap.min.css" rel="stylesheet">
<script src="./js/jquery.js"></script>
<script src="./js/common.js"></script>
<style>
body {
background-image: url("./imgs/阳台.png");
background-repeat: no-repeat;
background-size: cover;
}
#image {
width: 400px;
height: 250px;
border: 1px solid #eee;
}
.message {
width: 110px;
height: 50px;
line-height: 50px;
font-weight: 600;
}
</style>
</head>
<body>
<form enctype="multipart/form-data" id="form1">
<div class="container" style="width: 400px;">
<h3 style="text-align: center;">添加用户</h3>
<div class="form-group">
<label for="username">姓名:</label>
<input type="text" class="form-control" id="username" name="name" placeholder="请输入姓名"/>
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码 "/>
</div>
<div class="form-group">
<label for="password2">确认密码:</label>
<input type="password2" class="form-control" id="password2" name="password" placeholder="请输入密码"/>
</div>
<div class="form-group">
<label>性别:</label>
<input id="man" type="radio" name="sex" value="男" checked="checked"/>男
<input id="women" type="radio" name="sex" value="女"/>女
</div>
<div class="form-group">
<label for="address">籍贯:</label>
<select name="address" id="address" class="form-control">
<option value="">可以不选择</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="成都">成都</option>
<option value="杭州">杭州</option>
<option value="重庆">重庆</option>
<option value="西安">西安</option>
<option value="武汉">武汉</option>
<option value="沧州">沧州</option>
<option value="江西">江西</option>
</select>
</div>
<div class="form-group">
<label for="qq">QQ:</label>
<input type="text" id="qq" class="form-control" name="qq" placeholder="请输入QQ号码 (非必填) "/>
</div>
<div class="form-group">
<input type="file" name="filename" id="imgFile">
<span class="message">图片样式: </span>
<img src="" id="image"/>
</div>
<div class="form-group" style="text-align: center">
<input id="btn_sub" class="btn btn-primary" type="button" value="提交"/>
<input id="btn_back" class="btn btn-default" type="button" value="返回" onclick="location.href='list.html'"/>
</div>
</div>
</form>
<!--
// 拿到 input type 为 radio 中的内容 即 获取 男 女
let sex = $('input[name=sex]:checked').val();
let address = $("#address").val();
-->
<script>
let imgFile = document.querySelector("#imgFile");
// 这一部分 : 当我们上传图片后 , 我们的 图片样式 就会将图片显示出来
imgFile.onchange = function () {
let img = document.querySelector("#image");
let image = imgFile.files[0];
// let formData = new FormData();
if (image) {
// formData.append('filename', image);
img.src = window.URL.createObjectURL(image);
}
}
// 当点击 提交按钮后 构造数据 , 通过 ajax 发送请求给后端
let submit = document.querySelector("#btn_sub");
submit.onclick = function () {
let username = document.querySelector("#username");
let password = document.querySelector("#password");
let password2 = document.querySelector("#password2");
// 通过 jquery 拿到 性别框里面的内容
let sex = jQuery('input[name=sex]:checked').val();
let address = document.querySelector('#address').value;
let qq = document.querySelector("#qq").value;
// jQuery.trim 去掉 前后空格
if (jQuery.trim(username.value) === '') {
alert("请先输入用户名!!")
// 将焦点设置到 id 为 username 的输入 框上
username.focus();
return false;
}
if (jQuery.trim(password.value) === '') {
alert("请先输入密码");
password.focus();
return false;
}
if (jQuery.trim(password2.value) === '') {
alert("请输入确认密码");
password2.focus();
}
if (password.value !== password2.value) {
alert("两次密码不同,请重新输入")
password.focus();
return false;
}
// 使用 formData 类来返回 jQuery("#form1")[0]
let formData = new FormData();
formData.append('username', username.value);
formData.append('password', password.value);
formData.append('qq', qq);
formData.append('sex', sex);
formData.append('address', address);
let img = document.querySelector("#image");
let image = imgFile.files[0];
if (img.src === '') {
console.log('未上传图片 !!! ');
formData.append('filename', null);
} else {
formData.append('filename', image)
}
jQuery.ajax({
type: "POST",
url: "/user/add",
data: formData,
processData: false,
contentType: false,
success: function (result) {
if (result != null && result.data.status > 0) {
alert('注册成功!');
location.href = "login.html";
}else {
alert('注册失败')
}
},
error : function(){
alert("出错了, 请稍后再试!!!")
}
})
}
</script>
</body>
</html>
2. blog_detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客详情页</title>
<link rel="stylesheet" href="./css/common.css">
<link rel="stylesheet" href="./css/blog_detail.css">
<script src="./js/jquery.js"></script>
<!-- 引入 editor.md 的依赖-->
<link rel="stylesheet" href="./editor.md/css/editormd.min.css">
<script src="./editor.md/lib/marked.min.js"></script>
<script src="./editor.md/lib/prettify.min.js"></script>
<script src="./editor.md/editormd.min.js"></script>
</head>
<body>
<!-- 导航栏 -->
<div class="nav">
<img src="./imgs/阳台.png">
<span class="title">我的博客系统</span>
<!-- 这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="#" id = "remove">注销</a>
</div>
<!-- 页面主体部分 -->
<div class="container">
<!-- 左侧信息-->
<div class="container-left">
<!-- 使用 这个 .card 表示用户信息-->
<div class="card">
<img src="./imgs/girl.png" alt="图片显示失败">
<!-- 用户名-->
<h3>牧</h3>
<a href="#">Gitee 地址</a>
<div class='counter'>
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span>1</span>
<span>2</span>
</div>
</div>
</div>
<!-- 右侧信息-->
<div class="container-right">
<!-- 博客标题 -->
<h3 class="title" id="title">我的第一篇博客</h3>
<!-- 博客发布时间-->
<div class="date">2023-03-02</div>
<!-- 博客正文 -->
<div id="content">
<!-- <P>-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- </P>-->
<!-- <P>-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- </P>-->
<!-- <P>-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- 从今天开始我要认真敲代码-->
<!-- </P>-->
</div>
</div>
</div>
<script>
function getBlogDetail() {
jQuery.ajax({
type: "GET",
// location.search 就是 ?blogId=x
url: "/blog/getblog" + location.search,
success: function (result) {
if (result != null && result.data.status > 0) {
let data = result.data.data;
// 1. 构造博客标题
let title = document.querySelector("#title");
title.innerHTML = data.title;
// 2. 构造发布时间
let dateDiv = document.querySelector(".date");
dateDiv.innerHTML = data.postTime;
// 3. 构造正文部分
// let content = document.querySelector(".content");
//
// content.innerHTML = data.content;
// 使用 editormd.md 自带的方法 对内容进行渲染
editormd.markdownToHTML('content', {
markdown: data.content
})
let userid = data.userid;
jQuery.ajax({
type: "GET",
url: "/user/getuserbyid",
data: {
"userid": userid,
},
success: function (result) {
if (result != null && result.data.status > 0) {
let data = result.data.data;
// 通过子类选择器选中 img 元素
let img = document.querySelector(".card>img");
img.src = "product/" + data.user.url;
let title = document.querySelector(".card>h3");
title.innerHTML = data.user.username;
let spanArr = document.querySelectorAll(".counter>span")
spanArr[2].innerHTML = data.blogNumber;
spanArr[3].innerHTML = data.type;
} else {
alert("设置错误!!!")
}
},
error: function () {
alert("出错了,请稍后再试!!")
}
})
}
}
})
}
getBlogDetail();
function getLoginUser() {
jQuery.ajax({
type: "GET",
url: "/user/getuser",
success: function (result) {
if (result != null && result.data.status > 0) {
let img = document.querySelector(".nav>img");
img.src = "product/" + result.data.data.url;
}
}
})
}
getLoginUser();
</script>
<script src="./js/common.js"></script>
</body>
</html>
3. blog_edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客编辑页</title>
<link rel="stylesheet" href="./css/common.css">
<link rel="stylesheet" href="./css/blog_edit.css">
<script src="./js/jquery.js"></script>
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script>-->
<!-- 引入 editor.md 的依赖-->
<link rel="stylesheet" href="./editor.md/css/editormd.min.css">
<script src="./editor.md/lib/marked.min.js"></script>
<script src="./editor.md/lib/prettify.min.js"></script>
<script src="./editor.md/editormd.min.js"></script>
</head>
<body>
<!-- 导航栏 -->
<div class="nav">
<img src="./imgs/阳台.png">
<span class="title">我的博客系统</span>
<!-- 这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="#" id = "remove">注销</a>
</div>
<!-- 编辑区的容器 -->
<div class="blog-edit-container">
<!-- 博客标题编译去-->
<div class="title">
<input type="text" id="title" placeholder="请输入文章标题">
<input type="text" id="typeid" placeholder="请输入文章类型">
<button id="submit">发布文章</button>
</div>
<!-- 博客编译器,这里用 id 是为了和 markdown 对接-->
<div id="editor">
<!-- 通过 textare 可以设置 editor.md,让编辑器把 markdown 内容也同步的保存到这个隐藏的 textare 中 -->
<textarea name="content" style="display: none" id="content"></textarea>
</div>
<script>
<!-- 初始化编译器-->
let editor = editormd("editor", {
// 这里的尺寸必须在这试着 ,设置样式会被 editormd 自动覆盖
width: "100%",
// 设置编译器高度
height: "calc(100% - 50px)",
// 编译器中的初始内容
markdown: "# 在这里写一篇博客",
// 指定 editor.md 依赖的插件路径
path: "./editor.md/lib/",
saveHTMLToTextarea: true
})
let sumit = document.querySelector("#submit");
sumit.onclick = function addBlog() {
let title = document.querySelector("#title");
let type = document.querySelector("#typeid");
let content = document.querySelector("#content");
if (title.value.trim() === '') {
alert("请输入文章标题!!!")
return;
}
if (type.value.trim() === '') {
alert("请输入文章类型 !!!")
return;
}
jQuery.ajax({
type: "POST",
url: "/blog/add",
data: {
// trim() 去掉 前后的空格 , 这里 去掉 标题和类型的前后空格
"title": title.value.trim(),
"type": type.value.trim(),
"content": content.value
},
success: function (result) {
if (result != null && result.data.status > 0) {
// 此时上传 成功
location.href = "blog_list.html";
}else {
alert("上传失败,请稍后再试!!!")
}
}
})
}
</script>
<script src="./js/common.js"></script>
</div>
</body>
</html>
4. blog_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客列表页</title>
<link rel="stylesheet" href="./css/common.css">
<link rel="stylesheet" href="./css/blog_list.css">
<script src="./js/jquery.js"></script>
<link href="css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- 导航栏 -->
<div class="nav">
<img src="./imgs/阳台.png" id="image1">
<span class="title">我的博客系统</span>
<!-- 这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="#" id = "remove">注销</a>
</div>
<!-- 页面主体部分 -->
<div class="container">
<!-- 左侧信息-->
<div class="container-left">
<!-- 使用 这个 .card 表示用户信息-->
<div class="card">
<img src="./imgs/girl.png" alt="图片显示失败" id="image2">
<!-- 用户名-->
<h3 id="username"></h3>
<a href="#">Gitee 地址</a>
<div class='counter'>
<span>文章</span>
<span>分类</span>
</div>
<div class="counter">
<span id="number1">1</span>
<span id="number2">2</span>
</div>
</div>
</div>
<!-- 右侧信息-->
<div class="container-right">
<!-- <!–-->
<!-- 表示一篇博客-->
<!-- –>-->
<!-- <div class="blog">-->
<!-- <!–-->
<!-- 博客标题-->
<!-- –>-->
<!-- <div class="title">我的第一篇博客</div>-->
<!-- <!–-->
<!-- 发布时间-->
<!-- –>-->
<!-- <div class="data">-->
<!-- 2023-03-02-->
<!-- </div>-->
<!-- <!–-->
<!-- 博客的摘要-->
<!-- –>-->
<!-- <div class="desc">-->
<!-- 从今天起 , 我要认真敲代码-->
<!-- Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sint eaque facilis perferendis! Numquam-->
<!-- neque voluptatum ab vero expedita possimus fuga eos, illo sapiente delectus quidem natus maiores,-->
<!-- ipsum impedit rerum?-->
<!-- </div>-->
<!-- <!–-->
<!-- 查看全文按钮-->
<!-- –>-->
<!-- <a href="#">查看全文 >></a>-->
<!-- </div>-->
<div id="page">
<nav aria-label="Page navigation">
<ul id="all" class="pagination">
<li class="active"><a href="javascript:firstPage();">首页</a></li>
<li><a href="javascript:beforePage();">上一页</a></li>
<li><a href="javascript:nextPage();">下一页</a></li>
<li><a href="javascript:lastPage();">末页</a></li>
<span id="pageinfo" style="font-size: 20px;margin-left: 10px;">
</span>
</ul>
</nav>
</div>
</div>
</div>
<script>
// 这个 ajax 获取登录状态 :
jQuery.ajax({
type: "GET",
url: "/user/getuser",
success: function (result) {
if (result != null && result.data.status > 0) {
let image = document.querySelector("#image1");
image.src = "product/" + result.data.data.url;
let image2 = document.querySelector("#image2");
image2.src = "product/" + result.data.data.url;
// 将用户名 换上去
let username = document.querySelector("#username");
username.innerHTML = result.data.data.username;
}
}
})
// 这个 ajax 获取 分类
jQuery.ajax({
type: "GET",
url: "/blog/gettype",
success: function (result) {
if (result != null && result.data.status > 0) {
let number1 = document.querySelector("#number1");
let number2 = document.querySelector("#number2");
number1.innerHTML = result.data.data[0]
number2.innerHTML = result.data.data[1];
} else {
alert("分类设置失败 !!! ")
}
},
error: function () {
alert("出错了, 请稍后再试!!!")
}
})
// 分页功能 :
// 1. 当前的页码
let pIndex = 1;
// 2. 每页显示多少篇博客
let pSize = 2;
// 3. 总页数
let totalPage = 0;
// 4. 总条数 (当前所有的博客数目)
let totalCount = 0;
// 通过 ajax 获取 总页数 和 总条数
function getList() {
jQuery.ajax({
type: "GET",
url: "/blog/listbypage",
data: {
"pIndex": pIndex,
"pSize": pSize
},
success: function (result) {
if (result != null && result.data.status > 0) {
// 总博客数
totalCount = result.data.data.count;
// ceil 四舍五入并返回大于等于给定数字的最小整数。
totalPage = Math.ceil(parseInt(totalCount) / pSize);
let size = result.data.data.list.length;
// rightDiv 后面创建的 元素需要挂载 rightDiv 上
let rightDiv = document.querySelector(".container-right");
for (let i = 0; i < size; i++) {
let ret = result.data.data.list[i];
let blogDiv = document.createElement('div');
// 引入 class 属性
blogDiv.className = "blog";
// 1. 构造标题
let titleDiv = document.createElement('div');
titleDiv.innerHTML = ret.title;
titleDiv.className = "title";
// 将 titleDiv 挂到 blogDiv上
blogDiv.appendChild(titleDiv);
// 2. 构造 发布时间
let dataDiv = document.createElement('div');
dataDiv.innerHTML = ret.postTime;
dataDiv.className = "data";
blogDiv.appendChild(dataDiv);
// 3. 构造 文章描述
let descDiv = document.createElement('div');
descDiv.innerHTML = ret.content;
descDiv.className = "desc";
blogDiv.appendChild(descDiv);
// 4. 构造 查看全文按钮
let a = document.createElement('a');
a.innerHTML = "查看全文 >>";
// 重点 : 这里我们点击查看全文 跳转到 博客详情页 ,这里可以 在 url 里面添加一个 博客id ,
// 后面在 博客详情页就可以通过这个 blogId 获取到文章.
a.href = "blog_detail.html?blogId=" + ret.blogId;
blogDiv.appendChild(a);
rightDiv.appendChild(blogDiv);
}
let page = document.querySelector("#page");
rightDiv.appendChild(page);
let pageinfo = document.querySelector("#pageinfo");
pageinfo.innerHTML = "总共 " + totalCount +" 篇博客" + ",当前是第" + pIndex +"页"
pageinfo.style.color =" rgb(32,211,71)";
} else {
alert("获取失败!!!")
}
},
error: function () {
alert("出错了,请稍后在尝试!!!")
}
})
}
// getList();
// 首页
function firstPage() {
location.href = "blog_list.html?pIndex=1"
}
// 上一页
function beforePage() {
if (pIndex > 1) {
pIndex = parseInt(pIndex) - 1;
location.href = "blog_list.html?pIndex=" + pIndex;
} else {
alert("已经是首页了!!!")
}
}
// 下一页
function nextPage() {
if (pIndex < totalPage) {
pIndex = parseInt(pIndex) + 1;
location.href = "blog_list.html?pIndex=" + pIndex;
} else {
alert("已经是末页了!!!")
}
}
// 末页
function lastPage() {
location.href = "blog_list.html?pIndex=" + totalPage;
}
// 使用这个 方法来初始话页面
function initPage() {
// 获取 当前页面的 查询字符串 比如 :?pIndex=2
let url = location.search;
if (url != '') {
// 将 问好 去掉 此时就剩下了 pIndex=2 (假设页数是2)
url = url.substring(1);
// let kvs = url.split("&");
let kvs = url.split("=");
pIndex = kvs[1];
}
getList();
}
initPage();
</script>
<script src="./js/common.js"></script>
</body>
</html>
5. login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<link rel="stylesheet" href="./css/common.css">
<link rel="stylesheet" href="./css/login.css">
<script src="./js/jquery.js"></script>
</head>
<body>
<!-- 导航栏 -->
<div class="nav">
<img src="./imgs/阳台.png">
<span class="title">我的博客系统</span>
<!-- 这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
</div>
<!--
正文部分
这个 login-container 是贯穿整个页面的容器
-->
<div class="login-container">
<!-- 垂直水平居中的登录对话框 -->
<div class="login-dialog">
<h3>登录</h3>
<div class="row">
<span>用户名</span>
<input type="text" id="username" placeholder="输入用户名">
</div>
<div class="row">
<span>密码</span>
<input type="password" id="password" placeholder="输入密码">
</div>
<div class="row">
<button id="submit">提交</button>
</div>
<div class="row">
<a id="insert" href = "add.html">
注册
</a>
</div>
<div class="a">没有账户? 点击上面进行注册</div>
</div>
</div>
<script>
// 通过 id 选中输入框 ,
let username = document.querySelector("#username");
// 通过 id 选中密码框
let password = document.querySelector("#password");
let submit = document.querySelector("#submit");
// 点击按钮后触发
submit.onclick = function () {
// 1. 判断 username or password
if (jQuery.trim(username.value) === '') {
alert("请先输入用户名");
}
if (jQuery.trim(password.value) === '') {
alert("请先输入密码")
}
// 此时 用户 和密码 都有了 ,发送 请求
$.ajax({
url: "/user/login",
type: "POST",
data: {
"username": username.value,
"password": password.value
},
// 回调函数
success: function (result) {
if (result != null && result.data.status > 0) {
// 登录成功 , 跳转到 博客列表页
location.href = "blog_list.html";
} else {
//登录 登录失败 ,
alert("登录失败,请重新输入")
}
}
})
}
</script>
</body>
</html>
css 文件
1. blog_detail.css
/*
这个样式文件给博客详情页使用
*/
/*
设置标题样式
*/
.container-right .title {
/*
文字居中
*/
text-align: center;
/*
边距 : 距离产生美
*/
padding: 30px
}
/*
设置日期样式
*/
.container-right .date {
/*
文字颜色
*/
color: rgb(15, 189, 114);
/*
位子居中
*/
text-align: center;
/*
设置内边距 (下) 为 20px
*/
padding-bottom: 20px;
}
/*
设置 段落
*/
.container-right .content p {
/*
缩进 两字符
*/
text-indent: 2em;
/*
内边距
*/
padding: 10px 30px;
}
2. blog_edit.css
/*
这个文件用来写博客编辑页的样式
*/
.blog-edit-container {
width: 1000px;
height: calc(100% - 50px);
/*
外边距
*/
margin: 0 auto;
}
.blog-edit-container .title {
height: 50px;
/*
开启弹性布局
*/
display: flex;
/*
垂直居中
*/
align-items: center;
/*
中间空白环绕 左右两边没有
*/
justify-content: space-between;
}
#title {
height: 40px;
width: 595px;
/*
去掉边框
*/
border: none;
/*
文字大小
*/
font-size: 22px;
/*
圆角边框
*/
border-radius: 5px;
/*
设置左内边距
*/
padding-left: 5px;
/*
去掉轮廓线 , 鼠标点击后的黑圈
*/
outline: none;
/*
设置背景半透明
*/
background-color: rgb(255, 255, 255, 0.7);
}
/*
focus : 获取焦点后执行 (相当于鼠标点中 , 背景颜色改为下面)
失去焦点 , 回复成原来的样子.
*/
#title:focus {
background-color: rgb(255, 255, 255);
}
#submit {
height: 40px;
width: 100px;
color: white;
background-color: orange;
/*
去掉边框
*/
border: none;
/*
圆角矩形
*/
border-radius: 10px;
}
#submit:active {
background-color: skyblue;
}
#editor {
/*
圆角矩形
*/
border-radius: 15px;
/*background-color: rgba(255,255,255,0.8);*/
/*
设置半透明
*/
opacity: 75%;
}
#typeid{
height: 40px;
width: 285px;
/*
去掉边框
*/
border: none;
/*
文字大小
*/
font-size: 22px;
/*
圆角边框
*/
border-radius: 5px;
/*
设置左内边距
*/
padding-left: 5px;
/*
去掉轮廓线 , 鼠标点击后的黑圈
*/
outline: none;
/*
设置背景半透明
*/
background-color: rgb(255, 255, 255, 0.7);
}
#typeid:focus {
background-color: rgb(255, 255, 255);
}
3. blog_list.css
/*
这个文件是给博客列表页实现样式的
*/
/*
设置整个博客的容器元素样式
*/
.blog {
width: 100%;
/*
设置边距 : 此时 上下左右都有 20px
*/
padding: 20px;
}
/*
设置标题 :
*/
.blog .title {
/*
设置文字居中
*/
text-align: center;
/*
设置字体大小
*/
font-size: 24px;
/*
设置粗细
*/
font-weight: 700;
/*
设置内边距
*/
padding: 10px
}
/*
设置日期 :
*/
.blog .data {
/*
文本居中
*/
text-align: center;
/*
设置颜色
*/
color: rgb(15, 189, 114);
/*
设置内边距
*/
padding: 10px
}
/*
设置摘要
*/
.blog .desc {
/*
设置缩进 2 个汉字
*/
text-indent: 2em;
}
/*
设置 查看全文
*/
.blog a {
/*
a 标签不方便设置样式 , 转为块级元素
*/
display: block;
width: 120px;
height: 40px;
/*
设置上边距
*/
margin-top: 20px;
/*
设置水平居中 : 通过
margin-left 和 margin-right 来设置
*/
margin-left: auto;
margin-right: auto;
/*
设置边框
*/
border: 2px solid black;
/*
让文字水平居中
*/
text-align: center;
/*
让文字垂直居中
*/
line-height: 40px;
/*
去掉下划线
*/
text-decoration: none;
/*
文字颜色
*/
color: black;
/*
背景颜色
*/
background-color: orange;
transition: all 0.8s;
border-radius: 10px;
}
/*
鼠标滑倒按钮上有一些变化
*/
.blog a:hover {
color: white;
background: skyblue;
}
4. common.css
/* 写样式的起手式 , 先取出浏览器的公共样式 ,并且设置 border-box , 避免元素盒子被内边距和边距撑大 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 并集选择器 */
html, body {
/*
html 是页面的最顶成元素 ,高度 100% 是相对父元素来说高度是 100% (和父元素一样高)
对于 html 标签来说 , 父元素就是浏览器窗口 , 浏览器多高 , html 就多高
body 的父亲是 html ,设为 100% 意思是 body 和 html 一样高
此时 body 和 html 的高度都是和浏览器窗口一样高的
如果不设置高度 ,此时 元素的默认高度取决于内部的内容
*/
height: 100%;
}
body {
background-image: url("../imgs/abc.jpg");
/* 拒绝平铺 */
background-repeat: no-repeat;
/* 尽可能扩展 */
background-size: cover;
/* 水平垂直居中 */
background-position: center center;
}
/* 实现导航栏样式 */
.nav {
/*
设置 宽度和父元素一样宽
块级元素来说 , 默认就是 width : 100%
*/
width: 100%;
/*
这里的高度可以自己调整
*/
height: 50px;
background-color: rgba(51, 51, 51, 0.4);
color: white;
/*
导航栏里面的元素都是水平排列, 弹性布局来设置
*/
display: flex;
/*
垂直方向子元素居中 :
*/
align-items: center;
}
.nav img {
width: 40px;
height: 40px;
/* 左侧外边距 */
margin-left: 30px;
margin-right: 10px;
/* 设置圆角矩形 : 把内切圆设置为宽度的一般,就正好是一个圆形 */
border-radius: 50%;
}
.nav a {
color: white;
/* 去掉下划线 */
text-decoration: none;
margin: 0 10px;
}
.nav .spacer {
width: 70%
}
/*
编写页面主体样式
*/
.container {
/* 设置主体部分宽度 1000px*/
width: 1000px;
/*
高度能够填冲满整个页面 (这里需要减去导航栏的高度)
*/
height: calc(100% - 50px);
/* 水平居中 */
margin: 0 auto;
/*background-color: red;*/
/* 弹性布局*/
display: flex;
/* 垂直居中 */
align-items: center;
/* 中间使用空白分开 */
justify-content: space-between;
}
.container-left {
/*
尺寸写百分数, 是相对父元素为基准
container-left 的父元素 是 container
container 高度已经是 设置好了 ,所以这里的 100%
也是设置好了的
*/
height: 100%;
width: 200px;
/* 方便观察 加上一个背景色*/
/* background-color: black;*/
}
.container-right {
/*
与上面同理
*/
height: 100%;
/*
container 设置的总宽度是 1000px
container-left 设置的宽度是 200px
这里原本是设置 800px ,但是 需要 一点空隙
所以设置为 795px
*/
width: 795px;
/*background-color: green;*/
/*
添加背景颜色
*/
background-color: rgba(255, 255, 255, 0.8);
/*
圆角矩形
*/
border-radius: 10px;
/*
让这个元素自己能带上滚动条
这个属性表示 ,内容没有溢出 ,无滚动条,如果内容一处了,则自动加上滚动条
*/
overflow: auto;
}
/*
左侧用户信息
*/
.card {
/*
背景颜色 带透明
*/
background-color: rgba(255, 255, 255, 0.8);
/*
圆角矩形
*/
border-radius: 10px;
padding: 30px;
}
/*
用户头像
*/
.card img {
width: 140px;
height: 140px;
/*
内间距 , 让 内容与边框之间有一点距离
*/
border-radius: 50%;
}
/*
用户名字
*/
.card h3 {
/*
让文字水平居中
*/
text-align: center;
/*
让文字上下都由边距
使用内边距或者外边距均可, 但更倾向使用内边距
因为 外边距有时候有坑
*/
padding: 10px;
}
/*
用户的 Gitee 链接
*/
.card a {
/*
水平居中
*/
text-align: center;
color: #777;
text-decoration: none;
/*
为了配合上述样式 , 将
a 标签设置为块级样式
*/
display: block;
padding: 10px;
}
.card .counter {
/*
为了让里面的元素水平排列 , 使用弹性布局
*/
display: flex;
/*
每个元素左右两边都有等间距的空白
*/
justify-content: space-around;
/*
设置内边距
*/
padding: 5px;
}
5. login.css
/*
这个文件专门放登录页面的样式
*/
.login-container {
/**/
width: 100%;
/*
这里的高度与之前同理 ,需要去掉导航栏的高度
*/
height: calc(100% - 50px);
/*
暂时设置一个 背景颜色 方便观察效果
*/
/*background-color: rgb(128, 0, 0);*/
/*
开启弹性布局 (为了让对话框能够 垂直水平居中 使用弹性布局)
*/
display: flex;
/*
水平居中
*/
justify-content: center;
/*
垂直居中
*/
align-items: center;
}
.login-dialog {
width: 400px;
height: 380px;
/*background-color: rgb(0, 180, 0);*/
background-color: rgba(255, 255, 255, 0.8);
border-radius: 10px;
}
/*
标题
*/
.login-dialog h3 {
/*
文字居中
*/
text-align: center;
/*
设置 内边距 : 上下 为 50px 左右为 0
*/
padding: 50px 0;
}
.login-dialog .row {
height: 50px;
/*
开启弹性布局
*/
display: flex;
/*
水平居中
*/
justify-content: center;
/*
垂直居中
*/
align-items: center;
}
.login-dialog .row span {
width: 100px;
font-size: 22px
}
/*
id 选择器 , 并集选择器
*/
#username, #password {
width: 200px;
height: 40px;
/*
圆角矩形
*/
border-radius: 10px;
/*
去掉边框
*/
border: none;
/*
放大输入框内的字体
*/
font-size: 18px;
/*
内边距 : 输入框内文字与边框的内边距
*/
padding-left: 5px;
}
#submit {
width: 300px;
height: 40px;
color: white;
background-color: greenyellow;
/*
去除边框
*/
border: none;
/*
圆角矩形
*/
border-radius: 10px;
}
/*
伪类选择器 让鼠标点击有效果
*/
#submit:active {
background-color: skyblue;
}
#insert {
width: 300px;
height: 40px;
color: white;
background-color: greenyellow;
/*
去除边框
*/
border: none;
/*
圆角矩形
*/
border-radius: 10px;
/**
垂直居中
*/
align-items: center;
justify-content: center;
display: flex;
/*
使用 下面两个也能是 文字居中
*/
/*text-align: center;*/
/*line-height: 40px;*/
/**
去掉 a 标签下划线
*/
text-decoration: none;
}
#insert:active {
background-color: skyblue;
}
.login-dialog .a {
text-align: left;
padding-left: 47px;
color: #777777;
padding-top: 12px;
}
6. 分页器使用的 css
这里分页器使用的 css 可以去我们的资源里下载 , 这里比较多 ,不放上来了 .
后端代码
1.config 包
1.1 AppConfig类
package com.example.blog_ssm.config;
import com.example.blog_ssm.util.PasswordUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AppConfig implements WebMvcConfigurer {
// 1. 注入拦截器
@Autowired
private LoginIntercept loginIntercept;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/product/**").addResourceLocations("file:D:/ret/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginIntercept)
.addPathPatterns("/**")
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/add")
.excludePathPatterns("/css/**")
.excludePathPatterns("/js/**")
.excludePathPatterns("/imgs/**")
.excludePathPatterns("/login.html")
.excludePathPatterns("/add.html")
.excludePathPatterns("/product/**");
}
@Bean
public PasswordUtil passwordUtil() {
return new PasswordUtil();
}
}
1.2 LoginIntercept 类
package com.example.blog_ssm.config;
import com.example.blog_ssm.util.Constant;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 自定义拦截器
*/
@Component
public class LoginIntercept implements HandlerInterceptor {
/**
* true 表示已经登录 ,会继续访问目标方法
* false 表示未登录 , 跳转到登录页面
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// false : 如果 没有 session 也不会创建
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute(Constant.USERINFO_SESSION_KEY) != null) {
// 表示登录成功
return true;
}
// 403 当前你没有资格访问
response.setStatus(403);
// 重定向
response.sendRedirect("/login.html");
return false;
}
}
1.3 MyExceptionAdvice 类
package com.example.blog_ssm.config;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
/**
* 统一异常的拦截处理类
*/
@RestControllerAdvice
// 使用 @ControllerAdvice 需要再加一个注解 @ResponseBody (返回一个非静态页面)
public class MyExceptionAdvice {
@ExceptionHandler(Exception.class)
public Object exceptionAdvice(Exception e) {
HashMap<String, Object> result = new HashMap<>();
result.put("status", -1);
result.put("message", "程序异常 : " + e.getMessage());
result.put("data", "");
return result;
}
}
1.4 MyResponseAdvice 类
package com.example.blog_ssm.config;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.HashMap;
/**
* 返回统一的数据格式
* 1. 表示当前的类为 : ControllerAdvice
* 2. 实现一个 ResponseBodyAdvice 接口
*/
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice {
/**
* 是否需要对返回的数据进行重写
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// false 不重写
// ture 重写
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
HashMap<String, Object> result = new HashMap<>();
result.put("state", 1);
result.put("message", "");
result.put("data", body);
return result;
}
}
2. controller 包
2.1 UserController 类
package com.example.blog_ssm.controller;
import com.example.blog_ssm.model.Blog;
import com.example.blog_ssm.model.User;
import com.example.blog_ssm.service.BlogService;
import com.example.blog_ssm.util.Constant;
import com.example.blog_ssm.util.ResponseBodyMessage;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@RestController
@RequestMapping(value = "/blog")
public class BlogController {
@Autowired
private BlogService blogService;
@RequestMapping(value = "/gettype")
public ResponseBodyMessage<List<Integer>> getType(HttpServletRequest request) {
List<Integer> list = new ArrayList<>();
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
int ret1 = blogService.getTextNumber(user.getId());
int ret2 = blogService.getType(user.getId());
list.add(ret1);
list.add(ret2);
return new ResponseBodyMessage<>(1, "成功", list);
}
@RequestMapping(value = "/listbypage")
public ResponseBodyMessage<HashMap<String, Object>> getListByPage(Integer pIndex, Integer pSize) {
HashMap<String, Object> result = new HashMap<>();
// 这里 limit 就相当于 pSize
// 计算处 offset 从第几个下标 返回 , 当前页码为 0 pIndex - 1 就为 0 乘上我们的 规定一页显示的 5 (pSize)
// 等于 0 , 也就是说从 0 下标开始 算 5 个 , 页码为 1 通过这个公式 能得到从 5 下标开始
if (pIndex == null || pSize == null) {
return new ResponseBodyMessage<>(-1, "请传入 页码 或 页数", null);
}
if (pIndex - 1 == -1) {
pIndex = 0;
} else {
pIndex -= 1;
}
int offset = pIndex * pSize;
List<Blog> list = blogService.getListByPage(pSize, offset);
if (list == null) {
return new ResponseBodyMessage<>(-1, "获取失败", null);
}
Integer count = blogService.getTextCount();
if (count == 0) {
return new ResponseBodyMessage<>(-1, "获取失败", null);
}
for (int i = 0; i < list.size(); i++) {
Blog blog = list.get(i);
String content = blog.getContent();
if (content.length() > 50) {
content = content.substring(0, 50) + ".....";
}
blog.setContent(content);
}
result.put("list", list);
result.put("count", count);
return new ResponseBodyMessage<>(1, "获取成功", result);
}
@RequestMapping(value = "/getblog")
public ResponseBodyMessage<Blog> getBlog(Integer blogId) {
if (blogId <= 0) {
return new ResponseBodyMessage<>(-1, "查询失败,博客id 有误", null);
}
Blog blog = blogService.getBlogById(blogId);
if (blog == null) {
return new ResponseBodyMessage<>(-1, "无当前博客", null);
}
return new ResponseBodyMessage<>(1, "查询成功", blog);
}
@RequestMapping(value = "add")
public ResponseBodyMessage<Boolean> addBlog(String title, String type, String content,
HttpServletRequest request) {
// 1. 获取 userid
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
Integer ret = blogService.addBlog(user.getId(), title, content , type);
if (ret != 0) {
return new ResponseBodyMessage<>(1, "增加成功", true);
}
return new ResponseBodyMessage<>(-1, "增加失败", false);
}
}
2.2 BlogController类
package com.example.blog_ssm.controller;
import cn.hutool.core.util.IdUtil;
import com.example.blog_ssm.model.Blog;
import com.example.blog_ssm.model.User;
import com.example.blog_ssm.service.BlogService;
import com.example.blog_ssm.service.UserService;
import com.example.blog_ssm.util.Constant;
import com.example.blog_ssm.util.PasswordUtil;
import com.example.blog_ssm.util.ResponseBodyMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
@RestController
@RequestMapping(value = "/user")
public class UserController {
@Autowired
private UserService userService;
// 注入 加密工具类
@Autowired
private PasswordUtil passwordUtil;
@Autowired
private BlogService blogService;
@Value("${image.local.path}")
private String IMAGE_PATH;
// 1. 登录功能
@RequestMapping(value = "/login")
public ResponseBodyMessage<Boolean> login(String username, String password, HttpServletRequest request) {
// 1. 判断 用户名和密码 是否为空
if (!StringUtils.hasLength(username) && !StringUtils.hasLength(password)) {
return new ResponseBodyMessage<>(-1, "登录失败, 用户或密码不能空", false);
}
// 2. 通过 用户名获取 用户
User user = userService.getUserByUserName(username);
if (user == null) {
return new ResponseBodyMessage<>(-1, "登录失败 , 无当前用户", false);
}
// 3. 获取到 user , 对密码进行校验
boolean flag = passwordUtil.decrypt(password, user.getPassword());
if (flag) {
// 此时密码正确 , 登录成功
// 创建 session
HttpSession session = request.getSession(true);
// 保存 user
session.setAttribute(Constant.USERINFO_SESSION_KEY, user);
return new ResponseBodyMessage<>(1, "登录成功", true);
}
// 此时密码错误
return new ResponseBodyMessage<>(-1, "登录失败", false);
}
/**
* 2. 注册功能
*/
@RequestMapping(value = "/add")
@Transactional
public ResponseBodyMessage<Boolean> addUser(User user, @RequestPart(required = false, value = "filename") MultipartFile file) {
if (user == null) {
return new ResponseBodyMessage(-1, "注册失败", false);
}
// 1. 判断 必填参数是否为空 (这里可以不写 ,前端大概率 是会判断的 。 )
if ("".equals(user.getUsername())) {
return new ResponseBodyMessage<>(-1, "注册失败, 当前用户为输入用户名", false);
}
if ("".equals(user.getPassword())) {
return new ResponseBodyMessage<>(-1, "注册失败,当前用户未输入密码", false);
}
// 2. 校验用户名的 唯一性 : 如果 用户名已经纯在了 那么就不能注册
User user2 = userService.getUserByUserName(user.getUsername());
if (user2 != null) {
return new ResponseBodyMessage<>(-1, "注册失败, 用户名已存在", false);
}
// 3. 手动设置 为 '' 的数据
if ("".equals(user.getAddress())) {
user.setAddress(null);
}
if ("".equals(user.getQq())) {
user.setQq(null);
}
if ("".equals(user.getSex())) {
user.setSex(null);
}
if ("".equals(user.getUrl())) {
user.setUrl(null);
}
// 4. 对密码进行加密操作
user.setPassword(passwordUtil.encrypt(user.getPassword()));
// 5. 如果用户 上传了头像 ,可以将图片存入到本地
if (file != null) {
// 此时上传了头像 :
// 获取到文件名 + 类型
String fileNameAndType = file.getOriginalFilename();
// 比如文件名为 : 阳台.png , 此时可以获取到 . 的 下标
int index = fileNameAndType.lastIndexOf(".");
// 从 index 位置开始截取
String postfix = fileNameAndType.substring(index);
// 判断一下 图片的格式是否符合预期要求
if (".jpg".equals(postfix) || ".png".equals(postfix)) {
// 通过 uuid 来设置文件名
String uuid = IdUtil.simpleUUID();
String imgFileStr = uuid + postfix;
// 创建文件
String path = IMAGE_PATH + imgFileStr;
File imgFile = new File(path);
if (!imgFile.exists()) {
imgFile.mkdir();
}
try {
// 指定图片 , 上传之后的存储位置
file.transferTo(imgFile);
// 文件上传成功 :
user.setUrl(imgFileStr);
userService.addUser(user);
return new ResponseBodyMessage<>(1, "注册成功", true);
} catch (IOException e) {
// e.printStackTrace();
// 如果 上传失败 , 手动事务回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
// 此时 注册失败
return new ResponseBodyMessage<>(-1, "注册失败", false);
} else {
return new ResponseBodyMessage<>(-1, "图片格式有误", false);
}
}
// 6. 调用 userService 中的 addUser 方法 进行用户添加 (此时未上传图片 , 图片 为 默认)
Integer ret = userService.addUser(user);
if (ret != 1) {
return new ResponseBodyMessage<>(-1, "注册失败", false);
}
return new ResponseBodyMessage<>(1, "注册成功", true);
}
@RequestMapping(value = "/getuser")
public ResponseBodyMessage<User> getUserById(HttpServletRequest request) {
// 1. 通过 session 获取 user
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
// 2. 将密码置为空
user.setPassword("");
return new ResponseBodyMessage<>(1, "成功", user);
}
@RequestMapping(value = "/getuserbyid")
public ResponseBodyMessage<HashMap<String, Object>> getUserById2(Integer userid) {
HashMap<String, Object> result = new HashMap<>();
if (userid == null || userid <= 0) {
return new ResponseBodyMessage<>(-1, "获取失败!", null);
}
User user = userService.getUserById(userid);
if (user == null) {
return new ResponseBodyMessage<>(-1, "无当前用户!", null);
}
user.setPassword("");
int blogNumber = blogService.getTextNumber(userid);
int typeNumber = blogService.getType(userid);
result.put("user", user);
result.put("blogNumber", blogNumber);
result.put("type", typeNumber);
return new ResponseBodyMessage<>(1, "查询成功", result);
}
@RequestMapping(value = "/remove")
public ResponseBodyMessage<Boolean> remove(HttpServletRequest request) {
HttpSession session = request.getSession(false);
session.removeAttribute(Constant.USERINFO_SESSION_KEY);
return new ResponseBodyMessage<>(1, "成功", true);
}
}
3. mapper 包
3.1 UserMapper 接口
package com.example.blog_ssm.mapper;
import com.example.blog_ssm.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper {
// 1. 通过用户名 获取 用户
User getUserByUserName(@Param("username") String username);
// 2. 注册功能 (直接传一个 user 对象)
Integer addUser(User user);
// 3. 通过 用户id 获取 用户
User getUserById(@Param("id") Integer id);
}
3.2 BlogMapper 接口
package com.example.blog_ssm.mapper;
import com.example.blog_ssm.model.Blog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface BlogMapper {
// 1. 获取 分类 (每篇小说的 类型)
List<String> getType(@Param("userid") Integer userid);
// 2. 通过 用户id 获取 当前用户的文章数量
Integer getTextNumber(@Param("userid") Integer userid);
// 3. 分页查询
List<Blog> getListByPage(@Param("limit") Integer limit, @Param("offset") Integer offset);
// 4. 返回总文章数
Integer getTextCount();
// 5. 通过 blogId 获取博客
Blog getBlogById(@Param("blogId") Integer blogId);
// 6. 增加博客
Integer addBlog(@Param("userid") Integer userid, @Param("title") String title, @Param("content") String content, @Param("type") String type);
}
4. model 包
4.1 User类
package com.example.blog_ssm.model;
import lombok.Data;
@Data
public class User {
Integer id;
String username;
String password;
String qq;
String address;
String createTime;
String sex;
String url;
}
4.2 Blog 类
package com.example.blog_ssm.model;
import lombok.Data;
@Data
public class Blog {
private Integer blogId;
private String title;
private String content;
private Integer userid;
private String postTime;
private String type;
}
5. service 包
5.1 UserService 类
package com.example.blog_ssm.service;
import com.example.blog_ssm.mapper.UserMapper;
import com.example.blog_ssm.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 1. 根据用户名获取用户
public User getUserByUserName(String username) {
return userMapper.getUserByUserName(username);
}
// 2. 新增方法
public Integer addUser(User user) {
return userMapper.addUser(user);
}
// 3. 根据用户id获取用户
public User getUserById(Integer id){
return userMapper.getUserById(id);
}
}
5.2 BlogService 类
package com.example.blog_ssm.service;
import com.example.blog_ssm.mapper.BlogMapper;
import com.example.blog_ssm.model.Blog;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BlogService {
@Autowired
private BlogMapper blogMapper;
public Integer getType(Integer id) {
List<String> ret = blogMapper.getType(id);
if (ret == null) {
return 0;
}
return ret.size();
}
// 通过 id 获取 当前 用户的 文章数
public Integer getTextNumber(Integer id) {
return blogMapper.getTextNumber(id);
}
// 获取 blog 表中的所有文章
public Integer getTextCount() {
return blogMapper.getTextCount();
}
// 通过分页获取 blog
public List<Blog> getListByPage(Integer limit, Integer offset) {
return blogMapper.getListByPage(limit, offset);
}
// 通过 blogId 获取博客
public Blog getBlogById(Integer blogId) {
return blogMapper.getBlogById(blogId);
}
public Integer addBlog(Integer userid ,String title, String content,String type){
return blogMapper.addBlog(userid,title,content,type);
}
}
6. util 包
6.1 Constant 类
package com.example.blog_ssm.util;
public class Constant {
public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}
6.2 PasswordUtil类
package com.example.blog_ssm.util;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import org.springframework.util.StringUtils;
/**
* 密码工具类
*/
public class PasswordUtil {
/**
* 1. 加密 (加盐)
*/
public String encrypt(String password) {
// 密码 : 随机盐值 + 密码
String salt = IdUtil.simpleUUID();
String finalPassword = SecureUtil.md5(salt + password);
return salt + "$" + finalPassword;
}
/**
* 解密
*
* @param password 要验证的密码 (未加密)
* @return 数据库中的加了盐值的密码
*/
public boolean decrypt(String password, String securePassword) {
boolean result = false;
if (StringUtils.hasLength(password) && StringUtils.hasLength(securePassword)) {
// 注意 : $ 是特殊字符 , 使用 split 分割时 需要转移
if (securePassword.length() == 65 && securePassword.contains("$")) {
// 随机盐值 为 32 , md5 加密的 密码 32 加上 $ 1字符 总共 65 字符
String[] securePasswordArr = securePassword.split("\\$");
// 盐值
String salt = securePasswordArr[0];
// 根据盐值 加密的密码
String finalPassword = securePasswordArr[1];
// 根据盐值 对新的密码进行加密
password = SecureUtil.md5(salt + password);
// 进行对比
if (finalPassword.equals(password)) {
result = true;
}
}
}
return result;
}
}
6.3 ResponseBodyMessage 类
package com.example.blog_ssm.util;
import lombok.Data;
/**
* 用来统一数据格式
*
* @param <T>
*/
@Data
public class ResponseBodyMessage<T> {
// 1. 状态码
private Integer status;
// 2. 信息描述
private String message;
// 3. 数据
private T data;
public ResponseBodyMessage(Integer status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
}
7. Mybatis 包
7.1 UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.blog_ssm.mapper.UserMapper">
<insert id="addUser">
insert into user (username,password
<trim prefix="," suffixOverrides=",">
<if test="qq!=null">
qq,
</if>
<if test="address!=null">
address,
</if>
<if test="sex!=null">
sex,
</if>
<if test="url!=null">
url
</if>
</trim>
) values( #{username} , #{password}
<trim prefix="," suffixOverrides=",">
<if test="qq!=null">
#{qq},
</if>
<if test="address!=null">
#{address},
</if>
<if test="sex!=null">
#{sex},
</if>
<if test="url!=null">
#{url}
</if>
</trim>
)
</insert>
<select id="getUserByUserName" resultType="com.example.blog_ssm.model.User">
select *
from user
where username = #{username};
</select>
<select id="getUserById" resultType="com.example.blog_ssm.model.User">
select * from user where id = #{id};
</select>
</mapper>
<!--
分页公式 : (pageIndex - 1) * pageSize
-->
7.2 BlogMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.blog_ssm.mapper.BlogMapper">
<insert id="addBlog">
insert into blog(title,content,type,userid) values(#{title},#{content},#{type},#{userid});
</insert>
<!--
通过 先 通过 where 选中 对应用户 然后 分组操作 group by 将他 文章的类型进行分组
最后通过 count 聚合函数 拿到 分出来的组数 ,此时就是类型数了
-->
<select id="getType" resultType="java.lang.String">
select count(type) from blog where userid = #{userid} group by type;
</select>
<select id="getTextNumber" resultType="java.lang.Integer">
select count(title) from blog where userid = #{userid};
</select>
<select id="getListByPage" resultType="com.example.blog_ssm.model.Blog">
select * from blog order by postTime desc limit #{limit} offset #{offset} ;
</select>
<select id="getTextCount" resultType="java.lang.Integer">
select count(*) from blog;
</select>
<select id="getBlogById" resultType="com.example.blog_ssm.model.Blog">
select * from blog where blogId = #{blogId};
</select>
</mapper>
<!--
分页公式 : (pageIndex - 1) * pageSize
-->