文章目录
- 前言
- 一、service层
- BookServiceImpl.saveBook()
- BookBO
- 二、web层
- BookAdminController
- BookVO
- 最后
前言
在做了这么多架构铺垫之后,一位订阅同学非常期待我能更新主线API,我觉得他的想法非常合理,所以今天就来安排~~~
我主要考虑的是:首先输出主线API,是能让你先鸟瞰全貌,更容易发现设计上存在的问题,然后我再从架构设计上解决这些问题,那么你就能更清楚架构上为什么这么设计!自然水到渠成!
先抛出问题,本文主要引出的痛点
是:
1. 校验逻辑不通过时,如何更优雅的处理?
2. 校验是否是管理员,如何通用的实现?
OK,我在【4.2 图书借阅系统数据库设计】中有对需求和数据库设计的详细说明,本文不再赘述!对于图书管理
模块,我主要拆分为以下4个API:
- 图书录入和修改API
本文实现
包含字段:图书编号、图书名称、图书类型、作者、图书简介、图书封面、出版社、出版时间
注意: 需要验证图书编号不能重复
说明:之所以录入和修改合并为一个API,是因为修改与录入字段一致!仅通过id是否为空区分是录入还是修改。 - 图片上传API
7.2实现
- 图书列表API
7.3实现
- 图书详情API
7.4实现
以上API均是由管理员admin在后台系统操作,所以都需要验证是否为管理员身份!
对于图片录入和修改API,其实我们在【2-2. SpringBoot API开发详解 】曾定义过,不过定义的比较简单,没有考虑需求细节,所以接下来让我们来完善它!
一、service层
需要增加验证图书编号不能重复
的逻辑!
BookServiceImpl.saveBook()
这里使用在 5.6 Mybatis代码生成器Mybatis Generator (MBG)实战详解 学习过的mbg example
为例,实现使用Mybatis从Mysql查询图书编号是否存在的代码:
private boolean exists(String bookNo, Integer excludeId) {
BookExample example = new BookExample();
BookExample.Criteria criteria = example.createCriteria().andBookNoEqualTo(bookNo);
if (excludeId != null) {
// 排除
criteria.andIdNotEqualTo(excludeId);
}
return bookMapper.countByExample(example) > 0;
}
代码很简单,Criteria 相当于
where
条件,其中EqualTo代表=
,NotEqualTo代表<>
bookMapper.countByExample(example) 最终的查询sql如下:
- excludeId = null :
select count(*) from book where book_no = 'ts_00001'
- excludeId !=null :
select count(*) from book where book_no = 'ts_00001' and id<>1
OK,当我们判断图书编号已经存在,这时应该中断执行后面的逻辑,没问题吧?
但是,接下来问题来了,对于中断,你会如何处理呢?
我猜有的同学可能会说,直接返回-1或指定的负数就代表图书编号已存在!其实这种设计是脆弱的,我也经常会看到新手这么做!但我不推荐
!
因为这会使返回值与校验码耦合在一起
,一个API可能会有多个校验逻辑,不同的API校验逻辑又各不相同,所以只定义一个-1很难达到通用的效果!即使你把-1,-2,-3,-4,-5都定义了,可能还是不够!
而我们能总结出来的是这都属于校验逻辑
,所以我这里暂时把方法的返回值修改为 TgResult<Integer>
来简单处理这个问题,所以也请你思考,你是否还能想到其它更好的方法?欢迎你在评论区留言,我会在后面的文章(7.6)从架构上来解决这个问题!
完整的代码,返回的是新增或修改成功的id。我想你应该可以看的懂,所以不多解释!
@Override
public TgResult<Integer> saveBook(BookBO bookBO) {
// 验证图书编号不能重复
if (exists(bookBO.getBookNo(), bookBO.getId())) {
return TgResult.fail("400", "图书编号已存在!");
}
// 保存图书的主逻辑
Book book = new Book();
BeanUtils.copyProperties(bookBO, book);
if (bookBO.getId() == null) {
book.setGmtCreate(new Date());
book.setGmtModified(new Date());
book.setCreateUserId(AuthContextInfo.getAuthInfo().loginUserId());
bookMapper.insert(book);
return TgResult.ok(book.getId());
} else {
book.setGmtModified(new Date());
book.setModifyUserId(AuthContextInfo.getAuthInfo().loginUserId());
int rows = bookMapper.updateByPrimaryKeySelective(book);
return TgResult.ok(rows > 0 ? book.getId() : null);
}
}
这里的AuthContextInfo.getAuthInfo().loginUserId()是我新加的:
public Integer loginUserId() {
return Integer.valueOf(userId);
}
BookBO
我们前面创建过的bo对象,主要根据需求做一些字段上的小调整!代码如下:
package org.tg.book.service.bo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BookBO implements Serializable {
private Integer id;
private String bookNo;
private String bookName;
private Integer bookType;
private String author;
private String description;
private String publisher;
private Date publishDate;
private String coverImage;
}
二、web层
BookAdminController
这是我们之前定义过的接口,做一些小的调整,代码如下:
@PostMapping("/book")
public TgResult<Integer> saveBook(@RequestBody BookVO bookVO) {
BookBO bookBO = bookVO.toBookBO();
return bookService.saveBook(bookBO);
}
BookVO
也是我们前面创建的vo对象,现根据需求做一些字段上的小调整:
package org.tg.book.web.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import org.tg.book.service.bo.BookBO;
import java.io.Serializable;
import java.util.Date;
@Data
public class BookVO implements Serializable {
private Integer id;
private String bookNo;
private String bookName;
private Integer bookType;
private String author;
private String description;
private String publisher;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date publishDate;
private String coverImage;
public BookBO toBookBO() {
BookBO bookBO = new BookBO();
BeanUtils.copyProperties(this, bookBO);
return bookBO;
}
}
Git提交
最后
看到这,可能有的同学会质疑:你没有校验是否是管理员!
没错,由于这是通用逻辑,所以你认为如何实现比较好?我会在后面单写一篇文章来实现(7.5
),敬请期待!
最后,想要看更多实战好文章,还是给大家推荐我的实战专栏–>《基于SpringBoot+SpringCloud+Vue前后端分离项目实战》,由我和 前端狗哥 合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!
具体的优势、规划、技术选型都可以在《开篇》试读!
博主保证会用心持续高质量输出文章哦!
订阅专栏后也可以添加博主的微信,博主会为每一位用户进行针对性指导!
另外,别忘了关注我:天罡gg ,发布新文不容易错过: https://blog.csdn.net/scm_2008
老规矩,请投票给我反馈,谢谢大家的支持!