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

Springboot 手搓 后端 滑块验证码生成

目录

一、效果演示

二、后端滑块验证码生成思路

三、原理解析

四、核心代码拿走


滑块验证码react前端实现,见我的这篇博客:前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客

一、效果演示

生成的案例图片:

视频演示:

滑块验证码演示

二、后端滑块验证码生成思路

1、后端需要生成对应的两个图片(拼图图片和拼图背景图片,图片内存尽量小一点)和对应位置(x和y, 等高拼图只需要记录x即可);

2、验证码生成服务,生成唯一的标识uuid(可考虑雪花算法生成),将生成图片生成后得到的位置信息即x(非登高拼图x和y)记录到缓存中,建议使用redis存储,即使分布式也能使用;

3、将验证码数据返回给前端,格式参考如下:

三、原理解析

要想生成拼图形状的拼图,我们需要运用到一些数学知识,核心代码如下:

通过 圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆,>=的在外侧,<的内侧。

简单来看就是这样的一个模型:

 /*** 随机生成拼图图轮廓数据** @param randomR1 圆点距离随机值* @return 0和1,其中0表示没有颜色,1有颜色*/private static int[][] createTemplateData(int randomR1) {// 拼图轮廓数据int[][] data = new int[puzzleWidth][puzzleHeight];// 拼图去掉凹凸的白色距离int xBlank = puzzleWidth - distance;int yBlank = puzzleHeight - distance;// 记录圆心的位置值int topOrBottomX = puzzleWidth / 2;int leftOrRightY = puzzleHeight / 2;// 凹时对应的位置int topYOrLeftX = distance - randomR1 + radius;int rightX = puzzleWidth - topYOrLeftX;int bottomY = puzzleHeight - topYOrLeftX;// 凸时对应的位置int topYOrLeftXR = distance + randomR1 - radius;int rightXR = puzzleWidth - topYOrLeftXR;int bottomYR = puzzleHeight - topYOrLeftXR;double rPow = Math.pow(radius, 2);/* 随机获取判断条件 */Random random = new Random();Integer[] randomCondition = new Integer[]{random.nextInt(3),random.nextInt(3),random.nextInt(3),random.nextInt(3)};/*计算需要的拼图轮廓(方块和凹凸),用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆*/for (int i = 0; i < puzzleWidth; i++) {for (int j = 0; j < puzzleHeight; j++) {/* 凹时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftX)double top = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftX, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftX)double bottom = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomY, 2);// 左侧的圆心位置为(topYOrLeftX, puzzleHeight / 2)double left = Math.pow(i - topYOrLeftX, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftX, puzzleHeight / 2)double right = Math.pow(i - rightX, 2) + Math.pow(j - leftOrRightY, 2);/* 凸时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftXR)double topR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftXR, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftXR)double bottomR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomYR, 2);// 左侧的圆心位置为(topYOrLeftXR, puzzleHeight / 2)double leftR = Math.pow(i - topYOrLeftXR, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftXR, puzzleHeight / 2)double rightR = Math.pow(i - rightXR, 2) + Math.pow(j - leftOrRightY, 2);/* 随机获取条件 */Boolean[][] conditions = new Boolean[][]{new Boolean[]{(j <= distance && topR >= rPow),(j <= distance || top <= rPow),(j <= distance)},new Boolean[]{(j >= yBlank && bottomR >= rPow),(j >= yBlank || bottom <= rPow),(j >= yBlank)},new Boolean[]{(i <= distance && leftR >= rPow),(i <= distance || left <= rPow),(i <= distance)},new Boolean[]{(i >= xBlank && rightR >= rPow),(i >= xBlank || right <= rPow),(i >= xBlank)}};boolean hide = false;for (int c = 0; c < randomCondition.length; c++) {if (conditions[c][randomCondition[c]]) {hide = true;break;}}if (hide) {// 不显示的像素data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}

绘制好需要截取的数据位置后,再来进行剪切:

 /*** 裁剪拼图** @param bgImg             - 原图规范大小之后的大图* @param puzzleImg         - 小图* @param slideTemplateData - 拼图轮廓数据* @param x                 - 坐标x* @param y                 - 坐标y*/private static void cutByTemplate(BufferedImage bgImg, BufferedImage puzzleImg, int[][] slideTemplateData, int x, int y) {int[][] matrix = new int[3][3];int[] values = new int[9];// 虚假的x坐标int fakeX = getRandomFakeX(x);// 创建shape区域,即原图抠图区域模糊和抠出小图/*遍历小图轮廓数据,创建shape区域。即原图抠图处模糊和抠出小图*/for (int i = 0; i < puzzleImg.getWidth(); i++) {for (int j = 0; j < puzzleImg.getHeight(); j++) {// 获取大图中对应位置变色int rgb_ori = bgImg.getRGB(x + i, y + j);// 0和1,其中0表示没有颜色,1有颜色int rgb = slideTemplateData[i][j];if (rgb == 1) {// 设置小图中对应位置变色puzzleImg.setRGB(i, j, rgb_ori);// 大图抠图区域高斯模糊readPixel(bgImg, x + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(x + i, y + j, avgMatrix(matrix));// 抠虚假图readPixel(bgImg, fakeX + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(fakeX + i, y + j, avgMatrix(matrix));} else {// 这里把背景设为透明puzzleImg.setRGB(i, j, rgb_ori & 0x00ffffff);}}}}

四、核心代码拿走

SliderCaptchaUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.SliderCaptchaConfig;
import com.xloda.common.tool.captcha.pojo.SliderCaptcha;import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;/*** @author Dragon Wu* @since 2025/04/23 10:52* 滑块验证码生成器*/public class SliderCaptchaUtil implements SliderCaptchaConfig {/*** 生成滑块验证码** @param bgImg     1. 传入随机背景图* @param accordant 是否生成登高拼图* @return SliderCaptcha 验证码结果* @throws IOException IO异常*/public static SliderCaptcha generateCaptcha(BufferedImage bgImg, boolean accordant) throws IOException {// 2. 随机生成离左上角的(X,Y)坐标,上限为 [width-puzzleWidth, height-puzzleHeight]。最好离大图左边远一点,上限不要紧挨着大图边界Random random = new Random();// X范围:[puzzleWidth, width - puzzleWidth)int x = random.nextInt(width - 2 * puzzleWidth) + puzzleWidth;// Y范围:[puzzleHeight, height - puzzleHeight)int y = random.nextInt(height - 2 * puzzleHeight) + puzzleHeight;// 3. 创建拼图图像BufferedImage puzzleImg = new BufferedImage(puzzleWidth, puzzleHeight, BufferedImage.TYPE_4BYTE_ABGR);// 4. 随机获取位置数据int randomR1 = getRandomR1();// 5. 随机生成拼图轮廓数据int[][] slideTemplateData = createTemplateData(randomR1);// 6. 从大图中裁剪拼图。抠原图,裁剪拼图cutByTemplate(bgImg, puzzleImg, slideTemplateData, x, y);// 7. 给拼图加边框puzzleImg = ImageUtil.addBorderWithOutline(puzzleImg, borderSize, Color.white);// 8. 判断是否为登高拼图if (accordant) {puzzleImg = reshapeAccordant(puzzleImg, y);return new SliderCaptcha(ImageUtil.toBase64(bgImg),ImageUtil.toBase64(puzzleImg), x);}// 非登高拼图,记录x和yreturn new SliderCaptcha(ImageUtil.toBase64(bgImg),ImageUtil.toBase64(puzzleImg), x, y);}// 随机获取小圆距离点private static int getRandomR1() {Integer[] r1List = new Integer[]{radius * 3 / 2,radius,radius / 2,};int index = new Random().nextInt(r1List.length);return r1List[index];}/*** 随机生成拼图图轮廓数据** @param randomR1 圆点距离随机值* @return 0和1,其中0表示没有颜色,1有颜色*/private static int[][] createTemplateData(int randomR1) {// 拼图轮廓数据int[][] data = new int[puzzleWidth][puzzleHeight];// 拼图去掉凹凸的白色距离int xBlank = puzzleWidth - distance;int yBlank = puzzleHeight - distance;// 记录圆心的位置值int topOrBottomX = puzzleWidth / 2;int leftOrRightY = puzzleHeight / 2;// 凹时对应的位置int topYOrLeftX = distance - randomR1 + radius;int rightX = puzzleWidth - topYOrLeftX;int bottomY = puzzleHeight - topYOrLeftX;// 凸时对应的位置int topYOrLeftXR = distance + randomR1 - radius;int rightXR = puzzleWidth - topYOrLeftXR;int bottomYR = puzzleHeight - topYOrLeftXR;double rPow = Math.pow(radius, 2);/* 随机获取判断条件 */Random random = new Random();Integer[] randomCondition = new Integer[]{random.nextInt(3),random.nextInt(3),random.nextInt(3),random.nextInt(3)};/*计算需要的拼图轮廓(方块和凹凸),用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆*/for (int i = 0; i < puzzleWidth; i++) {for (int j = 0; j < puzzleHeight; j++) {/* 凹时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftX)double top = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftX, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftX)double bottom = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomY, 2);// 左侧的圆心位置为(topYOrLeftX, puzzleHeight / 2)double left = Math.pow(i - topYOrLeftX, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftX, puzzleHeight / 2)double right = Math.pow(i - rightX, 2) + Math.pow(j - leftOrRightY, 2);/* 凸时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftXR)double topR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftXR, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftXR)double bottomR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomYR, 2);// 左侧的圆心位置为(topYOrLeftXR, puzzleHeight / 2)double leftR = Math.pow(i - topYOrLeftXR, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftXR, puzzleHeight / 2)double rightR = Math.pow(i - rightXR, 2) + Math.pow(j - leftOrRightY, 2);/* 随机获取条件 */Boolean[][] conditions = new Boolean[][]{new Boolean[]{(j <= distance && topR >= rPow),(j <= distance || top <= rPow),(j <= distance)},new Boolean[]{(j >= yBlank && bottomR >= rPow),(j >= yBlank || bottom <= rPow),(j >= yBlank)},new Boolean[]{(i <= distance && leftR >= rPow),(i <= distance || left <= rPow),(i <= distance)},new Boolean[]{(i >= xBlank && rightR >= rPow),(i >= xBlank || right <= rPow),(i >= xBlank)}};boolean hide = false;for (int c = 0; c < randomCondition.length; c++) {if (conditions[c][randomCondition[c]]) {hide = true;break;}}if (hide) {// 不显示的像素data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}/*** 裁剪拼图** @param bgImg             - 原图规范大小之后的大图* @param puzzleImg         - 小图* @param slideTemplateData - 拼图轮廓数据* @param x                 - 坐标x* @param y                 - 坐标y*/private static void cutByTemplate(BufferedImage bgImg, BufferedImage puzzleImg, int[][] slideTemplateData, int x, int y) {int[][] matrix = new int[3][3];int[] values = new int[9];// 虚假的x坐标int fakeX = getRandomFakeX(x);// 创建shape区域,即原图抠图区域模糊和抠出小图/*遍历小图轮廓数据,创建shape区域。即原图抠图处模糊和抠出小图*/for (int i = 0; i < puzzleImg.getWidth(); i++) {for (int j = 0; j < puzzleImg.getHeight(); j++) {// 获取大图中对应位置变色int rgb_ori = bgImg.getRGB(x + i, y + j);// 0和1,其中0表示没有颜色,1有颜色int rgb = slideTemplateData[i][j];if (rgb == 1) {// 设置小图中对应位置变色puzzleImg.setRGB(i, j, rgb_ori);// 大图抠图区域高斯模糊readPixel(bgImg, x + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(x + i, y + j, avgMatrix(matrix));// 抠虚假图readPixel(bgImg, fakeX + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(fakeX + i, y + j, avgMatrix(matrix));} else {// 这里把背景设为透明puzzleImg.setRGB(i, j, rgb_ori & 0x00ffffff);}}}}/*** 随机获取虚假x坐标的值** @param x 真正的x坐标* @return fakeX*/private static int getRandomFakeX(int x) {int puzzleRealWidth = puzzleWidth + 2 * borderSize + 2;Random random = new Random();int fakeX = random.nextInt(width - 2 * puzzleRealWidth) + puzzleRealWidth;if (Math.abs(fakeX - x) <= puzzleRealWidth) {fakeX = width - x;}return fakeX;}/*** 通过拼图图片生成登高拼图图片** @param puzzleImg 拼图图片* @param offsetY   随机生成的y* @return 登高拼图图片*/private static BufferedImage reshapeAccordant(BufferedImage puzzleImg, int offsetY) {BufferedImage puzzleBlankImg = new BufferedImage(puzzleWidth + 2 * borderSize + 2, height, BufferedImage.TYPE_4BYTE_ABGR);Graphics2D graphicsPuzzle = puzzleBlankImg.createGraphics();graphicsPuzzle.drawImage(puzzleImg, 1, offsetY, null);graphicsPuzzle.dispose();return puzzleBlankImg;}private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {int xStart = x - 1;int yStart = y - 1;int current = 0;for (int i = xStart; i < 3 + xStart; i++) {for (int j = yStart; j < 3 + yStart; j++) {int tx = i;if (tx < 0) {tx = -tx;} else if (tx >= img.getWidth()) {tx = x;}int ty = j;if (ty < 0) {ty = -ty;} else if (ty >= img.getHeight()) {ty = y;}pixels[current++] = img.getRGB(tx, ty);}}}private static int avgMatrix(int[][] matrix) {int r = 0;int g = 0;int b = 0;for (int[] x : matrix) {for (int j = 0; j < x.length; j++) {if (j == 1) {continue;}Color c = new Color(x[j]);r += c.getRed();g += c.getGreen();b += c.getBlue();}}return new Color(r / 8, g / 8, b / 8).getRGB();}private static void fillMatrix(int[][] matrix, int[] values) {int filled = 0;for (int[] x : matrix) {for (int j = 0; j < x.length; j++) {x[j] = values[filled++];}}}
}

RandomUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.CaptchaConstants;import java.util.Random;/*** @author Dragon Wu* @since 2025/04/23 18:07* 随机生成器工具*/public class RandomUtil {// 随机获取背景图路径public static String randomBgImgPath() {int index = new Random().nextInt(CaptchaConstants.BG_IMAGES.length);return CaptchaConstants.BG_IMAGES[index];}
}

ImageUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.CaptchaConstants;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;/*** @author Dragon Wu* @since 2025/04/25 10:18* 图片处理工具*/public class ImageUtil {public static BufferedImage addBorderWithOutline(BufferedImage image, int borderWidth, Color borderColor) {// 创建新图像,尺寸扩大以容纳边框BufferedImage result = new BufferedImage(image.getWidth() + borderWidth * 2,image.getHeight() + borderWidth * 2,BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = result.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 获取图像的非透明区域Area area = new Area();for (int y = 0; y < image.getHeight(); y++) {for (int x = 0; x < image.getWidth(); x++) {if ((image.getRGB(x, y) >> 24) != 0x00) {area.add(new Area(new Rectangle(x, y, 1, 1)));}}}// 绘制边框g2d.setColor(borderColor);g2d.setStroke(new BasicStroke(borderWidth * 2));g2d.translate(borderWidth, borderWidth);g2d.draw(area);// 绘制原始图像g2d.drawImage(image, 0, 0, null);g2d.dispose();return result;}// 图片转Base64public static String toBase64(BufferedImage image) throws IOException {// 创建一个字节数组输出流ByteArrayOutputStream os = new ByteArrayOutputStream();// 将BufferedImage写入到输出流中,这里指定图片格式为"png"或"jpg"等ImageIO.write(image, CaptchaConstants.IMG_FORMAT, os);// 将输出流的字节数组转换为Base64编码的字符串String imageBase64 = Base64.getEncoder().encodeToString(os.toByteArray());// 关闭输出流os.close();return CaptchaConstants.BASE64_PREFIX + imageBase64;}
}

SliderCaptcha:

package com.xloda.common.tool.captcha.pojo;import lombok.*;/*** @author Dragon Wu* @since 2025/04/23 10:49* 滑块验证码*/
@AllArgsConstructor
@Getter
@ToString
public class SliderCaptcha {// 验证码背景图private String bgImg;// 验证码滑块private String puzzleImg;// 验证码正确的x位置(此值需自行存入缓存,用于验证码判断)private int x;// 等高拼图时,返回0(非登高拼图,此值需自行存入缓存,用于验证码判断)private int y;public SliderCaptcha(String bgImg, String puzzleImg, int x) {this.bgImg = bgImg;this.puzzleImg = puzzleImg;this.x = x;}
}

CaptchaConstants:

package com.xloda.common.tool.captcha.constant;/*** @author Dragon Wu* @since 2025/04/23 10:53*/
public interface CaptchaConstants {// 图片格式String IMG_FORMAT = "png";// base64前缀String BASE64_PREFIX = "data:image/" + IMG_FORMAT + ";base64,";// 图片存储的目录String FOLDER = "/static/img/captcha/";// 背景图列表(引入依赖后,记得在项目资源目录的该路径下添加对应图片)String[] BG_IMAGES = new String[]{FOLDER + "bg01.png",FOLDER + "bg01.png"};
}

SliderCaptchaConfig

package com.xloda.common.tool.captcha.constant;/*** @author Dragon Wu* @since 2025/04/23 11:09* 滑块验证码的配置*/public interface SliderCaptchaConfig {// 大图宽度(原图裁剪拼图后的背景图)int width = 280;// 大图高度int height = 173;// 小图宽度(滑块拼图),前端拼图的实际宽度:puzzleWidth + 2 * borderSize + 2int puzzleWidth = 66;// 小图高度,前端拼图的实际高度:puzzleHeight + 2 * borderSize + 2int puzzleHeight = 66;// 边框厚度int borderSize = 1;// 小圆半径,即拼图上的凹凸轮廓半径int radius = 8;// 图片一周预留的距离,randomR1最大值不能超过radius * 3 / 2int distance = radius * 3 / 2;
}

接下来继续手搓旋转验证码前后端。

本节,总结到此,学点数学挺有用的!

相关文章:

Springboot 手搓 后端 滑块验证码生成

目录 一、效果演示 二、后端滑块验证码生成思路 三、原理解析 四、核心代码拿走 滑块验证码react前端实现&#xff0c;见我的这篇博客&#xff1a;前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客 一、效果演示 生成的案例…...

QPS说明

QPS&#xff08;Queries Per Second&#xff0c;每秒查询数&#xff09;是用于衡量服务器或系统处理请求能力的一个关键性能指标。 它表示服务器在一秒钟内能够处理的查询或请求的数量。 QPS的详细说明&#xff1a; 1. 定义 QPS&#xff1a;每秒查询数&#xff0c;指服务器…...

(六)机器学习---聚类与K-means

到本篇文章&#xff0c;我们先对前几篇所学习的算法进行一个回顾&#xff1a; 而本篇文章我们将会介绍聚类以及K-means算法。 分类问题回归问题聚类问题各种复杂问题决策树√线性回归√K-means√神经网络√逻辑回归√岭回归密度聚类深度学习√集成学习√Lasso回归谱聚类条件随机…...

macOS 更新后找不到钥匙串访问工具的解决方案

macOS 更新后找不到钥匙串访问工具的解决方案 随着macOS的不断更新&#xff0c;一些系统工具的位置可能会发生变化&#xff0c;给用户带来不便。钥匙串访问&#xff08;Keychain Access&#xff09;是macOS中一个非常重要的工具&#xff0c;用于管理密码、证书等敏感信息。最近…...

Mac 「brew」快速安装MySQL

安装MySQL 在 macOS 上安装 MySQL 环境可以通过Homebrew快速实现&#xff0c;以下是步骤指南&#xff1a; 方法 1&#xff1a;使用 Homebrew 安装 MySQL 1. 安装 Homebrew 如果尚未安装 Homebrew&#xff0c;可以通过以下命令安装&#xff1a; /bin/bash -c "$(curl -…...

代码随想录算法训练营第五十八天 | 1.拓扑排序精讲 2.dijkstra(朴素版)精讲 卡码网117.网站构建 卡码网47.参加科学大会

1.拓扑排序精讲 题目链接&#xff1a;117. 软件构建 文章讲解&#xff1a;代码随想录 思路&#xff1a; 把有向无环图进行线性排序的算法都可以叫做拓扑排序。 实现拓扑排序的算法有两种&#xff1a;卡恩算法&#xff08;BFS&#xff09;和DFS&#xff0c;以下BFS的实现思…...

Flutter Dart中的函数参数 默函数的定义 可选参数 箭头函数 匿名函认参数 命名参类数 闭包等

//使用forEach 打印下面的List里面的数据List list ["西瓜", "苹果", "香蕉"];list.forEach((value) {print(value);});//箭头函数1list.forEach((value) > print(value)); //只能一句//箭头函数2list.forEach((value) >{print(value), /…...

京东平台关键字搜索接口开发指南:Python实现与代码详解

一、接口概述 京东关键字搜索接口允许开发者通过HTTP请求获取平台商品的关键字搜索结果&#xff0c;常用于商品比价、数据分析等场景。本文基于Python演示如何调用京东搜索接口&#xff0c;解析返回数据并实现基础功能。 二、技术实现步骤 接口地址分析‌ 京东未完全公开API…...

告别进度失控:用燃尽图补上甘特图的监控盲区

在职场中&#xff0c;项目经理最头疼的莫过于“计划赶不上变化”。明明用甘特图排好了时间表&#xff0c;任务却总像脱缰野马——要么进度滞后&#xff0c;要么资源分配失衡。甘特图虽能直观展示任务时间轴&#xff0c;但面对突发风险或团队效率波动时&#xff0c;它更像一张“…...

PHP框架在微服务迁移中能发挥什么作用?

微服务架构因其模块化、高可用性和弹性扩展能力&#xff0c;已成为现代分布式系统的核心设计模式。PHP作为一门长期服务于Web开发的脚本语言&#xff0c;其生态中的主流框架&#xff08;如Laravel、Symfony&#xff09;通过模块化设计、高效通信机制和丰富的工具链&#xff0c;…...

Linux驱动开发快速上手指南:从理论到实战

Linux驱动开发快速上手指南&#xff1a;从理论到实战 作为嵌入式Linux开发的核心技能之一&#xff0c;驱动开发对于硬件控制至关重要。面对众多章节和概念&#xff0c;初学者常感到无从下手。本文将为你梳理Linux驱动开发的关键路径&#xff0c;提供从理论到实战的完整指导&am…...

第1讲|R语言绘图体系总览(Base、ggplot2、ComplexHeatmap等)

目录 第1讲|R语言绘图体系总览 ✨ 引言:为什么R绘图如此重要? 🧩 1. Base绘图系统 🧩 2. ggplot2生态系统 🧩 3. ComplexHeatmap超级热图系统 🧩 4. 其他特色绘图库(快速了解) ✏️ 小结一句话 📅 预告下一讲 第1讲|R语言绘图体系总览 (Base、ggplot…...

Android FFmpeg 交叉编译全指南:NDK编译 + CMake 集成

开发环境搭建 下载最新版Android Studio&#xff0c;安装SDK和模拟器 在Android Studio中配置&#xff1a;Setting → Android SDK → SDK Tools → 勾选CMake 和 NDK → Apply&#xff0c;按照提示下载安装 SDK是Android应用开发的基础工具包&#xff0c;适合大多数上层逻辑…...

Spring AI 快速入门:从环境搭建到核心组件集成

Spring AI 快速入门&#xff1a;从环境搭建到核心组件集成 一、前言&#xff1a;Java开发者的AI开发捷径 对于Java生态的开发者来说&#xff0c;将人工智能技术融入企业级应用往往面临技术栈割裂、依赖管理复杂、多模型适配困难等挑战。Spring AI的出现彻底改变了这一局面——…...

研发内控新规下的合规之道:维拉工时助力企业穿越IPO审查雷区

&#x1f4cc; 背景 | 全面注册制下&#xff0c;研发内控成“必修课” 在全面注册制背景下&#xff0c;证监会发布的《监管规则适用指引——发行类第9号&#xff1a;研发人员及研发投入》&#xff08;简称“发行类9号”&#xff09;&#xff0c;对企业的研发费用归集、研发工时…...

PyTorch生成式人工智能实战(3)——分类任务详解

PyTorch生成式人工智能实战&#xff08;3&#xff09;——分类任务详解 0. 前言1. 使用 PyTorch 进行端到端的深度学习1.1 PyTorch 深度神经网络训练流程1.2 数据预处理 2. 二分类2.1 创建数据批次2.2 模型构建与训练2.3 模型测试 3. 多类别分类3.1 验证集和提前停止3.2 模型构…...

单机无穷大系统暂态稳定性仿真Matlab模型

1.模型简介 本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2017Ra&#xff09;软件。建议采用matlab2017 Ra及以上版本打开。&#xff08;若需要其他低版本可联系代为转换&#xff09; 电力系统是一个复杂的动态系统&#xff0c;系统一旦出现稳定性问题&#xff0c;可…...

12个HPC教程汇总!从入门到实战,覆盖分子模拟/材料计算/生物信息分析等多个领域

在科学研究、工程仿真、人工智能和大数据分析等领域&#xff0c;高性能计算 (High Performance Computing, HPC) 正扮演着越来越重要的角色。它通过并行处理、大规模计算资源的整合&#xff0c;极大提升了计算效率&#xff0c;使原本耗时数日的任务能够在数小时内完成。 随着计…...

零基础快速搭建AI绘画网站!用Gradio玩转Stable Diffusion

借助Gradio&#xff0c;简单几行Python代码即可快速搭建一个专属的AI绘画网站&#xff0c;轻松部署Stable Diffusion文生图应用&#xff0c;gradio自带的组件已经相当美化了&#xff0c;前端基础也省了&#xff01; 好像大神的Stable Diffusion WebUI项目也是用的Gradio搭建的&…...

SpringCloud搭建Eureka注册中心

1、Eureka 的简介 Eureka 是 Netflix 开源的 ‌服务注册与发现框架‌,后被 Spring Cloud 集成为核心组件,用于解决微服务架构中服务治理问题。其核心目标是通过动态管理服务实例信息,实现服务间的透明通信与负载均衡。Eureka 通过简洁的 Server-Client 模型,结合心跳、缓存…...

信息学奥赛一本通 1509:【例 1】Intervals | OpenJudge 百练 1201:Intervals

【题目链接】 ybt 1509&#xff1a;【例 1】Intervals OpenJudge 百练 1201:Intervals 【题目考点】 1. 贪心算法 树状数组 并查集 2. 差分约束算法 【解题思路】 解法1&#xff1a;贪心算法树状数组、并查集优化 该题属于区间选点问题&#xff0c;ybt 1324&#xff1a;…...

电子电子架构 --- 主机厂视角下ECU开发流程

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...

⭐Unity_Demolition Media Hap (播放Hap格式视频 超16K大分辨率视频 流畅播放以及帧同步解决方案)

播放大分辨率视频以及实现局域网视频同步是许多开发者会遇到的需求&#xff0c;AVPro有一个 Ultra Edition版本,也能播放Hap格式视频,之外就是Demolition Media Hap插件啦&#xff0c;实测即使是 7208*3808 大分辨率的视频帧率还是能稳定在30帧&#xff0c;它能帮助我们轻松解决…...

【MySQL】MySQL索引与事务

目录 前言 1. 索引 &#xff08;index&#xff09; 1.1 概念 1.2 作用 1.3 使用场景 1.4 索引的相关操作 查看索引 创建索引 删除索引 2. 索引背后的数据结构 2.1 B树 2.2 B&#xff0b;树的特点 2.3 B&#xff0b;树的优势 3. 事务 3.1 为什么使用事务 3.2 事…...

Apache Spark 源码解析

Apache Spark 是一个开源的分布式计算系统&#xff0c;提供了高效的大规模数据处理能力。下面我将对 Spark 的核心源码结构进行解析。 核心架构 Spark 的主要代码模块包括&#xff1a; Core (核心模块) 包含 Spark 的基本功能&#xff0c;如任务调度、内存管理、错误恢复等 …...

MySQL的日志--Undo Log【学习笔记】

MySQL的日志--Undo Log 知识来源&#xff1a; 《MySQL是怎样运行的》--- 小孩子4919 为了保证事务的原子性&#xff0c;当事务中途遇到各种错误需要将数据回滚&#xff08;rollback&#xff09;到原来的样子。为此MySQL提出撤销日志&#xff08;Undo Log&#xff0c;也称undo日…...

一洽 全力辅助商户平台在线咨询解决方案

在商业数字化转型加速的背景下&#xff0c;客户对高效服务的需求日益增强。商户平台需要通过优化在线咨询服务&#xff0c;提升客户沟通效率与服务质量。一套综合性的在线咨询解决方案&#xff0c;通过整合多维度功能与智能技术&#xff0c;能够有效满足商户与客户的双向需求&a…...

ctfshow-web-新春欢乐杯

这几天做了这个新春欢乐杯&#xff0c;对于我这个小萌新来说有难度&#xff0c;同时也是收获满满&#xff0c;以下是我解题流程和收获 热身 <?php/* # -*- coding: utf-8 -*- # Author: h1xa # Date: 2022-01-16 15:42:02 # Last Modified by: h1xa # Last Modified …...

信奥赛之c++基础(初识循环嵌套与ASCII密码本)

🎠 游乐园编程奇遇记——循环嵌套与ASCII密码本 🎡 第一章:摩天轮与旋转木马——循环嵌套 🎪 游乐场里的双重循环 for(int 排数=1; 排数<=3; 排数++){// 外层循环像摩天轮for(int 座位=1; 座位<=5; 座位++){// 内层循环像旋转木马cout << "🎪"…...

同一电脑下使用 python2 和 python3

我本地先安装的2&#xff0c;然后再安装3。在电脑的环境变量 - Path 内&#xff0c;发现3的路径没有被加上&#xff0c;所以在cmd内输入python调用的是python2目录下的python.exe文件。pip.exe则是在Python/Scripts目录下&#xff0c;也就是默认调用的pip也是2的。 解决方案&…...

第十二届蓝桥杯 2021 C/C++组 直线

目录 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路&#xff1a; 核心思路&#xff1a; 两点确定一条直线&#xff1a; 思路详解&#xff1a; 代码&#xff1a; 第一种方式代码详解&#xff1a; 第二种方式代码详解&#xff1a; 题目&#xff1a;…...

向量数据库实践:存储和检索向量数据

向量数据库是一种专门设计用于存储和检索向量嵌入的数据库系统&#xff0c;能够支持语义搜索、推荐系统、图像识别等 AI 应用场景。 下面将详细介绍向量数据库中向量数据的存储和检索原理及实际应用&#xff0c;希望对各位读者有所帮助。 一. 向量数据的存储与检索流程 在向量…...

Pandas 数据导出:如何将 DataFrame 追加到 Excel 的不同工作表

在数据分析和数据处理过程中&#xff0c;将数据导出到 Excel 文件是一个常见的需求。Pandas 提供了强大的功能来实现这一需求&#xff0c;尤其是将数据追加到同一个 Excel 文件的不同工作表&#xff08;Sheet&#xff09;中。本文将详细介绍如何使用 Pandas 实现这一功能&#…...

区块链驱动的供应链金融创新:模型构建与商业化路径研究

区块链驱动的供应链金融创新&#xff1a;模型构建与商业化路径研究 1. 研究背景与意义 1.1 背景介绍 全球供应链金融市场规模预计2025年将达到3.6万亿美元&#xff0c;但传统模式面临四大核心问题&#xff1a;信息孤岛导致信任成本高昂&#xff08;占交易成本15-20%&#xf…...

DAX Studio将PowerBI与EXCEL连接

DAX Studio将PowerBI与EXCEL连接 具体步骤如下&#xff1a; 第一步&#xff1a;先打开一个PowerBI的文件&#xff0c;在外部工具栏里打开DAXStudio&#xff0c;如图&#xff1a; 第二步&#xff1a;DAXStudio界面&#xff0c;点击Advanced选项卡-->Analyze in Excel&#…...

使用springboot+easyexcel实现导出excel并合并指定单元格

1&#xff1a;准备一个单元格合并策略类代码&#xff1a; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.Writ…...

conformer编码器

abstract 最近,基于Transformer和卷积神经网络(CNN)的模型在自动语音识别(ASR)中显示出有希望的结果,优于递归神经网络(RNN)。Transformer模型擅长捕捉基于内容的全局交互,而CNN则有效地利用了局部特征。在这项工作中,我们通过研究如何将联合收割机卷积神经网络和tr…...

每日c/c++题 备战蓝桥杯(P1252洛谷 马拉松接力赛)

洛谷P1060 马拉松接力赛题解&#xff1a;贪心算法在资源分配中的巧妙应用 题目描述 P1060 马拉松接力赛是一道结合贪心策略与动态规划思想的资源分配问题。题目要求将25公里的马拉松接力赛合理分配给5名选手&#xff0c;使得总耗时最短。每位选手可跑1-10公里的整数距离&…...

操作指南:vLLM 部署开源大语言模型(LLM)

vLLM 是一个专为高效部署大语言模型&#xff08;LLM&#xff09;设计的开源推理框架&#xff0c;其核心优势在于显存优化、高吞吐量及云原生支持。 vLLM 部署开源大模型的详细步骤及优化策略&#xff1a; 一、环境准备与安装 安装 vLLM 基础安装&#xff1a;通过 pip 直接安装…...

目前市面上知名的数据采集器

程序员爱自己动手打造一切&#xff0c;但这样离钱就会比较远。 市面上知名的数据采集工具 数据采集工具&#xff08;也称为网络爬虫或数据抓取工具&#xff09;在市场上有很多选择&#xff0c;以下是目前比较知名和广泛使用的工具分类介绍&#xff1a; 一、开源免费工具 Scra…...

BitNet: 微软开源的 1-bit 大模型推理框架

GitHub&#xff1a;https://github.com/microsoft/BitNet 更多AI开源软件&#xff1a;发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI 微软专为 CPU 本地推理和极致压缩&#xff08;低比特&#xff09;大模型设计的推理框架。它支持对 1-bit/1.58-bit 量化模型进行…...

前端如何获取文件的 Hash 值?多种方式详解、对比与实践指南

文章目录 前言一、Hash 值为何重要&#xff1f;二、Hash 值基础知识2.1 什么是 Hash&#xff1f;2.2 Hash 在前端的应用场景2.3 常见的 Hash 算法&#xff08;MD5、SHA 系列&#xff09; 三、前端获取文件 Hash 的常用方式3.1 使用 SparkMD5 计算 MD5 值3.2 使用 Web Crypto AP…...

Java与Kotlin在Android开发中的全面对比分析

趋势很重要 语言发展背景与现状 Android操作系统自2008年正式发布以来&#xff0c;Java长期作为其主要的开发语言。这种选择源于Java语言的跨平台特性、成熟的生态系统以及广泛开发者基础。然而&#xff0c;随着移动开发需求的快速演变&#xff0c;Java在Android开发中逐渐暴…...

Android Kotlin 依赖注入全解:Koin appModule 配置与多 ViewModel 数据共享实战指南

一、基础配置与概念 1. 什么是 appModule appModule 是 Koin 依赖注入框架中的核心配置模块&#xff0c;用于集中管理应用中的所有依赖项。它本质上是一个 Koin 模块&#xff08;org.koin.core.module.Module&#xff09;&#xff0c;通过 DSL 方式声明各种组件的创建方式和依…...

Flink TaskManager详解

1. TaskManager 概述 Apache Flink 的 TaskManager 是作业执行的核心工作节点&#xff0c;负责实际的数据处理任务。它与 JobManager 协同工作&#xff0c;接受其调度指令&#xff0c;管理本地资源&#xff08;如 CPU、内存、网络&#xff09;&#xff0c;并执行具体的算子&am…...

Docker安装(Ubuntu22版)

前言 你是否还在为Linux上配置Docker而感到烦恼&#xff1f; 你是否还在为docker search&#xff0c;docker pull连接不上&#xff0c;而感到沮丧&#xff1f; 本文将解决以上你的所有烦恼&#xff01;快速安装好docker&#xff01; Docker安装 首先&#xff0c;我们得先卸载…...

《深入浅出ProtoBuf:从环境搭建到高效数据序列化》​

ProtoBuf详解 1、初识ProtoBuf2、安装ProtoBuf2.1、ProtoBuf在Windows下的安装2.2、ProtoBuf在Linux下的安装 3、快速上手——通讯录V1.03.1、步骤1&#xff1a;创建.proto文件3.2、步骤2&#xff1a;编译contacts.proto文件&#xff0c;生成C文件3.3、步骤3&#xff1a;序列化…...

【含文档+PPT+源码】基于微信小程序连锁药店商城

项目介绍 本课程演示的是一款基于微信小程序连锁药店商城&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3.该项目附带的…...

再见,物理删除!MyBatis-Plus @TableLogic 优雅实现逻辑删除

在开发应用程序时&#xff0c;我们经常会遇到需要删除数据的场景。但直接从数据库中物理删除&#xff08;DELETE​&#xff09;数据有时并非最佳选择。为什么呢&#xff1f; 数据恢复: 一旦物理删除&#xff0c;数据通常难以恢复&#xff0c;误操作可能导致灾难性后果。审计追…...

uni-app中获取用户实时位置完整指南:解决权限报错问题

uni-app中获取用户实时位置完整指南&#xff1a;解决权限报错问题 在uni-app开发运行在微信小程序时&#xff0c;获取用户位置信息是一个常见的需求&#xff0c;无论是用于地图导航、附近推荐还是其他基于位置的服务。然而&#xff0c;许多开发者在调用位置相关API时会遇到各种…...