Chiriri's blog Chiriri's blog
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)

Iekr

苦逼后端开发
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)
  • JavaSE

  • JavaEE

  • Linux

  • MySQL

  • NoSQL

  • Python

  • Python模块

  • 机器学习

  • 设计模式

  • 传智健康

  • 畅购商城

  • 博客项目

    • 工程搭建及首页功能
    • 登录注册
      • 登录
        • 接口说明
        • JWT
        • 工具类
        • Controller
        • Vo
        • Service
      • 获取用户信息
        • 接口说明
        • Controller
        • Service
        • Vo
      • 退出登录
        • 接口说明
        • Controller
        • Service
      • 注册
        • 接口说明
        • Controller
        • Service
        • 事务
      • 登录拦截器
        • 拦截器实现
        • 使拦截器生效
      • ThreadLocal
        • 保存用户信息
        • ThreadLocal内存泄漏
      • 文章详情
        • 接口说明
        • 表结构
        • POJO
        • Controller
        • Vo
        • Service
        • Dao
  • JVM

  • JUC

  • Golang

  • Kubernetes

  • 硅谷课堂

  • C

  • 源码

  • 神领物流

  • RocketMQ

  • 短链平台

  • 后端
  • 博客项目
Iekr
2023-04-02
目录

登录注册

# 登录注册

# 登录

# 接口说明

接口 url:/login

请求方式:POST

请求参数:

参数名称 参数类型 说明
account string 账号
password string 密码

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}
1
2
3
4
5
6

# JWT

登录使用 JWT 技术。jwt 可以生成 一个加密的 token,做为用户登录的令牌,当用户登录成功之后,发放给客户端。

请求需要登录的资源或者接口的时候,将 token 携带,后端验证 token 是否合法。jwt 有三部分组成:A.B.C

  • A:Header,{"type":"JWT","alg":"HS256"} 固定
  • B:playload,存放信息,比如,用户 id,过期时间等等,可以被解密,不能存放敏感信息
  • C: 签证,A 和 B 加上秘钥加密而成,只要秘钥不丢失,可以认为是安全的。

jwt 验证,主要就是验证 C 部分是否合法。

# 工具类

创建 com.iekr.blog.utils 包

创建 JWTUtils 工具类

public class JWTUtils {

    private static final String jwtToken = "J77shRFHqwBzg^1^5$ko9HOQ96a$Ux@uE";

    public static String createToken(Long userId){
        Map<String,Object> claims = new HashMap<>();
        claims.put("userId",userId);
        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, jwtToken) // 签发算法,秘钥为jwtToken
                .setClaims(claims) // body数据,要唯一,自行设置
                .setIssuedAt(new Date()) // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 60 * 1000));// 一天的有效时间
        String token = jwtBuilder.compact();
        return token;
    }

    public static Map<String, Object> checkToken(String token){
        try {
            Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token);
            return (Map<String, Object>) parse.getBody();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;

    }

}
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

# Controller

在 application 添加 redis 配置

spring.redis.host=localhost
spring.redis.port=6379
1
2

LoginController

@RestController
@RequestMapping("login")
public class LoginController {

    @Autowired
    private LoginService loginService;

    @PostMapping
    public Result login(@RequestBody LoginParam loginParam) {
        return loginService.login(loginParam);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# Vo

登录表单 vo com.iekr.blog.vo.params.PageParmas

@Data
public class PageParmas {
    private int page = 1;
    private int pageSize = 10;
}
1
2
3
4
5

错误状态码 com.iekr.blog.vo.ErrorCode

public enum ErrorCode {

    PARAMS_ERROR(10001, "参数有误"),
    ACCOUNT_PWD_NOT_EXIST(10002, "用户名或密码不存在"),
    TOKEN_ERROR(10003, "token不合法"),
    ACCOUNT_EXIST(10004,"账号已存在"),
    NO_PERMISSION(70001, "无访问权限"),
    SESSION_TIME_OUT(90001, "会话超时"),
    NO_LOGIN(90002, "未登录"),
    ;

    private int code;
    private String msg;

    ErrorCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
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

com.iekr.blog.vo.params.LoginParam

@Data
public class LoginParam {

    private String account;

    private String password;

    private String nickname;
}
1
2
3
4
5
6
7
8
9

# Service

LoginService

public interface LoginService {
    /**
     * 登录功能
     * @param loginParam
     * @return
     */
    Result login(LoginParam loginParam);
}
1
2
3
4
5
6
7
8

LoginServiceImpl

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String slat = "0I3yo9h2WFm!asPM%Bum9liJz&HcdmJ&Q";

    @Override

    public Result login(LoginParam loginParam) {
        String account = loginParam.getAccount();
        String password = loginParam.getPassword();
        //参数错误
        if (StringUtils.isBlank(account) || StringUtils.isBlank(password)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.NO_LOGIN.getCode(), ErrorCode.PARAMS_ERROR.NO_LOGIN.getMsg());
        }
        //md5+盐
        password = DigestUtils.md5Hex(password + slat);
        SysUser sysUser = sysUserService.findUser(account, password);
        //用户不存在
        if (sysUser == null) {
            return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(), ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg());
        }
        //生产jwt
        String token = JWTUtils.createToken(sysUser.getId());
        //存入redis 并设置过期时间1天
        redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(sysUser), 1, TimeUnit.DAYS);


        return Result.success(token);
    }
}
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

SysUserService

    SysUser findUser(String account, String password);
1

SysUserServiceImpl

    @Override
    public SysUser findUser(String account, String password) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getAccount, account);
        queryWrapper.eq(SysUser::getPassword, password);
        queryWrapper.select(SysUser::getAccount, SysUser::getId, SysUser::getAvatar, SysUser::getNickname);
        queryWrapper.last("limit 1");
        return sysUserMapper.selectOne(queryWrapper);

    }
1
2
3
4
5
6
7
8
9
10

# 获取用户信息

# 接口说明

接口 url:/users/currentUser

请求方式:GET

请求参数:

参数名称 参数类型 说明
Authorization string 头部信息 (TOKEN)

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": {
        "id":1,
        "account":"1",
        "nickaname":"1",
        "avatar":"ss"
    }
}
1
2
3
4
5
6
7
8
9
10
11

# Controller

UserController

@RestController
@RequestMapping("users")
public class UserController {

    @Autowired
    private SysUserService sysUserService;

    @GetMapping("currentUser")
    public Result currentUser(@RequestHeader("Authorization") String token) {
        return sysUserService.findUserByToken(token);

    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Service

SysUserService

    /**
     * 根据token查询用户信息
     * @param token
     * @return
     */
    Result findUserByToken(String token);
1
2
3
4
5
6

SysUserServiceImpl

    @Override
    public Result findUserByToken(String token) {
        //检验token是否合法
        SysUser sysUser = loginService.checkToken(token);
        if (sysUser == null) {
            return Result.fail(ErrorCode.TOKEN_ERROR.getCode(), ErrorCode.TOKEN_ERROR.getMsg());
        }
        LoginUserVo loginUserVo = new LoginUserVo();
        loginUserVo.setId(sysUser.getId());
        loginUserVo.setAccount(sysUser.getAccount());
        loginUserVo.setNickname(sysUser.getNickname());
        loginUserVo.setAvatar(sysUser.getAvatar());
        return Result.success(loginUserVo);

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

LoginService

    SysUser checkToken(String token);
1

LoginServiceImpl

    @Override
    public SysUser checkToken(String token) {
        if (StringUtils.isBlank(token)) {
            return null;
        }
        Map<String, Object> stringObjectMap = JWTUtils.checkToken(token);
        if (stringObjectMap == null) {
            return null;
        }
        String userJson = redisTemplate.opsForValue().get("TOKEN_" + token);
        if (StringUtils.isBlank(userJson)) {
            return null;
        }
        SysUser sysUser = JSON.parseObject(userJson, SysUser.class);
        return sysUser;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Vo

com.iekr.blog.vo.LoginUserVo

@Data
public class LoginUserVo {
    private Long id;

    private String account;

    private String nickname;

    private String avatar;
}
1
2
3
4
5
6
7
8
9
10

# 退出登录

# 接口说明

接口 url:/logout

请求方式:GET

请求参数:

参数名称 参数类型 说明
Authorization string 头部信息 (TOKEN)

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": null
}
1
2
3
4
5
6

# Controller

LogoutController

@RestController
@RequestMapping("logout")
public class LogoutController {

    @Autowired
    private LoginService loginService;

    @GetMapping
    public Result logout(@RequestHeader("Authorization") String token) {
        return loginService.logout(token);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# Service

LoginService

    /**
     * 退出登录
     * @param token
     * @return
     */

    Result logout(String token);
1
2
3
4
5
6
7

LoginServiceImpl

    @Override
    public Result logout(String token) {
        redisTemplate.delete("TOKEN_" + token);
        return Result.success(null);
    }
1
2
3
4
5

# 注册

# 接口说明

接口 url:/register

请求方式:POST

请求参数:

参数名称 参数类型 说明
account string 账号
password string 密码
nickname string 昵称

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}
1
2
3
4
5
6

# Controller

RegisterController

@RestController
@RequestMapping("register")
public class RegisterController {

    @Autowired
    private LoginService loginService;

    @PostMapping
    public Result register(@RequestBody LoginParam loginParam) {
        //sso 单点登录,后期如果需要把登录注册功能 独立的话可以抽离出去接口
        return loginService.register(loginParam);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Service

LoginService

    /**
     * 注册功能
     * @param loginParam
     * @return
     */
    Result register(LoginParam loginParam);
1
2
3
4
5
6

LoginServiceImpl

    @Override
    public Result register(LoginParam loginParam) {
        //参数检验
        String account = loginParam.getAccount();
        String password = loginParam.getPassword();
        String nickname = loginParam.getNickname();
        if (StringUtils.isBlank(account)
                || StringUtils.isBlank(password)
                || StringUtils.isBlank(nickname)
        ) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMsg());
        }
        //判定账号是否存在
        SysUser sysUser = sysUserService.findUserByAccount(account);
        if (sysUser != null) {
            return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(), ErrorCode.ACCOUNT_EXIST.getMsg());
        }
        //账号不存在 填写注册信息
        sysUser = new SysUser();
        sysUser.setNickname(nickname);
        sysUser.setAccount(account);
        sysUser.setPassword(DigestUtils.md5Hex(password + slat));
        sysUser.setCreateDate(System.currentTimeMillis());
        sysUser.setLastLogin(System.currentTimeMillis());
        sysUser.setAvatar("/static/img/logo.b3a48c0.png");
        //1 为true
        sysUser.setAdmin(1);
        // 0 为false
        sysUser.setDeleted(0);
        sysUser.setSalt("");
        sysUser.setStatus("");
        sysUser.setEmail("");
        this.sysUserService.save(sysUser);

        //生成token
        String token = JWTUtils.createToken(sysUser.getId());
        //存入redis
        redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(sysUser), 1, TimeUnit.DAYS);

        return Result.success(token);
    }
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

SysUserService

    /**
     * 查询账号是否存在
     * @param account
     * @return
     */
    SysUser findUserByAccount(String account);

    /**
     * 保存用户
     * @param sysUser
     */
    void save(SysUser sysUser);
1
2
3
4
5
6
7
8
9
10
11
12

SysUserServiceImpl

    @Override
    public SysUser findUserByAccount(String account) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getAccount, account);
        queryWrapper.last("limit 1");
        return this.sysUserMapper.selectOne(queryWrapper);

    }

    @Override
    public void save(SysUser sysUser) {
        //mybatis-plus默认生成的id 是分布式id 采用了雪花算法
        this.sysUserMapper.insert(sysUser);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 事务

当注册时发生异常我们需要回滚,我们在 LoginServiceImpl 类上加本地事务注解,当然 一般建议加在 接口上,通用一些。

@Service
@Transactional
public class LoginServiceImpl implements LoginService {
    
}
1
2
3
4
5

# 登录拦截器

每次访问需要登录的资源的时候,都需要在代码中进行判断,一旦登录的逻辑有所改变,代码都得进行变动,非常不合适。

使用拦截器,进行登录拦截,如果遇到需要登录才能访问的接口,如果未登录,拦截器直接返回,并跳转登录页面。

# 拦截器实现

com.iekr.blog.handler.LoginInterceptor

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private LoginService loginService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //在执行controller方法(handler)之前进行执行
        //判定类型是否为handler
        if (!(handler instanceof HandlerMethod)) {
            //handler 有可能是RequestResourceHandler 访问静态资源的
            return true;
        }
        String token = request.getHeader("Authorization");
        log.info("=================request start===========================");
        String requestURI = request.getRequestURI();
        log.info("request uri:{}", requestURI);
        log.info("request method:{}", request.getMethod());
        log.info("token:{}", token);
        log.info("=================request end===========================");
        //token为空
        if (StringUtils.isBlank(token)) {
            Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(), ErrorCode.NO_LOGIN.getMsg());
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(JSON.toJSONString(result));
            return false;
        }
        SysUser sysUser = loginService.checkToken(token);
        //用户不存在
        if (sysUser == null) {
            Result resul    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/test");
    }t = Result.fail(ErrorCode.NO_LOGIN.getCode(), ErrorCode.NO_LOGIN.getMsg());
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(JSON.toJSONString(result));
            return false;
        }
        //登录验证成果,放行
        return true;
    }
}
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

# 使拦截器生效

com.iekr.blog.config.WebConfig

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //此时只拦截test路径,后期设置为需要拦截的路径
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/test");
    }
1
2
3
4
5
6
7
8
9

# ThreadLocal

# 保存用户信息

com.iekr.blog.utils.UserThreadLocal

public class UserThreadLocal {

    private UserThreadLocal() {
    }

    //线程变量隔离
    private static final ThreadLocal<SysUser> LOCAL = new ThreadLocal<>();

    public static void put(SysUser sysUser) {
        LOCAL.set(sysUser);
    }

    public static SysUser get() {
        return LOCAL.get();
    }

    public static void remove() {
        LOCAL.remove();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

LoginInterceptor

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private LoginService loginService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //在执行controller方法(handler)之前进行执行
        //判定类型是否为handler
        if (!(handler instanceof HandlerMethod)) {
            //handler 有可能是RequestResourceHandler 访问静态资源的
            return true;
        }
        String token = request.getHeader("Authorization");
        log.info("=================request start===========================");
        String requestURI = request.getRequestURI();
        log.info("request uri:{}", requestURI);
        log.info("request method:{}", request.getMethod());
        log.info("token:{}", token);
        log.info("=================request end===========================");
        //token为空
        if (StringUtils.isBlank(token)) {
            Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(), ErrorCode.NO_LOGIN.getMsg());
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(JSON.toJSONString(result));
            return false;
        }
        SysUser sysUser = loginService.checkToken(token);
        //用户不存在
        if (sysUser == null) {
            Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(), ErrorCode.NO_LOGIN.getMsg());
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(JSON.toJSONString(result));
            return false;
        }
        //将用户信息放到ThreadLocal中
        UserThreadLocal.put(sysUser);
        //登录验证成果,放行
        return true;
    }

    /**
        当所有方法执行完后执行
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //如果不删除ThreadLocal中用完的信息 会有内存泄露的风险
        UserThreadLocal.remove();
    }
}
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

之后我们可以在当前线程中使用 get() 方法获取我们放置到 ThreadLocal 中的信息

UserThreadLocal.get();
1

# ThreadLocal 内存泄漏

image-20230402212856447

实线代表强引用,虚线代表弱引用

每一个 Thread 维护一个 ThreadLocalMap, key 为使用弱引用的 ThreadLocal 实例,value 为线程变量的副本。

  • 强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不回收这种对象。 如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为 null,这样可以使 JVM 在合适的时间就会回收该对象。

  • 弱引用,JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在 java 中,用 java.lang.ref.WeakReference 类来表示。

# 文章详情

# 接口说明

接口 url:/articles/view/{id}

请求方式:POST

请求参数:

参数名称 参数类型 说明
id long 文章 id(路径参数)

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}
1
2
3
4
5
6

# 表结构

ms_article_body

CREATE TABLE `blog`.`ms_article_body`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `content_html` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `article_id` bigint(0) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `article_id`(`article_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1
2
3
4
5
6
7
8

ms_category

CREATE TABLE `blog`.`ms_category`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
  `category_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1
2
3
4
5
6
7

# POJO

ArticleBody

@Data
public class ArticleBody {

    private Long id;
    private String content;
    private String contentHtml;
    private Long articleId;
}
1
2
3
4
5
6
7
8

Category

@Data
public class Category {

    private Long id;

    private String avatar;

    private String categoryName;

    private String description;
}
1
2
3
4
5
6
7
8
9
10
11

# Controller

ArticleController

    /**
     * 文章详情
     */
    @PostMapping("view/{id}")
    public Result findArticleById(@PathVariable("id") Long articleId){
        return articleService.findArticleById(articleId);
    }
1
2
3
4
5
6
7

# Vo

CategoryVo

@Data
public class CategoryVo {

    private Long id;

    private String avatar;

    private String categoryName;
}
1
2
3
4
5
6
7
8
9

ArticleBodyVo

@Data
public class ArticleBodyVo {

    private String content;
}
1
2
3
4
5

ArticleVo 将之前注释的两条语句解除

@Data
public class ArticleVo {

    private String id;

    private String title;

    private String summary;

    private int commentCounts;

    private int viewCounts;

    private int weight;
    /**
     * 创建时间
     */
    private String createDate;

    private String author;

    private ArticleBodyVo body;

    private List<TagVo> tags;

    private List<CategoryVo> categorys;

}
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

# Service

ArticleService

    /**
     * 查询文章详情
     * @param id
     * @return
     */
    Result findArticleById(Long articleId);
1
2
3
4
5
6

ArticleServiceImpl

    @Override
    public Result findArticleById(Long articleId) {
        Article article = this.articleMapper.selectById(articleId);
        ArticleVo articleVo = copy(article, true, true, true, true);
        return Result.success(articleVo);
    }

    private List<ArticleVo> copyList(List<Article> records, boolean isTag, boolean isAuthor) {
        List<ArticleVo> articleVoList = new ArrayList<>();
        for (Article record : records) {
            articleVoList.add(copy(record, isTag, isAuthor, false, false));
        }
        return articleVoList;
    }

    private List<ArticleVo> copyList(List<Article> records, boolean isTag, boolean isAuthor, boolean isBody, boolean isCategory) {
        List<ArticleVo> articleVoList = new ArrayList<>();
        for (Article record : records) {
            articleVoList.add(copy(record, isTag, isAuthor, isBody, isCategory));
        }
        return articleVoList;
    }

    @Autowired
    private CategoryService categoryService;
	//修改
    private ArticleVo copy(Article article, boolean isTag, boolean isAuthor, boolean isBody, boolean isCategory) {
        ArticleVo articleVo = new ArticleVo();
        articleVo.setId(String.valueOf(article.getId()));
        //使用spring给我们提供的工具类进行相同属性相同类型的拷贝
        BeanUtils.copyProperties(article, articleVo);
        //不相同/不同类型的手动赋值
        articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
        if (isTag) {
            Long articleId = article.getId();
            articleVo.setTags(tagService.findTagsByArticleId(articleId));
        }
        if (isAuthor) {
            Long authorId = article.getAuthorId();
            articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
        }
        if (isBody) {
            ArticleBodyVo articleBody = findArticleBodyById(article.getBodyId());
            articleVo.setBody(articleBody);
        }
        if (isCategory) {
            CategoryVo categoryVo = categoryService.findCategoryById(article.getCategoryId());
            articleVo.setCategory(categoryVo);
        }
        return articleVo;
    }

    @Autowired
    private ArticleBodyMapper articleBodyMapper;

    private ArticleBodyVo findArticleBodyById(Long bodyId) {
        ArticleBody articleBody = articleBodyMapper.selectById(bodyId);
        ArticleBodyVo articleBodyVo = new ArticleBodyVo();
        articleBodyVo.setContent(articleBody.getContent());
        return articleBodyVo;
    }
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

CategoryService

public interface CategoryService {
    CategoryVo findCategoryById(Long categoryId);
}
1
2
3

CategoryServiceImpl

@Service
public class CategoryServiceImpl implements CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Override
    public CategoryVo findCategoryById(Long categoryId) {
        Category category = categoryMapper.selectById(categoryId);
        CategoryVo categoryVo = new CategoryVo();
        BeanUtils.copyProperties(category, categoryVo);
        return categoryVo;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Dao

ArticleBodyMapper

public interface ArticleBodyMapper extends BaseMapper<ArticleBody> {
}
1
2

CategoryMapper

public interface CategoryMapper extends BaseMapper<Category> {
}
1
2
编辑 (opens new window)
上次更新: 2023/12/06, 01:31:48
工程搭建及首页功能
字节码篇

← 工程搭建及首页功能 字节码篇→

最近更新
01
k8s
06-06
02
进程与线程
03-04
03
计算机操作系统概述
02-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Iekr | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式