当前位置: 首页 > news >正文

【实战】动态 SQL + 统一 Result + 登录校验:图书管理系统(下)

文章目录

  • 功能实现
    • (四) 修改图书
      • 1.约定前后端交互接口
      • 2.实现服务端代码
      • 3.实现客户端代码
    • (五) 删除图书
      • 1.约定前后端交互接口
      • 2. 实现客户端代码
    • (六) 批量删除
      • 1.约定前后端交互接口
      • 2.实现服务器代码
    • (七) 强制登录
      • 实现服务端代码
      • 实现客户端代码

功能实现

(四) 修改图书

1.约定前后端交互接口

[请求]
/book/queryBookById?bookId=25[参数]
无[响应]
{"id": 25,"bookName": "图书21","author": "作者2","count": 999,"price": 222.00,"publish": "出版社1","status": 2,"statusCN": null,"createTime": "2023-09-04T04:01:27.000+00:00","updateTime": "2023-09-05T03:37:03.000+00:00"
}

根据图书ID,获取当前图书的信息

点击修改按钮,修改图书信息

[请求]
/book/updateBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8[参数]
id=1&bookName=图书1&author=作者1&count=23&price=36&publish=出版社1&status=1[响应]
""  //失败信息,成功时返回空字符串

我们约定,浏览器给服务器发送一个 /book/updateBook 这样的 HTTP 请求, form表单的形式来提交数据

服务器返回处理结果,返回""表示添加图书成功,否则,返回失败信息.

2.实现服务端代码

  • BookController
 @RequestMapping("/queryBookById")public BookInfo queryBookById(Integer bookId){log.info("查询图书信息,bookId:" + bookId);return bookService.queryBookById(bookId);}@RequestMapping("/updateBook")public String updateBook(BookInfo bookInfo){log.info("修改图书,bookInfo: {}",bookInfo);try{bookService.updateBook(bookInfo);//成功return  "";}catch (Exception e){log.error("修改图书发生异常, e:",e);return "修改图书发生异常";}}
  • 业务层:

BookService

public void updateBook(BookInfo bookInfo){bookMapper.updateBook(bookInfo);}public Integer batchDelete(List<Integer> ids){return bookMapper.batchDelete(ids);}
  • 数据层:
@Select("select * from book_info where `status` != 0 and id = #{bookId}")BookInfo queryBookById(Integer bookId);
  • xml实现 更新

创建BookInfoMapper.xml文件

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chaoxin.book.mapper.BookMapper"><update id="updateBook">update book_info<set><if test="bookName!=null">book_name = #{bookName} ,</if><if test="author!=null">author = #{author} ,</if><if test="count!=null">count = #{count} ,</if><if test="price!=null">price = #{price} ,</if><if test="publish!=null">publish = #{publish} ,</if><if test="status!=null">status = #{status}</if></set>where id = #{id}</update><delete id="batchDelete">update book_info set status=0 where id in<foreach collection="list" item="id" open="(" close=")" separator=",">#{id}</foreach></delete>
</mapper>

在这里插入图片描述

3.实现客户端代码

http://127.0.0.1:8080/book_update.html?bookId=25 (25为对应的图书ID)

finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>';

点击[修改]链接时,就会自动跳转到 http://127.0.0.1:8080/book_update.html?bookId=25 (25为对应的图书ID)
进入[修改图书]页面时,需要先从后端拿到当前图书的信息,显示在页面上

//进⼊[修改图书]⻚⾯时需要先从后端拿到当前图书的信息,显⽰在⻚⾯上getBookInfo();function getBookInfo(){$.ajax({type: "get",url: "/book/queryBookById" + location.search,success: function(result){//TODO 检验非常简陋var bookInfo = result.data;if(bookInfo != null){$("#bookId").val(bookInfo.id);$("#bookName").val(bookInfo.bookName);$("#bookAuthor").val(bookInfo.author);$("#bookStock").val(bookInfo.count);$("#bookPrice").val(bookInfo.price);$("#bookPublisher").val(bookInfo.publish);$("#bookStatus").val(bookInfo.status);}else{console.log(result);}},error: function(error){if(error.status == 401){//用户未登录location.href = "login.html";}}});}//修改图书function update() {$.ajax({type: "post",url: "/book/updateBook",data: $("#updateBook").serialize(),success: function(result){if(result == ""){location.href = "book_list.html"}else{console.log(result);alert("修改失败:" + result);}},error: function(error){console.log(error);}});// alert("更新成功");// location.href = "book_list.html"}

我们修改图书信息,是根据图书ID来修改的,所以需要前端传递的参数中,包含图书ID.
有两种方式:

  1. 获取url中参数的值(比较复杂,需要拆分url)
  2. 在form表单中,再增加一个隐藏输入框,存储图书ID,随 $("#updateBook").serialize() 一起提交到后端

我们采用第二种方式

在form表单中,添加隐藏输入框

<form id="updateBook"><input type="hidden" class="form-control" id="bookId" name="id"><div class="form-group"><label for="bookName">图书名称:</label><input type="text" class="form-control" id="bookName" name="bookName"></div><div class="form-group"><label for="bookAuthor">图书作者</label><input type="text" class="form-control" id="bookAuthor" name="author"/></div><div class="form-group"><label for="bookStock">图书库存</label><input type="text" class="form-control" id="bookStock" name="count"/></div><div class="form-group"><label for="bookPrice">图书定价:</label><input type="number" class="form-control" id="bookPrice" name="price"></div><div class="form-group"><label for="bookPublisher">出版社</label><input type="text" id="bookPublisher" class="form-control" name="publish"/></div><div class="form-group"><label for="bookStatus">图书状态</label><select class="custom-select" id="bookStatus" name="status"><option value="1" selected>可借阅</option><option value="2">不可借阅</option></select></div><div class="form-group" style="text-align: right"><button type="button" class="btn btn-info btn-lg" onclick="update()">确定</button><button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button></div></form>

hidden 类型的 <input> 元素
隐藏表单,用户不可见、不可改的数据,在用户提交表单时,这些数据会一并发送出
使用场景: 正被请求或编辑的内容的 ID. 这些隐藏的 input 元素在渲染完成的页面中完全不可见,而且没有方法可以使它重新变为可见.

页面加载时,给该hidden框赋值

$("#bookId").val(book.id);

在这里插入图片描述

(五) 删除图书

1.约定前后端交互接口

删除分为逻辑删除和物理删除

逻辑删除
逻辑删除也称为软删除、假删除、Soft Delete,即不真正删除数据,而在某行数据上增加类型is_deleted的删除标识,一般使用UPDATE语句

物理删除
物理删除也称为硬删除,从数据库表中删除某一行或某一集合数据,一般使用DELETE语句

删除图书的两种实现方式

逻辑删除

update book_info set status=0 where id = 1

物理删除

delete from book_info where id=25

数据是公司的重要财产,通常情况下,我们采用逻辑删除的方式,当然也可以采用[物理删除+归档]的方式

物理删除并归档

  1. 创建一个与原表差不多结构,记录删除时间,实现INSERT … SELECT即可 [SQL INSERT INTO SELECT 语句 | 菜鸟教程](SQL INSERT INTO SELECT 语句 | 菜鸟教程链接)
  2. 插入和删除操作,放在同一个事务中执行

物理删除+归档的方式实现有些复杂,咱们采用逻辑删除的方式
逻辑删除的话,依然是更新逻辑,我们可以直接使用修改图书的接口/book/updateBook

[请求]
/book/updateBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
id=1&status=0
[响应]
""  //失败信息,成功时返回空字符串

2. 实现客户端代码

function deleteBook(id) {var isDelete = confirm("确认删除?");if (isDelete) {$.ajax({type: "post",url: "/book/deleteBook?bookId=" + id,success: function (result) {if (result.code == "SUCCESS" && result.data == "") {location.href = "book_list.html?currentPage=1";} else {alert(result.errMsg);}}});}}

在这里插入图片描述

(六) 批量删除

批量删除,其实就是批量修改数据

1.约定前后端交互接口

/book/batchDeleteBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8[参数][响应]
""  //失败信息,成功时返回空字符串

点击[批量删除]按钮时,只需要把复选框选中的图书的ID,发送到后端即可
多个id,我们使用List的形式来传递参数

2.实现服务器代码

  • 控制层:

BookController

 @RequestMapping("/batchDelete")public Boolean batchDelete(@RequestParam List<Integer> ids) {log.info("批量删除图书, ids:{}", ids);try {bookService.batchDelete(ids);return true;} catch (Exception e) {log.error("批量删除图书失败, e:", e);return false;}}
  • 业务层:
 public Integer batchDelete(List<Integer> ids){return bookMapper.batchDelete(ids);}
  • 数据层:

批量删除需要用到动态SQL,我们这里都用xml来实现

BookInfoMapper.java

接口定义

void batchDeleteBook(List<Integer> ids);

xml接口实现

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chaoxin.book.mapper.BookMapper"><update id="updateBook">update book_info<set><if test="bookName!=null">book_name = #{bookName} ,</if><if test="author!=null">author = #{author} ,</if><if test="count!=null">count = #{count} ,</if><if test="price!=null">price = #{price} ,</if><if test="publish!=null">publish = #{publish} ,</if><if test="status!=null">status = #{status}</if></set>where id = #{id}</update><delete id="batchDelete">update book_info set status=0 where id in<foreach collection="list" item="id" open="(" close=")" separator=",">#{id}</foreach></delete><select id="queryBookListByPage" resultType="com.chaoxin.book.model.BookInfo"></select>
</mapper>

在这里插入图片描述
在这里插入图片描述

(七) 强制登录

虽然我们做了用户登录,但是我们发现,用户不登录,依然可以操作图书.

这是有极大风险的.所以我们需要进行强制登录.

如果用户未登录就访问图书列表或者添加图书等页面,强制跳转到登录页面.

实现思路分析
用户登录时,我们已经把登录用户的信息存储在了Session中.那就可以通过Session中的信息来判断用户是否登录.

  1. 如果Session中可以取到登录用户的信息,说明用户已经登录了,可以进行后续操作
  2. 如果Session中取不到登录用户的信息,说明用户未登录,则跳转到登录页面.

直接访问接口,无需登录就可以对书籍进行删除添加

我们需要在增加一个属性告知后端的状态以及后端出错的原因:

@Data
public class PageResult<T> {private int status;private String errorMessage;private int total;//所有记录数private List<T> records; // 当前页数据private PageRequest pageRequest;public PageResult(Integer total, PageRequest pageRequest, List<T> records) {this.total = total;this.pageRequest = pageRequest;this.records = records;}
}

后端数据所有的接口都要跟着修改,这样修改代码效率低,不放对所有后端返回的数据进行一个封装

@Data
public class Result<T> {private int status;private String errorMessage;private T data;
}

data为之前接口返回的数据
status为后端业务处理的状态码,也可以使用枚举来表示

public enum ResultStatus {SUCCESS(200),UNLOGIN(-1),FAIL(-2);private Integer code;ResultStatus(int code) {this.code = code;}public Integer getCode() {return code;}
}

修改 Result 添加常用方法

@Data
public class Result<T> {private ResultCodeEnum code;  //-1 未登录    200 正常  -2 出错private String errMsg;private T data;public static <T> Result success(T data){Result result = new Result();result.setCode(ResultCodeEnum.SUCCESS);result.setErrMsg("");result.setData(data);return result;}public static <T> Result fail(String errMsg){Result result = new Result();result.setCode(ResultCodeEnum.FAIL);result.setErrMsg(errMsg);result.setData(null);return result;}public static <T> Result fail(String errMsg, T data){Result result = new Result();result.setCode(ResultCodeEnum.FAIL);result.setErrMsg(errMsg);result.setData(data);return result;}public static <T> Result unlogin(){Result result = new Result();result.setCode(ResultCodeEnum.UNLOGIN);result.setErrMsg("用户未登录");return result;}
}

实现服务端代码

修改图书列表接口,进行登录校验

@RequestMapping("/getListByPage")
public Result getListByPage(PageRequest pageRequest, HttpSession session) {log.info("获取图书列表, pageRequest:{}", pageRequest);//判断用户是否登录if (session.getAttribute("session_user_key")==null){return Result.unlogin();}UserInfo userInfo = (UserInfo) session.getAttribute("session_user_key");if (userInfo==null || userInfo.getId()<0 ||"".equals(userInfo.getUserName())){return Result.unlogin();}//用户登录,返回图书列表PageResult<BookInfo> pageResult = bookService.getBookListByPage(pageRequest);return Result.success(pageResult);
}

问题:如果修改常量session的key,就需要修改所有使用到这个key的地方,出于高内聚低耦合的思想,我们把常量集中在一个类里
创建类:Constants

public class Constants {public static final String SESSION_USER_KEY = "session_user_key";
}

常量命名规则:
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要在意名字长度.
正例: MAX_STOCK_COUNT / CACHE_EXPIRED_TIME
反例: COUNT / TIME

实现客户端代码

由于后端接口发生变化,所以前端接口也需要进行调整
这也就是为什么前后端交互接口一旦定义好,尽量不要发生变化.
所以后端接口返回的数据类型一般不定义为基本类型,包装类型或者集合类等,而是定义为自定义对象.方便后续做扩展
修改图书列表的方法(下面代码为修改部分):

function getBookList() {//...success: function (result) {//真实前端代码需要分的更细一点,此处不做判断if (result == null || result.data == null) {location.href = "login.html";return;}//....var data = result.data;$("#pageContainer").jqPaginator({totalCounts: data.total, //总记录数pageSize: 10,    //每页的个数visiblePages: 5, //可视页数currentPage: data.pageRequest.currentPage,  //当前页码//....});}
}
http://www.dtcms.com/a/602579.html

相关文章:

  • 双缓冲机制:如何避免读写冲突
  • C语言在线编译器网站 | 高效便捷的编程学习平台
  • 你的技术搭子在这里!来openFuyao社区SIG与大咖一起组队
  • 台州自助建站系统做兼职上哪个网站
  • 旅行社应做哪些网站做家电维修网站
  • 网站加入购物车的代码网站视频链接怎么做
  • atsec完成Newland NPT的P2PE PA评估
  • 网站推广预算在线网页爬虫工具
  • 从0开始学区块链第15天——发送和接受ETH
  • 如何批量建站在本地做的网站怎么修改域名
  • 崇左网站搭建给网站开发APP
  • C语言的编译器 | 如何选择适合你的编译器
  • 上位机项目列表
  • 如何用电脑记事本做网站别墅装修设计图片大全 效果图
  • 销售怎么做英文网站建设优化
  • Mac编译C语言 | 学会在Mac上使用终端编译和运行C语言程序
  • Go语言编译器及其使用分析
  • 做建筑的网站产品宣传类网站设计
  • LMV321、LMV358:低功耗轨到轨输入输出 CMOS 运算放大器
  • 有什么网站是做兼职的做网站学习什么
  • 在Python中配置高度交互的数据可视化:Highcharts完全指南
  • git使用应用实战大全
  • 自己做网站可以盗图吗体育新闻最新消息今天
  • 建设促销网站前的市场分析广西建设工程造价管理协会网站
  • 濮阳做网站推广网站备案完成通知
  • IntelliJ IDEA导出WAR包全指南
  • 做网站用的主机音乐网站 模板
  • 顺的品牌网站设计价位长沙 网页制作
  • c语言和c 哪个做网站好seo推广要多少钱
  • 做网站这么做中国国家住房和城乡建设部网站