1、后台功能开发

1.1 用户模块开发

  • 1.1 jwt和token介绍
    1. token介绍

      令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。普通的令牌可能以各种形式出现,如访问令牌、身份令牌、刷新令牌等。

      简单理解 : 每个用户生成的唯一字符串标识,可以进行用户识别和校验

      类似技术: 天王盖地虎 ,小鸡炖蘑菇

      优势: token验证标识无法直接识别用户的信息,盗取token后也无法`登录`程序! 相对安全!

    2. jwt介绍

      Token是一项规范和标准(接口)

      JWT(JSON Web Token)是具体可以生成,校验,解析等动作Token的技术(实现类)

    3. jwt工作流程

      • 用户提供其凭据(通常是用户名和密码)进行身份验证。
      • 服务器对这些凭据进行验证,并在验证成功后创建一个JWT。
      • 服务器将JWT发送给客户端,并客户端在后续的请求中将JWT附加在请求头或参数中。
      • 服务器接收到请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作
    4. jwt数据组成和包含信息

      JWT由三部分组成: header(头部).payload(载荷).signature(签名)

      我们需要理解的是, jwt可以携带很多信息! 一般情况,需要加入:有效时间,签名秘钥,其他用户标识信息!

      有效时间为了保证token的时效性,过期可以重新登录获取!

      签名秘钥为了防止其他人随意解析和校验token数据!

      用户信息为了我们自己解析的时候,知道Token对应的具体用户!

    5. jwt使用和测试

      1. 导入依赖

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
        </dependency>

        <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
        </dependency>
      2. 编写配置

        application.yaml

        1
        2
        3
        4
        5
        #jwt配置
        jwt:
        token:
        tokenExpiration: 120 #有效时间,单位分钟
        tokenSignKey: headline123456 #当前程序签名秘钥 自定义
      3. 导入工具类

        封装jwt技术工具类

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        package com.atguigu.utils;

        import com.alibaba.druid.util.StringUtils;
        import io.jsonwebtoken.*;
        import lombok.Data;
        import org.springframework.boot.context.properties.ConfigurationProperties;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.stereotype.Component;

        import java.util.Date;

        @Data
        @Component
        @ConfigurationProperties(prefix = "jwt.token")
        public class JwtHelper {

        private long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒
        private String tokenSignKey; //当前程序签名秘钥

        //生成token字符串
        public String createToken(Long userId) {
        System.out.println("tokenExpiration = " + tokenExpiration);
        System.out.println("tokenSignKey = " + tokenSignKey);
        String token = Jwts.builder()

        .setSubject("YYGH-USER")
        .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟
        .claim("userId", userId)
        .signWith(SignatureAlgorithm.HS512, tokenSignKey)
        .compressWith(CompressionCodecs.GZIP)
        .compact();
        return token;
        }

        //从token字符串获取userid
        public Long getUserId(String token) {
        if(StringUtils.isEmpty(token)) return null;
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        Integer userId = (Integer)claims.get("userId");
        return userId.longValue();
        }



        //判断token是否有效
        public boolean isExpiration(String token){
        try {
        boolean isExpire = Jwts.parser()
        .setSigningKey(tokenSignKey)
        .parseClaimsJws(token)
        .getBody()
        .getExpiration().before(new Date());
        //没有过期,有效,返回false
        return isExpire;
        }catch(Exception e) {
        //过期出现异常,返回true
        return true;
        }
        }
        }

      4. 使用和测试

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        @org.springframework.boot.test.context.SpringBootTest
        public class SpringBootTest {

        @Autowired
        private JwtHelper jwtHelper;

        @Test
        public void test(){
        //生成 传入用户标识
        String token = jwtHelper.createToken(1L);
        System.out.println("token = " + token);

        //解析用户标识
        int userId = jwtHelper.getUserId(token).intValue();
        System.out.println("userId = " + userId);

        //校验是否到期! false 未到期 true到期
        boolean expiration = jwtHelper.isExpiration(token);
        System.out.println("expiration = " + expiration);
        }

        }
  • 1.2 登录功能实现
    1. 需求描述

      用户在客户端输入用户名密码并向后端提交,后端根据用户名和密码判断登录是否成功,用户有误或者密码有误响应不同的提示信息!

    2. 接口描述

      url地址: user/login

      请求方式:POST

      请求参数:

      1
      2
      3
      4
      {
      "username":"zhangsan", //用户名
      "userPwd":"123456" //明文密码
      }

      响应数据:

      成功

      1
      2
      3
      4
      5
      6
      7
      {
      "code":"200", // 成功状态码
      "message":"success" // 成功状态描述
      "data":{
      "token":"... ..." // 用户id的token
      }
      }

      失败

      1
      2
      3
      4
      5
      {
      "code":"501",
      "message":"用户名有误"
      "data":{}
      }
      1
      2
      3
      4
      5
      {
      "code":"503",
      "message":"密码有误"
      "data":{}
      }
    3. 实现代码

      1. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        @RestController
        @RequestMapping("user")
        s
        public class UserController {


        @Autowired
        private UserService userService;

        /**
        * 登录需求
        * 地址: /user/login
        * 方式: post
        * 参数:
        * {
        * "username":"zhangsan", //用户名
        * "userPwd":"123456" //明文密码
        * }
        * 返回:
        * {
        * "code":"200", // 成功状态码
        * "message":"success" // 成功状态描述
        * "data":{
        * "token":"... ..." // 用户id的token
        * }
        * }
        *
        * 大概流程:
        * 1. 账号进行数据库查询 返回用户对象
        * 2. 对比用户密码(md5加密)
        * 3. 成功,根据userId生成token -> map key=token value=token值 - result封装
        * 4. 失败,判断账号还是密码错误,封装对应的枚举错误即可
        */
        @PostMapping("login")
        public Result login(@RequestBody User user){
        Result result = userService.login(user);
        System.out.println("result = " + result);
        return result;
        }

        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        @Service
        public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService{
        @Autowired
        private JwtHelper jwtHelper;
        @Autowired
        private UserMapper userMapper;

        /**
        * 登录业务实现
        * @param user
        * @return result封装
        */
        @Override
        public Result login(User user) {

        //根据账号查询
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername,user.getUsername());
        User loginUser = userMapper.selectOne(queryWrapper);

        //账号判断
        if (loginUser == null) {
        //账号错误
        return Result.build(null, ResultCodeEnum.USERNAME_ERROR);
        }

        //判断密码
        if (!StringUtils.isEmpty(user.getUserPwd())
        && loginUser.getUserPwd().equals(MD5Util.encrypt(user.getUserPwd())))
        {
        //账号密码正确
        //根据用户唯一标识生成token
        String token = jwtHelper.createToken(Long.valueOf(loginUser.getUid()));

        Map data = new HashMap();
        data.put("token",token);

        return Result.ok(data);
        }

        //密码错误
        return Result.build(null,ResultCodeEnum.PASSWORD_ERROR);
        }
        }
  • 1.3 根据token获取用户数据
    1. 需求描述

      客户端发送请求,提交token请求头,后端根据token请求头获取登录用户的详细信息并响应给客户端进行存储

    2. 接口描述

      url地址:user/getUserInfo

      请求方式:GET

      请求头:

      1
      token: token内容

      响应数据:

      成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {
      "code": 200,
      "message": "success",
      "data": {
      "loginUser": {
      "uid": 1,
      "username": "zhangsan",
      "userPwd": "",
      "nickName": "张三"
      }
      }
      }

      失败

      1
      2
      3
      4
      5
      {
      "code": 504,
      "message": "notLogin",
      "data": null
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        /**
        * 地址: user/getUserInfo
        * 方式: get
        * 请求头: token = token内容
        * 返回:
        * {
        * "code": 200,
        * "message": "success",
        * "data": {
        * "loginUser": {
        * "uid": 1,
        * "username": "zhangsan",
        * "userPwd": "",
        * "nickName": "张三"
        * }
        * }
        * }
        *
        * 大概流程:
        * 1.获取token,解析token对应的userId
        * 2.根据userId,查询用户数据
        * 3.将用户数据的密码置空,并且把用户数据封装到结果中key = loginUser
        * 4.失败返回504 (本次先写到当前业务,后期提取到拦截器和全局异常处理器)
        */
        @GetMapping("getUserInfo")
        public Result userInfo(@RequestHeader String token){
        Result result = userService.getUserInfo(token);
        return result;
        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        /**
        * 查询用户数据
        * @param token
        * @return result封装
        */
        @Override
        public Result getUserInfo(String token) {

        //1.判定是否有效期
        if (jwtHelper.isExpiration(token)) {
        //true过期,直接返回未登录
        return Result.build(null,ResultCodeEnum.NOTLOGIN);
        }

        //2.获取token对应的用户
        int userId = jwtHelper.getUserId(token).intValue();

        //3.查询数据
        User user = userMapper.selectById(userId);

        if (user != null) {
        user.setUserPwd(null);
        Map data = new HashMap();
        data.put("loginUser",user);
        return Result.ok(data);
        }

        return Result.build(null,ResultCodeEnum.NOTLOGIN);
        }
  • 1.4 注册用户名检查
    1. 需求描述

      用户在注册时输入用户名时,立刻将用户名发送给后端,后端根据用户名查询用户名是否可用并做出响应

    2. 接口描述

      url地址:user/checkUserName

      请求方式:POST

      请求参数:param形式

      1
      username=zhangsan

      响应数据:

      成功

      1
      2
      3
      4
      5
      {
      "code":"200",
      "message":"success"
      "data":{}
      }

      失败

      1
      2
      3
      4
      5
      {
      "code":"505",
      "message":"用户名占用"
      "data":{}
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        /**
        * url地址:user/checkUserName
        * 请求方式:POST
        * 请求参数:param形式
        * username=zhangsan
        * 响应数据:
        * {
        * "code":"200",
        * "message":"success"
        * "data":{}
        * }
        *
        * 实现步骤:
        * 1. 获取账号数据
        * 2. 根据账号进行数据库查询
        * 3. 结果封装
        */
        @PostMapping("checkUserName")
        public Result checkUserName(String username){
        Result result = userService.checkUserName(username);
        return result;
        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        /**
        * 检查账号是否可以注册
        *
        * @param username 账号信息
        * @return
        */
        @Override
        public Result checkUserName(String username) {

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername,username);
        User user = userMapper.selectOne(queryWrapper);

        if (user != null){
        return Result.build(null,ResultCodeEnum.USERNAME_USED);
        }

        return Result.ok(null);
        }
  • 1.5 用户注册功能
    1. 需求描述

      客户端将新用户信息发送给服务端,服务端将新用户存入数据库,存入之前做用户名是否被占用校验,校验通过响应成功提示,否则响应失败提示

    2. 接口描述

      url地址:user/regist

      请求方式:POST

      请求参数:

      1
      2
      3
      4
      5
      {
      "username":"zhangsan",
      "userPwd":"123456",
      "nickName":"张三"
      }

      响应数据:

      成功

      1
      2
      3
      4
      5
      {
      "code":"200",
      "message":"success"
      "data":{}
      }

      失败

      1
      2
      3
      4
      5
      {
      "code":"505",
      "message":"用户名占用"
      "data":{}
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        /**
        * url地址:user/regist
        * 请求方式:POST
        * 请求参数:
        * {
        * "username":"zhangsan",
        * "userPwd":"123456",
        * "nickName":"张三"
        * }
        * 响应数据:
        * {
        * "code":"200",
        * "message":"success"
        * "data":{}
        * }
        *
        * 实现步骤:
        * 1. 将密码加密
        * 2. 将数据插入
        * 3. 判断结果,成 返回200 失败 505
        */

        @PostMapping("regist")
        public Result regist(@RequestBody User user){
        Result result = userService.regist(user);
        return result;
        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        @Override
        public Result regist(User user) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername,user.getUsername());
        Long count = userMapper.selectCount(queryWrapper);

        if (count > 0){
        return Result.build(null,ResultCodeEnum.USERNAME_USED);
        }

        user.setUserPwd(MD5Util.encrypt(user.getUserPwd()));
        int rows = userMapper.insert(user);
        System.out.println("rows = " + rows);
        return Result.ok(null);
        }

1.2 首页模块开发

  • 2.1 查询首页分类
    1. 需求描述

      进入新闻首页,查询所有分类并动态展示新闻类别栏位

    2. 接口描述

      url地址:portal/findAllTypes

      请求方式:get

      请求参数:无

      响应数据:

      成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      {
      "code":"200",
      "message":"OK"
      "data":{
      [
      {
      "tid":"1",
      "tname":"新闻"
      },
      {
      "tid":"2",
      "tname":"体育"
      },
      {
      "tid":"3",
      "tname":"娱乐"
      },
      {
      "tid":"4",
      "tname":"科技"
      },
      {
      "tid":"5",
      "tname":"其他"
      }
      ]
      }
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        @RestController
        @RequestMapping("portal")
        @CrossOrigin
        public class PortalController {

        @Autowired
        private TypeService typeService;

        /**
        * 查询全部类别信息
        * @return
        */
        @GetMapping("findAllTypes")
        public Result findAllTypes(){
        //直接调用业务层,查询全部数据
        List<Type> list = typeService.list();
        return Result.ok(list);
        }
        }
  • 2.2 分页查询首页头条信息
    1. 需求描述

      • 客户端向服务端发送查询关键字,新闻类别,页码数,页大小
      • 服务端根据条件搜索分页信息,返回含页码数,页大小,总页数,总记录数,当前页数据等信息,并根据时间降序,浏览量降序排序
    2. 接口描述

      url地址:portal/findNewsPage

      请求方式:post

      请求参数:

      1
      2
      3
      4
      5
      6
      {
      "keyWords":"马斯克", // 搜索标题关键字
      "type":0, // 新闻类型
      "pageNum":1, // 页码数
      "pageSize":10 // 页大小
      }

      响应数据:

      成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      {
      "code":"200",
      "message":"success"
      "data":{
      "pageInfo":{
      "pageData":[
      {
      "hid":"1", // 新闻id
      "title":"尚硅谷宣布 ... ...", // 新闻标题
      "type":"1", // 新闻所属类别编号
      "pageViews":"40", // 新闻浏览量
      "pastHours":"3" , // 发布时间已过小时数
      "publisher":"1" // 发布用户ID
      },
      {
      "hid":"1", // 新闻id
      "title":"尚硅谷宣布 ... ...", // 新闻标题
      "type":"1", // 新闻所属类别编号
      "pageViews":"40", // 新闻浏览量
      "pastHours":"3", // 发布时间已过小时数
      "publisher":"1" // 发布用户ID
      },
      {
      "hid":"1", // 新闻id
      "title":"尚硅谷宣布 ... ...", // 新闻标题
      "type":"1", // 新闻所属类别编号
      "pageViews":"40", // 新闻浏览量
      "pastHours":"3", // 发布时间已过小时数
      "publisher":"1" // 发布用户ID
      }
      ],
      "pageNum":1, //页码数
      "pageSize":10, // 页大小
      "totalPage":20, // 总页数
      "totalSize":200 // 总记录数
      }
      }
      }
    3. 代码实现

      1. 准备条件实体类

        1
        2
        3
        4
        5
        6
        7
        8
        @Data
        public class PortalVo {

        private String keyWords;
        private Integer type;
        private Integer pageNum = 1;
        private Integer pageSize =10;
        }
      2. controller

        1
        2
        3
        4
        5
        6
        7
        8
        9
        /**
        * 首页分页查询
        * @return
        */
        @PostMapping("findNewPage")
        public Result findNewPage(@RequestBody PortalVo portalVo){
        Result result = headlineService.findNewPage(portalVo);
        return result;
        }
      3. service

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        @Service
        public class HeadlineServiceImpl extends ServiceImpl<HeadlineMapper, Headline>
        implements HeadlineService{

        @Autowired
        private HeadlineMapper headlineMapper;

        /**
        * 首页数据查询
        * @param portalVo
        * @return
        */
        @Override
        public Result findNewPage(PortalVo portalVo) {

        //1.条件拼接 需要非空判断
        LambdaQueryWrapper<Headline> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(!StringUtils.isEmpty(portalVo.getKeyWords()),Headline::getTitle,portalVo.getKeyWords())
        .eq(portalVo.getType()!= null,Headline::getType,portalVo.getType());

        //2.分页参数
        IPage<Headline> page = new Page<>(portalVo.getPageNum(),portalVo.getPageSize());

        //3.分页查询
        //查询的结果 "pastHours":"3" // 发布时间已过小时数 我们查询返回一个map
        //自定义方法
        headlineMapper.selectPageMap(page, portalVo);

        //4.结果封装
        //分页数据封装
        Map<String,Object> pageInfo =new HashMap<>();
        pageInfo.put("pageData",page.getRecords());
        pageInfo.put("pageNum",page.getCurrent());
        pageInfo.put("pageSize",page.getSize());
        pageInfo.put("totalPage",page.getPages());
        pageInfo.put("totalSize",page.getTotal());

        Map<String,Object> pageInfoMap=new HashMap<>();
        pageInfoMap.put("pageInfo",pageInfo);
        // 响应JSON
        return Result.ok(pageInfoMap);
        }
        }
      4. mapper

        接口:

        1
        2
        3
        4
        5
        6
        public interface HeadlineMapper extends BaseMapper<Headline> {

        //自定义分页查询方法
        IPage<Map> selectPageMap(IPage<Headline> page,
        @Param("portalVo") PortalVo portalVo);
        }

        mapperxml:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        <select id="selectPageMap" resultType="map">
        select hid,title,type,page_views pageViews,TIMESTAMPDIFF(HOUR,create_time,NOW()) pastHours,
        publisher from news_headline where is_deleted=0
        <if test="portalVo.keyWords !=null and portalVo.keyWords.length()>0 ">
        and title like concat('%',#{portalVo.keyWords},'%')
        </if>
        <if test="portalVo.type != null and portalVo.type != 0">
        and type = #{portalVo.type}
        </if>
        </select>
  • 2.3 查询头条详情
    1. 需求描述

      • 用户点击”查看全文”时,向服务端发送新闻id
      • 后端根据新闻id查询完整新闻文章信息并返回
      • 后端要同时让新闻的浏览量+1
    2. 接口描述

      url地址:portal/showHeadlineDetail

      请求方式:post

      请求参数:

      1
      hid=1 param形成参数

      响应数据:

      成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      {
      "code":"200",
      "message":"success",
      "data":{
      "headline":{
      "hid":"1", // 新闻id
      "title":"马斯克宣布 ... ...", // 新闻标题
      "article":"... ..." // 新闻正文
      "type":"1", // 新闻所属类别编号
      "typeName":"科技", // 新闻所属类别
      "pageViews":"40", // 新闻浏览量
      "pastHours":"3" , // 发布时间已过小时数
      "publisher":"1" , // 发布用户ID
      "author":"张三" // 新闻作者
      }
      }
      }
    3. 代码实现

      1. controller

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
         /**
        * 首页详情接口
        * @param hid
        * @return
        */
        @PostMapping("showHeadlineDetail")
        public Result showHeadlineDetail(Integer hid){
        Result result = headlineService.showHeadlineDetail(hid);
        return result;
        }
      2. service

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        /**
        * 详情数据查询
        * "headline":{
        * "hid":"1", // 新闻id
        * "title":"马斯克宣布 ... ...", // 新闻标题
        * "article":"... ..." // 新闻正文
        * "type":"1", // 新闻所属类别编号
        * "typeName":"科技", // 新闻所属类别
        * "pageViews":"40", // 新闻浏览量
        * "pastHours":"3" , // 发布时间已过小时数
        * "publisher":"1" , // 发布用户ID
        * "author":"张三" // 新闻作者
        * }
        * 注意: 是多表查询 , 需要更新浏览量+1
        *
        * @param hid
        * @return
        */
        @Override
        public Result showHeadlineDetail(Integer hid) {

        //1.实现根据id的查询(多表
        Map headLineDetail = headlineMapper.selectDetailMap(hid);
        //2.拼接头条对象(阅读量和version)进行数据更新
        Headline headline = new Headline();
        headline.setHid(hid);
        headline.setPageViews((Integer) headLineDetail.get("pageViews")+1); //阅读量+1
        headline.setVersion((Integer) headLineDetail.get("version")); //设置版本
        headlineMapper.updateById(headline);

        Map<String,Object> pageInfoMap=new HashMap<>();
        pageInfoMap.put("headline",headLineDetail);
        return Result.ok(pageInfoMap);
        }
      3. mapper

        接口:

        1
        2
        3
        4
        5
        6
        /**
        * 分页查询头条详情
        * @param hid
        * @return
        */
        Map selectDetailMap(Integer hid);

        mapperxml:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        <!--    Map selectDetailMap(Integer hid);-->
        <select id="selectDetailMap" resultType="map">
        select hid,title,article,type, h.version ,tname typeName ,page_views pageViews
        ,TIMESTAMPDIFF(HOUR,create_time,NOW()) pastHours,publisher
        ,nick_name author from news_headline h
        left join news_type t on h.type = t.tid
        left join news_user u on h.publisher = u.uid
        where hid = #{hid}
        </select>

1.3 头条模块开发

  • **3.1 登陆验证和保护 **

    1. 需求描述

      • 客户端在进入发布页前、发布新闻前、进入修改页前、修改前、删除新闻前先向服务端发送请求携带token请求头
      • 后端接收token请求头后,校验用户登录是否过期并做响应
      • 前端根据响应信息提示用户进入登录页还是进入正常业务页面
    2. 接口描述

      url地址:user/checkLogin

      请求方式:get

      请求参数: 无

      请求头: token: 用户token

      响应数据:

      未过期:

      1
      2
      3
      4
      5
      {
      "code":"200",
      "message":"success",
      "data":{}
      }

      过期:

      1
      2
      3
      4
      5
      {
      "code":"504",
      "message":"loginExpired",
      "data":{}
      }
    3. 代码实现

      1. controller 【登录检查】
        1
        2
        3
        4
        5
        6
        7
        8
        9
        @GetMapping("checkLogin")
        public Result checkLogin(@RequestHeader String token){
        if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){
        //没有传或者过期 未登录
        return Result.build(null, ResultCodeEnum.NOTLOGIN);
        }

        return Result.ok(null);
        }
      2. 拦截器 【所有/headline开头都需要检查登陆】
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        @Component
        public class LoginProtectInterceptor implements HandlerInterceptor {

        @Autowired
        private JwtHelper jwtHelper;

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){
        Result result = Result.build(null, ResultCodeEnum.NOTLOGIN);
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(result);
        response.getWriter().print(json);
        //拦截
        return false;
        }else{
        //放行
        return true;
        }
        }
        }
      3. 拦截器配置
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        @Configuration
        public class WebMvcConfig implements WebMvcConfigurer {

        @Autowired
        private LoginProtectInterceptor loginProtectInterceptor;

        @Override
        public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginProtectInterceptor).addPathPatterns("/headline/**");
        }
        }
  • 3.2 头条发布实现

    1. 需求描述

      • 用户在客户端输入发布的新闻信息完毕后
      • 发布前先请求后端的登录校验接口验证登录
      • 登录通过则提交新闻信息
      • 后端将新闻信息存入数据库
    2. 接口描述

      url地址:headline/publish

      请求方式:post

      请求头:

      1
      token: ... ...

      请求参数:

      1
      2
      3
      4
      5
      {
      "title":"尚硅谷宣布 ... ...", // 文章标题
      "article":"... ...", // 文章内容
      "type":"1" // 文章类别
      }

      响应数据:

      未登录

      1
      2
      3
      4
      5
      {
      "code":"504",
      "message":"loginExpired",
      "data":{}
      }

      成功

      1
      2
      3
      4
      5
      6
      {
      "code":"200",
      "message":"success",
      "data":{}
      }

    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        /**
        * 实现步骤:
        * 1. token获取userId [无需校验,拦截器会校验]
        * 2. 封装headline数据
        * 3. 插入数据即可
        */
        @PostMapping("publish")
        public Result publish(@RequestBody Headline headline,@RequestHeader String token){

        int userId = jwtHelper.getUserId(token).intValue();
        headline.setPublisher(userId);
        Result result = headlineService.publish(headline);
        return result;
        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        /**
        * 发布数据
        * @param headline
        * @return
        */
        @Override
        public Result publish(Headline headline) {
        headline.setCreateTime(new Date());
        headline.setUpdateTime(new Date());
        headline.setPageViews(0);
        headlineMapper.insert(headline);
        return Result.ok(null);
        }
  • 3.3 修改头条回显

    1. 需求描述

      • 前端先调用登录校验接口,校验登录是否过期
      • 登录校验通过后 ,则根据新闻id查询新闻的完整信息并响应给前端
    2. 接口描述

      url地址:headline/findHeadlineByHid

      请求方式:post

      请求参数:

      1
      hid=1 param形成参数

      响应数据:

      成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {
      "code":"200",
      "message":"success",
      "data":{
      "headline":{
      "hid":"1",
      "title":"马斯克宣布",
      "article":"... ... ",
      "type":"2"
      }
      }
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        @PostMapping("findHeadlineByHid")
        public Result findHeadlineByHid(Integer hid){
        Result result = headlineService.findHeadlineByHid(hid);
        return result;
        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        /**
        * 根据id查询详情
        * @param hid
        * @return
        */
        @Override
        public Result findHeadlineByHid(Integer hid) {
        Headline headline = headlineMapper.selectById(hid);
        Map<String,Object> pageInfoMap=new HashMap<>();
        pageInfoMap.put("headline",headline);
        return Result.ok(pageInfoMap);
        }
  • 3.4 头条修改实现

    1. 需求描述

      • 客户端将新闻信息修改后,提交前先请求登录校验接口校验登录状态
      • 登录校验通过则提交修改后的新闻信息,后端接收并更新进入数据库
    2. 接口描述

      url地址:headline/update

      请求方式:post

      请求参数:

      1
      2
      3
      4
      5
      6
      {
      "hid":"1",
      "title":"尚硅谷宣布 ... ...",
      "article":"... ...",
      "type":"2"
      }

      响应数据:

      成功

      1
      2
      3
      4
      5
      {
      "code":"200",
      "message":"success",
      "data":{}
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        @PostMapping("update")
        public Result update(@RequestBody Headline headline){
        Result result = headlineService.updateHeadLine(headline);
        return result;
        }
      2. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
         /**
        * 修改业务
        * 1.查询version版本
        * 2.补全属性,修改时间 , 版本!
        *
        * @param headline
        * @return
        */
        @Override
        public Result updateHeadLine(Headline headline) {

        //读取版本
        Integer version = headlineMapper.selectById(headline.getHid()).getVersion();

        headline.setVersion(version);
        headline.setUpdateTime(new Date());

        headlineMapper.updateById(headline);

        return Result.ok(null);
        }
  • 3.5 删除头条功能

    1. 需求描述

      • 将要删除的新闻id发送给服务端
      • 服务端校验登录是否过期,未过期则直接删除,过期则响应登录过期信息
    2. 接口描述

      url地址:headline/removeByHid

      请求方式:post

      请求参数:

      1
      hid=1 param形成参数

      响应数据:

      成功

      1
      2
      3
      4
      5
      {
      "code":"200",
      "message":"success",
      "data":{}
      }
    3. 代码实现

      1. controller
        1
        2
        3
        4
        5
        @PostMapping("removeByHid")
        public Result removeById(Integer hid){
        headlineService.removeById(hid);
        return Result.ok(null);
        }

postman://auth/callback?code=3ebd1ec6a92648076a9ff9c045e12e9a48add12b8f163f23174288ff879dbf3c