博客系统 实现 (前端 + 后端 )代码

博客系统

前端代码 :

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"/>&nbsp;&nbsp;&nbsp;
            <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">
        <!--        &lt;!&ndash;-->
        <!--            表示一篇博客-->
        <!--        &ndash;&gt;-->

        <!--        <div class="blog">-->
        <!--            &lt;!&ndash;-->
        <!--                   博客标题-->
        <!--            &ndash;&gt;-->
        <!--            <div class="title">我的第一篇博客</div>-->
        <!--            &lt;!&ndash;-->
        <!--                发布时间-->
        <!--            &ndash;&gt;-->
        <!--            <div class="data">-->
        <!--                2023-03-02-->
        <!--            </div>-->

        <!--            &lt;!&ndash;-->
        <!--                博客的摘要-->
        <!--            &ndash;&gt;-->
        <!--            <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>-->

        <!--            &lt;!&ndash;-->
        <!--                查看全文按钮-->
        <!--            &ndash;&gt;-->
        <!--            <a href="#">查看全文 &gt;&gt;</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 = "查看全文 &gt;&gt;";

                        // 重点 : 这里我们点击查看全文 跳转到 博客详情页 ,这里可以 在 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
        -->

猜你喜欢

转载自blog.csdn.net/mu_tong_/article/details/129425033