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

【基于SpringBoot的图书购买系统】操作Jedis对图书图书的增-删-改:从设计到实战的全栈开发指南

引言

在当今互联网应用开发中,缓存技术已成为提升系统性能和用户体验的关键组件。Redis作为一款高性能的键值存储数据库,以其丰富的数据结构、快速的读写能力和灵活的扩展性,被广泛应用于各类系统的缓存层设计。本文将围绕一个基于Redis的图书管理系统展开,详细介绍如何利用Redis实现图书信息的增删改查功能,并通过前后端分离的架构设计,构建一个完整的图书管理解决方案。

通过本案例,读者将深入理解Redis在实际项目中的应用场景,掌握RedisTemplate的配置与使用技巧,学习前后端交互的接口设计规范,并领略如何将Redis的高性能特性与业务逻辑完美结合。无论是初入职场的开发新人,还是希望拓展技术栈的资深工程师,都能从本文中获取实用的开发经验和技术 insights。

在这里插入图片描述

1. 前后端交互接口设计

在前后端分离的开发模式中,接口设计是连接前端展示与后端逻辑的桥梁。合理的接口设计不仅能提高开发效率,还能增强系统的可维护性和扩展性。本图书管理系统基于RESTful架构设计了一套简洁明了的接口规范,主要包含以下核心接口:

1.1 接口规范与设计原则

  • 接口命名:采用名词复数形式表示资源集合,如/special/bookInfos
  • 请求方法:遵循HTTP动词语义
    • GET:获取资源(如查询图书)
    • POST:创建资源(如添加图书)
    • PUT:更新资源(如修改图书)
    • DELETE:删除资源(如逻辑删除图书)
  • 参数传递
    • 简单查询参数通过URL query string传递
    • 复杂对象通过请求体(RequestBody)传递
  • 返回格式:统一使用JSON格式,包含状态码、状态信息和数据内容

1.2 核心接口列表

接口名称请求路径请求方法功能描述
添加图书/special/addBookInfoPOST向Redis中添加新的图书信息
根据ID查询图书/special/getBookInfoByIdGET根据图书ID获取详细信息
更新图书信息/special/updateSpecialBookPOST更新图书的全部信息
逻辑删除图书/special/updateBookInfoPOST修改图书状态为"无效"
批量删除图书/special/batchDeleteBookInfoByIdPOST批量修改多本图书的状态
分页查询图书列表/special/getSpecialListByPageGET按分页获取特价图书列表

1.3 接口交互示例

以"添加图书"接口为例,展示前后端数据交互流程:

前端请求(AJAX)

$.ajax({type: "post",url: "/special/addBookInfo",data: $("#addBook").serialize(), // 序列化表单数据success: function(result) {if (result == "") {location.href = "special_admin_list.html";alert("添加成功");} else {console.log(result);alert("添加失败:" + result);}},error: function(error) {console.log("请求失败:" + error);}
});

在这里插入图片描述

后端响应(Controller)

@RequestMapping("/addBookInfo")
public String addBookInfo(BookInfo bookInfo) {// 参数校验if (bookInfo == null || !StringUtils.hasLength(bookInfo.getBookName()) || !StringUtils.hasLength(bookInfo.getAuthor()) || !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getPrice() == null || bookInfo.getCount() == null || bookInfo.getStatus() == null) {return "输入的参数不合法,请检查后重新输入";}boolean b = true;try {b = specialDealAdminService.addBookInfo(bookInfo);} catch (Exception e) {log.error("添加图书发生报错e{}:" + e);return "添加图书失败,请联系管理员";}return b ? "" : "controller 添加图书失败";
}

这种接口设计模式实现了前后端的松耦合交互,前端只需关注UI展示和用户交互,后端专注于业务逻辑处理和数据操作,极大地提高了开发效率和系统的可维护性。

2. 整体代码逻辑架构

在深入分析各模块代码之前,我们需要先建立对整个系统架构的宏观认知。本图书管理系统采用了典型的三层架构设计,结合Redis的高性能特性,形成了一套完整的技术解决方案。

2.1 系统架构概述

系统整体架构可分为以下几个层次:

  1. 表示层(前端):负责用户界面展示和交互,采用HTML、CSS、JavaScript和jQuery技术栈
  2. 控制层(Controller):处理前端请求,协调服务层完成业务逻辑
  3. 服务层(Service):封装核心业务逻辑,操作Redis数据
  4. 数据访问层:通过RedisTemplate实现与Redis数据库的交互
  5. 模型层(Model):定义数据实体类,如BookInfo

2.2 核心业务流程

以"添加图书"功能为例,展示系统的整体逻辑流转:

  1. 前端交互:用户在添加页面填写图书信息,点击"确定"按钮
  2. 请求发送:前端通过AJAX将表单数据发送至/special/addBookInfo接口
  3. 参数校验:Controller层对请求参数进行合法性校验
  4. 业务处理:Service层执行添加图书的核心逻辑
    • 从Redis中获取现有图书ID,确定新图书的ID
    • 生成创建时间和更新时间
    • 将图书对象序列化为JSON格式存入Redis
  5. 结果返回:Service层返回操作结果,Controller层封装响应数据
  6. 前端反馈:根据响应结果,前端提示操作成功并跳转至列表页

2.3 Redis数据模型设计

在Redis中,图书数据采用以下存储策略:

  • 键命名规范:使用bookInfoId:{id}的格式作为图书对象的键,如bookInfoId:100001
  • 数据类型:采用String类型存储序列化后的JSON对象
  • ID生成策略:使用IdStatic工具类生成递增ID,确保唯一性
  • 状态标识:通过status字段标识图书状态(1-可借阅,2-不可借阅,0-无效,3-特价商品)

这种数据模型设计充分利用了Redis的键值存储特性,实现了快速的读写操作,同时通过合理的键命名和状态标识,为复杂查询和业务逻辑提供了支持。

3. Redis配置详解

Redis的正确配置是系统高性能运行的基础。在本系统中,我们通过Spring Data Redis提供的RedisTemplate实现了对Redis的操作,下面详细介绍配置过程和关键参数。

在这里插入图片描述

3.1 RedisTemplate配置类

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {// 创建redisTemplate对象RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 设置连接工厂redisTemplate.setConnectionFactory(connectionFactory);// 创建JSON序列化工具GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();// 设置Key的序列化(使用StringSerializer)redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setHashKeySerializer(RedisSerializer.string());// 设置Value的序列化(使用JSON序列化)redisTemplate.setValueSerializer(jsonRedisSerializer);redisTemplate.setHashValueSerializer(jsonRedisSerializer);return redisTemplate;}
}

3.2 配置参数解析

  1. 连接工厂(RedisConnectionFactory)

    • 由Spring自动注入,负责创建与Redis服务器的连接
    • 支持单机、哨兵、集群等多种部署模式
  2. 序列化配置

    • Key序列化:使用StringSerializer,确保键的可读性和唯一性
    • Value序列化:使用GenericJackson2JsonRedisSerializer,将Java对象序列化为JSON格式
    • 为什么选择JSON序列化?
      • 跨语言兼容性好
      • 可读性强,便于调试
      • 支持复杂对象结构
  3. 模板功能

    • RedisTemplate提供了丰富的操作方法,如opsForValue()opsForHash()
    • 支持事务、管道等高级功能,提升批量操作性能

3.3 序列化方案对比

在Redis的使用中,序列化方案的选择至关重要。本系统采用JSON序列化而非默认的JDK序列化,主要基于以下考虑:

对比项JDK序列化JSON序列化(Jackson)
数据格式二进制文本(JSON)
可读性差(二进制不可读)好(JSON文本易读)
跨语言支持仅Java多语言支持
空间占用较大较小(压缩后更优)
性能较快稍慢(但在实际应用中可接受)
依赖无额外依赖需要Jackson库

在实际项目中,JSON序列化因其良好的可读性和跨语言特性,成为分布式系统中更常用的选择。而通过Jackson的优化配置,其性能损耗在大多数场景下是可以接受的。

4. 后端代码讲解

后端代码是整个系统的核心,负责处理业务逻辑和数据操作。本节将详细解析Controller层和Service层的实现细节,揭示如何利用Redis实现图书的增删改查功能。

4.1 Controller层实现

Controller层作为前端请求的入口,主要负责请求接收、参数校验和结果返回,不包含复杂的业务逻辑。以下是核心Controller的实现分析:

@Slf4j
@RequestMapping("/special")
@RestController
public class SpecialDealAdminController {@Autowiredprivate SpecialDealAdminService specialDealAdminService;// 添加图书@RequestMapping("/addBookInfo")public String addBookInfo(BookInfo bookInfo) {// 参数合法性校验if (bookInfo == null || !StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getPrice() == null|| bookInfo.getCount() == null|| bookInfo.getStatus() == null) {return "输入的参数不合法,请检查后重新输入";}boolean b = true;try {b = specialDealAdminService.addBookInfo(bookInfo);} catch (Exception e) {log.error("添加图书发生报错e{}:" + e);return "添加图书失败,请联系管理员";}return b ? "" : "controller 添加图书失败";}// 根据ID查询图书@RequestMapping("/getBookInfoById")public BookInfo getBookInfoById(Integer bookId) {log.info("-------bookId:" + bookId);if (bookId == null) {return null;}BookInfo bookInfo = specialDealAdminService.getBookInfoById(bookId);if (bookInfo == null) {return null;}log.info("--------controller---bookInfo:" + bookInfo);return bookInfo;}// 更新图书信息@RequestMapping("/updateSpecialBook")public String updateSpecialBook(BookInfo bookInfo) {if (bookInfo == null) {log.info("特价秒杀--update --传参为空");return " 错误";}boolean b = specialDealAdminService.updateSpecialBook(bookInfo);return b ? "" : "错误";}// 逻辑删除图书@RequestMapping("/updateBookInfo")public String updateBookInfo(BookInfo bookInfo) {if (bookInfo == null) {log.info("controller-special-单个删除传参为空");return "错误";}boolean b = specialDealAdminService.updateBookInfo(bookInfo);return b ? "" : "错误";}// 批量删除图书@RequestMapping("/batchDeleteBookInfoById")public String batchDeleteBookInfoById(@RequestParam("idList") List<Integer> idList) {if (idList == null) {log.info("controller--batchDelete--传来的参数为空");return "错误";}BookInfo bookInfo = new BookInfo();try {for (Integer id : idList) {bookInfo.setId(id);bookInfo.setStatus(0); // 设置状态为"无效"boolean b = specialDealAdminService.updateBookInfo(bookInfo);if (b == false) {log.info("批量删除错误");return "错误";}}} catch (Exception e) {log.info("批量删除抛异常");return "错误";}return "";}
}

控制器实现了图书管理的核心功能,通过简洁的接口设计和明确的职责划分,将前端请求与业务逻辑分离。

4.2 Service和Mapper层实现

4.2.1 代码展示

Service层是业务逻辑的核心,负责与Redis交互并执行业务规则。本系统中,SpecialDealAdminService类实现了所有图书管理相关的业务逻辑:

@Slf4j
@Service
public class SpecialDealAdminService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 从Redis中获取所有图书信息public List<BookInfo> getAllBookInfoFromRedis() {List<BookInfo> resultList = new ArrayList<>();// 获取所有以"bookInfoId:"开头的键String keyPattern = "bookInfoId:*";Set<String> keys = redisTemplate.keys(keyPattern);// 遍历键,获取对应的值for (String key : keys) {BookInfo value = (BookInfo) redisTemplate.opsForValue().get(key);log.info("----------value-----" + value);resultList.add(value);}return resultList;}// 添加图书public boolean addBookInfo(BookInfo bookInfo) {// 查找当前最大的IDInteger maxId = 0;String keyPattern = "bookInfoId:*";Set<String> keys = redisTemplate.keys(keyPattern);// 遍历获取最大IDfor (String key : keys) {BookInfo value = (BookInfo) redisTemplate.opsForValue().get(key);if (value.getId() > maxId) {maxId = value.getId();}}// 设置ID生成器的起始值IdStatic.setId(maxId + 1);try {// 生成新IDInteger id = IdStatic.getId();bookInfo.setId(id);bookInfo.setCreateTime();bookInfo.setUpdateTime();// 构造Redis键并存储String idString = "bookInfoId:" + id;redisTemplate.opsForValue().set(idString, bookInfo);return true;} catch (Exception e) {log.error("redis-添加图书失败");return false;}}// 根据ID查询图书public BookInfo getBookInfoById(Integer id) {BookInfo bookInfo = null;String keyPattern = "bookInfoId:*";Set<String> keys = redisTemplate.keys(keyPattern);// 遍历查找匹配ID的图书for (String key : keys) {BookInfo value = (BookInfo) redisTemplate.opsForValue().get(key);if (id.equals(value.getId())) {bookInfo = value;return bookInfo;}}return bookInfo;}// 更新图书信息public boolean updateSpecialBook(BookInfo bookInfo) {String keyPattern = "bookInfoId:*";Set<String> keys = redisTemplate.keys(keyPattern);// 查找并更新对应ID的图书for (String key : keys) {BookInfo value = (BookInfo) redisTemplate.opsForValue().get(key);if (bookInfo.getId().equals(value.getId())) {bookInfo.setUpdateTime();// 替换bookInforedisTemplate.opsForValue().set(key, bookInfo);log.info("--------service---bookInfo:" + bookInfo);return true;}}return false;}public boolean updateBookInfo(BookInfo bookInfo) {String keyPattern = "bookInfoId:*";Set<String> keys = redisTemplate.keys(keyPattern);for (String key : keys) {BookInfo value = (BookInfo) redisTemplate.opsForValue().get(key);if (bookInfo.getId().equals(value.getId())) {value.setUpdateTime();value.setStatus(bookInfo.getStatus());redisTemplate.opsForValue().set(key, value);log.info("--------service---bookInfo:" + bookInfo);return true;}}return false;}
}

4.2.2 Service层核心逻辑解析

  • 数据获取策略:在多个方法中,通过 redisTemplate.keys("bookInfoId:*") 获取所有符合条件的键,再遍历键获取对应图书对象。这种方式在数据量较小时可行,但当Redis中存储大量图书数据时,可能会影响性能,后续可考虑使用Redis的scan命令进行优化,避免阻塞Redis服务器。
  • ID生成机制addBookInfo 方法中,通过遍历现有图书ID找出最大值,再利用 IdStatic 工具类生成新的唯一ID。这种方式在分布式环境下可能会出现ID重复问题,可引入Redis的 INCR 命令或分布式ID生成算法(如雪花算法)来确保ID的唯一性和高效性。
  • 数据更新逻辑updateSpecialBookupdateBookInfo 方法看似功能相似,实则有细微差别。前者直接用传入的 bookInfo 对象替换Redis中原有数据,适合图书信息全面更新的场景;后者仅更新图书的状态和更新时间,适用于仅修改图书状态的业务需求,体现了对不同业务场景的针对性处理。

4.2.3 与Redis交互的细节

RedisTemplate 提供的 opsForValue() 操作,是处理字符串类型数据的核心工具。在本系统中,将 BookInfo 对象直接作为值存储,得益于之前配置的 GenericJackson2JsonRedisSerializer 序列化器,它会自动将Java对象转换为JSON字符串存入Redis,读取时再反序列化为 BookInfo 对象,极大简化了数据处理流程。同时,在操作过程中,合理使用日志记录关键步骤,方便排查问题和监控系统运行状态。

5. 前端代码讲解

前端页面通过简洁直观的UI设计,结合JavaScript和AJAX技术,实现与后端接口的交互,完成图书的增删改操作。下面分别对各操作对应的前端代码进行详细解读。

5.1 删除操作

删除操作分为单个删除和批量删除,页面通过HTML表格展示图书列表,并提供复选框用于批量选择。

<table><thead><tr><td>选择</td><td class="width100">图书ID</td><td>书名</td><td>作者</td><td>数量</td><td>定价</td><td>出版社</td><td>状态</td><td class="width200">操作</td></tr></thead><tbody></tbody>
</table>
  • 单个删除
function deleteBook(id) {var isDelete = confirm("确认删除?");if (isDelete) {// 删除图书$.ajax({type: "post",url: "/special/updateBookInfo", // 逻辑删除,改图书的状态data: {id: id,status: 0},success: function (result) {if (result == "") {alert("删除成功!");location.href = "book_list.html";} else {alert("删除失败:" + result);}},error: function (error) {alert("请求失败,请联系管理员");}});}
}

当用户点击删除按钮时,弹出确认框,确认后通过AJAX向后端 /special/updateBookInfo 接口发送请求,传递图书ID和要设置的状态(0表示无效),根据后端返回结果提示操作是否成功,并刷新页面。

  • 批量删除
function batchDelete() {var isDelete = confirm("确认批量删除?");if (isDelete) {// 获取复选框的idvar ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});// 发送请求,批量删除$.ajax({type: "post",url: "/special/batchDeleteBookInfoById?idList=" + ids, // 逻辑删除,改图书的状态success: function (result) {if (result == "") {alert("批量删除成功!")location.href = "book_list.html";} else {alert("删除失败");}},error: function (error) {console.log("请求失败");}});}
}

批量删除时,先获取所有选中复选框的图书ID,拼接成参数发送到后端 /special/batchDeleteBookInfoById 接口,后端接收到ID列表后循环更新每本图书的状态,前端根据后端返回结果进行相应提示和页面刷新。

5.2 添加操作

添加页面通过表单收集图书信息,点击确定按钮触发添加逻辑。

<form id="addBook"><div class="form-group"><label for="bookName">图书名称:</label><input type="text" class="form-control" placeholder="请输入图书名称" id="bookName" name="bookName"></div><div class="form-group"><label for="bookAuthor">图书作者</label><input type="text" class="form-control" placeholder="请输入图书作者" id="bookAuthor" name="author"></div><div class="form-group"><label for="bookStock">图书库存</label><input type="text" class="form-control" placeholder="请输入图书库存" id="bookStock" name="count"></div><div class="form-group"><label for="bookPrice">图书定价:</label><input type="number" class="form-control" placeholder="请输入价格" id="bookPrice" name="price"></div><div class="form-group"><label for="bookPublisher">出版社</label><input type="text" id="bookPublisher" class="form-control" placeholder="请输入图书出版社" name="publish"></div><div class="form-group"><label for="bookStatus">图书状态</label><select class="custom-select" id="bookStatus" name="status"><option value="3">特价秒杀</option></select></div><div class="form-group" style="text-align: right"><button type="button" class="btn btn-info btn-lg" onclick="add()">确定</button><button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button></div>
</form>

在这里插入图片描述

function add() {$.ajax({type: "post",url: "/special/addBookInfo",data: $("#addBook").serialize(),success: function (result) {if (result == "") {location.href = "special_admin_list.html";alert("添加成功");} else {console.log(result);alert("添加失败:" + result);}},error: function (error) {console.log("请求失败:" + error);}});
}

点击确定按钮调用 add 函数,通过 $.ajax 发送POST请求到 /special/addBookInfo 接口,使用 $("#addBook").serialize() 将表单数据序列化为键值对形式发送,后端处理成功后,前端提示添加成功并跳转至图书列表页。

5.3 修改操作

修改页面同样以表单形式展示图书信息,并且在页面加载时自动填充原有数据。

<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="3" selected>特价秒杀</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>

在这里插入图片描述

$.ajax({type: "get",url: "/special/getBookInfoById" + location.search,success: function (bookInfo) {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 {alert("用户访问失败,请联系管理员");}}
});function update() {$.ajax({type: "post",url: "/special/updateSpecialBook",data: $("#updateBook").serialize(),success: function (result) {if (result == "") {location.href = "/special_admin_list.html";alert("修改成功!");} else {console.log(result);alert("修改失败:" + result);}},error: function (error) {console.log("请求失败:" + error);}});
}

页面加载时,通过AJAX请求 /special/getBookInfoById 接口获取当前要修改图书的信息,并填充到表单中。用户修改信息后点击确定按钮,调用 update 函数,将表单数据发送到 /special/updateSpecialBook 接口,后端更新成功后,前端提示修改成功并跳转回图书列表页。

6. 总结

通过对基于Redis的图书管理系统的全栈开发实践,我们深入了解了Redis在实际项目中的应用方式,以及前后端交互的完整流程。从Redis的配置优化,到后端业务逻辑的实现,再到前端页面的交互设计,每个环节都体现了技术的巧妙运用和对业务需求的精准实现。

在Redis的使用上,合理的配置和数据模型设计是系统高效运行的关键,同时要注意数据操作的性能问题和分布式场景下的兼容性。后端代码通过分层架构,将业务逻辑与数据访问分离,提高了代码的可维护性和扩展性。前端页面则以用户体验为核心,通过简洁直观的交互设计和AJAX技术,实现了与后端的无缝对接。

然而,本系统仍存在一些可优化的空间。例如,Redis数据查询的性能优化、分布式环境下的ID生成方案改进、前端页面的响应式设计等。未来,可以进一步引入Redis的高级特性(如缓存淘汰策略、事务处理),结合更先进的前端框架(如Vue.js、React),打造功能更强大、性能更优越的图书管理系统。希望本文能为读者在Redis应用开发和全栈项目实践中提供有益的参考和启发。如果你在实际开发中遇到问题,或者有更多优化想法,欢迎在评论区交流讨论!

相关文章:

【基于SpringBoot的图书购买系统】操作Jedis对图书图书的增-删-改:从设计到实战的全栈开发指南

引言 在当今互联网应用开发中&#xff0c;缓存技术已成为提升系统性能和用户体验的关键组件。Redis作为一款高性能的键值存储数据库&#xff0c;以其丰富的数据结构、快速的读写能力和灵活的扩展性&#xff0c;被广泛应用于各类系统的缓存层设计。本文将围绕一个基于Redis的图…...

Ubuntu中TFTP服务器安装使用

TFTP服务器 在 Ubuntu 下使用 TFTP&#xff08;Trivial File Transfer Protocol&#xff09; 服务&#xff0c;通常用于简单的文件传输&#xff08;如网络设备固件更新、嵌入式开发等&#xff09;。 1 TFTP服务器安装 sudo apt-get install tftp-hpa sudo apt-get install…...

Spring Boot微服务架构(十):Docker与K8S部署的区别

Spring Boot微服务在Docker与Kubernetes&#xff08;K8S&#xff09;中的部署存在显著差异&#xff0c;主要体现在技术定位、管理能力、扩展性及适用场景等方面。以下是两者的核心区别及实践对比&#xff1a; 一、技术定位与核心功能 Docker 功能&#xff1a;专注于单节点容器化…...

接口重试的7种常用方案!

前言 记得五年前的一个深夜&#xff0c;某个电商平台的订单退款接口突发异常&#xff0c;因为银行系统网络抖动&#xff0c;退款请求连续失败。 原本技术团队只是想“好心重试几次”&#xff0c;结果开发小哥写的重试代码竟疯狂调用了银行的退款接口 82次&#xff01; 最终导致…...

vue3:Table组件动态的字段(列)权限、显示隐藏和左侧固定

效果展示 根据后端接口返回&#xff0c;当前登录用户详情中的页面中el-table组件的显示隐藏等功能。根据菜单id查询该菜单下能后显示的列。 后端返回的数据类型: 接收到后端返回的数据后处理数据结构. Table组件文件 <!-- 自己封装的Table组件文件 --> onMounted(()>…...

pikachu靶场通关笔记13 XSS关卡09-XSS之href输出

目录 一、href 1、常见取值类型 2、使用示例 3、安全风险 二、源码分析 1、进入靶场 2、代码审计 3、渗透思路 三、渗透实战 1、注入payload1 2、注入payload2 3、注入payload3 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#xff…...

MCP客户端Client开发流程

1. uv工具入门使用指南 1.1 uv入门介绍 MCP开发要求借助uv进行虚拟环境创建和依赖管理。 uv 是一个Python 依赖管理工具&#xff0c;类似于pip 和 conda &#xff0c;但它更快、更高效&#xff0c;并且可以更好地管理 Python 虚拟环境和依赖项。它的核心目标是 替代 pip 、…...

学习日记-day21-6.3

完成目标&#xff1a; 目录 知识点&#xff1a; 1.集合_哈希表存储过程说明 2.集合_哈希表源码查看 3.集合_哈希表无索引&哈希表有序无序详解 4.集合_TreeSet和TreeMap 5.集合_Hashtable和Vector&Vector源码分析 6.集合_Properties属性集 7.集合_集合嵌套 8.…...

C语言探索之旅:深入理解结构体的奥秘

目录 引言 一、什么是结构体&#xff1f; 二、结构体类型的声明和初始化 1、结构体的声明 2、结构体的初始化 3、结构体的特殊声明 4、结构体的自引用 5、结构体的重命名 三、结构体的内存对齐 1、对齐规则 2、为什么存在内存对齐&#xff1f; 3、修改默认对齐数 三…...

uniapp 开发企业微信小程序,如何区别生产环境和测试环境?来处理不同的服务请求

在 uniapp 开发企业微信小程序时&#xff0c;区分生产环境和测试环境是常见需求。以下是几种可靠的方法&#xff0c;帮助你根据环境处理不同的服务请求&#xff1a; 一、通过条件编译区分&#xff08;推荐&#xff09; 使用 uniapp 的 条件编译 语法&#xff0c;在代码中标记…...

Dockerfile常用指令介绍

Dockerfile常用指令介绍 Dockerfile是一个文本文件&#xff0c;用于定义Docker镜像的构建过程。下面介绍一些最常用的Dockerfile指令及其用法&#xff1a; 基础指令 FROM - 指定基础镜像 FROM python:3.9-slim这是Dockerfile的第一个指令&#xff0c;用于指定构建镜像的基础镜…...

Docker 容器化:核心技术原理与实践

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker 的基本概念与核心组件 Docker 是一个开源的容器化平台&#xff0c;能够将应用程序及其依赖项打包成一个容器&#xff0c;确保在任何环境中都能一致运行。Docker 的核心在于其容器化技术&#xff0c;这种…...

不确定性分析在LEAP能源-环境系统建模中的整合与应用

本内容突出与实例结合&#xff0c;紧密结合国家能源统计制度及《省级温室气体排放编制指南》&#xff0c;深入浅出地介绍针对不同级别研究对象时如何根据数据结构、可获取性、研究目的&#xff0c;构建合适的能源生产、转换、消费、温室气体排放&#xff08;以碳排放为主&#…...

经典算法回顾之最小生成树

最小生成树&#xff08;Minimum Spanning Tree&#xff0c;简称MST&#xff09;是图论中的一个重要概念&#xff0c;主要用于解决加权无向图中连接所有顶点且总权重最小的树结构问题。本文对两种经典的算法即Prim算法和Kruskal算法进行回顾&#xff0c;并对后者的正确性给出简单…...

Ubuntu下实现nginx反向代理

1. 多个ngx实例安装 脚本已经在deepseek的指导下完成啦&#xff01; deepseek写的脚本支持ubuntu/centos两种系统。 ins_prefix"/usr/local/" makefile_gen() {ngx$1 ngx_log_dir"/var/log/"$ngx"/"ngx_temp_path"/var/temp/"${ngx}…...

c++ QicsTable使用实例

效果图&#xff1a; #include <QicsTable.h> #include <QicsDataModelDefault.h> #include <QVBoxLayout> Demo1::Demo1(QWidget *parent) : QWidget(parent) { ui.setupUi(this); const int numRows 10; const int numCols 5; // create th…...

在WordPress上添加隐私政策页面

在如今的互联网时代&#xff0c;保护用户隐私已经成为每个网站管理员的责任。隐私政策不仅是法律要求&#xff0c;还能提高用户对网站的信任。本文将介绍两种常用方法&#xff0c;帮助你在WordPress上轻松创建并发布隐私政策页面。这些方法简单易行&#xff0c;符合中国用户的阅…...

二维 根据矩阵变换计算镜像旋转角度

在二维变换中&#xff0c;镜像&#xff08;Reflection&#xff09; 是一种特殊的线性变换&#xff0c;它会将图形对称地翻转到某个轴线或点。镜像的存在会显著影响圆弧变换后的参数&#xff08;圆心、半径、起始角度&#xff09;&#xff0c;尤其是在角度方向和旋转方向的处理上…...

你工作中涉及的安全方面的测试有哪些怎么回答

在面试或工作总结中&#xff0c;回答 **“工作中涉及的安全测试”** 时&#xff0c;可以结合具体场景、测试方法和工具&#xff0c;突出你的技术广度和深度。以下是结构化回答建议&#xff1a; --- ### **1. 分类说明安全测试范围** #### **(1) Web 应用安全测试** - **OWASP…...

阿里云ACP云计算备考笔记 (3)——云服务器ECS

目录 第一章 整体概览 第二章 ECS简介 1、产品概念 2、ECS对比本地IDC 3、BGP机房优势 第三章 ECS实例 1、实例规格族 2、实例系列 3、应用场景推荐选型 4、实例状态 5、创建实例 ① 完成基础配置 ② 完成网络和安全组配置 ③ 完成管理配置和高级选项 ④ 确认下单…...

Eigen实现非线性最小二乘拟合 + Gauss-Newton算法

下面是使用 Eigen 实现的 非线性最小二乘拟合 Gauss-Newton 算法 的完整示例&#xff0c;拟合模型为&#xff1a; 拟合目标模型&#xff1a; y exp ⁡ ( a x 2 b x c ) y \exp(a x^2 b x c) yexp(ax2bxc) 已知一组带噪声数据点 ( x i , y i ) (x_i, y_i) (xi​,yi​)&…...

区块链技术:原理、应用与发展趋势

区块链技术&#xff1a;原理、应用与发展趋势 引言 区块链作为一种去中心化的分布式账本技术&#xff0c;自2008年比特币白皮书发布以来&#xff0c;已经从简单的加密货币底层技术发展成为具有广泛应用前景的创新技术。区块链通过独特的数据结构和加密机制&#xff0c;实现了…...

从零开始:用Tkinter打造你的第一个Python桌面应用

目录 一、界面搭建&#xff1a;像搭积木一样组合控件 二、菜单系统&#xff1a;给应用装上“控制中枢” 三、事件驱动&#xff1a;让界面“活”起来 四、进阶技巧&#xff1a;打造专业级体验 五、部署发布&#xff1a;让作品触手可及 六、学习路径建议 在Python生态中&am…...

Web开发主流前后端框架总结

&#x1f5a5; 一、前端主流框架 前端框架的核心是提升用户界面开发效率&#xff0c;实现高交互性应用。当前三大主流框架各有侧重&#xff1a; React (Meta/Facebook) 核心特点&#xff1a;采用组件化架构与虚拟DOM技术&#xff08;减少真实DOM操作&#xff0c;优化渲染性能&…...

Java Spring Boot 自定义注解详解与实践

目录 一、自定义注解的场景与优势1.1 场景1.2 优势 二、创建自定义注解2.1 定义注解2.2 创建注解处理器 三、使用自定义注解3.1 在业务方法上使用注解3.2 配置类加载注解 四、总结 在 Spring Boot 中&#xff0c;自定义注解为我们提供了一种灵活且强大的方式来简化开发、增强代…...

GlobalSign、DigiCert、Sectigo三种SSL安全证书有什么区别?

‌GlobalSign、DigiCert和Sectigo是三家知名的SSL证书颁发机构&#xff0c;其产品在安全性、功能、价格和适用场景上存在一定差异。选择SSL证书就像为你的网站挑选最合身的“安全盔甲”&#xff0c;核心是匹配你的实际需求&#xff0c;避免过度配置或防护不足。 一、核心特点对…...

力扣面试150题--二叉搜索树中第k小的元素

Day 58 题目描述 思路 直接采取中序遍历&#xff0c;不过我们将k参与到中序遍历中&#xff0c;遍历到第k个元素就结束 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* …...

SQL Server Agent 不可用怎么办?

在 SQL Server Management Studio (SSMS) 中&#xff0c;SQL Server Agent 通常位于对象资源管理器&#xff08;Object Explorer&#xff09;的树形结构中&#xff0c;作为 SQL Server 实例的子节点。以下是详细说明和可能的原因&#xff1a; 1. SQL Server Agent 的位置 默认路…...

css-塞贝尔曲线

文章目录 1、定义2、使用和解释 1、定义 cubic-bezier() 函数定义了一个贝塞尔曲线(Cubic Bezier)语法&#xff1a;cubic-bezier(x1,y1,x2,y2) 2、使用和解释 x1,y1,x2,y2&#xff0c;表示两个点的坐标P1(x1,y1),P2(x2,y2)将以一条直线放在范围只有 1 的坐标轴中&#xff0c;并…...

Java并发编程哲学系列汇总

文章目录 并发编程基础并发编程进阶并发编程实践 并发编程基础 Java并发编程基础小结 Java线程池知识点小结 详解JUC包下各种锁的使用 并发编程利器Java CAS原子类全解 深入理解Java中的final关键字 Java并发容器深入解析&#xff1a;HashMap与ArrayList线程安全问题及解…...