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

news2024/12/26 1:00:04

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<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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/618138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库期末复习(10)数据库规范化理论

函数依赖(概念):FD 范式分解(评估准则): 模式分解(工具): 函数依赖 如何衡量一个数据库好不好:准确 高效如果一个数据库设计的不好的话的&#xff0c;会带来哪些问题 删除异常 数据冗余为什么会导致出现上方的问题:数据依赖数据依赖的分类:完全依赖&#xff0c;部分依赖&am…

OpenCV实战(26)——视频序列处理

OpenCV实战&#xff08;26&#xff09;——视频序列处理 0. 前言1. 读取视频序列2. 处理视频帧2.1 视频处理2.2 自定义视频处理类 VideoProcessor2.3 处理一系列图像2.4 使用帧处理器类 3. 存储视频序列3.1 存储视频文件3.2 修改 VideoProcessor 类3.3 编解码器四字符编码 4. 完…

第九章:子查询

第九章&#xff1a;子查询 9.1&#xff1a;子查询的基本使用 子查询的基本语法结构 SELECT .... FROM .... WHERE expr operator (SELECT ...FROM ...WHERE ...);子查询(内查询)在主查询之前一次执行完成。子查询的结果被主查询(外查询)使用。注意事项 子查询要包含在括号内。…

【JavaSE】Java(五十五):核心要点总结

文章目录 1. 为什么不允许静态方法访问非静态变量2. Java的内存模型3. 在Java中什么时候用重载什么时候用重写4. 举例说明什么情况下更倾向于用抽象类而不是接口5. 实例化对象有哪几种方式 1. 为什么不允许静态方法访问非静态变量 在Java中&#xff0c;静态方法属于类级别的方法…

【HTML】第 4 节 - 列表标签

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、列表 3、无序列表 4、有序列表 5、定义列表 6、总结 1、缘起 微信小程序的列表标签是一种用于展示多个数据项的…

量子 AI,是融合还是颠覆?

光子盒研究院 前言&#xff1a;如今&#xff0c;量子技术早已走出实验室、广泛赋能电力、化学、医学等各个领域&#xff1b;创新赛道上&#xff0c;加速奔跑的量子产业&#xff0c;将带来无限可能。现在&#xff0c;光子盒特开启「量子」专栏&#xff0c;一一解读量子技术将为下…

chatgpt赋能python:Python安装教程:从下载到配置

Python安装教程&#xff1a;从下载到配置 Python作为一门高级编程语言&#xff0c;越来越受到开发人员的欢迎。Python的灵活性和易用性&#xff0c;让许多人选择Python作为他们的程序语言。本文将详细介绍Python安装教程&#xff0c;帮助初学者轻松入门。 1. 下载Python安装包…

【JavaSE】Java(五十四):核心要点总结

文章目录 1. try-catch-finally中 如果 catch 中 return 了&#xff0c;finally 还会执行吗?2. 常见的异常类有哪些3. hashcode 是什么 &#xff0c;有什么作用4. java中操作字符串有哪些类&#xff0c;他们之间有什么区别5. Java 中有哪些引用类型 1. try-catch-finally中 如…

$2$驱动模块

目录 1.驱动模块&#xff08;驱动程序的框架&#xff09; 2.内核中的打印函数&#xff08;编写第一个驱动程序&#xff09; Source Insight 使用&#xff1a; 打印函数编写 分析 3.驱动的多文件编译 4.模块传递参数 安装好驱动之后如何传参&#xff1f; 多驱动之间调用&…

智能照明控制系统在现代建筑工程中的应用 安科瑞 许敏

摘要&#xff1a; 文章分析了在现代建筑工程中智能照明控制系统所具有的优越性&#xff0c;并对如何解决该技术在实际应用中遇到的问题提出了看法与建议。 关键词&#xff1a;智能照明 控制系统 应用节能 引言 随着人们的物质和精神生活水平不断提高&#xff0c;对生活的追求…

MMC整流器Matlab仿真模型子模块个数N=18(含技术文档)

资源地址&#xff1a; MMC整流器Matlab仿真模型子模块个数N&#xff1d;18&#xff08;含技术文档&#xff09;资源-CSDN文库 模型介绍&#xff1a; 1.MMC工作在整流侧&#xff0c;子模块个数N&#xff1d;18&#xff0c;直流侧电压Udc&#xff1d;25.2kV&#xff0c;交流侧…

算法设计与分析期末复习(二)

动态规划 基本思想&#xff1a;把求解的问题分成许多阶段或多个子问题&#xff0c;然后按顺序求解各个子问题。**前一个子问题的解为后一个子问题的求解提供了有用的信息。**在求解任何一子问题时&#xff0c;列出各种可能的局部解&#xff0c;通过决策保留那些有可能达到最优…

Linux面试题汇总

Linux面试题汇总 网络拓展Linux 概述什么是LinuxUnix和Linux有什么区别&#xff1f;什么是 Linux 内核&#xff1f;Linux的基本组件是什么&#xff1f;Linux 的体系结构BASH和DOS之间的基本区别是什么&#xff1f;Linux 开机启动过程&#xff1f;Linux系统缺省的运行级别&#…

javaScript蓝桥杯----外卖给好评

目录 一、介绍二、准备三、⽬标四、代码五、完成 一、介绍 外卖是现代⽣活中必备的⼀环。收到外卖后&#xff0c;各⼤平台软件常常会邀请⽤户在⼝味&#xff0c;配送速度等多个⽅⾯给与评分。在 element-ui 组件中&#xff0c;已经有相应的 Rate 组件&#xff0c;但是已有组件…

前端052_单点登录SSO_单点退出系统

单点退出系统 1、 需求分析2、EasyMock 添加退出系统模拟接口3、定义Api调用退出接口4、定义 Vuex 退出行为1、 需求分析 所有应用系统退出,全部发送请求到当前认证中心进行处理,发送请求后台删除用户登录数据,并将 cookie 中的用户数据清除。 2、EasyMock 添加退出系统模拟…

大数据分析案例-基于LightGBM算法构建银行客户流失预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

第四章:运算符

第四章&#xff1a;运算符 4.1&#xff1a;算术运算符 ​ 算术运算符主要用于数学运算&#xff0c;其可以连接运算符前后的两个数值或表达值&#xff0c;对数值或表达式进行加()、减(-)、乘(*)、除(/)、取模(%)运算。 运算符名称作用示例加法运算符计算两个值或表达式的和SE…

chatgpt赋能python:Python如何遍历文件:一篇完整的指南

Python如何遍历文件: 一篇完整的指南 在进行文件操作时&#xff0c;遍历文件是相当普遍的需求。Python中提供了多种方法来遍历文件夹和文件&#xff0c;包括os模块&#xff0c;glob模块和os.walk方法。这篇文章将会介绍这些方法及其应用。 什么是遍历文件&#xff1f; 遍历文…

使用 ConstraintLayout

ConstraintLayout解析 1.前言2.了解ConstraintLayout3.基本用法3.1 看一个布局3.2再看一个布局 1.前言 你是不是一直不敢用ConstraintLayout&#xff0c;是以为属性太多太复杂&#xff1f;你心理上的惰性&#xff0c;畏惧它。它其实很好用很强大&#xff0c;如果要用就需要一个…

Day_40关于图的总结

一. 实际问题的抽象与建模 如果我们需要研究一个实际问题&#xff0c;首先第一步就是对这个实际问题进行抽象&#xff0c;抽象是从众多的事物中抽取出共同的、本质性的特征&#xff0c;而舍弃其非本质的特征的过程。具体地说&#xff0c;抽象就是人们在实践的基础上&#xff0c…