Pre Merge pull request !649 from AprilWind/feat/dev-barcode

This commit is contained in:
AprilWind 2025-03-10 03:12:45 +00:00 committed by Gitee
commit f5662cadb9
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
8 changed files with 540 additions and 0 deletions

View File

@ -39,6 +39,8 @@
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
<!-- 条形码生成工具库 -->
<barcode.version>3.5.3</barcode.version>
<!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version>
@ -320,6 +322,12 @@
<version>${ip2region.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${barcode.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>

View File

@ -34,6 +34,7 @@
<module>ruoyi-common-tenant</module>
<module>ruoyi-common-websocket</module>
<module>ruoyi-common-sse</module>
<module>ruoyi-common-barcode</module>
</modules>
<artifactId>ruoyi-common</artifactId>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-barcode</artifactId>
<description>
ruoyi-common-barcode 条形码模块
</description>
<dependencies>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<!-- 条形码生成工具库 https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);// 蓝色通道
}
}
}
}
}

View File

@ -179,6 +179,13 @@
<version>${revision}</version>
</dependency>
<!-- 条形码模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-barcode</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>