quartz整合前端vue加后端springboot

因工作需求,需要能修改定时的任务,前端vue3,后端是springboot

看看页面效果:

首先maven加上引入

<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.1</version></dependency>

然后yaml加上配置(Quartz就这点好,自动给你建表了)

#服务器配置
server:port: 8080undertow:threads:# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程io: 16# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载worker: 400# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理buffer-size: 1024# 是否分配的直接内存direct-buffers: truespring:datasource:driver-class-name: com.mysql.cj.jdbc.Driver#driver-class-name: org.postgresql.Driver#driver-class-name: oracle.jdbc.OracleDriver#driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriverdruid:# MySql、PostgreSQL、SqlServer、DaMeng校验validation-query: select 1# Oracle、YashanDB校验#oracle: true#validation-query: select 1 from dualvalidation-query-timeout: 2000initial-size: 5max-active: 20min-idle: 5max-wait: 60000test-on-borrow: falsetest-on-return: falsetest-while-idle: truetime-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000stat-view-servlet:enabled: truelogin-username: login-password: web-stat-filter:enabled: trueurl-pattern: /*exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'session-stat-enable: truesession-stat-max-count: 10quartz:# 任务存储类型job-store-type: "jdbc"# 关闭时等待任务完成wait-for-jobs-to-complete-on-shutdown: false# 是否覆盖已有的任务overwrite-existing-jobs: true# 是否自动启动计划程序auto-startup: true# 延迟启动startup-delay: 0sjdbc:# 数据库架构初始化模式(never:从不进行初始化;always:每次都清空数据库进行初始化;embedded:只初始化内存数据库(默认值))initialize-schema: "always"#todo 后续改# 相关属性配置properties:org:quartz:scheduler:# 调度器实例名称instanceName: QuartzScheduler# 分布式节点ID自动生成instanceId: AUTOjobStore:class: org.springframework.scheduling.quartz.LocalDataSourceJobStoredriverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate# 表前缀tablePrefix: QRTZ_# 是否开启集群isClustered: true# 数据源别名(自定义)dataSource: quartz# 分布式节点有效性检查时间间隔(毫秒)clusterCheckinInterval: 10000useProperties: false# 线程池配置threadPool:class: org.quartz.simpl.SimpleThreadPoolthreadCount: 10threadPriority: 5threadsInheritContextClassLoaderOfInitializingThread: true

然后开始正式后台代码:

package org.springblade.etl.source.controller;import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.etl.source.entity.JobInfo;
import org.springblade.etl.source.service.QuartzService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/quartz")
public class QuartzController {@Autowiredprivate QuartzService quartzService;//创建任务@PostMapping("/save")public R createJob(@RequestBody JobInfo jobInfo) {return quartzService.addCronJob(jobInfo.getJobName(), jobInfo.getCron(), "org.springblade.etl.source.task.TaskJob").equals("SUCCESS")?R.status(true):R.status(false);//todo 类名写在这}//删除任务@PostMapping("/remove")public R deleteJob(@RequestParam("jobName")String jobName) {return quartzService.deleteCronJob(jobName, null, null, null).equals("SUCCESS")?R.status(true):R.status(false);}//执行一次@PostMapping("/executeImmediately")public String executeImmediately(@RequestBody JobInfo jobInfo) {return quartzService.executeImmediately(jobInfo.getJobName(), "org.springblade.etl.source.task.TaskJob");}//获取任务状态@PostMapping("/detail")public R<JobInfo> getJobStatus(@RequestParam("jobName")String jobName) {return R.data(quartzService.getJobStatus(jobName, null));}//获取所有任务@PostMapping("/list")public R getAllJob() {// 创建分页对象,指定当前页码和每页显示的数量long currentPage = 1; // 当前页码long pageSize = 10; // 每页显示数量Page<JobInfo> page = new Page<>(currentPage, pageSize);page.setRecords(quartzService.getAllJob());return R.data(page);}//修改定时任务时间@PostMapping("/submit")public R updateJob(@RequestBody JobInfo jobInfo) {quartzService.deleteCronJob(jobInfo.getJobName(), jobInfo.getJobGroup(), jobInfo.getTriggerName(), jobInfo.getTriggerGroup());return quartzService.addCronJob(jobInfo.getJobName(), jobInfo.getCron(), "org.springblade.etl.source.task.TaskJob").equals("SUCCESS")?R.status(true):R.status(false);}}
package org.springblade.etl.source.service;import org.springblade.etl.source.entity.JobInfo;import java.util.List;public interface QuartzService {/*** 新增** @param jobName* @param cron* @param jobClassName* @return*/String addCronJob(String jobName, String cron, String jobClassName);/*** 停止** @param jobName* @param jobGroup* @param triggerName* @param triggerGroup* @return*/String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup);/*** 立即执行,不定时** @param jobName* @param jobClassName* @return*/String executeImmediately(String jobName, String jobClassName);// 暂停// 获取状态JobInfo getJobStatus(String jobName, String jobGroup);List<JobInfo> getAllJob();
}
package org.springblade.etl.source.service.impl;import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.etl.source.entity.JobInfo;
import org.springblade.etl.source.service.QuartzService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
@Slf4j
public class QuartzServiceImpl implements QuartzService {@Autowiredprivate Scheduler scheduler;private static final String DEFAULT_JOB_GROUP = "default_job_group";private static final String DEFAULT_TRIGGER_GROUP = "default_trigger_group";private static final String TRIGGER_PRE = "Trigger_";@Overridepublic String addCronJob(String jobName, String cron, String jobClassName) {try {// 当前任务不存在才进行添加JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);if (scheduler.checkExists(jobKey)) {log.info("[添加定时任务]已存在该作业,jobkey为:{}", jobKey);return "已存在该作业";}// 构建 JobJobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobKey).build();// cron表达式定时构造器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);// 构建 TriggerTrigger trigger = TriggerBuilder.newTrigger().withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP))
//                .startAt(DateUtil.parseDate(start))
//                .endAt(DateUtil.parseDate(end)).withSchedule(cronScheduleBuilder).build();// 启动调度器scheduler.scheduleJob(job, trigger);scheduler.start();return "SUCCESS";} catch (Exception e) {log.error("[新增定时任务]失败,报错:", e);return "FAIL";}}@Overridepublic String deleteCronJob(String jobName, String jobGroup, String triggerName, String triggerGroup) {try {JobKey jobKey = JobKey.jobKey(jobName, jobGroup);//如果triggerName和triggerGroup为空,则使用默认值if (StringUtil.isBlank(triggerName)&&StringUtil.isBlank(triggerGroup)){triggerName = TRIGGER_PRE + jobName;triggerGroup = DEFAULT_TRIGGER_GROUP;}TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);Trigger trigger = scheduler.getTrigger(triggerKey);if (null == trigger) {log.info("[停止定时任务]根据triggerName:{}和triggerGroup:{}未查询到相应的trigger!");return "SUCCESS";}//暂停触发器scheduler.pauseTrigger(triggerKey);// 移除触发器scheduler.unscheduleJob(triggerKey);// 删除任务scheduler.deleteJob(jobKey);log.info("[停止定时任务]jobName:{},jobGroup:{}, triggerName:{}, triggerGroup:{},停止--------------", jobName, jobGroup, triggerName, triggerGroup);return "SUCCESS";} catch (SchedulerException e) {log.error("[停止定时任务]失败,报错:", e);return "FAIL";}}public static Job getClass(String className) throws Exception {Class<?> classTemp = Class.forName(className);return (Job) classTemp.newInstance();}@Overridepublic String executeImmediately(String jobName, String jobClassName) {try {JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);JobDetail job = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobKey).build();Trigger trigger = TriggerBuilder.newTrigger().withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP)).build();// 启动调度器scheduler.scheduleJob(job, trigger);scheduler.start();return "SUCCESS";} catch (Exception e) {log.error("[立即执行一次任务,不定时]失败,报错:", e);return "FAIL";}}@Overridepublic JobInfo getJobStatus(String jobName, String jobGroup) {try {// 当前任务不存在才进行添加JobKey jobKey = JobKey.jobKey(jobName, DEFAULT_JOB_GROUP);// 利用JobKey查找任务是否存在if (scheduler.checkExists(jobKey)) {log.info("查找到该任务,任务名:{}, 状态为:{}", jobName, scheduler.getTriggerState(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP)));// 获取Cron触发器,从而获得Cron表达式CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP));String cronExpression = cronTrigger.getCronExpression();JobInfo jobInfo = new JobInfo();jobInfo.setJobName(jobKey.getName());jobInfo.setJobGroup(jobKey.getGroup());jobInfo.setStatus( scheduler.getTriggerState(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP)).toString());jobInfo.setTriggerName(TriggerKey.triggerKey(TRIGGER_PRE + jobName, DEFAULT_TRIGGER_GROUP).getName());jobInfo.setTriggerGroup(DEFAULT_TRIGGER_GROUP);jobInfo.setCron(cronExpression);return jobInfo;} else {throw  new RuntimeException("任务不存在");}} catch (SchedulerException e) {log.error("[查询任务状态]失败,报错:", e);throw  new RuntimeException("任务不存在");}}@Overridepublic List<JobInfo> getAllJob() {ArrayList<JobInfo> jobInfos = new ArrayList<JobInfo>();try {// 获取所有的触发器组for (String triggerGroup : scheduler.getTriggerGroupNames()) {// 获取指定触发器组下的所有触发器for (TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroup))) {Trigger trigger = scheduler.getTrigger(triggerKey);// 判断触发器类型是否为 CronTriggerif (trigger instanceof CronTrigger) {CronTrigger cronTrigger = (CronTrigger) trigger;JobKey jobKey = cronTrigger.getJobKey();JobInfo jobInfo = new JobInfo();jobInfo.setJobName(jobKey.getName());jobInfo.setJobGroup(jobKey.getGroup());jobInfo.setTriggerName(triggerKey.getName());jobInfo.setTriggerGroup(triggerKey.getGroup());//获取一下cron表达式jobInfo.setCron(cronTrigger.getCronExpression());jobInfo.setStatus( scheduler.getTriggerState(TriggerKey.triggerKey(TRIGGER_PRE + jobKey.getName(), DEFAULT_TRIGGER_GROUP)).toString());jobInfos.add(jobInfo);}}}if (jobInfos.size() == 0) {log.info("暂无定时任务");return null;}} catch (SchedulerException e) {log.error("[查询所有定时任务状态]失败,报错:", e);throw new RuntimeException("查询所有定时任务状态失败");}return jobInfos;}}

这个时间转换cron是我的自己的业务需要(因为前段不能让用户输入cron表达式,所以我让用户输入时间即可转换适合的cron表达式)

package org.springblade.common.utils;import org.quartz.CronExpression;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class CronTimeConverter {public static String convertCronToTime(String cronExpression) {try {CronExpression cron = new CronExpression(cronExpression);Date nextExecutionTime = cron.getTimeAfter(new Date());// 转换为指定的时间格式,比如 HH:mm// 这里使用 SimpleDataFormat 进行格式化SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");return sdf.format(nextExecutionTime);} catch (ParseException e) {e.printStackTrace();return ""; // 处理异常情况,返回空字符串或其他默认值}}//    public static void main(String[] args) {
//        String cronExpression = "0 40 14 * * ?";
//        String time = convertCronToTime(cronExpression);
//        System.out.println("Time based on cron expression " + cronExpression + " is: " + time);
//    }public static String convertTimeToCron(String time) {String[] timeParts = time.split(":");if (timeParts.length != 2) {return ""; // 处理异常情况,返回空字符串或其他默认值}String cronExpression = String.format("0 %s %s * * ?", timeParts[1], timeParts[0]);return cronExpression;}public static void main(String[] args) {String time = "14:40";String cronExpression = convertTimeToCron(time);System.out.println("Cron expression for time " + time + " is: " + cronExpression);}
}

你需要一个entity对象:

package org.springblade.etl.source.entity;import lombok.Data;
import org.springblade.common.utils.CronTimeConverter;
import org.springblade.core.tool.utils.StringUtil;import java.time.*;
import java.time.format.DateTimeFormatter;@Data
public class JobInfo {private String jobName;private String time = "2024-03-19T08:26:58.000Z" ;private String cron = "0 16 10 ? * *";private String jobGroup;private String triggerName;private String triggerGroup;private String status;public void setCron(String cron) {//顺便将时间写入if (StringUtil.isNotBlank(cron)) {this.cron = cron;String time = CronTimeConverter.convertCronToTime(cron);LocalTime localTime = LocalTime.parse(time);// 将当天的本地时间与当天的日期结合,并转换为ZonedDateTime对象ZonedDateTime localDateTime = ZonedDateTime.now().withHour(localTime.getHour()).withMinute(localTime.getMinute());// 将本地时间转换为UTC时间,并进行格式化ZonedDateTime utcDateTime = localDateTime.withZoneSameInstant(ZoneOffset.UTC);DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");this.time = utcDateTime.format(formatter);}}public void setTime(String utcTime){if (StringUtil.isNotBlank(utcTime)) {this.time =utcTime;//顺便将cron表达式写入// 将UTC时间字符串转换为Instant对象Instant instant = Instant.parse(utcTime);// 将Instant对象转换为本地时间ZonedDateTime localTime = instant.atZone(ZoneId.systemDefault());// 格式化本地时间为 "HH:mm" 形式DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");String formattedTime = localTime.format(formatter);String cron = CronTimeConverter.convertTimeToCron(formattedTime);this.cron = cron;}}public static void main(String[] args) {// UTC时间字符串String utcTime = "2024-03-19T08:26:58.000Z";// 将UTC时间字符串转换为Instant对象Instant instant = Instant.parse(utcTime);// 将Instant对象转换为本地时间ZonedDateTime localTime = instant.atZone(ZoneId.systemDefault());// 格式化本地时间为 "HH:mm" 形式DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");String formattedTime = localTime.format(formatter);System.out.println("本地时间(HH:mm):" + formattedTime);System.out.println("UTC时间:" + instant);System.out.println("本地时间:" + formattedTime);}}

下面是前端vue

<template><basic-container><avue-crud :option="option"v-model:search="search"v-model:page="page"v-model="form":table-loading="loading":data="data":permission="permissionList":before-open="beforeOpen"ref="crud"@row-update="rowUpdate"@row-save="rowSave"@row-del="rowDel"@search-change="searchChange"@search-reset="searchReset"@selection-change="selectionChange"@current-change="currentChange"@size-change="sizeChange"@refresh-change="refreshChange"@on-load="onLoad"><template #menu-left><el-button type="primary"icon="el-icon-s-promotion"@click="handleManualTrigger">手动触发</el-button>
<!--        <el-button type="danger"-->
<!--                   icon="el-icon-delete"-->
<!--                   plain-->
<!--                   v-if="permission.jobInfo_delete"-->
<!--                   @click="handleDelete">删 除-->
<!--        </el-button>-->
<!--        <el-button type="warning"-->
<!--                   plain-->
<!--                   icon="el-icon-download"-->
<!--                   @click="handleExport">导 出-->
<!--        </el-button>--></template></avue-crud></basic-container>
</template><script>import {getList, getDetail, add, update, remove,startETLForSYNL} from "@/api/source/jobInfo";import option from "@/option/source/jobInfo";import {mapGetters} from "vuex";import {exportBlob} from "@/api/common";import {getToken} from '@/utils/auth';import {downloadXls} from "@/utils/util";import {dateNow} from "@/utils/date";import NProgress from 'nprogress';import 'nprogress/nprogress.css';export default {data() {return {form: {},query: {},search: {},loading: true,page: {pageSize: 10,currentPage: 1,total: 0},selectionList: [],option: option,data: []};},computed: {...mapGetters(["permission"]),permissionList() {return {addBtn: this.validData(this.permission.jobInfo_add, false),viewBtn: this.validData(this.permission.jobInfo_view, false),delBtn: this.validData(this.permission.jobInfo_delete, false),editBtn: this.validData(this.permission.jobInfo_edit, false)};},ids() {let ids = [];this.selectionList.forEach(ele => {ids.push(ele.id);});return ids.join(",");}},methods: {rowSave(row, done, loading) {row.cron = null;//表达式不往后端传add(row).then(() => {this.onLoad(this.page);this.$message({type: "success",message: "操作成功!"});done();}, error => {loading();window.console.log(error);});},rowUpdate(row, index, done, loading) {row.cron = null;表达式不往后端传update(row).then(() => {this.onLoad(this.page);this.$message({type: "success",message: "操作成功!"});done();}, error => {loading();console.log(error);});},rowDel(row) {this.$confirm("确定将选择数据删除?", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(() => {return remove(row.jobName);}).then(() => {this.onLoad(this.page);this.$message({type: "success",message: "操作成功!"});});},handleDelete() {if (this.selectionList.length === 0) {this.$message.warning("请选择至少一条数据");return;}this.$confirm("确定将选择数据删除?", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(() => {return remove(this.jobName);}).then(() => {this.onLoad(this.page);this.$message({type: "success",message: "操作成功!"});this.$refs.crud.toggleSelection();});},handleExport() {let downloadUrl = `/blade-jobInfo/jobInfo/export-jobInfo?${this.website.tokenHeader}=${getToken()}`;const {} = this.query;let values = {};this.$confirm("是否导出数据?", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(() => {NProgress.start();exportBlob(downloadUrl, values).then(res => {downloadXls(res.data, `自动任务表${dateNow()}.xlsx`);NProgress.done();})});},beforeOpen(done, type) {if (["edit", "view"].includes(type)) {getDetail(this.form.jobName).then(res => {this.form = res.data.data;});}done();},searchReset() {this.query = {};this.onLoad(this.page);},searchChange(params, done) {this.query = params;this.page.currentPage = 1;this.onLoad(this.page, params);done();},selectionChange(list) {this.selectionList = list;},selectionClear() {this.selectionList = [];this.$refs.crud.toggleSelection();},currentChange(currentPage){this.page.currentPage = currentPage;},sizeChange(pageSize){this.page.pageSize = pageSize;},refreshChange() {this.onLoad(this.page, this.query);},onLoad(page, params = {}) {this.loading = true;const {} = this.query;let values = {};getList(page.currentPage, page.pageSize, values).then(res => {console.log("定时任务得到的数据=",res);const data = res.data.data;this.page.total = data.total;this.data = data.records;this.loading = false;this.selectionClear();});},handleManualTrigger() {this.$confirm("确定手动触发定时任务?", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning"}).then(() => {this.$message({type: "success",message: "操作成功!"});// 调用 startETLForSYNL 方法startETLForSYNL().then(response => {const result = response.data.data; // 获取返回值console.log("返回值=",response)this.$message({type: "info",message: ` ${result}`});}).catch(error => {this.$message.error("操作失败");});});}}};
</script><style>
</style>
export default {height:'auto',calcHeight: 30,tip: false,searchShow: true,searchMenuSpan: 6,// border: true,// index: true,// viewBtn: true,selection: true,// dialogClickModal: false,column: [{label: "",prop: "id",type: "input",addDisplay: false,editDisplay: false,viewDisplay: false,hide: true,},{label: "任务名称",prop: "jobName",type: "input",},{label: "执行时间",prop: "time",type: "time",format: "HH:mm" // 设置时间格式,例如 "HH:mm"},{label: "定时表达式",prop: "cron",type: "input",editDisplay: false,addDisplay: false,},{label: "任务分组",prop: "jobGroup",type: "input",addDisplay: false,editDisplay: false,hide: true},{label: "触发器名称",prop: "triggerName",type: "input",addDisplay: false,editDisplay: false,hide: true},{label: "触发器分组",prop: "triggerGroup",type: "input",addDisplay: false,editDisplay: false,hide: true},{label: "状态",prop: "status",type: "input",addDisplay: false,editDisplay: false,}]
}
import request from '@/axios';export const getList = (current, size, params) => {return request({url: '/quartz/list',method: 'post',params: {...params,current,size,}})
}export const getDetail = (jobName) => {return request({url: '/quartz/detail',method: 'post',params: {jobName}})
}export const remove = (jobName) => {return request({url: '/quartz/remove',method: 'post',params: {jobName,}})
}export const add = (row) => {return request({url: '/quartz/submit',method: 'post',data: row})
}export const update = (row) => {return request({url: '/quartz/submit',method: 'post',data: row})
}export const startETLForSYNL = () => {return request({url: '/startETLForSYNL',method: 'get'})
}

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

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

相关文章

电源配小了,是不是容易烧?是的!

电源小的话会不会容易烧毁&#xff1f; 是的。 功率电压*电流。 随着功率增大&#xff0c;电压不变&#xff0c;电流增大&#xff0c;发热量增大&#xff0c;可能会烧毁。 今天给大家推荐一款650w的电脑电源&#xff0c;不过在推荐之前&#xff0c;首先要确认自己的电脑功耗…

串行通信接口之RS232、RS485与RS422

前言 RS232&#xff0c;RS485&#xff0c;RS422都是串口通信协议家族的一员&#xff0c;那么他们有什么区别呢&#xff1f;首先让我们来了解俩个简单的知识点&#xff0c;TTL电平以及CMOS电平&#xff0c;单端传输以及差分传输。 TTL电平 TTL电平接口电路由晶体管构成&#x…

Oracle19C静默安装教程

文章目录 一、安装前的准备1、安装Linux操作系统2、配置网络源或者本地源3、hosts文件配置 二、准备安装环境1、安装依赖包2、创建oracle用户组3、配置系统内核参数4、关闭selinux5、配置oracle用户环境6、修改用户的Shell限制 三、静默安装Oracle数据库1、创建oracle安装目录2…

spring MVC是如何找到html文件并返回的?

Spring MVC 搜索路径 启动一个SpringBoot项目时&#xff0c;访问http://localhost:8080&#xff0c;对于SpringMVC&#xff0c;它会默认把这段url看成http://localhost:8080/index.html&#xff0c;所以这两个url是等价的。 .html, .css, .js, .img …都是静态资源文件&#x…

基于Spring Boot+Vue的社区医院管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 一、研究背景 二…

ubuntu20.04搭建rtmp视频服务

1.安装软件 sudo apt-get install ffmpeg sudo apt-get install nginx sudo apt-get install libnginx-mod-rtmp 2.nginx配置 修改/etc/nginx/nginx.conf文件&#xff0c;在末尾添加&#xff1a; rtmp {server {listen 1935;application live {live on;}} } 3.视频测试 本…

idea创建maven-archetype-quickstart框架无法显示src/目录

一、配置好idea中Maven目录 1、不使用idea自带Maven&#xff0c; 2、配置好Maven环境变量M2_HOME 3、修改maven中 setting.xml文件 <?xml version"1.0" encoding"UTF-8"?><settings xmlns"http://maven.apache.org/SETTINGS/1.2.0"…

【Qt】常用控件

目录 一、控件概述 二、QWidget 三、Buttons类控件 3.1 QPushButton 3.2 QRadioButton 3.3 QCheckBox 3.4 QToolButton 四、Display Widgets&#xff08;显示类控件&#xff09; 4.1 QLabel 4.2 QLCDNumber 4.3 QProgressBar 4.4 QCalendarWidget 五、Input Widge…

C#中右键通过listview来控制datagridview字段值的是否显示、显示顺序,并存储到XML中。

最终显示效果&#xff0c;如下图所示&#xff1a; datagridview开始显示通过调用XML存储的字段值及顺序来显示&#xff0c;右键调出Tools来控制显示的顺序及是否显示&#xff0c;通过加号和减号进行调整顺序。 XML存储字段值及顺序 主要代码及事件&#xff1a; 获取datagridv…

GitHub配置SSH Key(详细版本)

GitHub配置SSH Key的目的是为了帮助我们在通过git提交代码是&#xff0c;不需要繁琐的验证过程&#xff0c;简化操作流程。比如新建的仓库可以下载, 但是提交需要账号密码。 步骤 一、设置git的user name和email 如果你是第一次使用&#xff0c;或者还没有配置过的话需要操作…

鸿蒙Harmony应用开发—ArkTS-高级组件:@ohos.arkui.advanced.ComposeTitleBar(头像和单双行文本标题栏)

一种普通标题栏&#xff0c;支持设置标题、头像&#xff08;可选&#xff09;和副标题&#xff08;可选&#xff09;&#xff0c;可用于一级页面、二级及其以上界面配置返回键。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角…

演讲嘉宾公布 | 3D音频专题论坛将于3月27日举办

一、3D音频专题论坛 3D音频技术不仅能够提供更加真实、沉浸的虚拟世界体验&#xff0c;跨越时空的限制&#xff0c;探索未知的世界。同时&#xff0c;提供更加丰富、立体的情感表达和交流方式&#xff0c;让人类能够更加深入地理解彼此&#xff0c;建立更加紧密的联系。3D音频未…

风速预测(八)VMD-CNN-Transformer预测模型

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较-CSDN博客 风速预测&#xff08;一&#xff09;数据集介绍和预处理-CSDN博客 风速预测&#xff08;二&#xff09;基于Pytorch的EMD-LSTM模型-CSDN博客 风速预测&#xff…

图片编辑器中实现文件上传的三种方式和二进制流及文件头校验文件类型

背景 最近在 vue-design-editor 开源项目中实现 psd 等多种文件格式上传解析成模板过程中, 发现搞定设计文件上传没有使用 input 实现文件上传, 所以我研究了一下相关技术, 总结了以下三种文件上传方法 input 文件选择window.showOpenFilePicker 和 window.showDirectoryPicke…

何恺明重提十年之争——模型表现好是源于能力提升还是捕获数据集偏见

2011年,知名学者Antonio Torralba和Alyosha Efros提出了“数据集偏差之战”&#xff0c;他们发现机器学习模型很容易“过拟合”到特定的数据集上&#xff0c;导致在其他数据集上表现不佳。过去十年&#xff0c;随着深度学习革命的到来&#xff0c;建立多样化、大规模、全面且尽…

应急响应-Web2

应急响应-Web2 1.攻击者的IP地址&#xff08;两个&#xff09;&#xff1f; 192.168.126.135 192.168.126.129 通过phpstudy查看日志&#xff0c;发现192.168.126.135这个IP一直在404访问 &#xff0c; 并且在日志的最后几条一直在访问system.php &#xff0c;从这可以推断 …

机器学习_聚类(Clustering)

文章目录 简介K-均值算法(K_Means) 简介 你经常跟哪些人联系&#xff0c;而这些人又经常给哪些人发邮件&#xff0c;由此找到关系密切的人群。因此&#xff0c;这可能需要另一个聚类算法&#xff0c;你希望用它发现社交网络中关系密切的朋友。 K-均值算法(K_Means) K-均值是…

查看angular版本的问题The Angular CLI requires a minimum Node.js version of v18.13.

angular版本与node.js版本不匹配的问题 下载安装angular 查看版本&#xff0c;发现不匹配 安装指定版本即可 查看版本并运行

stm32f103c8t6学习笔记(学习B站up江科大自化协)-ADC

ADC简介 ADC&#xff0c;英文全称是Analog to Digital Convert&#xff0c;意为模拟数字转换器&#xff0c;简称模数转换器&#xff0c;或者叫AD转换器&#xff0c;STM32主要是数字电路&#xff0c;数字电路只有高低电平&#xff0c;没有几V电压的概念&#xff0c;如果想读取电…

使用ansible批量修改操作系统管理员账号密码

一、ansible server端配置 1、对于Linux主机配置免密登录ssh-copy-id -i ~/.ssh/id_rsa.pub rootremote_ip 2、在/etc/ansible/hosts文件中添加相应主机IP 3、对于Windows主机需要在/etc/ansible/hosts文件中进行以下配置 192.168.83.132 ansible_ssh_useradministrator an…