diff --git a/pom.xml b/pom.xml
index f420f3cd4..df0ca4ad9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,8 @@
1.16.7
2.7.0
+
+ 3.5.3
2.28.22
@@ -320,6 +322,12 @@
${ip2region.version}
+
+ com.google.zxing
+ core
+ ${barcode.version}
+
+
commons-io
commons-io
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 2930fd0b0..f65d2a055 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -34,6 +34,7 @@
ruoyi-common-tenant
ruoyi-common-websocket
ruoyi-common-sse
+ ruoyi-common-barcode
ruoyi-common
diff --git a/ruoyi-common/ruoyi-common-barcode/pom.xml b/ruoyi-common/ruoyi-common-barcode/pom.xml
new file mode 100644
index 000000000..932b44dc1
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-barcode/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ org.dromara
+ ruoyi-common
+ ${revision}
+
+ 4.0.0
+
+ ruoyi-common-barcode
+
+
+ ruoyi-common-barcode 条形码模块
+
+
+
+
+ org.dromara
+ ruoyi-common-core
+
+
+
+
+ com.google.zxing
+ core
+
+
+
+
+
diff --git a/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/constant/BarcodeConstant.java b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/constant/BarcodeConstant.java
new file mode 100644
index 000000000..f895c01cb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/constant/BarcodeConstant.java
@@ -0,0 +1,29 @@
+package org.dromara.common.barcode.constant;
+
+/**
+ * 条形码常量
+ *
+ * @author AprilWind
+ */
+public interface BarcodeConstant {
+
+ /**
+ * 宽度,单位像素
+ */
+ Integer CODE_WIDTH = 300;
+
+ /**
+ * 高度,单位像素
+ */
+ Integer CODE_HEIGHT = 150;
+
+ /**
+ * 前景色,0x000000 表示黑色
+ */
+ Integer FRONT_COLOR = 0x000000;
+
+ /**
+ * 背景色,0xFFFFFF 表示白色
+ */
+ Integer BACKGROUND_COLOR = 0xFFFFFF;
+}
diff --git a/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/enums/BarcodeType.java b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/enums/BarcodeType.java
new file mode 100644
index 000000000..b8dc970c9
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/enums/BarcodeType.java
@@ -0,0 +1,155 @@
+package org.dromara.common.barcode.enums;
+
+import com.google.zxing.BarcodeFormat;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 条形码的种类
+ *
+ * @author AprilWind
+ */
+@Getter
+@AllArgsConstructor
+public enum BarcodeType {
+
+ /**
+ * Aztec 2D 条形码格式
+ * 适用于高信息密度的编码,类似 QR 码
+ */
+ AZTEC("Aztec", Integer.MAX_VALUE, BarcodeFormat.AZTEC),
+
+ /**
+ * CODABAR 1D 条形码格式
+ * 主要用于物流标签和医疗行业
+ * 最大长度:16字符
+ */
+ CODABAR("Codabar", 16, BarcodeFormat.CODABAR),
+
+ /**
+ * Code 39 1D 条形码格式
+ * 广泛用于工业和物流领域
+ * 最大长度:43字符
+ */
+ CODE_39("Code 39", 43, BarcodeFormat.CODE_39),
+
+ /**
+ * Code 93 1D 条形码格式
+ * 改进的 Code 39,存储密度更高
+ * 最大长度:48字符
+ */
+ CODE_93("Code 93", 48, BarcodeFormat.CODE_93),
+
+ /**
+ * Code 128 1D 条形码格式
+ * 支持整个 ASCII 字符集,适用于物流、快递和仓库管理
+ * 最大长度:48字符
+ */
+ CODE_128("Code 128", 48, BarcodeFormat.CODE_128),
+
+ /**
+ * Data Matrix 2D 条形码格式
+ * 适用于小尺寸高信息密度的编码
+ */
+ DATA_MATRIX("Data Matrix", Integer.MAX_VALUE, BarcodeFormat.DATA_MATRIX),
+
+ /**
+ * EAN-8 1D 条形码格式
+ * 适用于小型商品包装
+ * 最大长度:8字符
+ */
+ EAN_8("EAN-8", 8, BarcodeFormat.EAN_8),
+
+ /**
+ * EAN-13 1D 条形码格式
+ * 国际商品条形码标准
+ * 最大长度:13字符
+ */
+ EAN_13("EAN-13", 13, BarcodeFormat.EAN_13),
+
+ /**
+ * ITF (Interleaved Two of Five) 1D 条形码格式
+ * 适用于物流和仓库
+ * 最大长度:14字符
+ */
+ ITF("ITF", 14, BarcodeFormat.ITF),
+
+ /**
+ * MaxiCode 2D 条形码格式
+ * 美国 UPS 物流专用
+ */
+ MAXICODE("MaxiCode", Integer.MAX_VALUE, BarcodeFormat.MAXICODE),
+
+ /**
+ * PDF417 2D 条形码格式
+ * 适用于身份信息存储,如身份证、机票等
+ */
+ PDF_417("PDF417", Integer.MAX_VALUE, BarcodeFormat.PDF_417),
+
+ /**
+ * QR Code 2D 条形码格式
+ * 适用于网址、名片、支付等广泛场景
+ */
+ QR_CODE("QR Code", Integer.MAX_VALUE, BarcodeFormat.QR_CODE),
+
+ /**
+ * RSS-14 1D 条形码格式
+ * 常用于食品和药品追溯
+ * 最大长度:14字符
+ */
+ RSS_14("RSS-14", 14, BarcodeFormat.RSS_14),
+
+ /**
+ * RSS Expanded 1D 条形码格式
+ * 支持更多信息存储
+ */
+ RSS_EXPANDED("RSS Expanded", Integer.MAX_VALUE, BarcodeFormat.RSS_EXPANDED),
+
+ /**
+ * UPC-A 1D 条形码格式
+ * 美国零售商品标准条形码
+ * 最大长度:12字符
+ */
+ UPC_A("UPC-A", 12, BarcodeFormat.UPC_A),
+
+ /**
+ * UPC-E 1D 条形码格式
+ * UPC-A 的简化版本,适用于小商品
+ * 最大长度:8字符
+ */
+ UPC_E("UPC-E", 8, BarcodeFormat.UPC_E),
+
+ /**
+ * UPC/EAN 扩展格式
+ * 通常作为附加条码使用
+ * 最大长度:2字符
+ */
+ UPC_EAN_EXTENSION("UPC/EAN Extension", 2, BarcodeFormat.UPC_EAN_EXTENSION);
+
+ /**
+ * 条形码类型的名称
+ */
+ private final String typeName;
+
+ /**
+ * 每种条形码类型的最大长度限制
+ */
+ private final int maxLength;
+
+ /**
+ * ZXing 对应的条形码格式
+ */
+ private final BarcodeFormat zxingFormat;
+
+ /**
+ * 校验条形码内容是否符合长度要求
+ *
+ * @param content 条形码内容
+ * @return 是否符合长度限制
+ */
+ public boolean isLengthValid(String content) {
+ return content.length() <= this.maxLength;
+ }
+
+}
+
diff --git a/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/enums/ImageFormat.java b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/enums/ImageFormat.java
new file mode 100644
index 000000000..592716aac
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/enums/ImageFormat.java
@@ -0,0 +1,52 @@
+package org.dromara.common.barcode.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 图片枚举
+ *
+ * @author AprilWind
+ */
+@Getter
+@AllArgsConstructor
+public enum ImageFormat {
+
+ /**
+ * PNG格式:无损压缩,适用于需要高质量图像的场景,通常用于条形码和二维码等
+ */
+ PNG("PNG"),
+
+ /**
+ * JPEG格式:有损压缩,适用于存储较大的彩色图像,常用于照片和Web图像
+ */
+ JPEG("JPEG"),
+
+ /**
+ * GIF格式:支持动画和透明背景,但只支持256种颜色。适用于简单图像或小动画
+ */
+ GIF("GIF"),
+
+ /**
+ * BMP格式:未压缩格式,适用于高质量图像存储,但文件较大,通常不适合Web使用
+ */
+ BMP("BMP"),
+
+ /**
+ * TIFF格式:无损压缩,通常用于扫描和打印高质量图像,适合需要高精度的图像存储
+ */
+ TIFF("TIFF"),
+
+ /**
+ * WEBP格式:由Google开发,支持有损和无损压缩,文件较小,适用于Web,特别适合移动端使用
+ */
+ WEBP("WEBP"),
+
+ /**
+ * JPG格式:是JPEG格式的简称,常用于照片和图像压缩。与JPEG等效,可以接受`.jpg`和`.jpeg`两种扩展名
+ */
+ JPG("JPG");
+
+ private final String format;
+
+}
diff --git a/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/utils/BarcodeUtil.java b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/utils/BarcodeUtil.java
new file mode 100644
index 000000000..12aec702a
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-barcode/src/main/java/org/dromara/common/barcode/utils/BarcodeUtil.java
@@ -0,0 +1,256 @@
+package org.dromara.common.barcode.utils;
+
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.barcode.constant.BarcodeConstant;
+import org.dromara.common.barcode.enums.BarcodeType;
+import org.dromara.common.barcode.enums.ImageFormat;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * 条形码工具类
+ *
+ * @author AprilWind
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class BarcodeUtil {
+
+ /**
+ * 生成条形码并保存为指定文件
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码种类
+ * @param format 图像格式
+ * @param file 保存条形码图像的文件
+ * @throws Exception 可能的异常
+ */
+ public static void generateBarcodeToFile(String content, BarcodeType barcodeType, ImageFormat format, File file) throws Exception {
+ generateBarcodeToFile(content, barcodeType, BarcodeConstant.CODE_WIDTH, BarcodeConstant.CODE_HEIGHT, format, file);
+ }
+
+ /**
+ * 生成条形码并保存为指定文件
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码种类
+ * @param width 图像宽度
+ * @param height 图像高度
+ * @param format 图像格式
+ * @param file 保存条形码图像的文件
+ * @throws Exception 可能的异常
+ */
+ public static void generateBarcodeToFile(String content, BarcodeType barcodeType, int width, int height, ImageFormat format, File file) throws Exception {
+ // 生成条形码的 BufferedImage 图像
+ BufferedImage image = generateBarcodeImage(content, width, height, barcodeType);
+ // 将图像保存为指定格式的文件
+ ImageIO.write(image, format.getFormat(), file);
+ }
+
+ /**
+ * 生成条形码并将图像写入指定的输出流
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码种类
+ * @param format 图像格式
+ * @param outputStream 输出流,用于写入条形码图像
+ * @throws Exception 可能的异常
+ */
+ public static void generateBarcodeToOutputStream(String content, BarcodeType barcodeType, ImageFormat format, OutputStream outputStream) throws Exception {
+ generateBarcodeToOutputStream(content, barcodeType, BarcodeConstant.CODE_WIDTH, BarcodeConstant.CODE_HEIGHT, format, outputStream);
+ }
+
+ /**
+ * 生成条形码并将图像写入指定的输出流
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码种类
+ * @param width 图像宽度
+ * @param height 图像高度
+ * @param format 图像格式
+ * @param outputStream 输出流,用于写入条形码图像
+ * @throws Exception 可能的异常
+ */
+ public static void generateBarcodeToOutputStream(String content, BarcodeType barcodeType, int width, int height, ImageFormat format, OutputStream outputStream) throws Exception {
+ // 生成条形码的 BufferedImage 图像
+ BufferedImage image = generateBarcodeImage(content, width, height, barcodeType);
+
+ // 将图像写入输出流,使用指定的图像格式
+ ImageIO.write(image, format.getFormat(), outputStream);
+ }
+
+ /**
+ * 生成条形码并返回指定格式的字节数组
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码种类
+ * @param format 图像格式
+ * @return 生成的条形码图像字节数组
+ * @throws Exception 可能的异常
+ */
+ public static byte[] generateBarcodeByte(String content, BarcodeType barcodeType, ImageFormat format) throws Exception {
+ return generateBarcodeByte(content, barcodeType, BarcodeConstant.CODE_WIDTH, BarcodeConstant.CODE_HEIGHT, format);
+ }
+
+ /**
+ * 生成条形码并返回指定格式的字节数组
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码种类
+ * @param width 图像宽度
+ * @param height 图像高度
+ * @param format 图像格式
+ * @return 生成的条形码图像字节数组
+ * @throws Exception 可能的异常
+ */
+ public static byte[] generateBarcodeByte(String content, BarcodeType barcodeType, int width, int height, ImageFormat format) throws Exception {
+ BufferedImage image = generateBarcodeImage(content, width, height, barcodeType);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ImageIO.write(image, format.getFormat(), baos);
+ return baos.toByteArray();
+ }
+
+ /**
+ * 生成条形码并返回 BufferedImage
+ *
+ * @param content 条形码内容
+ * @param barcodeType 条形码的种类
+ * @return 生成的 BufferedImage
+ * @throws WriterException 生成失败异常
+ */
+ public static BufferedImage generateBarcodeImage(String content, BarcodeType barcodeType) throws WriterException {
+ return generateBarcodeImage(content, BarcodeConstant.CODE_WIDTH, BarcodeConstant.CODE_HEIGHT, barcodeType);
+ }
+
+ /**
+ * 生成条形码并返回 BufferedImage
+ *
+ * @param content 条形码内容
+ * @param width 宽度
+ * @param height 高度
+ * @param barcodeType 条形码的种类
+ * @return 生成的 BufferedImage
+ * @throws WriterException 生成失败异常
+ */
+ public static BufferedImage generateBarcodeImage(String content, int width, int height, BarcodeType barcodeType) throws WriterException {
+ //校验条形码内容是否符合长度要求
+ if (!barcodeType.isLengthValid(content)) {
+ log.error("条形码内容长度超出限制: 最大长度为 {},实际长度为 {}", barcodeType.getMaxLength(), content.length());
+ throw new IllegalArgumentException("条形码内容长度超出限制");
+ }
+
+ log.debug("开始生成条形码,内容:{}", content);
+ log.debug("条形码尺寸:{}x{}", width, height);
+
+ // 生成 BitMatrix(二维码的黑白数据矩阵)
+ MultiFormatWriter writer = new MultiFormatWriter();
+ BitMatrix bitMatrix = writer.encode(content, barcodeType.getZxingFormat(), width, height);
+
+ // 获取矩阵大小
+ int matrixWidth = bitMatrix.getWidth();
+ int matrixHeight = bitMatrix.getHeight();
+
+ // 创建 BufferedImage,使用 RGB 颜色模式
+ BufferedImage image = new BufferedImage(matrixWidth, matrixHeight, BufferedImage.TYPE_INT_RGB);
+
+ // 根据图片大小选择合适的填充方式
+ if (matrixWidth * matrixHeight < 1000 * 1000) {
+ // 适用于小图的填充方式(批量 setRGB)
+ fillImageWithSetRGB(image, bitMatrix);
+ } else {
+ // 适用于大图的填充方式(直接操作 WritableRaster)
+ fillImageWithWritableRaster(image, bitMatrix);
+ }
+
+ log.debug("条形码图像生成完毕");
+ return image;
+ }
+
+ /**
+ * 方案 1:小尺寸(小于 1000x1000 像素)条形码填充,使用 setRGB 批量写入
+ *
+ * @param image 目标 BufferedImage
+ * @param bitMatrix 二维码矩阵
+ */
+ private static void fillImageWithSetRGB(BufferedImage image, BitMatrix bitMatrix) {
+ int width = bitMatrix.getWidth();
+ int height = bitMatrix.getHeight();
+
+ // 创建图形上下文
+ Graphics2D graphics = image.createGraphics();
+ try {
+ // 先填充白色背景
+ graphics.setColor(Color.WHITE);
+ graphics.fillRect(0, 0, width, height);
+ } finally {
+ graphics.dispose();
+ }
+
+ // 创建像素数组
+ int[] pixels = new int[width * height];
+ int white = Color.WHITE.getRGB();
+ int black = Color.BLACK.getRGB();
+
+ // **先填充白色背景**
+ Arrays.fill(pixels, white);
+
+ // **再填充黑色条形码**
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (bitMatrix.get(x, y)) {
+ pixels[y * width + x] = black;
+ }
+ }
+ }
+
+ // **将数据写入 BufferedImage**
+ image.setRGB(0, 0, width, height, pixels, 0, width);
+ }
+
+ /**
+ * 方案 2:大尺寸(大于等于 1000x1000 像素)条形码填充,使用 WritableRaster 直接写入
+ *
+ * @param image 目标 BufferedImage
+ * @param bitMatrix 二维码矩阵
+ */
+ private static void fillImageWithWritableRaster(BufferedImage image, BitMatrix bitMatrix) {
+ int width = bitMatrix.getWidth();
+ int height = bitMatrix.getHeight();
+
+ // 创建图形上下文
+ Graphics2D graphics = image.createGraphics();
+ try {
+ // 先填充白色背景
+ graphics.setColor(Color.WHITE);
+ graphics.fillRect(0, 0, width, height);
+ } finally {
+ graphics.dispose();
+ }
+ // 获取 WritableRaster(直接操作像素数据)
+ WritableRaster raster = image.getRaster();
+ // **遍历 bitMatrix,将黑色部分填充**
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (bitMatrix.get(x, y)) {
+ // 设置 RGB 三个通道都为 0(黑色)
+ raster.setSample(x, y, 0, 0);// 红色通道
+ raster.setSample(x, y, 1, 0);// 绿色通道
+ raster.setSample(x, y, 2, 0);// 蓝色通道
+ }
+ }
+ }
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml
index 24acb086d..478b9e993 100644
--- a/ruoyi-common/ruoyi-common-bom/pom.xml
+++ b/ruoyi-common/ruoyi-common-bom/pom.xml
@@ -179,6 +179,13 @@
${revision}
+
+
+ org.dromara
+ ruoyi-common-barcode
+ ${revision}
+
+