7.1「实战」图书录入和修改API --如何优雅处理校验逻辑?

CSDN成就一亿技术人

文章目录

  • 前言
  • 一、service层
    • BookServiceImpl.saveBook()
    • BookBO
  • 二、web层
    • BookAdminController
    • BookVO
  • 最后


前言

在做了这么多架构铺垫之后,一位订阅同学非常期待我能更新主线API,我觉得他的想法非常合理,所以今天就来安排~~~
我主要考虑的是:首先输出主线API,是能让你先鸟瞰全貌,更容易发现设计上存在的问题,然后我再从架构设计上解决这些问题,那么你就能更清楚架构上为什么这么设计!自然水到渠成!

先抛出问题,本文主要引出的痛点是:
1. 校验逻辑不通过时,如何更优雅的处理?
2. 校验是否是管理员,如何通用的实现?

OK,我在【4.2 图书借阅系统数据库设计】中有对需求和数据库设计的详细说明,本文不再赘述!对于图书管理模块,我主要拆分为以下4个API:

  1. 图书录入和修改API 本文实现
    包含字段:图书编号、图书名称、图书类型、作者、图书简介、图书封面、出版社、出版时间
    注意: 需要验证图书编号不能重复
    说明:之所以录入和修改合并为一个API,是因为修改与录入字段一致!仅通过id是否为空区分是录入还是修改。
  2. 图片上传API 7.2实现
  3. 图书列表API 7.3实现
  4. 图书详情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来简单处理这个问题,所以也请你思考,你是否还能想到其它更好的方法?欢迎你在评论区留言,我会在后面的文章(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.1「实战」图书录入和修改API --如何优雅处理校验逻辑?_第1张图片


最后

看到这,可能有的同学会质疑:你没有校验是否是管理员! 没错,由于这是通用逻辑,所以你认为如何实现比较好?我会在后面单写一篇文章来实现(7.5),敬请期待!

最后,想要看更多实战好文章,还是给大家推荐我的实战专栏–>《基于SpringBoot+SpringCloud+Vue前后端分离项目实战》,由我和 前端狗哥 合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!

具体的优势、规划、技术选型都可以在《开篇》试读!

博主保证会用心持续高质量输出文章哦!

订阅专栏后也可以添加博主的微信,博主会为每一位用户进行针对性指导!

另外,别忘了关注我:天罡gg ,发布新文不容易错过: https://blog.csdn.net/scm_2008

老规矩,请投票给我反馈,谢谢大家的支持!

你可能感兴趣的:(mybatis,java,开发语言,springboot,实战API)