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

news2024/10/9 12:53:28

📕开源风云系列

  • 🍊本系列将从开源名将若依出发,探究优质开源项目脚手架汲取编程之道。
  • 🍉从不分离版本开写到前后端分离版,再到微服务版本,乃至其中好玩的一系列增强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容器中。

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

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

相关文章

基于Java(Jsp+Sevlet)+MySql 实现的(Web)成绩管理系统

1 概述 1.1 开发背景 随着学生数量的日渐增多&#xff0c;学生教务系统的数据量也不断增加&#xff0c;这无疑大大增加了教务系统的负担。如果能把负责学生成绩管理的模块独立出来形成一个独立的系统&#xff0c;便可以有效降低教务系统的数据量&#xff0c;不仅可以方便管理…

封装el-upload组件,用于上传图片和视频的组件

使用环境 vue3element plus 需要根据后端返回结构修改的函数&#xff1a;onPreview onRemove onSuccess 组件使用 基本使用 源代码&#xff1a; <script setup> import AutoUploadFile from /components/auto-upload-file/index.vue function change(urls){console.log…

懂球短视频系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;上传视频管理&#xff0c;用户管理&#xff0c;懂球视频管理&#xff0c;分享视频管理&#xff0c;收藏视频管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;上传视频&a…

深入剖析递归算法:原理、特点、应用与优化策略

在上一篇文章&#x1f449;【剖析十大经典二叉树题目】中&#xff0c;运用到了大量的递归算法&#xff0c;故本文将解析递归算法。 目录 &#x1f4af;引言 &#x1f4af;递归算法的定义与原理 ⭐定义 ⭐原理 &#x1f4af;递归算法的特点 ⭐简洁性 ⭐可读性 ⭐通用性 …

【Java】单例模式详解与实践

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持&#xff01; 单例模式 Singleton是一种常用的软件模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问方法来获取这个实例。这种模式广泛应用于需要控制实例化次数的场景&#xff0c;如数据库…

昇思MindSpore进阶教程--数据处理性能优化(中)

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 shuffle性能优化 shuffle操作主要是对有…

VMware ESXi 8.0U3 集成 AQC 网卡定制版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 8.0U3 集成 AQC 网卡定制版更新 OEM BIOS 2.7 支持 Windows Server 2025 VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) 发布 ESXi 8.0U3 集成驱动版&#xff0c;在个人电脑上运行企业级工作负载 请访问原文链接&…

数字化转型引领新时代:从架构到产品的全链路创新解析

在当前瞬息万变的商业环境中&#xff0c;数字化转型已经成为各类组织的核心战略手段。本文从数字化专业知识体系 (DPBOK) 中提炼出最具价值的核心观点&#xff0c;详细分析了数字化转型对企业的影响、实现路径&#xff0c;以及如何通过技术创新、文化转变和管理优化&#xff0c…

YOLO11涨点优化:注意力魔改 | 新颖的多尺度卷积注意力(MSCA),即插即用,助力小目标检测

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文全网首发独家改进&#xff1a;多尺度卷积注意力&#xff08;MSCA&#xff09;&#xff0c;有效地提取上下文信息&#xff0c;新颖度高&#xff0c;创新十足。 &#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进&#xff1a;分别加入…

协议转换器——连接未来生产的纽带

智能制造作为制造业前沿趋势&#xff0c;面临不同设备和系统间通信协议不兼容导致的信息交换困难。我们自主研发的MG协议转换器作为桥梁与纽带&#xff0c;实现了不同设备和系统间的顺畅数据交换&#xff0c;提高了生产效率&#xff0c;降低了生产成本。在工业自动化和能源管理…

【d63】【Java】【力扣】142.训练计划IV

思路 出口&#xff1a; 1. l1 null && l2 null 2. 一个null 一个不为bull,但是还需要向下递归 每层&#xff1a; 判断哪一个更小&#xff0c;更小的放进新的数组 代码 递归实现 /*** Definition for singly-linked list.* public class ListNode {* int va…

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

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

labview和QT编程

Labview LabView所面向的并非传统意义上的程序员。他的所有功能都可以通过组合某些组件来完成。程序的流程控制&#xff0c;【www.zhugedz.com】比如循环之类的也是通过画图一样的操作来做的。 所有的程序功能几乎都可以通过鼠标来构造出来。优点是做一个能运行的程序非常简单…

有关环境变量的一些话题-----环境变量的分类

配置环境变量的文件&#xff1a; 环境变量的分类&#xff1a; 环境变量加载顺序 一般添加系统环境变量&#xff0c;修改/etc/profile文件&#xff0c;如果操作失误&#xff0c;删除重要配置&#xff0c;影响系统运行。 centos7版本中 /etc/profile 默认扫描路径 /etc/profile.…

微信小程序处理交易投诉管理,支持多小程序,一键授权模式

大家好&#xff0c;我是小悟 1、问题背景 玩过微信小程序生态的&#xff0c;或许就有这种感受&#xff0c;如果收到投诉单&#xff0c;不会及时通知到手机端&#xff0c;而是每天早上10:00向小程序的管理员及运营者推送通知。通知内容为截至前一天24时该小程序账号内待处理的交…

188页企业数字化转型建设方案(数据中台、业务中台、AI中台)

建设背景 随着信息技术的不断进步&#xff0c;企业面临着前所未有的机遇与挑战。数字化转型不仅是技术层面的革新&#xff0c;更是企业运营模式和思维方式的深刻变革。通过数字化转型&#xff0c;企业可以实现资源的最优配置、业务的精准量化以及管理的智能化&#xff0c;从而…

服装生产管理:SpringBoot框架的高效实现

3 系统分析 3.1 可行性分析 可行性分析是该平台系统进行投入开发的基础第一步&#xff0c;必须对其进行可行性分析才能够降低不必要的需要从而使资源合理利用&#xff0c;更具有性价比和降低成本&#xff0c;同时也是系统平台的成功的未雨绸缪的一步。 3.1.1 技术可行性 技术…

【网易buff】无法登录steam,显示创建账号,无法解决

手机速度大提升&#xff01;浏览器内核WebView升级指南 WebViewUpgrade 心血来潮想通过网易buff花钱快乐一下&#xff0c;结果发现这app有问题&#xff0c;是因为webview版本问题&#xff0c;这开发真傻逼啊 发现经过 在buff重新登陆Steam的时候&#xff0c;页面只有创建账号…

服装生产管理:SpringBoot框架的创新设计

4 系统设计 4.1 系统结构设计 在结构设计过程中&#xff0c;首先对系统进行需求分析&#xff0c;然后进行系统初步设计&#xff0c;将系统功能模块细化&#xff0c;具体分析每一个功能模块具体应该首先哪些功能&#xff0c;最后将各个模块进行整合&#xff0c;实现系统结构的…

基于SSM的学生社团管理系统—计算机毕业设计源码37391

目 录 摘要 1 绪论 1.1研究背景 1.2研究目的和意义 1.3系统开发技术的特色 1.4 ssm框架介绍 1.5论文结构与章节安排 2 学生社团管理系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1数据流程 2.3.2业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能…