【图书管理系统】全栈开发图书管理系统获取图书列表接口(后端:计算图书页数、查询当前页展示的书籍)

news2025/4/13 7:44:46

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


图书列表


实现服务器代码(计算图书总数量+查询当前页需要展示的书籍)


后端响应时,需要响应给前端的数据

  • records:第 pageNum 页要展示的图书有哪些(存储到List集合中)
  • total:计算一共有多少本书(用于告诉前端显示多少页)

image-20250408224403942

我们创建一个类 ResponseResult ,用于存放后端计算的 records,total,并且返回前端;

围绕着这两个功能,来完善我们的服务端代码;


控制层 BookController


image-20250408212938351


为了可复用性,我们引入泛型(修改一个类名):

image-20250408213834468


写好框架之后,我们在 Controller 层先调用这个接口:

image-20250408213906136


对于接口参数,如果参数个数比较多,还是建议直接以对象作为参数:

image-20250408214134584


我们给这两个属性先加上一个默认值:

image-20250408214355246


数据层:BookInfoMapper


查询第1页的SQL语句

select * from book_info where `status` != 0 limit 0,10;

image-20250408211303566


查询第2页的SQL语句

select * from book_info where `status` != 0 limit 10,10;

image-20250408211433468


查询第3页的SQL语句

select * from book_info where `status` != 0 limit 20,10;

image-20250408211510253

观察以上SQL语句,发现:开始索引(offset)一直在改变,每页显示条数(limit)是固定的

select * from book_info where status != 0 limit `offset`, `limit`;

开始索引的计算公式:开始索引 = (当前页码 - 1) * 每页显示条数


接下来,我们实现**对列表翻页时,查询第 PageNum 页的图书列表** 的功能:

image-20250408215519228


我们还需要计算出 offset 的值,不妨在 PageRequest 类中再增加一个属性:

image-20250408215859573


并且,我们可以直接在这个类中重写 offset 的 getter() 方法,也就是在 PageRequest 类中,直接计算出 offset 的值,并且可以在其他类获取到这个值:

image-20250408220230778


image-20250408220535082


image-20250408220639529


在 PageRequest 中并没有 limt 属性,所以我们修改一下 SQL 参数,避免出现 MyBatis 反射异常

image-20250409093749258

通过以上操作,我们就实现了第一个功能:第 pageNum 页要展示的图书有哪些


接下来,我们实现第二个功能:计算一共有多少本书

插入数据后,我们使用聚合函数 count 计算一下数据库中有多少条记录:

image-20250408210657988


接下来,我们回忆 BookInfo 中 status 属性的定义(0 - 无效,1 - 可借阅,2 - 不可借阅),因此,我们要先查询有效的书籍(status != 0):

image-20250408211117125

我们这里有26条数据,如果一页展示十条记录,那么26条记录,就需要三页;


image-20250408222912122


业务层 BookService


image-20250408223726591


计算出书的数量和当前页要展示的书后,接下来,识别展示图书的 status,转换为中文说明 statusCN:

image-20250408224152991


处理好 statusCN 后,我们根据方法返回类型 ResponseResult<BookInfo> ,设置好返回的书籍数量和当前页展示的书籍列表:

image-20250408224540754


image-20250408224914348


引入@AllArgsConstructor、 @NoArgsConstructor简化代码


这篇博客的5(2)有详细的 lombok 及相关注解的使用说明,包含 @AllArgsConstructor、 @NoArgsConstructor 的使用说明

image-20250408225249103


image-20250408225410291


引入枚举类 Enums 简化代码


这样的写法其实是有点啰嗦的,status 的状态只有(0,1,2)三种,因此,我们可以使用枚举类简化一下上面的代码:

image-20250409085625718


image-20250409090330756


image-20250409090753624


通过上面的封装和注解的巧用,我们的 BookService 代码变得更加精简了:

image-20250409091308254


总结:

需求/问题描述解决方案技术实现说明
翻页信息需要返回数据总数列表需要执行两次SQL查询1. 第一次查询总数SELECT COUNT(*)
2. 第二次查询分页数据LIMIT语句)
图书状态数据库status 字段映射使用枚举类处理状态码映射关系定义枚举类状态码(如0,1,2)文字描述(如"可借阅",“已借出”)绑定
状态码变动导致多处代码修改通过枚举类集中管理状态码修改时只需调整枚举类,无需全局搜索替换代码

image-20250409092430335


测试接口


启动服务,访问后端程序:


http://127.0.0.1:9090/book/getListByPage 返回1-10条记录(按id降序)

image-20250409093945645


http://127.0.0.1:9090/book/getListByPage?currentPage=2 返回11-20条记录

image-20250409094045394


后端思维导图梳理


在这里插入图片描述


实现客户端代码


在这里插入图片描述


ResponseResult 类中添加 private PageRequest pageRequest 属性的主要目的是实现分页信息的完整闭环传递,具体作用包括:

在这里插入图片描述

  1. 前端状态保持
    • 让前端知道当前返回的数据是依据哪些分页参数查询的
    • 避免前端在接收数据后丢失原始的分页请求参数

  1. 分页上下文传递
    • 当前页码(currentPage)
    • 每页条数(pageSize)
    • 从第几本书开始展示(offset)

在这里插入图片描述


  1. 扩展性考虑
    • 方便后续添加排序字段等扩展参数
    • 为前端提供生成分页控件所需的完整信息

在这里插入图片描述


在这里插入图片描述

onPageChange:回调函数,当换页时触发(包括初始化第一页的时候),会传入两个参数:

  • 1、"目标页"的页码,Number类型
  • 2、触发类型,可能的值:“init”(初始化),“change”(点击分页)

在这里插入图片描述


测试接口


ctrl+s 保存前端代码,重新允许程序,访问登录页面 http://127.0.0.1:9090/login.html

在这里插入图片描述


登录后,跳转到图书列表第一页:

在这里插入图片描述


点击页码,页面信息得到正确的处理

image-20250408124057586


按 F12 打开控制台,查看报错信息:

在这里插入图片描述


在这里插入图片描述


保存代码,重新运行程序,打开页面:

在这里插入图片描述


翻页试试效果:

在这里插入图片描述


完整服务器代码


PageRequest

package com.bit.book.model;

import lombok.Data;


@Data
public class PageRequest {
    private Integer currentPage = 1;
    private Integer pageSize = 10;
    private Integer offset;

    public Integer getOffset() {
        return (currentPage-1)*pageSize;
    }
}


ResponseResult

package com.bit.book.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ResponseResult<T> {
    private Integer total;    // 所查询到的数据列表(存储到List集合中)
    private List<T> records;  // 总记录数(用于告诉前端显示多少页)
    private PageRequest pageRequest;

}


BookController

package com.bit.book.Controller;

import com.bit.book.model.BookInfo;
import com.bit.book.model.PageRequest;
import com.bit.book.model.ResponseResult;
import com.bit.book.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/book")
@Slf4j
public class BookController {
    @Autowired
    private BookService bookService;
    
    // 返回页数接口
    @RequestMapping("/getListByPage")
    public ResponseResult<BookInfo> getListByPage(PageRequest pageRequest){
        // 参数校验, 此处涉及复杂的校验逻辑,由公司负责安全的团队负责,这里就省略了
        // 接收前端发送的请求,调用底层处理请求
        ResponseResult<BookInfo> listByPage = bookService.getListByPage(pageRequest);
        // 接收 service 处理好的响应,并返回给前端
        return listByPage;
    }
}


BookService

package com.bit.book.service;

import com.bit.book.enums.BookStatusEnum;
import com.bit.book.mapper.BookMapper;
import com.bit.book.model.BookInfo;
import com.bit.book.model.PageRequest;
import com.bit.book.model.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


@Service
public class BookService {
    @Autowired
    private BookMapper bookMapper;

    public ResponseResult<BookInfo> getListByPage(PageRequest pageRequest){
        // 1. 获取总的图书数
        Integer count = bookMapper.count();

        // 2. 获取当前页需要展示的图书有哪些
        List<BookInfo> bookInfos = bookMapper.selectBooksByPage(pageRequest);

        // 3. 识别展示图书的 status, 转换为中文说明 statusCN
        for (BookInfo bookInfo : bookInfos) {
            bookInfo.setStatusCN(BookStatusEnum.getStatusByCode(bookInfo.getStatus()).getDesc());
        }
        
        return new ResponseResult<>(count,bookInfos,pageRequest);
    }
}

BookStatusEnum

package com.bit.book.enums;

public enum BookStatusEnum {
    DELETED(0, "该图书不存在"),
    NORMAL(1,"允许借阅"),
    FORBIDDEN(2,"不允许借阅");

    private Integer code;
    private String desc;

    public static BookStatusEnum getStatusByCode(Integer code){
        switch (code){
            case 0: return BookStatusEnum.DELETED;
            case 1: return BookStatusEnum.NORMAL;
            case 2: return  BookStatusEnum.FORBIDDEN;
            default:
                return null;
        }
    }


    BookStatusEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public Integer getCode() {
        return code;
    }

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

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}


BookMapper

package com.bit.book.mapper;

import com.bit.book.model.BookInfo;
import com.bit.book.model.PageRequest;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface BookMapper {

    @Insert("insert into book_info " +
            "(book_name, author, count, price, publish, `status`) " +
            "values (#{bookName}, #{author}, #{count}, #{price}, #{publish}, #{status})")
    Integer addBook(BookInfo bookInfo);


    @Select("select * from book_info " +
            "where `status` !=0 " +
            "limit #{offset}, #{pageSize}")
    List<BookInfo> selectBooksByPage(PageRequest pageRequest);

    @Select("select count(1) " +
            "from book_info " +
            "where `status` != 0")
    Integer count();
}


在这里插入图片描述

在这里插入图片描述

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

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

相关文章

正则表达式补充——python

简介 本章是对前面正则表达式的补充。 一、复杂的查找替换等任务 content 张三是脑卒中病 李四&#xff0c;是高血脂 苏齐&#xff0c;是肺结核病 六六&#xff0c;是血血血血import re p re.compile(r...病) for one in p.findall(content):print(one) 运行结果&#xf…

[ctfshow web入门] web7

信息收集 题目提示&#xff1a;版本控制很重要&#xff0c;但不要部署到生产环境更重要。 那么很有可能&#xff0c;版本控制相关的信息被部署到环境了&#xff0c;比如比如version.txt记录了一些相关配件的版本&#xff0c;git版本管理工具中的.git文件夹未删除 信息收集就是…

DeepSeek-V3 API:开启下一代AI应用开发的新篇章

引言 在人工智能技术日新月异的今天&#xff0c;大型语言模型(LLM)正以前所未有的速度改变着我们与技术互动的方式。DeepSeek-V3作为国内领先的大语言模型之一&#xff0c;其API的开放为开发者提供了强大的AI能力集成方案。 DeepSeek-V3 API的核心优势 1.强大的语言理解与生…

go语言应该如何学习

以下是学习Go语言的高效路径及关键技巧&#xff0c;结合多个优质来源整理而成&#xff0c;适合不同基础的学习者&#xff1a; 一、基础语法快速入门&#xff08;1-2周&#xff09; 1、环境搭建 下载安装Go SDK&#xff0c;配置GOPATH和GOROOT环境变量&#xff0c;推荐使用Go…

NO.84十六届蓝桥杯备战|动态规划-路径类DP|矩阵的最小路径和|迷雾森林|过河卒|方格取数(C++)

路径类dp是线性dp的⼀种&#xff0c;它是在⼀个nm的矩阵中设置⼀个⾏⾛规则&#xff0c;研究从起点⾛到终点的⽅案数、最⼩路径和或者最⼤路径和等等的问题 矩阵的最小路径和_牛客题霸_牛客网 状态表⽰&#xff1a; dp[i][j]表⽰&#xff1a;到达[i, j]位置处&#xff0c;最⼩…

React + TipTap 富文本编辑器 实现消息列表展示,类似Slack,Deepseek等对话框功能

经过几天折腾再折腾&#xff0c;弄出来了&#xff0c;弄出来了&#xff01;&#xff01;&#xff01; 消息展示 在位编辑功能。 两个tiptap实例1个用来展示 消息列表&#xff0c;一个用来在位编辑消息。 tiptap灵活富文本编辑器&#xff0c;拓展性太好了!!! !!! 关键点&#x…

博途 TIA Portal之1200做主站与汇川EASY的TCP通讯

前言,虽然已经做了几篇关于TCP通讯的文章,但是不同的PLC之间的配合可能不同,下面将演示这种差异。 关于汇川EASY做从站的配置请参见下方链接文章:汇川EASY系列之以太网通讯(套接字socket做从站)_汇川以太网tcp套接字fb块-CSDN博客 1、硬件准备: 1200PLC,汇川EASY320…

蓝桥杯速成刷题清单(上)

一、1.排序 - 蓝桥云课 &#xff08;快速排序&#xff09;算法代码&#xff1a; #include <bits/stdc.h> using namespace std; const int N 5e5 10; int a[N];int main() {int n;cin >> n;for (int i 0; i < n; i) {cin >> a[i];}sort(a, a n);for …

Go并发背后的双引擎:CSP通信模型与GMP调度|Go语言进阶(4)

为什么需要理解CSP与GMP&#xff1f; 当我们启动一个Go程序时&#xff0c;可能会创建成千上万个goroutine&#xff0c;它们是如何被调度到有限的CPU核心上的&#xff1f;为什么Go能够如此轻松地处理高并发场景&#xff1f;为什么有时候我们的并发程序会出现奇怪的性能瓶颈&…

Linux服务器——Samba服务器

简介 Samba 是一个开源的跨平台文件共享服务​​&#xff0c;允许 Linux/Unix 系统与 Windows 系统实现文件和打印机的共享与互操作。其核心协议为 ​​SMB/CIFS​​&#xff08;Server Message Block / Common Internet File System&#xff09;&#xff0c;是 Windows 网络中…

华为网路设备学习-17

目录 一、加密算法 二、验证算法 三、IPsec协议 1.IKE协议&#xff08;密钥交换协议&#xff09; ①‌ISAKMP&#xff08;Internet Security Association and Key Management Protocol&#xff09;互联网安全关联和密钥管理协议 ②安全关联&#xff08;SA&#xff09; ③…

机器学习12-集成学习-案例

参考 【数据挖掘】基于XGBoost的垃圾短信分类与预测 【分类】使用XGBoost算法对信用卡交易进行诈骗预测 银行卡电信诈骗危险预测(LightGBM版本) 【数据挖掘】基于XGBoost的垃圾短信分类与预测 基于XGBoost的垃圾短信分类与预测 我分享了一个项目给你《【数据挖掘】基于XG…

【数据库原理及安全实验】实验二 数据库的语句操作

目录 指导书原文 实操备注 指导书原文 【实验目的】 1) 掌握使用SQL语言进行数据操纵的方法。 【实验原理】 1) 面对三个关系表student&#xff0c;course&#xff0c;sc。利用SQL语句向表中插入数据&#xff08;insert&#xff09;&#xff0c;然后对数据进行delete&…

【BFT帝国】20250409更新PBFT总结

2411 2411 2411 Zhang G R, Pan F, Mao Y H, et al. Reaching Consensus in the Byzantine Empire: A Comprehensive Review of BFT Consensus Algorithms[J]. ACM COMPUTING SURVEYS, 2024,56(5).出版时间: MAY 2024 索引时间&#xff08;可被引用&#xff09;: 240412 被引:…

Linux-CentOS-7—— 配置静态IP地址

文章目录 CentOS-7——配置静态IP地址VMware workstation的三种网络模式配置静态IP地址1. 编辑虚拟网络2. 确定网络接口名称3. 切换到网卡所在的目录4. 编辑网卡配置文件5. 查看网卡文件信息6. 重启网络服务7. 测试能否通网8. 远程虚拟主机&#xff08;可选&#xff09; 其他补…

Jupyter Lab 无法启动 Kernel 问题排查与解决总结

&#x1f4c4; Jupyter Lab 无法启动 Kernel 问题排查与解决总结 一、问题概述 &#x1f6a8; 现象描述&#xff1a; 用户通过浏览器访问远程服务器的 Jupyter Lab 页面&#xff08;http://xx.xx.xx.xx:8891/lab&#xff09;后&#xff0c;.ipynb 文件可以打开&#xff0c;但无…

算法训练之位运算

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

C++设计模式+异常处理

#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory> #include <stdexcept> // 包含异常类using namespace std;// 该作业要求各位写一…

checkra1n越狱出现的USB error -10问题解决

使用checkra1n进行越狱是出现&#xff1a; 解决办法(使用命令行进行越狱)&#xff1a; 1. cd /Applications/checkra1n.app/Contents/MacOS 2. ./checkra1n -cv 3. 先进入恢复模式 a .可使用爱思助手 b. 或者长按home,出现关机的滑条&#xff0c;同时按住home和电源键&#…

golang-defer延迟机制

defer延迟机制 defer是什么 defer是go中一种延迟调用机制。 执行时机 defer后面的函数只有在当前函数执行完毕后才能执行。 执行顺序 将延迟的语句按defer的逆序进行执行&#xff0c;也就是说先被defer的语句最后被执行&#xff0c;最后被defer的语句&#xff0c;最先被执…