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

【开源风云】从若依系列脚手架汲取编程之道(五)

📕开源风云系列

  • 🍊本系列将从开源名将若依出发,探究优质开源项目脚手架汲取编程之道。
  • 🍉从不分离版本开写到前后端分离版,再到微服务版本,乃至其中好玩的一系列增强Plus操作
  • 🍈希望你具备如下技术栈
    • 🍎Spring
    • 🍍SpringMVC
    • 🍐Mybatis/Mybatis-plus
    • 🍅Thymeleaf
    • 🥝SpringBoot
    • 🍓Shiro
    • 🍏SpringSecurity
    • 🍌SpringCloud
    • 🍒云服务器相关知识
  • 本篇是分离版第三篇

在这里插入图片描述

  • 文章同时同步到我的个人站点🍊欢迎来访!
    • 🍎国内:https://www.linxiaoqin.netlify.app
    • 🍏国际:https://www.linxiaoqin.vercel.app

目录

  • 📕开源风云系列
  • 1、分页
    • 1.1、前端封装分页
    • 1.2、后端分页方式
  • 2、MyBatisPlus整合
    • 2.1、MP测试
  • 3、多数据源
  • 4、事务
  • 5、操作日志
  • 6、数据权限
  • 7、服务监控
    • 7.1、服务监控前端
    • 7.2、栅格布局
  • 8、Swagger
  • 9、XSS脚本攻击
  • 10、防止重复提交过滤

1、分页

[!NOTE]

分页主要是后端做,前端只需要传给后端,我要第几页,每页多少条数据。

1.1、前端封装分页

操作日志页面为例子,前端每次给后端发请求的时候,都是带了分页参数的,默认都是请求第一页,每页10条数据。

在这里插入图片描述

我们请求后端接口的时候会带上queryParams对象,我们进入日志页面,就会执行getList方法,进而执行list方法发送请求,发送请求的时候会带上queryParams、dateRange 参数,这个dateRange参数是日期范围,也就是前端搜索框筛选日期的参数,为什么这个日期范围参数不放在 queryParams 对象里面一起传参进去呢?

其实 dateRange 日期范围是在 data 里面,和 element ui 的日期选择器进行了 v-model 双向绑定

若依对 element ui 的分页进行了封装,包括分页的页码数等等,可以结合 element ui 的 Pagination 分页的参数进行更改。

在这里插入图片描述

[!NOTE]

前端只需要维护好数据,我需要当前是第几页,一共有几条数据,每页多少条

前端只需要维护好这三个数据,其他的交给后端就行了。

1.2、后端分页方式

后端是通过pagehelper插件实现的。ry通过pagehelper间接引入了mybatis:

在这里插入图片描述

[!NOTE]

  • 若依在 BaseController中封装了分页方法。也就是说我们自己写的controller只要继承了BaseController,直接调用startPage方法即可完成分页。
  • 分页的源码调试讲解:源码解析 很牛,值得一看

我们的Controller继承BaseController,只需要在我们的Service的查询方法之前加上startPage()就会默认表示sql需要分页。

在这里插入图片描述

2、MyBatisPlus整合

关于若依集成MyBatisPlus,我们可以按照步骤如下:

  1. 在根pom.xml中引入mybatis-plus,在common模块中继承

在这里插入图片描述

  1. 在common模块下删除mybatis冲突依赖

在这里插入图片描述

  1. 填写 yml 配置
mybatis-plus:# 配置mapper的扫描,找到所有的mapper.xml映射文件configLocation: classpath:mybatis/mybatis-config.xml# 搜索指定包别名typeAliasesPackage: com.kuang.**.domain

在这里插入图片描述

  1. 删除MyBatisConfig,也就是删除ry自己的配置文件,全部注释掉也可以

在这里插入图片描述

然后直接重启项目,成功!


  1. 为什么要排除pagehelper中的mybatis依赖?

答:因为mybatis-plus的启动器和pagehelper都引入了mybatis,mybatis-plus的启动器的mybatis版本比较高。pagehelper引入的mybatis的版本比较低,mybatis-plus用到了高版本mybatis的方法。所以需要排除pagehelper中的mybatis。否则报错没找到方法。但是我自己的版本如下:

Pagehelper V1.4.7:

  • mybatis:3.5.13
  • mybatis-spring:2.1.1

mybatis-plus V3.5.3:

  • mybatis:3.5.10
  • mybatis-spring:2.0.7

也就是其实mybatis-plus用的版本都是低的版本,那么maven会自动将高的版本排除,保留低版本的依赖。所以不需要手动排除,Maven会自动排除。

在这里插入图片描述

那么我把低版本的依赖排除,使用高版本的依赖,发现也是OK的。

在这里插入图片描述

2.1、MP测试

  1. 我们测试一下,改造一下登录日志的查询,首先给 mapper 继承 BaseMapper

在这里插入图片描述

  1. 在impl实现类下执行查询

在这里插入图片描述

  1. 访问日志管理会发现报错

在这里插入图片描述

控制台打印的SQL语句是:

SELECT  info_id,user_name,status,ipaddr,login_location,browser,os,msg,login_time,
search_value,create_by,create_time,update_by,update_time,remark,params 
FROM sys_logininfor  LIMIT ?

可以发现我们的登录日志数据库表中就没有 search_value 后面的字段了,那么为什么这个sql语句会带呢?

在这里插入图片描述

是因为继承关系导致的,SysLogininfor 继承了 BaseEntity,BaseEntity的属性也被写入了SQL中

在这里插入图片描述

所以我们只需要给BaseEntity的不需要的属性上加上注解@TableField(exist = false)

在这里插入图片描述

完成上述操作后,再进行登录日志菜单的点击,就可以正常查询显示了。

3、多数据源

多数据源不是必须的,需求是让一个项目同时连接操作多个数据库。

  1. 对于一个请求(假如此请求需要查询数据库),我们首先是前端点击,然后前端给后端发请求,后端controller层拿到请求,controller把请求发给service层,service层把数据再递交给mapper层,mapper层在若依中是mybatis接管。

  2. 当mybatis看到数据库查询请求后,这时候代理模式就开始了,我们的mapper层的接口被代理,框架直接通过反射拿到当前的代理对象、方法、相关参数、DefaultSqlSession去执行相关查询。

  3. 重点来了,怎么查询?首先要干嘛?当然是得到当前数据库的连接Connection,然后通过此数据库连接进行执行相关查询。

  4. 我们自己写一个类DynamicDataSource(动态数据源的英文),实现DataSource接口,然后重写getConnection方法,我们可以在DynamicDataSource类中准备好几个连接,然后getConnection方法中根据情况获得不同的连接。

[!NOTE]

总结:如果需要切换多个数据源,只需要把@DataSource注解加到方法上或者类上,切面会把注解中的value读出来并设置到线程私有上下文中,然后在getConnection的时候读取线程私有的value来确定用哪个数据源。

我们来玩一下三个MySQL数据源

  1. 首先在DruidConfig中增加一个数据源

在这里插入图片描述

  1. 修改一下数据库的枚举

在这里插入图片描述

  1. 注入三个数据库到动态数据库
@Bean
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource,DataSource slaveDataSource,DataSource thirdDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);targetDataSources.put(DataSourceType.SLAVE.name(),slaveDataSource);targetDataSources.put(DataSourceType.THIRD.name(),thirdDataSource);return new DynamicDataSource(masterDataSource, targetDataSources);
}

在这里插入图片描述

  1. 我们在某个方法上面加上注解@DataSource(DataSourceType.THIRD),就会使用第三个数据库,就完成了切库的操作。

4、事务

若依-Vue中的事务和SpringBoot中完全一致。

在方法或者类上可以假如@Transactional注解,只要该注解加上,方法中无论有多少个执行SQL的地方,只要执行遇到一个异常,所有的操作都将会回滚。

@Transactional注解默认是捕捉运行时异常,如果需要抓更大的异常,可以设置参数:

@Transactional(rollbackFor = Exception.class)

5、操作日志

操作日志是记录用户的某些操作。

为什么要记录呢?

  1. 可能后续系统出现问题,需要回来找证据的时候会用到。

  2. 供管理员查看用户的行为。

  3. 记录正确或者错误记录,也方便程序员修改程序。

总而言之,操作日志有它存在的必要。但是操作日志多少也会消耗一些性能。我们所记录的操作日志,一般都是用户对数据库进行增删改的记录。对于查询,用户并没有更改数据库,所以往往不会去记录。

老生常谈AOP。操作日志是通过下面的类实现的:

在这里插入图片描述

切面类会切Log注解,意思是要某个方法有操作日志,只需要手动加上@Log注解。IDEA可以帮我们看到我们切了哪个方法。

在这里插入图片描述

以新增部门方法为例,处理完请求后执行会拿到切入点、拿到@Log()注解、拿到请求返回的结果:

在这里插入图片描述

以用户修改菜单为例,我们只需要加上@Log(title = "用户管理", businessType = BusinessType.UPDATE)

在这里插入图片描述

当我们对用户信息进行修改的时候,就可以在操作日志菜单中看到

在这里插入图片描述

6、数据权限

所谓数据权限,就是不同的人对数据的权限不同。举个例子,一个学校的校长能看到学生管理系统的全部学生的信息,进行管理维护;而一个老师只能维护管理系统中自己班的学生,别的人根本看不到。

一般来说,但凡是一个管理类的系统,都有着数据方面的权限。ry中的数据权限是AOP切入,修改SQL实现的,让SQL中拼接一些关于权限的SQL来实现。

在这里插入图片描述

可以看到DataScopeAspect切面类,切的是注解@DataScope,处理请求前执行会拿到切入点、拿到@DataScope()注解。


我们来实操一下:

首先随便建立一个表,注意,表字段必须有user_id,不然怎么和用户关联,不和用户表关联谈什么数据权限?

比如说,有这样一个需求:

  • 作为一个高校的研究生,有可能要每周收青年大学习的截图。那么我现在需要学生自己去我的网站上面上传截图,并且用户自己只能看到自己上传的截图,但是管理员可以看到所有的截图。这样一个需求,我们马上安排。

SQL语句如下:

-- 创建kuang_picture表
CREATE TABLE `kuangstudy_vue`.`kuang_picture`  (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`uri` varchar(500) NULL COMMENT '图片uri',`user_id` bigint(20) NULL COMMENT '关联用户user_id',`create_by` varchar(50) NULL COMMENT '创建人',`create_time` datetime NULL COMMENT '创建时间',`update_by` varchar(50) NULL COMMENT '更新人',`update_time` datetime NULL COMMENT '更新时间',`remark` varchar(500) NULL COMMENT '备注',PRIMARY KEY (`id`)
);

这里我就直接使用若依的代码生成器来生成页面,只需要注意图片的URI显示类型是图片上传就好。

在这里插入图片描述

然后我们使用两个普通用户分别上传图片,发现这俩人竟然可以看到别人的图片。

在这里插入图片描述

这就不合理了,我们要自己开始编码,首先要填充 user_id,不然谈什么数据权限?填充之前表里面这么显示的:

在这里插入图片描述

我们开始编码填充:

在这里插入图片描述

然后重启项目,我们再次使用普通角色上传图片,就会连带着 user_id 显示

在这里插入图片描述

那么我们开始进行数据权限的编码,需要在查询的SQL上进行改动,我们对查询图片列表的sql进行改动:

在这里插入图片描述

我们给 kuang_picture 、 sys_user 、 sys_dept 三张表分别起别名为 kud ,由于 sys_user 中也有 create_by等字段,所以我们要指定查询的是哪张表的user_idcreate_by等字段。

在这里插入图片描述

所以我们加上查询指定的表名.id、表名.uri等等

在这里插入图片描述

最重要的一步:记得在sql最后加上 ${params.dataScope}

在这里插入图片描述

最后在方法上加上注解@DataScope,d表示部门表的别名,u表示用户表的别名。

在这里插入图片描述

这样就实现了我们使用 Gitee 登录的用户上传图片只有 Gitee 登录的用户可以看到, ry 登录的用户上传图片只有 ry 用户可以看到,管理员可以看到所有人的图片。

7、服务监控

从服务监控的前端页面展示看到,有CPU信息、内存信息、服务器信息、Java虚拟机信息和磁盘信息。

在这里插入图片描述

后端代码如上,就是调用API封装成Server对象,将信息返回给前端。

在这里插入图片描述

7.1、服务监控前端

当打开服务监控的页面时,页面会有一个加载的 loading

在这里插入图片描述

可能有小伙伴问,为啥先getList,后loading的,为啥loading还能走到?

在这里插入图片描述

因为getList里面有Promise的异步请求操作。异步不会阻塞。此外,getList方法的接口还睡了一秒钟,所以正常来说getList后执行完毕。

在这里插入图片描述

7.2、栅格布局

我们自己来仿照若依的服务监控来写一个栅格布局,首先新建菜单:

在这里插入图片描述

  1. 之后去views/kuang下新建kuang_server.vue
<template><div class="kuang-contain"><el-row :gutter="30"><el-col :span="12"><el-card><div slot="header"><span><i class="el-icon-cpu"></i> CPU</span><el-button style="float: right; padding: 3px 0" type="text">操作按钮</el-button></div><div><el-descriptions :column="2"><el-descriptions-item label="核心数" v-if="server.cpu"><el-tag size="medium">{{ server.cpu.cpuNum }}核</el-tag></el-descriptions-item><el-descriptions-item label="系统使用率"v-if="server.cpu"><el-tag size="small">{{ server.cpu.sys }}%</el-tag></el-descriptions-item><el-descriptions-item label="用户使用率"v-if="server.cpu"><el-tag size="small">{{ server.cpu.used }}%</el-tag></el-descriptions-item><el-descriptions-item label="当前等待率"v-if="server.cpu"><el-tag size="small">{{ server.cpu.wait }}%</el-tag></el-descriptions-item><el-descriptions-item label="当前空闲率"v-if="server.cpu"><el-tag size="small">{{ server.cpu.free }}%</el-tag></el-descriptions-item></el-descriptions></div></el-card></el-col><el-col :span="6"><el-card><div slot="header"><span><i class="el-icon-tickets"></i> 服务器内存</span><el-button style="float: right; padding: 3px 0" type="text">操作按钮</el-button></div><div><el-descriptions :column="2"><el-descriptions-item label="总内存" v-if="server.mem"><el-tag size="medium">{{ server.mem.total }}G</el-tag></el-descriptions-item><el-descriptions-item label="已用内存"v-if="server.mem"><el-tag size="small">{{ server.mem.used }}G</el-tag></el-descriptions-item><el-descriptions-item label="剩余内存"v-if="server.mem"><el-tag size="small">{{ server.mem.free }}G</el-tag></el-descriptions-item><el-descriptions-item label="使用率"v-if="server.mem"><el-tag size="small">{{ server.mem.usage }}%</el-tag></el-descriptions-item></el-descriptions></div></el-card></el-col><el-col :span="6"><el-card><div slot="header"><span><i class="el-icon-tickets"></i> JVM内存</span><el-button style="float: right; padding: 3px 0" type="text">操作按钮</el-button></div><div><el-descriptions :column="2"><el-descriptions-item label="总内存" v-if="server.mem"><el-tag size="medium">{{ server.jvm.total }}M</el-tag></el-descriptions-item><el-descriptions-item label="已用内存"v-if="server.mem"><el-tag size="small">{{ server.jvm.used }}M</el-tag></el-descriptions-item><el-descriptions-item label="剩余内存"v-if="server.mem"><el-tag size="small">{{ server.jvm.free }}M</el-tag></el-descriptions-item><el-descriptions-item label="使用率"v-if="server.mem"><el-tag size="small">{{ server.jvm.usage }}%</el-tag></el-descriptions-item></el-descriptions></div></el-card></el-col></el-row><el-row class="kuang-row"><el-col><el-card><div slot="header"><span><i class="el-icon-monitor"></i> 服务器信息</span><el-button style="float: right; padding: 3px 0" type="text">操作按钮</el-button></div><div><el-descriptions :column="2"><el-descriptions-item label="服务器IP" v-if="server.sys"><el-tag size="medium">{{ server.sys.computerIp }}</el-tag></el-descriptions-item><el-descriptions-item label="服务器名称"v-if="server.sys"><el-tag size="small">{{ server.sys.computerName }}</el-tag></el-descriptions-item><el-descriptions-item label="系统架构"v-if="server.sys"><el-tag size="small">{{ server.sys.osArch }}</el-tag></el-descriptions-item><el-descriptions-item label="操作系统"v-if="server.sys"><el-tag size="small">{{ server.sys.osName }}</el-tag></el-descriptions-item><el-descriptions-item label="项目路径"v-if="server.sys"><el-tag size="medium">{{ server.sys.userDir }}</el-tag></el-descriptions-item></el-descriptions></div></el-card></el-col></el-row><el-row class="kuang-row"><el-col><el-card><div slot="header"><span>Java虚拟机</span><el-button style="float: right; padding: 3px 0" type="text">操作按钮</el-button></div><div><el-descriptions :column="3"><el-descriptions-item label="Java名称"  v-if="server.jvm"><el-tag size="medium">{{ server.jvm.name }}</el-tag></el-descriptions-item><el-descriptions-item label="服务启动时间" v-if="server.jvm"><el-tag size="small">{{ server.jvm.startTime }}</el-tag></el-descriptions-item><el-descriptions-item label="jdk安装路径" v-if="server.jvm"><el-tag size="small">{{ server.jvm.home }}</el-tag></el-descriptions-item><el-descriptions-item label="jdk版本" v-if="server.jvm"><el-tag size="small">{{ server.jvm.version }}</el-tag></el-descriptions-item><el-descriptions-item label="最大可用内存数" v-if="server.jvm"><el-tag size="small">{{ server.jvm.max }}M</el-tag></el-descriptions-item><el-descriptions-item label="运行时长" v-if="server.jvm"><el-tag size="medium">{{ server.jvm.runTime }}</el-tag></el-descriptions-item></el-descriptions></div></el-card></el-col></el-row></div>
</template><script>
import {getServer} from "@/api/monitor/server";export default {name: "kuang_server",created() {this.getList();this.openLoading();},data(){return {server: [],}},methods: {/** 查询服务器信息 */getList() {getServer().then(response => {this.server = response.data;console.log(this.server);this.$modal.closeLoading();});},// 打开加载层openLoading() {this.$modal.loading("正在加载服务监控数据,请稍候!");}}
}
</script><style scoped>
.kuang-contain{margin: 30px;
}.kuang-row{margin-top: 30px;
}</style>

在这里插入图片描述

仿造若依写一个监控页面就完成了。

8、Swagger

原生swagger不但很丑,而且参数配置的请求list还有必填项很难绕过!所以我们来换个皮肤:

  1. 在跟pom.xml中引入
<!--Swagger增强-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-ui</artifactId><version>${knife4j-spring-ui.version}</version>
</dependency>
  1. 在admin的pom.xml中继承
<!--Swagger增强-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-ui</artifactId>
</dependency>
  1. 编写前端vue文件
<template><i-frame :src="url" />
</template>
<script>
import iFrame from "@/components/iFrame/index";
export default {name: "kuang_swagger",components: { iFrame },data() {return {url: process.env.VUE_APP_BASE_API + "/doc.html"};},
};
</script>
  1. 添加菜单

在这里插入图片描述

如此便大功告成

在这里插入图片描述

我们来看一下SwaggerConfig,启用Swagger,哪些接口暴露给Swagger展示呢?我们可以使用注解,也可以指定扫描哪个包中的注解,也可以扫描所有的api。最后还设置了一个请求前缀pathMapping,在 yml 中配置为 /dev-api,意思是说请求项目的/abc接口实际上需要请求/dev-api/abc,这一点需要特别注意!

在这里插入图片描述

同时在类中可以看到Swagger文档的标题、描述设置等。

在这里插入图片描述

那么我们如何使用Swagger呢?我们想要自己的API展示到Swagger里面该怎么做呢?很简单,我们在自己包也引入关于Swagger的依赖

<!-- swagger3-->
<dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId>
</dependency><!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
<dependency><groupId>io.swagger</groupId><artifactId>swagger-models</artifactId><version>1.6.2</version>
</dependency><!--Swagger增强-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-ui</artifactId>
</dependency>

然后在自己的方法上加上注解@ApiOperation("xxxx")

在这里插入图片描述

然后就可以在Swagger中看到啦!

在这里插入图片描述

9、XSS脚本攻击

以若依系统为例,我们在修改用户的时候,企图在备注中植入 JavaScript 脚本,然后将会被ry的XSS脚本攻击拦截:

在这里插入图片描述

若依如何抵抗XSS攻击呢?

当xss.enabled为true的时候,会开启XSS攻击防护,

首先new一个过滤注册器FilterRegistrationBean,当请求来的时候会触发这个过滤器,注册器里面设置拦截路径,拦截路径也就是我们在yml中配置的/system/*,/monitor/*,/tool/*,给过滤器设置一个名字叫xssFilter,设置过滤器的执行顺序,优先级为最高,最终将yml中排除的链接初始化进过滤器,也就是排除的链接/system/notice不需要开启XSS防护。

在这里插入图片描述

  • 过滤器可以根据DispatcherType的类型选择是否执行
  • FilterRegistrationBean,这个类用于把我们的过滤器注册到servlet容器中
  • registration.addUrlPatterns用于设置拦截的URL
  • registration.setOrder用于设置优先级,数字越小优先级越高
  • registration.setInitParameters用于设置初始化参数,例如可以设置excludes(排除的URL,然而逻辑需要自己实现,默认不支持),接受参数类型为Map<String, String>

在这里插入图片描述

接着我们看看XSS过滤器到底干了什么

在这里插入图片描述

我们这里直接说结论:

  • init方法主要是为了处理排除的URL
  • handleExcludeURL方法用于看看URL是不是排除的URL,如果是GET和DELETE则直接不过滤;另外,如果所请求的URL在排除URL也直接不过滤
  • XSS过滤原理是重新包装了Request,重写了getParameterValues方法和getInputStream方法。
  • getParameterValues方法用于获取参数值的数组,如果存在值,那么就进行HTML过滤,并且排除前后空格。
  • getInputStream方法用于处理JSON类型,如果发现是JSON,那么会先HTML过滤,然后重新包装成流进行返回。

10、防止重复提交过滤

我们讲述过前端的防止重复提交,如果请求数据请求URL最近一次请求一致,并且请求间隔小于1000ms,就进行请求拦截,直接拒绝当前请求。

从我上面的描述,发现了一个bug,总有手快的人,喜欢点A按钮,然后立刻点B按钮,然后又立刻点A按钮。那么对于A按钮是重复提交了,但是又不满足前端判断重复请求的条件,于是重复请求进入了后端,这时候就需要后端再次校验,是不是重复请求。

后端过滤重复请求也是通过过滤器,并且和XSS过滤惊人的相似。

在这里插入图片描述

首先new一个注册器,然后注册一个重复过滤器,拦截所有路径,优先级为最低,注入到Spring容器中。

相关文章:

【开源风云】从若依系列脚手架汲取编程之道(五)

&#x1f4d5;开源风云系列 &#x1f34a;本系列将从开源名将若依出发&#xff0c;探究优质开源项目脚手架汲取编程之道。 &#x1f349;从不分离版本开写到前后端分离版&#xff0c;再到微服务版本&#xff0c;乃至其中好玩的一系列增强Plus操作。 &#x1f348;希望你具备如下…...

金融市场的衍生品交易及其风险管理探讨

金融衍生品市场是现代金融体系的重要组成部分&#xff0c;其交易量和复杂性在过去几十年中迅速增长。衍生品&#xff0c;如期权、期货、掉期等&#xff0c;因其灵活性和杠杆效应&#xff0c;广泛应用于风险管理、投机和资产配置等多个领域。本文将探讨金融衍生品交易的关键特点…...

一、创建型(单例模式)

单例模式 概念 单例模式是一种创建型设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。它控制类的实例化过程&#xff0c;防止外部代码创建新的实例。 应用场景 日志记录&#xff1a;确保只有一个日志记录器&#xff0c;以便于管理和避免重复记…...

毕业设计项目-古典舞在线交流平台的设计与实现(源码/论文)

项目简介 基于springboot实现的&#xff0c;主要功能如下&#xff1a; 技术栈 后端框框&#xff1a;springboot/mybatis 前端框架&#xff1a;html/JavaScript/Css/vue/elementui 运行环境&#xff1a;JDK1.8/MySQL5.7/idea&#xff08;可选&#xff09;/Maven3&#xff08…...

【秋招笔试】10.09华子秋招(已改编)-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 本次的三题全部上线…...

【算法笔记】双指针算法深度剖析

【算法笔记】双指针算法深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;算法笔记 文章目录 【算法笔记】双指针算法深度剖析前言一.移动零1.1题目1.2思路分析1.3代码实现 二.复写零2.1题目2.2思路分析2.3代码实现 三.快乐数3.1题目…...

第二十二天|回溯算法| 理论基础,77. 组合(剪枝),216. 组合总和III,17. 电话号码的字母组合

目录 回溯算法理论基础 1.题目分类 2.理论基础 3.回溯法模板 补充一个JAVA基础知识 什么时候用ArrayList什么时候用LinkedList 77. 组合 未剪枝优化 剪枝优化 216. 组合总和III 17. 电话号码的字母组合 回溯法的一个重点理解&#xff1a;细细理解这句话&#xff01;…...

关闭IDM自动更新

关闭IDM自动更新 1 打开注册表2 找到IDM注册表路径 1 打开注册表 winR regedit 2 找到IDM注册表路径 计算机\HKEY_CURRENT_USER\Software\DownloadManager 双击LstCheck&#xff0c;把数值数据改为0 完成 感谢阅读...

Go 性能剖析工具 pprof 与 Graphviz 教程

在 Golang 开发中&#xff0c;性能分析是确保应用高效运行的重要环节。本文介绍如何使用 gin-contrib/pprof 在 Gin 应用中集成性能剖析工具&#xff0c;并结合 Graphviz 生成图形化的性能分析结果&#xff0c;如火焰图。这套流程帮助开发者更好地理解和优化 Go 应用的性能。 目…...

【题目解析】蓝桥杯23国赛C++中高级组 - 斗鱼养殖场

【题目解析】蓝桥杯23国赛C中高级组 - 斗鱼养殖场 题目链接跳转&#xff1a;点击跳转 前置知识&#xff1a; 了解过基本的动态规划。熟练掌握二进制的位运算。 题解思路 这是一道典型的状压动态规划问题。设 d p i , j dp_{i, j} dpi,j​ 表示遍历到第 i i i 行的时候&a…...

JavaScript可视化:探索顶尖的图表库

JavaScript可视化&#xff1a;探索顶尖的图表库 在这个被数据驱动的时代&#xff0c;你有没有想过&#xff0c;数据本身是如何变得有意义的&#xff1f;答案就是数据可视化。通过图表和图形&#xff0c;我们不仅可以看到数据&#xff0c;还可以感受到它&#xff0c;从而做出明…...

谷歌AI大模型Gemini API快速入门及LangChain调用视频教程

1. 谷歌Gemini API KEY获取及AI Studio使用 要使用谷歌Gemini API&#xff0c;首先需要获取API密钥。以下是获取API密钥的步骤&#xff1a; 访问Google AI Studio&#xff1a; 打开浏览器&#xff0c;访问Google AI Studio。使用Google账号登录&#xff0c;若没有账号&#xf…...

进入容器:掌控Docker的世界

进入容器:掌控Docker的世界 在这个快速发展的技术时代,你是否曾被Docker的庞大生态所吸引?那么,有没有想过在这个容器化的世界里,如何快速高效地“进入”这些隐藏在虚拟墙后的容器呢?容器就如同魔法箱,装载着应用与服务,而你,通过探索这些容器,能够更好地管理、排除…...

初始Linux(二)基础命令

前言&#xff1a; 之前那一篇我们已经介绍了一部分的基础命令&#xff0c;当然那只不过是九牛一毛&#xff0c;本篇我们继续介绍一些比较重要且需要掌握的基础命令。 mv命令&#xff1a; 其实这个命令有两个功能&#xff0c;一个是移动&#xff08;剪切&#xff09;文件&#…...

STM32 OLED

文章目录 前言一、OLED是什么&#xff1f;二、使用步骤1.复制 OLED.C .H文件1.1 遇到问题 2.统一风格3.主函数引用头文件3.1 oled.h 提供了什么函数 4.介绍显示一个字符的函数5. 显示十进制函数的讲解 三、使用注意事项3.1 配置符合自己的引脚3.2 花屏总结 前言 提示&#xff…...

伦敦金实时行情决策辅助!

在伦敦金实时交易的过程中&#xff0c;投资者主要依赖技术分析来辅助自己的投资决策。与基本面分析不同&#xff0c;技术分析侧重于研究金价的走势和市场行为&#xff0c;通过图表和技术指标来预测未来的市场走势。常用的技术分析方法包括&#xff1a; 趋势线和支撑阻力位&…...

​Leetcode 746. 使用最小花费爬楼梯​ 入门dp C++实现

问题&#xff1a;Leetcode 746. 使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你…...

路由协议常见知识点

路由协议是网络通信的基础&#xff0c;主要负责在网络中传递数据包&#xff0c;并确保它们从源节点传递到目标节点。本文将介绍一些常见的路由协议知识点&#xff0c;包括路由协议的分类、特性、配置与管理以及常见问题。 一、路由协议的分类 距离矢量路由协议&#xff1a; R…...

多模态大语言模型(MLLM)-InstructBlip深度解读

前言 InstructBlip可以理解为Blip2的升级版&#xff0c;重点加强了图文对话的能力。 模型结构和Blip2没差别&#xff0c;主要在数据集收集、数据集配比、指令微调等方面下文章。 创新点 数据集收集&#xff1a; 将26个公开数据集转换为指令微调格式&#xff0c;并将它们归类…...

网页前端开发之Javascript入门篇(7/9):字符串

Javascript字符串 什么是字符串&#xff1f; 答&#xff1a;其概念跟 Python教程 介绍的一样&#xff0c;只是语法上有所变化。 在 Javascript 中&#xff0c;一个字符串变量可以看做是其内置类String的一个实例&#xff08;Javascript会自动包装&#xff09;。 因此它拥有一…...

双登股份再战IPO:数据打架,实控人杨善基千万元股权激励儿子

撰稿|行星 来源|贝多财经 近日&#xff0c;双登集团股份有限公司&#xff08;下称“双登股份”&#xff09;递交招股书&#xff0c;准备在港交所主板上市&#xff0c;中金公司、建银国际、华泰国际为其联席保荐人。 贝多财经了解到&#xff0c;这并非双登股份首次向资本市场…...

4.Python 函数(函数的定义、函数的传入参数、函数的返回值、None 类型、函数说明文档、变量的作用域)

一、函数快速入门 1、函数概述 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现特定功能的代码段 name "Hello World" name_length len(name)print(f"{name} 的长度为 {name_length}") # Hello World 的长度为 11len() 是Python 内置的函…...

【JavaEE】——文件IO

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;认识文件 1&#xff1a;文件的概念 2&#xff1a;文件的结构 3&#xff1a;文件路径…...

Python的pandas库基本操作(数据分析)

一、安装&#xff0c;导入 1、安装 使用包管理器安装&#xff1a; pip3 install pandas 2、导入 import pandas as pd as是为了方便引用起的别名 二、DateFrame 在Pandas库中&#xff0c;DataFrame 是一种非常重要的数据结构&#xff0c;它提供了一种灵活的方式来存储和…...

软件测试(平铺版本)

目录 黑盒测试&#xff1a; 定义: 示例&#xff1a;登录功能的黑盒测试 适合使用黑盒测试的情况 几种常见的黑盒测试方法&#xff1a; 1. 等价类划分&#xff08;Equivalence Partitioning&#xff09; 2. 边界值分析&#xff08;Boundary Value Analysis&#xff09; …...

树控件QTreeWidget

树控件跟表格控件类似&#xff0c;也可以有多列&#xff0c;也可以只有1列&#xff0c;可以有多行&#xff0c;只不过每一行都是一个QTreeWidgetItem&#xff0c;每一行都是一个可以展开的树 常用属性和方法 显示和隐藏标题栏 树控件只有水平标题栏 //获取和设置标题栏的显…...

Python酷库之旅-第三方库Pandas(139)

目录 一、用法精讲 626、pandas.plotting.scatter_matrix方法 626-1、语法 626-2、参数 626-3、功能 626-4、返回值 626-5、说明 626-6、用法 626-6-1、数据准备 626-6-2、代码示例 626-6-3、结果输出 627、pandas.plotting.table方法 627-1、语法 627-2、参数 …...

昇思学习打卡营学习记录:CycleGAN壁画修复

按照提示&#xff0c;运行实训代码 进入实训平台&#xff1a;https://xihe.mindspore.cn/projects 选择“jupyter 在线编辑器” 启动“Ascend开发环境” &#xff1a;Ascend开发环境需要申请&#xff0c;大家可以申请试试看 启动开发环境后&#xff0c;在左边的文件夹中&am…...

南京大学《软件分析》李越, 谭添——1. 导论

导论 主要概念: soundcompletePL领域概述 动手学习 本节无 文章目录 导论1. PL(Programming Language) 程序设计语言1.1 程序设计语言的三大研究方向1.2 与静态分析相关方向的介绍与对比静态程序分析动态软件测试形式化(formal)语义验证(verification) 2. 静态分析:2.1莱斯…...

使用seata管理分布式事务

做应用开发时&#xff0c;要保证数据的一致性我们要对方法添加事务管理&#xff0c;最简单的处理方案是在方法上添加 Transactional 注解或者通过编程方式管理事务。但这种方案只适用于单数据源的关系型数据库&#xff0c;如果项目配置了多个数据源或者多个微服务的rpc调用&…...