登录注册
# 登录注册
# 登录
# 接口说明
接口 url:/login
请求方式:POST
请求参数:
| 参数名称 | 参数类型 | 说明 |
|---|---|---|
| account | string | 账号 |
| password | string | 密码 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": "token"
}
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;
}
}
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
2
LoginController
@RestController
@RequestMapping("login")
public class LoginController {
@Autowired
private LoginService loginService;
@PostMapping
public Result login(@RequestBody LoginParam loginParam) {
return loginService.login(loginParam);
}
}
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;
}
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;
}
}
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;
}
2
3
4
5
6
7
8
9
# Service
LoginService
public interface LoginService {
/**
* 登录功能
* @param loginParam
* @return
*/
Result login(LoginParam loginParam);
}
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);
}
}
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);
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);
}
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"
}
}
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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Service
SysUserService
/**
* 根据token查询用户信息
* @param token
* @return
*/
Result findUserByToken(String token);
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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LoginService
SysUser checkToken(String token);
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;
}
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;
}
2
3
4
5
6
7
8
9
10
# 退出登录
# 接口说明
接口 url:/logout
请求方式:GET
请求参数:
| 参数名称 | 参数类型 | 说明 |
|---|---|---|
| Authorization | string | 头部信息 (TOKEN) |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": null
}
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);
}
}
2
3
4
5
6
7
8
9
10
11
12
# Service
LoginService
/**
* 退出登录
* @param token
* @return
*/
Result logout(String token);
2
3
4
5
6
7
LoginServiceImpl
@Override
public Result logout(String token) {
redisTemplate.delete("TOKEN_" + token);
return Result.success(null);
}
2
3
4
5
# 注册
# 接口说明
接口 url:/register
请求方式:POST
请求参数:
| 参数名称 | 参数类型 | 说明 |
|---|---|---|
| account | string | 账号 |
| password | string | 密码 |
| nickname | string | 昵称 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": "token"
}
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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Service
LoginService
/**
* 注册功能
* @param loginParam
* @return
*/
Result register(LoginParam loginParam);
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);
}
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);
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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 事务
当注册时发生异常我们需要回滚,我们在 LoginServiceImpl 类上加本地事务注解,当然 一般建议加在 接口上,通用一些。
@Service
@Transactional
public class LoginServiceImpl implements LoginService {
}
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;
}
}
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");
}
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();
}
}
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();
}
}
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();
# ThreadLocal 内存泄漏

实线代表强引用,虚线代表弱引用
每一个 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"
}
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;
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;
2
3
4
5
6
7
# POJO
ArticleBody
@Data
public class ArticleBody {
private Long id;
private String content;
private String contentHtml;
private Long articleId;
}
2
3
4
5
6
7
8
Category
@Data
public class Category {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
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);
}
2
3
4
5
6
7
# Vo
CategoryVo
@Data
public class CategoryVo {
private Long id;
private String avatar;
private String categoryName;
}
2
3
4
5
6
7
8
9
ArticleBodyVo
@Data
public class ArticleBodyVo {
private String content;
}
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;
}
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);
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;
}
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);
}
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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Dao
ArticleBodyMapper
public interface ArticleBodyMapper extends BaseMapper<ArticleBody> {
}
2
CategoryMapper
public interface CategoryMapper extends BaseMapper<Category> {
}
2