项目实战 — 博客系统③ {功能实现}

这篇具有很好参考价值的文章主要介绍了项目实战 — 博客系统③ {功能实现}。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

 一、编写注册功能

🍅 1、使用ajax构造请求(前端)

🍅 2、统一处理

        🎄 统一对象处理

        🎄 保底统一返回处理 

        🎄 统一异常处理

 🍅 3、处理请求

二、编写登录功能

 🍅 1、使用ajax构造请求

🍅 2、处理请求

        🎄 创建实体类

        🎄 添加接口方法

         🎄 service层

        🎄 controller:处理请求并返回

三、编写博客列表页面

 🍅 1、SessionUtils

 🍅 2、处理请求

🍅 3、使用ajax构造请求

三、编写注销功能

🍅 1、使用ajax获取请求

🍅 2、处理请求

四、编写删除文章功能

🍅 1、使用ajax构造请求

🍅 2、处理请求

🎄 mapper层:接口方法

🎄 service层

🎄 controller层:处理请求

五、编写文章添加功能

🍅 1、使用ajax构造请求

🍅 2、处理请求

 🎄mapper层:添加接口方法

 🎄service层:返回受影响行数

🎄 controller:处理添加文章的请求

六、修改功能:查询到修改的文章

🍅 1、通过ajax构造请求

🍅 2、处理请求

🎄Mapper层:添加接口

🎄service层

🎄controller层

七、修改功能:修改查询到的文章

🍅 1、通过ajax构造请求           

🍅 2、处理请求

🎄 mapper层

🎄 service层

🎄 controller层     

八、文章详情

🍅 1、前端页面编写

🎄 给定id值

 🎄 使用ajax构造请求

🍅 2、处理请求

🎄 mapper层

🎄 service层

🎄 controller层 

九、阅读量设置

🍅 处理请求

🎄 mapper层

🎄 service层

🎄 controller层 

 十、分页处理

🍅 1、前端处理

🍅 2、后端处理

🎄 mapper层

🎄 service层

🎄 controller层 

十一、密码加盐

🍅 1、定义加密规则

🍅 2、修改UserController代码

十二、session升级存储到redis

🍅 1、先添加redis相关依赖

🍅 2、添加redis配置信息(properties)

 十三、实现自定义拦截器


前置知识:

前后端交互的关键:使用AJAX(异步局部提交)

写法:

        1、原生写法(兼容性差)

        2、使用框架,jQuery ajax(简单 / 通用性浩)

语法:

        jQuery.ajax({
                url:"接口地址",           //提高的接口地址
                type:"GET",               //请求的类型
                data:{                         //传递的数据
                        "username":"张三",
                        "password":"123"
                },

                success:function(res){        //相应的结果是什么
                        //后端返回数据后的业务处理代码
                }
        });

 一、编写注册功能

🍅 1、使用ajax构造请求(前端)

在前端页面的<head></head>添加jquery

<script src="js/jquery.min.js"></script>

添加事件:点击提交按钮,触发mysub()

<div class="row">
       <button id="submit" onclick="mysub()">提交</button>
</div>

在<script></script>中提交用户注册信息

主要有这几步:

        (1)参数效验(获取到数据|非空效验)

        (2)将数据提交给后端

        (3)将后端返回的结果给用户

username.focus():指将光标返回

trim():去空格操作(校验到全是空格)

<script>
        //  提交用户注册信息
        function mysub(){
            // 参数校验
            var username = jQuery("#username");
            var password = jQuery("#password");
            var password2 = jQuery("#password2");
            if(username.val().trim()==""){
                alert("请先输入用户名!");
                username.focus();
                return false;
            }
            if(password.val().trim()==""){
                alert("请先输入密码!");
                password.focus();
                return false;
            }
            if(password2.val().trim()==""){
                alert("请先输入确认密码!");
                password2.focus();
                return false;
            }
            // 效验两次输入的密码是否一致
            if(password.val()!=password2.val()){
                alert("两次密码不一致,请先检查!");
                return false;
            }
        //     提交数据给后端
            jQuery.ajax({
                url:"/user/reg",
                // 查询使用get,非查询使用post
                type:"POST",
                data:{
                    "username":username.val().trim(),
                    "password":password.val().trim()
                },
                success:function (res){
                //     返回结果给用户
                    if (res.code==200 && res.data==1){
                        alert("注册成功!")
                        location.href = "login.html";
                    }else {
                        alert("注册失败!" + res.msg);
                    }
                }
            });
        }
    </script>
</body>

🍅 2、统一处理

        🎄 统一对象处理

 在common包中,编写ResultAjax,表示前后端交互的统一对象,统一处理

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;

    public static ResultAjax success(Object data){
        ResultAjax result = new ResultAjax();
        result.setCode(200);
        result.setMsg("");
        result.setData(data);
        return result;
    }

    public static ResultAjax success(int code,String msg,Object data){
        ResultAjax result = new ResultAjax();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    public static ResultAjax fail(int code,String msg){
        ResultAjax result = new ResultAjax();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(null);
        return result;
    }

    public static ResultAjax fail(int code,String msg,Object data){
        ResultAjax result = new ResultAjax();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
}

        🎄 保底统一返回处理 

        在common中创建类

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ResultAjax){
            return body;
        }
        if (body instanceof String){
            ResultAjax resultAjax = ResultAjax.success(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return ResultAjax.success(body);
    }
}

        🎄 统一异常处理

/*
* 统一异常处理
* */
@RestControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public ResultAjax doException(Exception e){
        return ResultAjax.fail(-1,e.getMessage());
    }
}

 🍅 3、处理请求

 编写Mapper层,将拿到的数据进行插入

@Mapper
public interface UserMapper {
    @Insert("insert into userinfo(username,password) values (#{username},#{password})")
    int reg(Userinfo userinfo);
}

然后在Service层中使用@Autowired将userMapper注入

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;
    public int reg(Userinfo userinfo){
        return userMapper.reg(userinfo);
    }
}

 在UserController中使用Userinfo对象接收请求信息

        (1)校验参数

        (2)请求 service 进行添加操作

        (3)将执行的结果返回给前端

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping("/reg")
    public ResultAjax reg(Userinfo userinfo){
        if (userinfo == null || !StringUtils.hasLength(userinfo.getUsername())
        || !StringUtils.hasLength(userinfo.getPassword())){
            return ResultAjax.fail(-1,"非法参数");
        }
        int result = userService.reg(userinfo);
        return ResultAjax.success(result);
    }

检验该功能是否实现

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

 查看数据库是否插入张三的信息:

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

二、编写登录功能

 🍅 1、使用ajax构造请求

 在前端页面的<head></head>添加jquery

<script src="js/jquery.min.js"></script>

添加事件:点击提交按钮,触发mysub()

<div class="row">
       <button id="submit" onclick="doLogin()">提交</button>
</div>

        (1)校验参数

        (2)将数据提交给后端

        (3)将结果展示给前端

<script>
    function doLogin(){
        // 参数校验
        var username = jQuery("#username");
        var password = jQuery("#password");
        if(username.val().trim()==""){
            alert("请先输入用户名!");
            username.focus();
            return false;
        }
        if(password.val().trim()==""){
            alert("请先输入密码!");
            password.focus();
            return false;
        }
    //     将数据提交给后端
        jQuery.ajax({
            url:"/user/login",
            type:"GET",
            data:{
                "username":username.val(),
                "password":password.val()
            },
            success:function(res){
            //     将结果展示给用户
                if (res.code==200 && res.data==1){
                    alert("登录成功!");
                    location.href="myblog_list.html";
                }else {
                    alert("登录失败!" + res.msg);
                }
            }
        });
    }
</script>

🍅 2、处理请求

        🎄 创建实体类

首先,再创建一个扩展Userinfo的实体类,UserinfoVO

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

/*
* userinfo扩展类
* */
@Data
public class UserinfoVO extends Userinfo {
    private String checkCode;
}

        🎄 添加接口方法

在UserMapper中添加以下方法:

@Select("select * from userinfo where username = #{username} order by id desc")
    Userinfo getUserByName(@Param("username")String username);

         🎄 service层

 public Userinfo getUserByName(String username){
        return userMapper.getUserByName(username);
    }

        🎄 controller:处理请求并返回

首先创建一个公共类(common包中):AppVariable

设置一个用户的session key

/*
* 全局变量
* */
public class AppVariable {
    public static final String SESSION_USERINFO_KEY = "SESSION_USERINFO";
}

        (1)参数校验

        (2)根据用户名查询对象

        (3)适用对象中的密码和用户输入的密码进行比较

          登录验证思路:根据用户名查询对象,如果查询到对象,则用查询到的对象和传递过              来的密码对比。如果相同,则说明登陆成功,反之则不能。   

        (4)比较成功之后,将对象存储到session中

        (5)将结果返回给用户

 @RequestMapping("/login")
    public ResultAjax login(UserinfoVO userinfoVO, HttpServletRequest request){
        //参数校验
        if (userinfoVO == null || !StringUtils.hasLength(userinfoVO.getUsername()) ||
                !StringUtils.hasLength(userinfoVO.getPassword())){
//            是非法登录
            return ResultAjax.fail(-1,"参数有误!");
        }
        //根据用户查询对象
        Userinfo userinfo = userService.getUserByName(userinfoVO.getUsername());
        if (userinfo == null || userinfo.getId() == 0){
            return ResultAjax.fail(-2,"用户名或密码错误!");
        }
        //使用对象中的密码和用户输入的密码进行比较
        if (!userinfoVO.getPassword().equals(userinfo.getPassword())){
//            密码错误
            return ResultAjax.fail(-2,"用户名或密码错误!");
        }
        //比较成功,将对象存储到session中
        HttpSession session = request.getSession();
        session.setAttribute(AppVariable.SESSION_USERINFO_KEY,userinfo);
        //将结果返回给用户
        return ResultAjax.success(1);
    }

三、编写博客列表页面

 🍅 1、SessionUtils

由于要拿到用户的信息,也就是拿到用户对象,比如修改和删除博客等功能(都是需要拿到用户),所以得到用户对象就属于一个高频事件。

所以就创建一个类,可以得到用户对象。

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

/*
* session工具类
* */
public class SessionUtils {
//    得到登录用户
    public static Userinfo getUser(HttpServletRequest request){
//        false代表有session就创建session,没有就创建
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(AppVariable.SESSION_USERINFO_KEY) != null){
//            登录状态
            return (Userinfo) session.getAttribute(AppVariable.SESSION_USERINFO_KEY);
        }
        return null;
    }
}

 🍅 2、处理请求

🎄 添加接口方法(mapper)

@Mapper
public interface ArticleMapper {
    @Select("select * from articleinfo where uid=#{uid}")
    List<ArticleMapper> getListByUid(@Param("uid")int uid);
}

🎄 service层

对象注入

@Service
public class ArticleService {
    @Autowired
    private ArticleMapper articleMapper;
    public List<ArticleMapper> getListByUid(int uid){
        return articleMapper.getListByUid(uid);
    }
}

🎄controller处理请求并返回响应

使用并行的方式处理文章正文(_DESC_LENGTH是简介)

@RestController
@RequestMapping("art")
public class ArticleController {
    @Autowired
    public ArticleService articleService;
    private static final int _DESC_LENGTH = 120;
    @RequestMapping("/mylist")
    public ResultAjax myList(HttpServletRequest request){
//        得到登录用户
        Userinfo userinfo = SessionUtils.getUser(request);
        if (userinfo == null){
            return ResultAjax.fail(-1,"请先登录");
        }
//      根据用户id查询用户发表的文章
        List<Articleinfo> list = articleService.getListByUid(userinfo.getId());
//        处理list -> 将文章正文变成简介
        if (list != null && list.size() > 0){
//            并行处理list集合
            list.stream().parallel().forEach((art)->{
                if (art.getContent().length() > _DESC_LENGTH){
                    art.setContent(art.getContent().substring(0,_DESC_LENGTH));
                }
            });
        }
//        返回前端
        return ResultAjax.success(list);
    }
}

🍅 3、使用ajax构造请求

在前端页面的<head></head>添加jquery

<script src="js/jquery.min.js"></script>

添加一个id,包含了文章列表的div

       <div id="artListDiv" class="container-right">
 <script>
        // 初始化方法
        function init(){
            jQuery.ajax({
                url:"/art/mylist",
                type:"GET",
                data:{},
                success:function(res){
                    if(res.code==200){
                        // 请求成功
                        var createHtml = "";
                        var artList = res.data;
                        if(artList==null || artList.length==0){
                            // 未发表文章
                            createHtml += "<h3 style='margin-left:20px;margin-top:20px'>暂无文章,请先"+
                                "<a href='blog_add.html'>添加</a>!</h3>";
                        }else{
                            for(var i=0;i<artList.length;i++){
                                var art = artList[i];
                                createHtml += '<div class="blog">';
                                createHtml += '<div class="title">'+art.title+'</div>';
                                createHtml += '<div class="date">'+art.createtime+'</div>';
                                createHtml += '<div class="desc">';
                                createHtml += art.content;
                                createHtml += '</div>';
                                createHtml += ' <a href="blog_content.html?aid='+
                                    art.id + '" class="detail">查看全文 &gt;&gt;</a>&nbsp;&nbsp;';
                                createHtml += '<a href="blog_edit.html?aid='+
                                    art.id + '" class="detail">修改 &gt;&gt;</a>&nbsp;&nbsp;';
                                createHtml += ' <a href="javascript:del('+art.id+')" class="detail">删除 &gt;&gt;</a>';
                                createHtml += '</div>';
                            }
                        }
                        jQuery("#artListDiv").html(createHtml);
                    }else{
                        alert("抱歉:操作失败!"+res.msg);
                    }
                }
            });
        }
        init();
    </script>
</body>

三、编写注销功能

🍅 1、使用ajax获取请求

由于注销功能,在很多页面都需要用到,所以这里就给一个公共的js

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

function logout(){
    if (confirm("是否确定注销?")){
        // 1、在后端删除session信息
        jQuery.ajax({
            url:"/user/logout",
            type:"POST",
            data:{},
            success:function (res){}
        });
        // 2、跳转到登录页
        location.href = "login.html"
    }
}

 进入到myblog_list.html中:

<script src="js/logout.js"></script>
<a href="javascript:logout()">注销</a>

🍅 2、处理请求

//注销
    @RequestMapping("/logout")
    public ResultAjax logout(HttpServletRequest request){
        HttpSession session = request.getSession(false);
        if (session!=null &&
                session.getAttribute(AppVariable.SESSION_USERINFO_KEY)!=null){
            session.removeAttribute(AppVariable.SESSION_USERINFO_KEY);
        }
        return ResultAjax.success(1);
    }

四、编写删除文章功能

🍅 1、使用ajax构造请求

在myblog_list.html中添加对应的js代码

主要是根据id删除文章:

        1、校验参数

        2、将数据返回给后端进行删除操作

        2、将结果展示给用户

 //     删除文章操作(根据id删除)
        function del(aid){
        //     参数校验
            if(aid=="" || aid<=0){
                alert("参数错误");
                return false;
            }
        //     将数据返回给后端进行删除操作
            jQuery.ajax({
                url:"/art/del",
                type:"POST",
                data:{
                    "aid":aid
                },
                success:function (res){
                //     将结果展示给用户
                    if (res.code==200 && res.data==1){
                        alert("恭喜:删除成功!");
                    //     刷新页面
                        location.href = location.href;
                    }else {
                    //     删除失败
                        alert("抱歉:操作失败!"+res.msg);
                    }
                }
            });
        }

🍅 2、处理请求

🎄 mapper层:接口方法

在ArticleMapper中添加接口方法

@Delete("delete from articleinfo where id=#{aid} and uid=#{uid}")
int del(@Param("aid")Integer aid,int uid);

🎄 service层

public int del(Integer aid,int uid){
        return articleMapper.del(aid,uid);
    }

🎄 controller层:处理请求

主要有以下几步:

        1、参数校验

        2、得到当前登录的用户

        3、判断文章的归属人并且进行删除操作

        4、将结果返回给前端

//    删除文章
    @RequestMapping("/del")
    public ResultAjax del(Integer aid,HttpServletRequest request){
        if (aid==null || aid<=0){
            return ResultAjax.fail(-1,"参数错误!");
        }
        Userinfo userinfo = SessionUtils.getUser(request);
        if (userinfo == null){
            return ResultAjax.fail(-1,"请先登录!");
        }
        int result = articleService.del(aid,userinfo.getId());
        return ResultAjax.success(result);
    }

五、编写文章添加功能

🍅 1、使用ajax构造请求

        (1)非空校验

        (2)将用户提交的数据传递给后端

        (3)将后端返回的结果展示给用户

进入到blog_add.html中,添加如下代码

 function mysub(){
            var title = jQuery("#title");
            if (title.val.trim()==""){
                alert("请先输入标题!");
                title.focus();
                return false;
            }
            if (editor.getValue()==""){
                alert("请先输入正文!");
                return false;
            }
            jQuery.ajax({
                url:"/art/add",
                type:"POST",
                data:{
                    "title":title.val(),
                    "content":editor.getValue()
                },
                success:function (res){
                    // 文章添加成功
                    if (res.code==200 && res.data==1){
                        if (confirm("恭喜:添加成功!是否继续添加文章?")){
                            location.href=location.href;
                        }else{
                            location.href="myblog_list.html"
                        }
                    }else {
                    //文章添加失败
                        alert("抱歉:操作失败!"+res.msg);
                    }
                }
            });
        }

🍅 2、处理请求

 🎄mapper层:添加接口方法

@Insert("insert into articleinfo(title,content,uid) values(#{title},#{content},#{uid})")
    int add(Articleinfo articleinfo);

 🎄service层:返回受影响行数

public int add(Articleinfo articleinfo){
        return articleMapper.add(articleinfo);
    }

🎄 controller:处理添加文章的请求

主要分为以下几步:

        (1)校验参数

        (2)组装数据

        (3)将数据入库

        (4)将结果返回给前端

@RequestMapping("/add")
    public ResultAjax add(Articleinfo articleinfo,HttpServletRequest request){
        if (articleinfo==null || !StringUtils.hasLength(articleinfo.getTitle())
                || !StringUtils.hasLength(articleinfo.getContent())){
            return ResultAjax.fail(-1,"非法参数!");
        }
        Userinfo userinfo = SessionUtils.getUser(request);
        if (userinfo == null){
            return ResultAjax.fail(-2,"请先登录");
        }
        articleinfo.setUid(userinfo.getId());
        int result = articleService.add(articleinfo);
        return ResultAjax.success(result);
    }

六、修改功能:查询到修改的文章

🍅 1、通过ajax构造请求

(1)获取url中的参数:

(例如)localhost:8080/blog_edit.html?aid=2&uid=1

        a、通过location.search获取“?”后面的参数

        b、去除“?”

        c、根据“&”将参数分割成多个数组

        d、循环对比key,并返回查询value

创建一个公共的js:

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

// 根据key获取到对应的url中对应的value
function getParamValue(key){
    var params = location.search;
    if (params.indexOf("?")>=0){
        params=params.substring(1);
        var paramArray=params.split("&");
        if (paramArray.length>=1){
            for (var i = 0; i < paramArray.length; i++) {
                var item=paramArray[i].split("=");
                if (item[0]==key[1]){
                    return item;
                }
            }
        }
    }
    return null;
}

(2)校验 aid

(3)查询文章详情

(4)将文章的详情信息展示到页面

首先在head中添加得到参数的方法

    <script src="js/urlutils.js"></script>

在blog_edit.html中的<script></script>中添加代码

        var aid=getParamValue("aid");
        function init(){
            if (aid==null || aid<=0){
                alert("非法参数")
                return false;
            }
            jQuert.ajax({
                url:"/art/getdetail",
                type:"GET",
                data:{
                  "aid":aid
                },
                success:function(res){
                    if (res.code==200 && res.data!=null && res.data.id>0){
                        jQuery("#title").val(res.data.title);
                        initEdit(res.data.content);
                    }else {
                        alert("抱歉,查询失败!"+res.msg);
                    }
                }
            });
        }
        init();

🍅 2、处理请求

🎄Mapper层:添加接口

进入到ArticleMapper中

@Select("select  * from aeticleinfo where id=#{aid} and uid=#{uid}")
    Articleinfo getArticleByIdAndUid(@Param("aid")int aid,
                                     @Param("uid")int uid);

🎄service层

 public Articleinfo getArticleByIdAndUid(int aid,int uid){
        return ArticleMapper.getArticleByIdAndUid(aid,uid);
    }

🎄controller层

(1)校验参数

(2)得到当前登录用户id

(3)查询文章并且校验权限

@RequestMapping("/update_init")
    public ResultAjax updateInit(Integer aid,HttpServletRequest request){
        if (aid==null || aid<=0 ){
            return ResultAjax.fail(-1,"参数有误!");
        }
        Userinfo userinfo = SessionUtils.getUser(request);
        if (userinfo == null){
            return ResultAjax.fail(-2,"请先登录!");
        }
        Articleinfo articleinfo = articleService.getArticleByIdAndUid(aid,userinfo.getId())
        return ResultAjax.success(articleinfo);
    }

七、修改功能:修改查询到的文章

🍅 1、通过ajax构造请求           

进入到blog_edit.html中     

        <button onclick="doUpdate()">修改文章</button>
function doUpdate(){
            if(title.val().trim()==""){
                alert("请先输入标题!");
                title.focus();
                return false;
            }
            if(editor.getValue()==""){
                alert("请先输入正文!");
                return false;
            }

            jQuery.ajax({
                url:"/art/update",
                type:"POST",
                data:{
                    "id":aid,
                    "title":title.val(),
                    "content":editor.getValue()
                },
                success:function(res){
                    if(res.code==200 && res.data==1){
                        // 修改成功
                        alert("恭喜:修改成功!");
                        // 跳转到我的文章管理员
                        location.href = "myblog_list.html";
                    }else if(res.code==-2){
                        alert("请先的登录!");
                        location.href = "login.html";
                    }
                    else{
                        alert("抱歉:修改失败!"+res.msg);
                    }
                }
            });
        }

🍅 2、处理请求

🎄 mapper层

进入到ArticleMappe中

@Update("update articleinfo set title=#{title},content=#{content} where id=#{id} and uid=#{uid}")
    int update(Articleinfo articleinfo);

🎄 service层

进入到ArticleService中

public int update(Articleinfo articleinfo){
        return articleMapper.update(articleinfo);
    }

🎄 controller层     

(1)参数校验

(2)获取登录用户

(3)修改文章,并且校验归属人

(4)返回结果

//    修改文章信息
    @RequestMapping("/update")
    public ResultAjax update(Articleinfo articleinfo,HttpServletRequest request){
        if (articleinfo==null || !StringUtils.hasLength(articleinfo.getTitle())
                || !StringUtils.hasLength(articleinfo.getContent())
                || articleinfo.getId() == 0){
            return ResultAjax.fail(-1,"非法参数!");
        }

        Userinfo userinfo = SessionUtils.getUser(request);
        if (userinfo == null){
            return ResultAjax.fail(-2,"请先登录!");
        }
        articleinfo.setUid(userinfo.getId());
        int result = articleService.update(articleinfo);
        return ResultAjax.success(result);
    }

八、文章详情

🍅 1、前端页面编写

进入到blog_content.html中

🎄 给定id值

    //左侧的个人信息
                <img id="photo" src="img/avatar.png" class="avtar" alt="">
                <h3 id="username"></h3>
                <a href="http:www.github.com">github 地址</a>
        //右侧的博客信息
                 <!-- 博客标题 -->
                <h3 id="title"></h3>
                <!-- 博客时间 -->
                <div class="date">
                    发布时间:<span id="createtime"></span>
                     /
                    阅读量:<span id="rcount"></span>
                </div>

 🎄 使用ajax构造请求

        var aid = getParamValue("aid");
//初始化页面
            function init(){
                var aid = getParamValue("aid");
                if (aid==null || aid<=0){
                    alert("参数有误!");
                    return false;
                }
                jQuery.ajax({
                    url:"/art/detail",
                    type:"GET",
                    data:{
                        "aid":aid
                    },
                    success: function (res){
                        if (res.code==200 && res.data!=null){
                            var user = res.data.user;
                            var art = res.data.art;
                            if (user!=null){
                                if(user.photo!=""){
                                    jQuery("#photo").art("src,user.photo");
                                }
                                jQuery("#username").html(user.username);
                                jQuery("#artcount").html(user.artCount);
                            }else{
                                alert("抱歉:查询失败!"+reg.msg);
                            }
                            if(art!=null){
                                jQuery("#title").html(art.title);
                                jQuery("#createtime").html(art.createtime);
                                jQuery("#rcount").html(art.rcount);
                                initEdit(art.content);
                            }else{
                                alert("抱歉:查询失败!"+reg.msg)
                            }
                        }else{
                            alert("抱歉,查询失败!"+res.msg);
                        }
                    }
                });
            }

🍅 2、处理请求

首先在userinfoVO中添加一个变量:

    private int artCount;   //用户发布的文章总数

🎄 mapper层

首先在Articleinfo中添加一条sql:根据id查询文章

@Select("select * from articleinfo where id=#{aid}")
    Articleinfo getDetaiById(@Param("aid") int aid);

 @Select("select count(*) from articleinfo where uid=#{uid}")
    int getArticleByUid(@Param("uid")int uid);

 然后再Userinfo中添加一条sql:根据id查询用户

@Select("select * from us erinfo where id=#{uid}")
    UserinfoVO getUserById(@Param("uid") int uid);

🎄 service层

ArticleService中

    public Articleinfo getDetail(int aid){
        return articleMapper.getDetaiById(aid);
    }

 public int getArtCountByUid(int uid){
        return articleMapper.getArticleByUid(uid);
    }

 UserService中

    public UserinfoVO getUserById(int uid){
        return userMapper.getUserById(uid);
    }

🎄 controller层 

(1)参数校验

(2)查询文章详情 

(3)根据uid查询用户的详情

(4)根据uid查询用户发表的总文章数

(5)组装数据

(6)返回结果给前端

首先创建一个线程池

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

使用ThreadPoolExecutor来构造线程池,该方法其实和JDK中的ThreadPool区别就在于,ThreadPoolExecutor式通过参数的方式去设置,而ThreadPool是通过构造方法,ThreadPoolExecutor中的方式更加直观简单。

ThreadPool然后将其注入依赖

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

 处理请求:

先注入依赖:

    @Autowired
    private UserService userService;

 使用多线程并发编程就可以很快的查询到对应的文章详情了。

//查询文章详情页
    @RequestMapping("/detail")
    public ResultAjax detail(Integer aid) throws ExecutionException, InterruptedException {
        if(aid==null || aid<=0){
            return ResultAjax.fail(-1,"非法参数");
        }

        Articleinfo articleinfo = articleService.getDetail(aid);
        if (articleinfo == null || articleinfo.getId() <= 0){
            return ResultAjax.fail(-1,"非法参数!");
        }
        //根据uid查询用户的详情
        FutureTask<UserinfoVO> userTask = new FutureTask(()->{
            return userService.getUserById(articleinfo.getUid());
        });
        taskExecutor.submit(userTask);

        //根据uid查询用户发表的文章数
        FutureTask<Integer> artCountTask = new FutureTask<>(()->{
            return articleService.getArtCountByUid(articleinfo.getUid());
        });
        taskExecutor.submit(artCountTask);

        //等待任务(线程池)执行完
        UserinfoVO userinfoVO = userTask.get();
        int artCount = artCountTask.get();
        userinfoVO.setArtCount(artCount);

        //数据组装
        HashMap<String,Object> result = new HashMap<>();
        result.put("user",userinfoVO);
        result.put("art",articleinfo);

        return ResultAjax.success(result);
    }

九、阅读量设置

🍅 使用ajax构造请求

 //访问量+1
            function increamentRCount(){
                if (aid==null || aid<=0 ){
                    return false;
                }
                jQuery.ajax({
                   url:"/art/increment_rcount",
                   type:"POST",
                   data:{
                       "aid":aid
                   },
                    success:function(res){}
                });
            }
            increamentRCount();

🍅 处理请求

🎄 mapper层

    @Update("update articleinfo set rcount=rcount+1 where id=#{aid}")
    int incrementRCount(@Param("aid") int aid);

🎄 service层

    public int incrementRCount(int aid){
        return articleMapper.incrementRCount(aid);
    }

🎄 controller层 

(1)校验参数

(2)更改数据库

(3)返回结果

 @RequestMapping("/increment_rcount")
    public ResultAjax incrementRCount(Integer aid){
        if (aid==null || aid<=0){
            return ResultAjax.fail(-1,"参数有误!");
        }

        int result = articleService.incrementRCount(aid);
        return ResultAjax.success(result);
    }

 十、分页处理

核心参数:

        (1)页码(当前在第几页)

        (2)每页显示的最大条数(前端灵活的控制分页功能)

后端分页返回:

        (1)当前页面的文章列表

        (2)总共有多少页:根据博客总条数/每一页显示条数的结果,向上取整(比如3.3333             取为4)

🍅 1、前端处理

首先将对应的js引入进,来根据总条数每页显示条数(向上取整

添加一个id属性:主要是为了构造出每一条博客的div出来

<!-- 每一篇博客包含标题, 摘要, 时间 -->    
     <div id="artListDiv">

            </div>

 然后初始化数据:

        (1)得到url中的分页参数

        (2)qing求后端接口

        (3)将结果返回给用户

var psize = 2; // 每页显示条数
    var pindex = 1; // 页码
    var totalpage = 1; // 总共有多少页
    // 初始化数据
    function init(){
        // 1.处理分页参数
        psize = getParamValue("psize");
        if(psize==null){
            psize = 2; // 每页显示条数
        }
        pindex = getParamValue("pindex");
        if(pindex==null){
            pindex = 1; // 页码
        }
        jQuery("#pindex").html(pindex);
        // 2.请求后端接口
        jQuery.ajax({
            url:"/art/getlistbypage",
            type:"GET",
            data:{
                "pindex":pindex,
                "psize":psize
            },
            success:function(res){
                // 3.将结果展示给用户
                if(res.code==200 && res.data!=null){
                    var createHtml = "";
                    if(res.data.list!=null && res.data.list.length>0){
                        // 有文章
                        totalpage = res.data.size;
                        jQuery("#pszie").html(totalpage);
                        var artlist = res.data.list;
                        for(var i=0;i<artlist.length;i++){
                            var art = artlist[i]; // 文章对象
                            createHtml += '<div class="blog" >';
                            createHtml += '<div class="title">'+art.title+'</div>';
                            createHtml += '<div class="date">'+art.createtime+'</div>';
                            createHtml += '<div class="desc">'+art.content+'</div>';
                            createHtml += '<a href="blog_content.html?aid='+
                                art.id+'" class="detail">查看全文 &gt;&gt;</a>';
                            createHtml += '</div>';
                        }
                    }else{
                        // 暂无文章
                        createHtml += '<h3 style="margin-top:20px;margin-left:20px;">暂无文章!</h3>';
                    }
                    jQuery("#artListDiv").html(createHtml);
                }else{
                    alert("抱歉:查询失败!"+res.msg);
                }
            }
        });
    }
    init();

    
    //点击首页
    function doFirst(){
        if (pindex<=1){
            alert("已经在首页了,无需跳转");
            return false;
        }
        location.href = "blog_list.html";
    }
    //跳转到末页
    function doLast(){
        if (pindex>=totalpage){
            alert("已经在末页了,无需跳转!");
            return false;
        }
        location.href="blog_list.html?pindex="+totalpage;
    }
    //点击上一页
    function doBefore(){
        if (pindex<=1){
            alert("已经在首页了,无需跳转");
            return false;
        }
        location.href="blog_list.html?pindex="+(parseInt(pindex)-1);
    }
    //点击下一页
    function doNext(){
        if (pindex>=totalpage){
            alert("已经在末页了,无需跳转!");
            return false;
        }
        location.href="blog_list.html?pindex="+(parseInt(pindex)+1);
    }

🍅 2、后端处理

推导到分页公式:每页显示2条数据

每次只有偏移量offset变了

# 第一页
mysql> select * from articleinfo order by id limit 2 offset 0;

# 第二页
mysql> select * from articleinfo order by id limit 2 offset 2;

# 第三页
mysql> select * from articleinfo order by id limit 2 offset 4;
.....

假设每条显示条数为pageSize,页码为pageIndex

故而推到出的公式为:offset = pageSize*(pageIndex-1)

🎄 mapper层

    @Select("select * from articleinfo order by id desc limit #{psize} offset #{offset} ")
    public List<Articleinfo> getListByPage(@Param("psize") int psize,@Param("offset")int offset);

        @Select("select count(*) from articleinfo")
    int getCount();

🎄 service层

   public List<Articleinfo> getListByPage(int psize,int offset){
        return articleMapper.getListByPage(psize, offset);
    }

🎄 controller层 

        (1)加工矫正

        (2)并发进行文章列表和总页数的查询

        (3)组装数据

        (4)将结果返回给前端

   @RequestMapping("/getlistbypage")
    public ResultAjax getListByPage(Integer pindex,Integer psize) throws ExecutionException, InterruptedException {
        if (pindex == null || pindex < 1){
            pindex = 1;
        }
        if (psize==null || psize<1){
            psize = 2;
        }

        //查询分页列表数据:
        Integer finalPsize = psize;
        Integer finalPindex = pindex;
        FutureTask<List<Articleinfo>> listTask = new FutureTask<>(()->{
            int finalOffset = finalPsize *(finalPindex -1);
            return articleService.getListByPage(finalPsize,finalOffset);
        }) ;

        //查询总页数
        FutureTask<Integer> sizeTask = new FutureTask<>(()->{
            int totalCount = articleService.getCount();
            double sizeTemp = (totalCount * 1.0) / (finalPsize * 1.0);
            return (int) Math.ceil(sizeTemp);
        });
        taskExecutor.submit(listTask);
        taskExecutor.submit(sizeTask);

        List<Articleinfo> list = listTask.get();
        int size = sizeTask.get();
        HashMap<String,Object> map = new HashMap<>();
        map.put("list",list);
        map.put("size",size);

        return ResultAjax.success(map);
    }

十一、密码加盐

先修改数据库中的字段:

mysql> alter table userinfo modify password varchar(65) not null;

🍅 1、定义加密规则

加密流程: 

使用加盐算法,也就是使用一个不重复随机的盐值+密码,得到一个无规律的密码                 (1)生成一个盐值

(2)(根据盐值 + 固定密码)进行加密 -> md5(盐值 + 密码)= 最终密码

(3)将[盐值]+ [分隔符$] + 最终密码保存到数据库中 

验证密码流程:

(1)得到盐值

(2)md5(盐值+待验证密码)->  最终待验证的密码

(3)对比最终待验证的密码和数据中的最终密码是否相同-> 相同密码正确

        

创建一个加密解密的类:                                  

 项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

public class PasswordUtils {
    //加盐加密
    public static String encrypt(String password){
        String salt = UUID.randomUUID().toString().replace("-","");
        String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));

        return salt+"$"+finalPassword;
    }

    //待验证密码
    public static boolean decrypt(String password,String dbPassword){
        if (!StringUtils.hasLength(password) || !StringUtils.hasLength(dbPassword) ||
                dbPassword.length() != 65){
            return false;
        }
        String[] dbPasswordArray = dbPassword.split("$");
        if (dbPasswordArray.length!=2){
            return false;
        }
        String salt = dbPasswordArray[0];
        String dbFinalPassword = dbPasswordArray[1];

        String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
        if (finalPassword.equals(dbFinalPassword)){
            return true;
        }
        return false;
    }
}

🍅 2、修改UserController代码

reg()方法中添加以下代码:

        userinfo.setPassword(PasswordUtils.encrypt(userinfo.getPassword()));

 在login()方法中添加以下代码

//使用对象中的密码和用户输入的密码进行比较
       if (!PasswordUtils.decrypt(userinfoVO.getPassword(),userinfo.getPassword())){
           return ResultAjax.fail(-2,"用户名或者密码错误!");
       }

十二、session升级存储到redis

这个部分,还需要部署redis,先使用finalShell或者xshell远程连接Linux服务器,然后参考后面的博客去安装Redis 安装以及配置隧道连接_点子李的博客-CSDN博客

🍅 1、先添加redis相关依赖

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.6.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

🍅 2、添加redis配置信息(properties)

spring.redis.host=39.107.159.218
spring.redis.port=6379
spring.redis.password=
spring.redis.database=2
spring.session.store-type=redis

打开登录页面,发现存储成功

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

 十三、实现自定义拦截器

Spring MVC中的拦截器(Interceptor)类似于ServLet中的过滤器(Filter),它主要用于拦截用户请求并作出相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。

首先创建拦截器

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java

public class LoginIntercept implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session!=null && session.getAttribute(AppVariable.SESSION_USERINFO_KEY)!=null){
            //用户登录
            return true;
        }
        response.sendRedirect("/login.html");
        return false;
    }
}

然后创建一个类,定义拦截器规则

项目实战 — 博客系统③ {功能实现},项目实战 — 博客系统,okhttp,项目,java文章来源地址https://www.toymoban.com/news/detail-661160.html

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginIntercept())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/user/detail")
                .excludePathPatterns("/art/getlistbypage")
                .excludePathPatterns("/editor.md/*")
                .excludePathPatterns("/img/*")
                .excludePathPatterns("/js/*")
                .excludePathPatterns("/css/*")
                .excludePathPatterns("/login.html")
                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/blog_list.html")
                .excludePathPatterns("/blog_content");
    }
}

到了这里,关于项目实战 — 博客系统③ {功能实现}的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 项目实战-前后端分离博客系统

    纯后端讲解 完整的前台后台代码编写 主流技术栈(SpringBoot,MybatisPlus,SpringSecurity,EasyExcel,Swagger2,Redis,Echarts,Vue,ElementUI....) 完善细致的需求分析 由易到难循序渐进 ​我们有前台和后台两套系统。两套系统的前端工程都已经提供好了。所以我们只需要写两套系统的后端。 ​但是

    2024年02月14日
    浏览(13)
  • 谷粒学苑项目实战(十四):实现阿里云视频点播功能(java编码实现)

            视频点播(ApsaraVideo for VoD)是集音视频采集、编辑、上传、自动化转码处理、媒体资源管理、分发加速于一体的一站式音视频点播解决方案。          在阿里云产品中找到视频点播,开通。         选择按流量计费(小视频大概也就话几毛钱)         点击开

    2023年04月25日
    浏览(20)
  • 自动化项目实战 [个人博客系统]

    效验第一篇博客 不是 “自动化测试” 退出到登录页面,用户名密码为空

    2024年02月08日
    浏览(14)
  • 博客系统自动化测试项目实战(测试系列9)

    目录 前言: 1.博客前端页面测试用例图 2.测试用例的代码实现 2.1登录页面的测试 2.2博客列表页面的测试 2.3写博客测试 2.4博客详情页面的测试 2.5已发布博客的标题和时间的测试 2.6注销用户的测试 结束语: 之前小编给大家讲解了有关于Selenium和Junit5自动化测试的一些基础知

    2024年02月10日
    浏览(10)
  • 博客系统的后端设计(八) - 实现发布博客功能

    在原来的编辑页面点击发布文章按钮,是不会有什么效果的。 这是因为此时还不能实现前后端的交互。 请求使用 POST ,路径是 /blog title=这是标题content=这是正文 请求中要有 body,按照 form 表单的方式添加进去。 响应使用 HTTP/1.1 302 跳转到列表页:Location: blog.list.html 在一篇博

    2024年02月07日
    浏览(15)
  • 博客系统后端设计(五) - 实现登录页面功能

    这里约定 请求 是一个 POST 请求,路径是 /login ,使用的是以下的格式: usernam=zhangsanpassword=123 响应是 HTTP/1.1 302 ,因为在成功登录之后,会直接跳转到列表页, 因此此时的 Location 是 blog.list.html 。 此时的响应要求是 302,因此要使用 form 表单才可以进行页面的跳转; 如果是

    2024年02月05日
    浏览(19)
  • 【Servlet综合项目练习】实现一个简单的博客系统~

    目录 🌟一、数据库设计部分 1、建表分析:系统中一共要实现几张表?  2、开始建表 🌟二、大概框架与实现功能 🌟 三、代码实现部分 🌈前言1:工具类的实现(utils包下) 1、数据库连接的工具类 2 、 用户信息判空的工具类 3、判断当前用户是否已经登录的工具类 🌈前

    2024年02月15日
    浏览(7)
  • Java项目(二)--Springboot + ElasticSearch 构建博客检索系统(3)- 分词器介绍

    ES作为全文检索服务,势必要对原始的文本进行内容的拆分,才能进行有效的索引。而拆分原始内容到一个一个小的词,或语义单元,这部分的功能由ES的分词器去完成的。 常见分词器 standard:ES默认的分词器,会将词汇单元进行小写形式,并且去除一些停用词和标点符号等等

    2024年02月10日
    浏览(14)
  • 【Node.js实战】一文带你开发博客项目之Koa2重构(实现session、开发路由、联调、日志)

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端 📃 个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力n年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2Vue3项目实战 🥝

    2024年01月16日
    浏览(16)
  • Java项目实战笔记--基于SpringBoot3.0开发仿12306高并发售票系统--(二)项目实现-第二篇-前端模块搭建及单点登录的实现

    本文参考自 Springboot3+微服务实战12306高性能售票系统 - 慕课网 (imooc.com) 本文是仿12306项目实战第(二)章——项目实现 的第二篇,详细讲解使用Vue3 + Vue CLI 实现前端模块搭建的过程,同时其中也会涉及一些前后端交互的实现,因此也会开发一些后端接口;搭建好前端页面后,

    2024年03月26日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包