《JavaEE进阶》----21.<基于Spring图书管理系统②(图书列表+删除图书+更改图书)>

PS:

开闭原则

定义和背景‌
‌开闭原则(Open-Closed Principle, OCP)‌,也称为开放封闭原则,是面向对象设计中的一个基本原则。该原则强调软件中的模块、类或函数应该对扩展开放,对修改封闭。这意味着一个软件实体应该在不修改现有代码的基础上,能够适应新的变化和需求。

核心思想
开闭原则的核心思想是:

  • ‌对扩展开放‌:当新的需求或变化出现时,可以通过扩展现有代码来适应新的情况,而不是修改现有的代码。
  • ‌对修改封闭‌:一旦类或模块设计完成,就应该能够独立完成其工作,不再对其进行任何修改。(能够兼容之前的版本)

针对先上线的程序而言

比如后端接口从A改成了B。(前端接口也要修改)

如果后端先上线:前端调用就会出错,找不到A接口

如果前端先上线,前端调用依然会报错,找不到B接口。因为此时后端还没上线。

正确的做法:后端对之前的接口进行兼容,如果兼容则同时存在A和B接口

实现方式
实现开闭原则的方式主要包括:

  • ‌抽象编程‌:通过面向对象的继承和多态机制,实现对抽象体的继承和方法的覆写,从而实现新的扩展方法。
  • ‌依赖抽象‌:类依赖于固定的抽象,而不是具体的实现,这样可以保证类的稳定性。

相关设计原则
开闭原则与其他设计原则密切相关,包括:

  • ‌里氏替换原则‌:子类对象能够替代父类对象出现的地方,并且保证原有逻辑行为不变。
  • ‌接口隔离原则‌:接口调用方和使用者只关心自己相关的接口,不依赖于不需要的接口。
  • ‌依赖反转原则‌:高模块不直接依赖低模块,而是通过抽象来互相依赖。
  • ‌单一职责原则‌:一个类或模块只负责完成一个职责或功能。

 一、图书列表展示功能

1.1 实现分页功能

提到展示图书列表,就不得不提到分页了

分页时,数据是如何展示的呢

第1页:显示1-10 条的数据

第2页:显示11-20 条的数据

第3页:显示 21-30 条的数据

以此类推...

要想实现这个功能,从数据库中进行分页查询,我们要使用 LIMIT 关键字,格式为:limit 开始索引 每页显示的条数(开始索引从0开始)。

select * from book_info where status <> 0 limit 0,10;
select * from book_info where status <> 0 limit 10,10;
select * from book_info where status <> 0 limit 20,10;

我们发现只有开始索引在改变。每页显示的条数是固定的。

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

因此:

1.前端发起查询请求时,需要向服务器端传递的参数。

currentPage 当前页码 :默认值为1

pageSize 每页显示条数 默认值为10

注:

为了项目更好的扩展性,通常不设置固定值,而是是以参数的形式来进行传递

扩展性: 软件系统具备面对未来需求变化而进行扩展的能力。 比如当前需求一页显示10条,后期需求改为一页显示20条,

后端代码不需要任何修改。

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

records :所查询到的数据列表(存储到List集合中)

count :总记录数(用于告诉前端显示多少页,

显示页数为:(count + pageSize -1)/pageSize

翻页请求和响应部分, 我们通常封装在两个对象中

1.1.1 翻页请求对象PageRequest

创建PageRequest

前端进行请求

1.会请求当前页 和 每页显示的个数。

2.由上面两个数据计算出offset,用作参数传递给SQL语句

package com.qiyangyang.springbook.demos.model;
import lombok.Data;
@Data
public class PageRequest {private Integer currentPage = 1;//当前页private Integer pageSize = 10;//每页显示个数private Integer offset;/*** 从多少条记录开始查询* @return*/public Integer getOffset() {return (currentPage-1) * pageSize;}
}

1.1.2  翻页响应对象

package com.qiyangyang.springbook.demos.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;/*** 定义一个泛型类,就是,这个地方不定义具体类型* 我们在进行对象的生成的时候,它才有具体的类型。* @param <T>*/
//上面两个注解用来创建构造方法
@AllArgsConstructor
@NoArgsConstructor
@Data
public class PageResult<T> {/*** 返回的结果也是一个泛型* 不定义具体类型,在对象的创建才会有具体类型*/private List<T> records; //当前页数据private Integer count; //所有记录数private PageRequest pageRequest; //小驼峰,用来返回给前端当前页数// 这里将整个对象告诉result。用来给前端获取多少页}

返回结果中, 使用泛型来定义记录的类型

后端定义参数。

offset(起始序号)和limit(显示多少条)

 MySQL语句

 前端根据总记录数,来显示分了多少页。

1.2使用枚举来处理status/stateCN字段。 (可借阅/不可借阅)

1.创建enums文件夹

2.创建BookStatusEnums类

package com.qiyangyang.springbook.demos.enums;/*** 枚举类* 可以列举出来的,是一个有限的个数,我们将他们定义成枚举类* 方便定义类似* 根据状态设置描述*             if(bookInfo.getStatus() == 1){*                 bookInfo.setStateCN("可借阅");*             } else if (bookInfo.getStatus() == 2) {*                 bookInfo.setStateCN("不可借阅");*             }else {*                 bookInfo.setStateCN("无效");*             }*/
public enum BookStatusEnums {DELETE(0,"无效"), //删除NORMAL(1,"可借阅"), //有效的FORBIDDEN(2,"不可借阅"), //禁止;private int code;private String desc;BookStatusEnums(int code, String desc) {this.code = code;this.desc = desc;}/*** 我们将这个封装成一个方法* 根据code获取描述** @return*/public static  BookStatusEnums getDescByCode(int code){switch (code){case 0: return BookStatusEnums.DELETE;case 1: return BookStatusEnums.NORMAL;case 2: return BookStatusEnums.FORBIDDEN;}return BookStatusEnums.DELETE;}public int getCode() {return code;}public String getDesc() {return desc;}
}

稍后在业务层我们会对这个方法进行调用

1.3约定前后端交互接口

接口定义:

url:/book/getListByPage?currentPage=1

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

参数:

当前页数

返回结果:

当前页的数据+总记录数(决定前端显示多少页数)

我们约定,浏览器给服务器发送一个

/book/getListByPage 这样的 HTTP 请求,

通过 currentPage 参数告诉服务器,当前请求为第几页的数据,

后端根据请求参数,返回对应页的数据

第一页可以不传参数, currentPage默认值为1。

1.4实现服务器代码

1.4.1控制层:

完善 BookController

    @RequestMapping("getListByPage")public PageResult<BookInfo> getListByPage(PageRequest pageRequest){log.info("查询列表信息,pageRequest:{}",pageRequest);if(pageRequest.getCurrentPage()< 1){return null;}//这里返回null。会导致前端不知道是没有数据为null。还是当前页错误返回null//先不管,后续改进/*** 通过Service来去调用数据库*/return bookService.getListByPage(pageRequest);}

1.4.2 业务层 :

完善 BookService

    public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {/*** 1.查询记录的总数* 2.查询当前页的数据*/Integer count = bookInfoMapper.count();//bookInfos来接收查询到的数据List<BookInfo> bookInfos = bookInfoMapper.queryListByPage(pageRequest);for(BookInfo bookInfo : bookInfos){/*** 根据book状态设置描述(stateCN)*/                    bookInfo.setStateCN(BookStatusEnums.getDescByCode(bookInfo.getStatus()).getDesc());}return new PageResult<>(bookInfos,count);}

1.4.3 数据层 :

完善 BookInfoMapper

    /*** 查询总数* @return*///count(1):返回满足条件的记录数(即行数)。count(1) 和 count(*) 基本等效,都是用于统计记录数。@Select(("select count(1) from book_info where status <> 0"))Integer count();//希望把新添加的图书放到下面,因此order by id desc降序。@Select("select * from book_info where status <> 0 order by id desc limit #{offset},#{pageSize}")List<BookInfo> queryListByPage(PageRequest pageRequest);

 1.5校验后端

不用传参也行,因为我们默认currentPage 为1。且pageSize为5。

我们发现返回正确。

总记录数也返回正确。为46。

我们发现后端接口没有问题。

 1.6实现前端代码

1.6.1显示图书数据的内容

将前端<tbody>标签中的内容,也就是

        <tbody>//这里的内容我们用findHtml变量拼接并传送到这个标签里了</tbody>

我们写在ajax中使用findHtml变量进行拼接。并用如下方法传送到这个标签中。

$("tbody").html(findHtml); //塞到tbody这个标签里面
            success: function (result) {var books = result.records;console.log(books); //如果前端没有报错,那么我们打印日志。观察后端返回结果对不对var findHtml = "";  //用这个变量来拼接HTMLfor (var book of books) {//拼接html。假如后端返回10个tr那么直接for循环拼接在这里面。findHtml//我们用单引号拼接,因为里面有双引号findHtml += '<tr>';findHtml += '<td><input type="checkbox" name="selectBook" value="' +book.id +'" id="selectBook" class="book-select"></td>';findHtml += "<td>" + book.id + "</td>";findHtml += "<td>" + book.bookName + "</td>";findHtml += "<td>" + book.author + "</td>";findHtml += "<td>" + book.count + "</td>";findHtml += "<td>" + book.price + "</td>";findHtml += "<td>" + book.publish + "</td>";findHtml += "<td>" + book.stateCN + "</td>";findHtml += "<td>";findHtml += '<div class="op">';findHtml +='<a href="book_update.html?bookId=' + book.id + '">修改</a>';findHtml +='<a href="javascript:void(0)" onclick="deleteBook('+book.id +')">删除</a>';findHtml += "</div>";findHtml += "</td>";findHtml += "</tr>";}$("tbody").html(findHtml); //塞到tbody这个标签里面​

 1.6.2处理翻页信息

我们需要在前端head标签中引入jquery 和 paginator

相当于引入插件

    <script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/bootstrap.min.js"></script><script src="js/jq-paginator.js"></script>
      <div class="demo"><ul id="pageContainer" class="pagination justify-content-center"></ul></div>
              //处理翻页信息console.log(result);console.log(result.pageRequest);//翻页信息$("#pageContainer").jqPaginator({totalCounts: result.count, //总记录数pageSize: 10, //每页的个数visiblePages: 5, //可视页数currentPage: result.pageRequest.currentPage, //当前页码first:'<li class="page-item"><a class="page-link">首页</a></li>',prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',//页面初始化和页码点击时都会执行onPageChange: function (page, type) {console.log("第" + page + "页, 类型:" + type);if(type == "change"){location.href = "book_list.html?currentPage="+page;}},});

1.7校验前后端

成功实现图书列表显示以及翻页功能。

二、修改图书列表功能

2.1约定前后端交互接口

1.进入修改页面,需要显示当前 Id 图书的信息

[请求]

/book/queryBookById?bookId=25

[参数] 

bookId

[响应]

{

"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,获取当前图书的信息

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

[请求]

/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

[响应] true/false

我们约定,

浏览器给服务器发送一个 /book/updateBook 这样的HTTP请求,

form表单的形式来 提交数据

服务器返回处理结果,

返回"修改成功"修改图书成功,否则,返回失败信息.

2.2实现服务器端代码

2.2.1控制层:

    @RequestMapping("/queryBookById")public BookInfo queryBookById(Integer bookId){log.info("查询图书信息,bookId:"+bookId);if(bookId == null || bookId<=0){return new BookInfo();}return bookService.queryBookById(bookId);}@RequestMapping("/updateBook")//先使用boolean类型返回。后续我们还会再进行完善。public boolean upDateBook(BookInfo bookInfo){log.info("修改图书信息, updateBook{}:",bookInfo);if(!StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getCount() <=0|| bookInfo.getPrice()==null){return false;}try {Integer result = bookService.updateBook(bookInfo);if(result <= 0){return false;}}catch (Exception e){log.error("更新图书失败");return false;}return true;}

2.2.2 业务层 :

    public BookInfo queryBookById(Integer bookId) {return bookInfoMapper.queryBookById(bookId);}public Integer updateBook(BookInfo bookInfo) {return bookInfoMapper.updateBook(bookInfo);}

2.2.3 数据层 :

    /*** 根据Id查询图书信息* @param id* @return*/@Select("select * from book_info where status <> 0 and  id = #{id}")BookInfo queryBookById(Integer id);/*** 根据Id修改图书信息*/Integer updateBook(BookInfo bookInfo);

根据 Id 修改图书信息 我们使用的是XML方式实现的SQL

<?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.qiyangyang.springbook.demos.mapper.BookInfoMapper"><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>
</mapper>

2.3校验后端

我发现这个接口都校验成功。

2.3.1校验queryBookById接口 

2.3.2校验updateBook接口 

 

修改成功!

2.4实现前端代码

注意:前端传递数据的时候记得加上id。

    <script type="text/javascript" src="js/jquery.min.js"></script><script>//查询当前ID图书$.ajax({type: "get",url: "book/queryBookById"+location.search,success:function(book){if(book!=null){$("#bookId").val(book.id);$("#bookName").val(book.bookName);$("#bookAuthor").val(book.author);$("#bookStock").val(book.count);$("#bookPrice").val(book.price);$("#bookPublisher").val(book.publish);$("#bookStatus").val(book.status);}}});//更新当前Id图书function update() {$.ajax({type: "get",url: "/book/updateBook",data:$("#updateBook").serialize(),//提交整个表单success:function(result){if(result == true){alert("更新成功");location.href = "book_list.html"}else{alert("更新失败");}}});}</script>

2.5整体测试

比如我们要将图书ID为135的图书

作者 修改为 洋洋

数量 修改为 888

价格 修改为 666

出版社 修改为 人民出版社

可借阅 修改为 不可借阅

修改成功!!!!! 

三、逻辑删除图书

删除图书分为

逻辑删除(update):

从逻辑上进行删除,数据并没有真实删除

物理删除(delete语句):

数据真实删除。

但数据并没有真实清空,只是数据库上看不到了。

硬件存储上还是存在的

删除并归档(操作交为复杂):insert into... select....语句

1.删除(delete or update)

2.归档(把已经删除的数据存储下来)

3.1约定前后端交互接口

逻辑删除的话,

依然是更新逻辑,我们可以直接使用修改图书的接口

[请求]

/book/updateBook

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

[参数]

id=1&status=0

[响应]

true / false

3.2实现服务器代码

3.2.1控制层:

由于我们在upadate接口中只需要传递 id 和 status 两个参数。

因此我们需要修改控制层中的校验参数的步骤

    @RequestMapping("/updateBook")//先使用boolean类型返回。后续我们还会再进行完善。public boolean upDateBook(BookInfo bookInfo){log.info("修改图书信息, updateBook{}:",bookInfo);if(!StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getCount() <=0|| bookInfo.getPrice()==null){return false;}try {Integer result = bookService.updateBook(bookInfo);if(result <= 0){return false;}}catch (Exception e){log.error("更新图书失败");return false;}return true;}

修改为

    @RequestMapping("/updateBook")//先使用boolean类型返回。后续我们还会再进行完善。public boolean updateBook(BookInfo bookInfo){log.info("修改图书信息, updateBook{}:",bookInfo);if(bookInfo.getId()<0){return false;}try {Integer result = bookService.updateBook(bookInfo);if(result <= 0){return false;}}catch (Exception e){log.error("更新图书失败");return false;}return true;}

3.2.2 业务层 :

同之前的updateBook一样

    public Integer updateBook(BookInfo bookInfo) {return bookInfoMapper.updateBook(bookInfo);}

3.2.3 数据层 :

同之前的update一样

    /*** 根据Id修改图书信息*/Integer updateBook(BookInfo bookInfo);
    <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>

校验后端接口 

 

134号图书 status 成功修改为0 

 

3.3实现前端代码

        function deleteBook(id) {//删除图书var isDelete = confirm("确认删除?");if (isDelete) {$.ajax({type: "post",url: "/book/updateBook",data:{id: id,status: 0},success:function(result){if(result == true){alert("删除成功");location.href = "book_list.html";}}});}}

3.4整体校验

删除132号图书三体

成功删除!!!!!!!!!!! 

 四、批量逻辑删除

4.1定义前后端交互接口

请求:

/book/batchDeleteBook

参数:

响应:

true/false

4.2实现服务器端代码

4.2.1控制层:

注意加上:注解@RequestParam

    @RequestMapping("/batchDelete")public boolean batchDelete(@RequestParam List<Integer> ids){log.info("批量删除数据,ids:{}",ids);try {Integer result = bookService.batchDelete(ids);if(result <= 0){return false;}}catch (Exception e){log.info("批量删除失败,id:{},e:{}", ids, e);return false;}return true;}

4.2.2 业务层 :

    public Integer batchDelete(List<Integer> ids) {return bookInfoMapper.batchDelete(ids);}

4.2.3 数据层 :

注意加上注解:

@Param("ids")

    Integer batchDelete(@Param("ids") List<Integer> ids);
    <update id="batchDelete">update book_infoset status = 0where id in<foreach collection="ids" open="(" close=")" item="id" separator=",">#{id}</foreach></update>

4.3后端校验

后端操作成功

4.4实现前端代码 

        function batchDelete() {var isDelete = confirm("确认批量删除?");if (isDelete) {//获取复选框的idvar ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});console.log(ids);$.ajax({type: "post",url: "/book/batchDelete?ids="+ids,success:function(result){if(result == true){alert("批量删除成功");location.href = "book_list.html";}else{alert("删除失败,请联系管理员!");}}});}}

4.5整体测试

测试成功!!!!

五、强制登录

这个功能的实现我们下一篇文章再讲哦!!!!

到这里其实这个图书管理系统的功能就基本实现完成了。

不过对于这个图书管理系统。

我们没有进行登录也可以进行操作。

因此我们下一篇文章会详细讲解强制登录功能。

并且后续会讲到统一功能!!!!!!!!!!!!!!! 

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

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

相关文章

仿真APP助力汽车零部件厂商打造核心竞争力

汽车零部件是汽车工业的基石&#xff0c;是构成车辆的基础元素。一辆汽车通常由上万件零部件组成&#xff0c;包括发动机系统、传动系统、制动系统、电子控制系统等&#xff0c;它们共同确保了汽车的安全、可靠性及高效运行。 在汽车产业快速发展的今天&#xff0c;汽车零部件…

现代Web开发:Vue 3 组件化开发实战

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 现代Web开发&#xff1a;Vue 3 组件化开发实战 现代Web开发&#xff1a;Vue 3 组件化开发实战 现代Web开发&#xff1a;Vue 3 组…

Unity引擎智能座舱解决方案

作为全球领先的 3D 引擎之一&#xff0c;Unity引擎为车载3D HMI提供全栈支持。即为从概念设计到量产部署的整个 HMI 工作流程提供创意咨询、性能调优、项目开发等解决方案&#xff0c;从而为车载信息娱乐系统和智能驾驶座舱打造令人惊叹的交互式体验。 专为中国车企打造的HMI引…

MySQL必会知识精华6(组合WHERE子句)

我们的目标是&#xff1a;按照这一套资料学习下来&#xff0c;大家可以完成数据库增删改查的实际操作。同时轻松应对面试或者笔试题中MySQL相关题目。 上篇文章我们先做一下数据库的where条件过滤的方法&#xff0c;都是单个条件的过滤。本篇文章主要介绍查询的组合WHERE子句的…

[C++11] 可变参数模板

文章目录 基本语法及原理可变参数模板的基本语法参数包的两种类型可变参数模板的定义 sizeof... 运算符可变参数模板的实例化原理可变参数模板的意义 包扩展包扩展的基本概念包扩展的实现原理编译器如何展开参数包包扩展的高级应用 emplace 系列接口emplace_back 和 emplace 的…

欺诈文本分类检测(十八):基于llama.cpp+CPU推理

1. 前言 前文我们用Lora训练出自己的个性化模型后&#xff0c;首先面临的问题是&#xff1a;如何让模型在普通机器上跑起来&#xff1f;毕竟模型微调时都是在几十G的专用GPU上训练的&#xff0c;如果换到只有CPU的普通电脑上&#xff0c;可能会面临几秒蹦一个词的尴尬问题。 …

硬件基础06 滤波器——无源、有源(含Filter Solutions、Filter Pro、MATLAB Fdatool)

目录 一、Filter Solutions 1、软件资源及安装教程如下 2、使用相关内容 二、Filter Pro使用 1、软件资源及安装教程如下 2、使用相关内容 三、MATLAB Fdatool 1、在matlab命令中输入fdatool 2、输入相关参数&#xff0c;例如低通、FIR、20阶、hamming窗 3、调用 &am…

【HGT】文献精讲:Heterogeneous Graph Transformer

【HGT】文献精讲&#xff1a;Heterogeneous Graph Transformer 标题&#xff1a; Heterogeneous Graph Transformer &#xff08;异构图Transformer&#xff09; 作者团队&#xff1a; 加利福尼亚大学Yizhou Sun 摘要&#xff1a; 近年来&#xff0c;图神经网络&#xff08;GN…

大厂基本功 | MySQL 三大日志 ( binlog、redo log 和 undo log ) 的作用?

前言 MySQL日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中&#xff0c;比较重要的还要属二进制日志binlog&#xff08;归档日志&#xff09;和事务日志redo log&#xff08;重做日志&#xff09;和undo log&#xff08;回滚日志&#xff09;…

【系统架构设计师(第2版)】五、软件工程基础知识

5.1 软件工程 20世纪60年代&#xff0c;为了解决软件危机&#xff0c;提出了软件工程的概念。 软件危机的具体表现&#xff1a; 软件开发进度难以预测&#xff1b;软件开发成本难以控制&#xff1b;软件功能难以满足用户期望&#xff1b;软件质量无法保证&#xff1b;软件难以…

手机内卷下一站,AI Agent

作者 | 辰纹 来源 | 洞见新研社 2024年除夕夜&#xff0c;OPPO在央视春晚即将开始前举办了一场“史上最短发布会”&#xff0c;OPPO首席产品官刘作虎宣布&#xff0c;“OPPO正式进入AI手机时代”。 春节假期刚过&#xff0c;魅族又公开表示&#xff0c;将停止“传统智能手机…

科研绘图系列:R语言组合堆积图(stacked plot)

文章目录 介绍加载R包数据数据预处理画图1画图2组合图形系统信息介绍 堆积图(Stacked Chart),也称为堆叠图,是一种常用的数据可视化图表,主要用于展示不同类别的数据量在总体中的分布情况。堆积图可以是柱状图、条形图或面积图的形式,其中各个类别的数据量被叠加在一起,…

Node.js 完全教程:从入门到精通

Node.js 完全教程&#xff1a;从入门到精通 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;允许开发者在服务器端使用 JavaScript。它的非阻塞 I/O 和事件驱动架构使得 Node.js 非常适合于构建高性能的网络应用。本文将详细介绍 Node.js 的安装、基本语…

微服务day03

导入黑马商城项目 创建Mysql服务 由于已有相关项目则要关闭DockerComponent中的已开启的项目 [rootserver02 ~]# docker compose down WARN[0000] /root/docker-compose.yml: version is obsolete [] Running 4/4✔ Container nginx Removed …

每日一题之二叉树

已知结点元素值为正整数且值不相同的一棵二叉树。 该二叉树通过给出其先序遍历序列和中序遍历序列构造而成。 输入一个整数x&#xff0c;针对此二叉树编写程序求出x的右子树中所有结点值的和&#xff08;若x不在树上&#xff0c;输出-1&#xff09;。 输入说明&#xff1a;第一…

win10系统使用Visual Studio 2019或cmake编译SDL2为32位库时出现error C2118: 负下标winnt.h的解决方法

提示&#xff1a; 下图蓝体字中的VS2008是错误的&#xff0c;其实SDL.sln是用VS2010版本的软件开发的&#xff08;对于SDL-release-2.0.5.zip源码而言至少是这样&#xff0c;而2024-11-6为止SDL是2.30.9版本了&#xff0c;2.30.9版本则无需自己编译&#xff0c;只需下载带后缀…

推荐一款管道数据检索工具:Pipedata-Pro

Pipedata-Pro是一款专为设计石油、天然气、水和蒸汽管道及管道系统的工程师开发的应用程序。该应用程序提供了设计管道系统所需的工程数据&#xff0c;拥有一个全面的管道类型、配件和材料数据库。 软件特点&#xff1a; 1. 技术参数查询&#xff1a;Pipedata-Pro 提供关于管道…

算法竞赛(Python)-数组

文章目录 一 、排序算法二 、二分查找1 二分查找讲解2 二分查找题目&#xff08;1&#xff09;二分查找&#xff08;2&#xff09;在排序数组中查找元素的第一个和最后一个位置&#xff08;3&#xff09;两数之和 II - 输入有序数组 三、数组双指针1对撞指针对撞指针题目1&…

基于STM32的LCD1602显示Proteus仿真设计(仿真+程序+设计报告+讲解视频)

这里写目录标题 1.主要功能0. 资料清单&下载链接资料下载链接&#xff1a;2.仿真设计3. 程序设计4. 设计报告5. 框图 基于STM32的LCD1602显示Proteus仿真设计(仿真程序设计报告讲解视频&#xff09; 仿真图proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a…

SpringBoot项目编译报错 类文件具有错误的版本 61.0, 应为 52.0

springboot项目在编译时报错&#xff1a; /Users/Apple/Developer/art/caicai/cai-api/dubbo-samples/1-basic/dubbo-samples-spring-boot/dubbo-samples-spring-boot-provider/src/main/java/org/apache/dubbo/springboot/demo/provider/ProviderApplication.java:22:32 java…