前言

二维码作为现代移动互联网时代的重要信息载体,广泛应用于支付、登录、营销、物流等各个领域。Hutool作为Java生态中优秀的工具库,提供了简洁易用的二维码生成和解析功能。本文从Hutool二维码工具到生成解析,从基础应用到企业级方案,系统梳理基于Hutool的二维码应用完整解决方案。

一、Hutool二维码架构设计

1.1 Hutool二维码整体架构

1.2 二维码应用场景架构

二、Hutool二维码基础使用

2.1 Hutool依赖配置

2.1.1 Maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- Hutool核心依赖 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>

<!-- 或者单独引入二维码模块 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-qrcode</artifactId>
<version>5.8.22</version>
</dependency>

<!-- ZXing依赖(Hutool底层使用) -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.1</version>
</dependency>

<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.1</version>
</version>

2.1.2 Gradle依赖

1
2
3
4
5
6
7
8
9
// Hutool核心依赖
implementation 'cn.hutool:hutool-all:5.8.22'

// 或者单独引入二维码模块
implementation 'cn.hutool:hutool-qrcode:5.8.22'

// ZXing依赖
implementation 'com.google.zxing:core:3.5.1'
implementation 'com.google.zxing:javase:3.5.1'

2.2 基础二维码生成

2.2.1 简单二维码生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 基础二维码生成示例
*/
@Service
public class BasicQRCodeService {

/**
* 生成简单二维码
*/
public String generateSimpleQRCode(String content) {
try {
// 生成二维码到文件
QrCodeUtil.generate(content, 300, 300, FileUtil.file("qrcode.png"));

// 生成二维码到字节数组
byte[] qrCodeBytes = QrCodeUtil.generatePng(content, 300, 300);

// 转换为Base64
String base64QRCode = Base64.encode(qrCodeBytes);

return base64QRCode;

} catch (Exception e) {
log.error("生成二维码失败", e);
throw new RuntimeException("生成二维码失败", e);
}
}

/**
* 生成带Logo的二维码
*/
public String generateQRCodeWithLogo(String content, String logoPath) {
try {
// 配置二维码参数
QrConfig config = new QrConfig(300, 300);
config.setMargin(1);
config.setErrorCorrection(ErrorCorrectionLevel.H);

// 设置Logo
config.setImg(logoPath);

// 生成二维码
BufferedImage qrCodeImage = QrCodeUtil.generate(content, config);

// 转换为Base64
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(qrCodeImage, "PNG", outputStream);
byte[] qrCodeBytes = outputStream.toByteArray();

return Base64.encode(qrCodeBytes);

} catch (Exception e) {
log.error("生成带Logo的二维码失败", e);
throw new RuntimeException("生成带Logo的二维码失败", e);
}
}

/**
* 生成彩色二维码
*/
public String generateColorfulQRCode(String content) {
try {
// 配置二维码参数
QrConfig config = new QrConfig(300, 300);
config.setMargin(1);
config.setErrorCorrection(ErrorCorrectionLevel.H);

// 设置前景色和背景色
config.setForeColor(Color.BLACK);
config.setBackColor(Color.WHITE);

// 生成二维码
BufferedImage qrCodeImage = QrCodeUtil.generate(content, config);

// 转换为Base64
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(qrCodeImage, "PNG", outputStream);
byte[] qrCodeBytes = outputStream.toByteArray();

return Base64.encode(qrCodeBytes);

} catch (Exception e) {
log.error("生成彩色二维码失败", e);
throw new RuntimeException("生成彩色二维码失败", e);
}
}
}

2.2.2 高级二维码配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* 高级二维码配置示例
*/
@Service
public class AdvancedQRCodeService {

/**
* 生成高级配置二维码
*/
public String generateAdvancedQRCode(String content) {
try {
// 创建高级配置
QrConfig config = new QrConfig();

// 设置尺寸
config.setWidth(300);
config.setHeight(300);

// 设置边距
config.setMargin(2);

// 设置错误纠正级别
config.setErrorCorrection(ErrorCorrectionLevel.M);

// 设置前景色和背景色
config.setForeColor(Color.BLACK);
config.setBackColor(Color.WHITE);

// 设置图片类型
config.setImgType(ImageType.PNG);

// 生成二维码
BufferedImage qrCodeImage = QrCodeUtil.generate(content, config);

// 转换为Base64
return convertToBase64(qrCodeImage);

} catch (Exception e) {
log.error("生成高级配置二维码失败", e);
throw new RuntimeException("生成高级配置二维码失败", e);
}
}

/**
* 生成自定义样式二维码
*/
public String generateCustomStyleQRCode(String content) {
try {
// 创建自定义配置
QrConfig config = new QrConfig(400, 400);

// 设置边距
config.setMargin(3);

// 设置错误纠正级别
config.setErrorCorrection(ErrorCorrectionLevel.H);

// 设置自定义颜色
config.setForeColor(new Color(0, 102, 204)); // 蓝色前景
config.setBackColor(new Color(255, 255, 240)); // 米色背景

// 生成二维码
BufferedImage qrCodeImage = QrCodeUtil.generate(content, config);

// 添加边框
BufferedImage borderedImage = addBorder(qrCodeImage, 20, Color.GRAY);

// 转换为Base64
return convertToBase64(borderedImage);

} catch (Exception e) {
log.error("生成自定义样式二维码失败", e);
throw new RuntimeException("生成自定义样式二维码失败", e);
}
}

/**
* 生成批量二维码
*/
public List<String> generateBatchQRCodes(List<String> contents) {
List<String> qrCodes = new ArrayList<>();

for (String content : contents) {
try {
String qrCode = generateAdvancedQRCode(content);
qrCodes.add(qrCode);
} catch (Exception e) {
log.error("生成二维码失败: {}", content, e);
qrCodes.add(null); // 失败时添加null
}
}

return qrCodes;
}

/**
* 添加边框
*/
private BufferedImage addBorder(BufferedImage image, int borderSize, Color borderColor) {
int newWidth = image.getWidth() + 2 * borderSize;
int newHeight = image.getHeight() + 2 * borderSize;

BufferedImage borderedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = borderedImage.createGraphics();

// 填充边框颜色
g2d.setColor(borderColor);
g2d.fillRect(0, 0, newWidth, newHeight);

// 绘制原图
g2d.drawImage(image, borderSize, borderSize, null);
g2d.dispose();

return borderedImage;
}

/**
* 转换为Base64
*/
private String convertToBase64(BufferedImage image) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", outputStream);
byte[] imageBytes = outputStream.toByteArray();
return Base64.encode(imageBytes);
}
}

2.3 二维码解析

2.3.1 基础二维码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 二维码解析服务
*/
@Service
public class QRCodeParseService {

/**
* 解析文件中的二维码
*/
public String parseQRCodeFromFile(String filePath) {
try {
// 从文件解析二维码
String content = QrCodeUtil.decode(FileUtil.file(filePath));
return content;

} catch (Exception e) {
log.error("解析文件二维码失败: {}", filePath, e);
throw new RuntimeException("解析文件二维码失败", e);
}
}

/**
* 解析字节数组中的二维码
*/
public String parseQRCodeFromBytes(byte[] qrCodeBytes) {
try {
// 从字节数组解析二维码
String content = QrCodeUtil.decode(qrCodeBytes);
return content;

} catch (Exception e) {
log.error("解析字节数组二维码失败", e);
throw new RuntimeException("解析字节数组二维码失败", e);
}
}

/**
* 解析Base64编码的二维码
*/
public String parseQRCodeFromBase64(String base64QRCode) {
try {
// 解码Base64
byte[] qrCodeBytes = Base64.decode(base64QRCode);

// 解析二维码
return parseQRCodeFromBytes(qrCodeBytes);

} catch (Exception e) {
log.error("解析Base64二维码失败", e);
throw new RuntimeException("解析Base64二维码失败", e);
}
}

/**
* 解析图片流中的二维码
*/
public String parseQRCodeFromStream(InputStream inputStream) {
try {
// 从输入流解析二维码
String content = QrCodeUtil.decode(inputStream);
return content;

} catch (Exception e) {
log.error("解析输入流二维码失败", e);
throw new RuntimeException("解析输入流二维码失败", e);
}
}

/**
* 批量解析二维码
*/
public List<String> parseBatchQRCodes(List<byte[]> qrCodeBytesList) {
List<String> contents = new ArrayList<>();

for (byte[] qrCodeBytes : qrCodeBytesList) {
try {
String content = parseQRCodeFromBytes(qrCodeBytes);
contents.add(content);
} catch (Exception e) {
log.error("批量解析二维码失败", e);
contents.add(null); // 失败时添加null
}
}

return contents;
}
}

2.3.2 高级二维码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/**
* 高级二维码解析服务
*/
@Service
public class AdvancedQRCodeParseService {

/**
* 解析二维码并获取详细信息
*/
public QRCodeParseResult parseQRCodeWithDetails(byte[] qrCodeBytes) {
try {
// 解析二维码
String content = QrCodeUtil.decode(qrCodeBytes);

// 创建解析结果
QRCodeParseResult result = new QRCodeParseResult();
result.setContent(content);
result.setSuccess(true);
result.setParseTime(System.currentTimeMillis());

// 分析内容类型
QRCodeContentType contentType = analyzeContentType(content);
result.setContentType(contentType);

// 验证内容格式
boolean isValid = validateContent(content, contentType);
result.setValid(isValid);

return result;

} catch (Exception e) {
log.error("解析二维码详细信息失败", e);

QRCodeParseResult result = new QRCodeParseResult();
result.setSuccess(false);
result.setErrorMessage(e.getMessage());
result.setParseTime(System.currentTimeMillis());

return result;
}
}

/**
* 解析二维码并验证签名
*/
public QRCodeParseResult parseQRCodeWithSignature(byte[] qrCodeBytes, String secretKey) {
try {
// 解析二维码
String content = QrCodeUtil.decode(qrCodeBytes);

// 验证签名
boolean signatureValid = validateSignature(content, secretKey);

QRCodeParseResult result = new QRCodeParseResult();
result.setContent(content);
result.setSuccess(true);
result.setSignatureValid(signatureValid);
result.setParseTime(System.currentTimeMillis());

return result;

} catch (Exception e) {
log.error("解析带签名的二维码失败", e);

QRCodeParseResult result = new QRCodeParseResult();
result.setSuccess(false);
result.setErrorMessage(e.getMessage());
result.setParseTime(System.currentTimeMillis());

return result;
}
}

/**
* 分析内容类型
*/
private QRCodeContentType analyzeContentType(String content) {
if (content.startsWith("http://") || content.startsWith("https://")) {
return QRCodeContentType.URL;
} else if (content.startsWith("tel:")) {
return QRCodeContentType.PHONE;
} else if (content.startsWith("mailto:")) {
return QRCodeContentType.EMAIL;
} else if (content.startsWith("sms:")) {
return QRCodeContentType.SMS;
} else if (content.startsWith("geo:")) {
return QRCodeContentType.LOCATION;
} else if (content.startsWith("wifi:")) {
return QRCodeContentType.WIFI;
} else if (content.matches("^[0-9]+$")) {
return QRCodeContentType.NUMBER;
} else if (content.matches("^[A-Za-z0-9+/=]+$")) {
return QRCodeContentType.BASE64;
} else {
return QRCodeContentType.TEXT;
}
}

/**
* 验证内容格式
*/
private boolean validateContent(String content, QRCodeContentType contentType) {
switch (contentType) {
case URL:
return isValidUrl(content);
case PHONE:
return isValidPhone(content);
case EMAIL:
return isValidEmail(content);
case SMS:
return isValidSms(content);
case LOCATION:
return isValidLocation(content);
case WIFI:
return isValidWifi(content);
case NUMBER:
return isValidNumber(content);
case BASE64:
return isValidBase64(content);
default:
return true; // 文本类型默认有效
}
}

/**
* 验证签名
*/
private boolean validateSignature(String content, String secretKey) {
try {
// 分离内容和签名
String[] parts = content.split("\\|");
if (parts.length != 2) {
return false;
}

String data = parts[0];
String signature = parts[1];

// 计算签名
String expectedSignature = calculateSignature(data, secretKey);

return signature.equals(expectedSignature);

} catch (Exception e) {
log.error("验证签名失败", e);
return false;
}
}

/**
* 计算签名
*/
private String calculateSignature(String data, String secretKey) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
mac.init(secretKeySpec);

byte[] signature = mac.doFinal(data.getBytes());
return Base64.encode(signature);

} catch (Exception e) {
throw new RuntimeException("计算签名失败", e);
}
}

// 各种验证方法
private boolean isValidUrl(String url) {
try {
new URL(url);
return true;
} catch (Exception e) {
return false;
}
}

private boolean isValidPhone(String phone) {
return phone.matches("^tel:\\+?[1-9]\\d{1,14}$");
}

private boolean isValidEmail(String email) {
return email.matches("^mailto:[\\w\\.-]+@[\\w\\.-]+\\.[a-zA-Z]{2,}$");
}

private boolean isValidSms(String sms) {
return sms.matches("^sms:\\+?[1-9]\\d{1,14}:.*$");
}

private boolean isValidLocation(String location) {
return location.matches("^geo:-?\\d+\\.?\\d*,-?\\d+\\.?\\d*$");
}

private boolean isValidWifi(String wifi) {
return wifi.startsWith("wifi:");
}

private boolean isValidNumber(String number) {
return number.matches("^\\d+$");
}

private boolean isValidBase64(String base64) {
try {
Base64.decode(base64);
return true;
} catch (Exception e) {
return false;
}
}
}

三、企业级二维码应用

3.1 二维码服务架构

3.1.1 二维码服务接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 二维码服务接口
*/
public interface QRCodeService {

/**
* 生成二维码
*/
QRCodeResponse generateQRCode(QRCodeRequest request);

/**
* 解析二维码
*/
QRCodeParseResponse parseQRCode(QRCodeParseRequest request);

/**
* 批量生成二维码
*/
List<QRCodeResponse> generateBatchQRCodes(List<QRCodeRequest> requests);

/**
* 批量解析二维码
*/
List<QRCodeParseResponse> parseBatchQRCodes(List<QRCodeParseRequest> requests);

/**
* 获取二维码模板
*/
QRCodeTemplate getQRCodeTemplate(String templateId);

/**
* 创建二维码模板
*/
QRCodeTemplate createQRCodeTemplate(QRCodeTemplateRequest request);
}

3.1.2 二维码服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/**
* 二维码服务实现
*/
@Service
public class QRCodeServiceImpl implements QRCodeService {

@Autowired
private QRCodeRepository qrCodeRepository;

@Autowired
private QRCodeTemplateRepository templateRepository;

@Autowired
private FileStorageService fileStorageService;

@Autowired
private QRCodeCacheService cacheService;

/**
* 生成二维码
*/
@Override
public QRCodeResponse generateQRCode(QRCodeRequest request) {
try {
// 1. 验证请求参数
validateQRCodeRequest(request);

// 2. 检查缓存
String cacheKey = generateCacheKey(request);
QRCodeResponse cachedResponse = cacheService.get(cacheKey);
if (cachedResponse != null) {
return cachedResponse;
}

// 3. 生成二维码
QRCodeData qrCodeData = generateQRCodeData(request);

// 4. 保存到数据库
qrCodeRepository.save(qrCodeData);

// 5. 构建响应
QRCodeResponse response = buildQRCodeResponse(qrCodeData);

// 6. 缓存结果
cacheService.put(cacheKey, response, Duration.ofMinutes(30));

return response;

} catch (Exception e) {
log.error("生成二维码失败", e);
throw new QRCodeException("生成二维码失败", e);
}
}

/**
* 解析二维码
*/
@Override
public QRCodeParseResponse parseQRCode(QRCodeParseRequest request) {
try {
// 1. 验证请求参数
validateQRCodeParseRequest(request);

// 2. 解析二维码
QRCodeParseResult result = parseQRCodeContent(request.getQrCodeData());

// 3. 构建响应
QRCodeParseResponse response = buildQRCodeParseResponse(result);

return response;

} catch (Exception e) {
log.error("解析二维码失败", e);
throw new QRCodeException("解析二维码失败", e);
}
}

/**
* 批量生成二维码
*/
@Override
public List<QRCodeResponse> generateBatchQRCodes(List<QRCodeRequest> requests) {
List<QRCodeResponse> responses = new ArrayList<>();

// 并行处理
requests.parallelStream().forEach(request -> {
try {
QRCodeResponse response = generateQRCode(request);
responses.add(response);
} catch (Exception e) {
log.error("批量生成二维码失败", e);
responses.add(QRCodeResponse.error("生成失败"));
}
});

return responses;
}

/**
* 批量解析二维码
*/
@Override
public List<QRCodeParseResponse> parseBatchQRCodes(List<QRCodeParseRequest> requests) {
List<QRCodeParseResponse> responses = new ArrayList<>();

// 并行处理
requests.parallelStream().forEach(request -> {
try {
QRCodeParseResponse response = parseQRCode(request);
responses.add(response);
} catch (Exception e) {
log.error("批量解析二维码失败", e);
responses.add(QRCodeParseResponse.error("解析失败"));
}
});

return responses;
}

/**
* 生成二维码数据
*/
private QRCodeData generateQRCodeData(QRCodeRequest request) {
try {
// 创建配置
QrConfig config = createQrConfig(request);

// 生成二维码
BufferedImage qrCodeImage = QrCodeUtil.generate(request.getContent(), config);

// 保存图片
String imageUrl = saveQRCodeImage(qrCodeImage, request);

// 创建数据对象
QRCodeData qrCodeData = new QRCodeData();
qrCodeData.setId(UUID.randomUUID().toString());
qrCodeData.setContent(request.getContent());
qrCodeData.setImageUrl(imageUrl);
qrCodeData.setWidth(request.getWidth());
qrCodeData.setHeight(request.getHeight());
qrCodeData.setCreateTime(System.currentTimeMillis());
qrCodeData.setExpireTime(request.getExpireTime());

return qrCodeData;

} catch (Exception e) {
throw new RuntimeException("生成二维码数据失败", e);
}
}

/**
* 创建二维码配置
*/
private QrConfig createQrConfig(QRCodeRequest request) {
QrConfig config = new QrConfig();

// 设置尺寸
config.setWidth(request.getWidth());
config.setHeight(request.getHeight());

// 设置边距
config.setMargin(request.getMargin());

// 设置错误纠正级别
config.setErrorCorrection(request.getErrorCorrection());

// 设置颜色
config.setForeColor(request.getForeColor());
config.setBackColor(request.getBackColor());

// 设置Logo
if (request.getLogoPath() != null) {
config.setImg(request.getLogoPath());
}

return config;
}

/**
* 保存二维码图片
*/
private String saveQRCodeImage(BufferedImage image, QRCodeRequest request) {
try {
// 生成文件名
String fileName = generateFileName(request);

// 保存到文件存储
String imageUrl = fileStorageService.saveImage(image, fileName);

return imageUrl;

} catch (Exception e) {
throw new RuntimeException("保存二维码图片失败", e);
}
}

/**
* 生成文件名
*/
private String generateFileName(QRCodeRequest request) {
String timestamp = String.valueOf(System.currentTimeMillis());
String hash = DigestUtil.md5Hex(request.getContent());
return String.format("qrcode_%s_%s.png", timestamp, hash);
}

/**
* 生成缓存键
*/
private String generateCacheKey(QRCodeRequest request) {
StringBuilder key = new StringBuilder();
key.append("qrcode:");
key.append(DigestUtil.md5Hex(request.getContent()));
key.append(":");
key.append(request.getWidth());
key.append("x");
key.append(request.getHeight());
key.append(":");
key.append(request.getMargin());
return key.toString();
}
}

3.2 二维码模板管理

3.2.1 二维码模板服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/**
* 二维码模板服务
*/
@Service
public class QRCodeTemplateService {

@Autowired
private QRCodeTemplateRepository templateRepository;

@Autowired
private QRCodeService qrCodeService;

/**
* 创建二维码模板
*/
public QRCodeTemplate createTemplate(QRCodeTemplateRequest request) {
try {
// 验证模板参数
validateTemplateRequest(request);

// 创建模板
QRCodeTemplate template = new QRCodeTemplate();
template.setId(UUID.randomUUID().toString());
template.setName(request.getName());
template.setDescription(request.getDescription());
template.setWidth(request.getWidth());
template.setHeight(request.getHeight());
template.setMargin(request.getMargin());
template.setErrorCorrection(request.getErrorCorrection());
template.setForeColor(request.getForeColor());
template.setBackColor(request.getBackColor());
template.setLogoPath(request.getLogoPath());
template.setCreateTime(System.currentTimeMillis());
template.setStatus(TemplateStatus.ACTIVE);

// 保存模板
templateRepository.save(template);

return template;

} catch (Exception e) {
log.error("创建二维码模板失败", e);
throw new QRCodeException("创建二维码模板失败", e);
}
}

/**
* 使用模板生成二维码
*/
public QRCodeResponse generateQRCodeWithTemplate(String templateId, String content) {
try {
// 获取模板
QRCodeTemplate template = templateRepository.findById(templateId)
.orElseThrow(() -> new QRCodeException("模板不存在"));

// 检查模板状态
if (template.getStatus() != TemplateStatus.ACTIVE) {
throw new QRCodeException("模板已禁用");
}

// 创建请求
QRCodeRequest request = new QRCodeRequest();
request.setContent(content);
request.setWidth(template.getWidth());
request.setHeight(template.getHeight());
request.setMargin(template.getMargin());
request.setErrorCorrection(template.getErrorCorrection());
request.setForeColor(template.getForeColor());
request.setBackColor(template.getBackColor());
request.setLogoPath(template.getLogoPath());

// 生成二维码
return qrCodeService.generateQRCode(request);

} catch (Exception e) {
log.error("使用模板生成二维码失败", e);
throw new QRCodeException("使用模板生成二维码失败", e);
}
}

/**
* 更新模板
*/
public QRCodeTemplate updateTemplate(String templateId, QRCodeTemplateRequest request) {
try {
// 获取模板
QRCodeTemplate template = templateRepository.findById(templateId)
.orElseThrow(() -> new QRCodeException("模板不存在"));

// 更新模板属性
template.setName(request.getName());
template.setDescription(request.getDescription());
template.setWidth(request.getWidth());
template.setHeight(request.getHeight());
template.setMargin(request.getMargin());
template.setErrorCorrection(request.getErrorCorrection());
template.setForeColor(request.getForeColor());
template.setBackColor(request.getBackColor());
template.setLogoPath(request.getLogoPath());
template.setUpdateTime(System.currentTimeMillis());

// 保存模板
templateRepository.save(template);

return template;

} catch (Exception e) {
log.error("更新二维码模板失败", e);
throw new QRCodeException("更新二维码模板失败", e);
}
}

/**
* 删除模板
*/
public void deleteTemplate(String templateId) {
try {
// 获取模板
QRCodeTemplate template = templateRepository.findById(templateId)
.orElseThrow(() -> new QRCodeException("模板不存在"));

// 软删除
template.setStatus(TemplateStatus.DELETED);
template.setDeleteTime(System.currentTimeMillis());

// 保存模板
templateRepository.save(template);

} catch (Exception e) {
log.error("删除二维码模板失败", e);
throw new QRCodeException("删除二维码模板失败", e);
}
}

/**
* 获取模板列表
*/
public List<QRCodeTemplate> getTemplateList(TemplateQueryRequest request) {
try {
// 构建查询条件
Specification<QRCodeTemplate> spec = buildTemplateSpecification(request);

// 查询模板
List<QRCodeTemplate> templates = templateRepository.findAll(spec);

return templates;

} catch (Exception e) {
log.error("获取模板列表失败", e);
throw new QRCodeException("获取模板列表失败", e);
}
}

/**
* 构建查询条件
*/
private Specification<QRCodeTemplate> buildTemplateSpecification(TemplateQueryRequest request) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();

// 状态条件
if (request.getStatus() != null) {
predicates.add(cb.equal(root.get("status"), request.getStatus()));
}

// 名称条件
if (StringUtils.hasText(request.getName())) {
predicates.add(cb.like(root.get("name"), "%" + request.getName() + "%"));
}

// 创建时间条件
if (request.getStartTime() != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get("createTime"), request.getStartTime()));
}

if (request.getEndTime() != null) {
predicates.add(cb.lessThanOrEqualTo(root.get("createTime"), request.getEndTime()));
}

return cb.and(predicates.toArray(new Predicate[0]));
};
}

/**
* 验证模板请求
*/
private void validateTemplateRequest(QRCodeTemplateRequest request) {
if (StringUtils.isEmpty(request.getName())) {
throw new QRCodeException("模板名称不能为空");
}

if (request.getWidth() <= 0 || request.getHeight() <= 0) {
throw new QRCodeException("模板尺寸必须大于0");
}

if (request.getMargin() < 0) {
throw new QRCodeException("边距不能小于0");
}
}
}

3.3 二维码缓存服务

3.3.1 缓存服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
* 二维码缓存服务
*/
@Service
public class QRCodeCacheService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private CaffeineCache localCache;

private final String QR_CODE_PREFIX = "qrcode:";
private final String TEMPLATE_PREFIX = "template:";

/**
* 获取缓存
*/
public <T> T get(String key, Class<T> clazz) {
try {
// 1. 尝试从本地缓存获取
T value = localCache.getIfPresent(key);
if (value != null) {
return value;
}

// 2. 从Redis获取
Object redisValue = redisTemplate.opsForValue().get(key);
if (redisValue != null) {
T result = convertValue(redisValue, clazz);
// 写入本地缓存
localCache.put(key, result);
return result;
}

return null;

} catch (Exception e) {
log.error("获取缓存失败: {}", key, e);
return null;
}
}

/**
* 设置缓存
*/
public void put(String key, Object value, Duration duration) {
try {
// 1. 写入本地缓存
localCache.put(key, value);

// 2. 写入Redis
redisTemplate.opsForValue().set(key, value, duration);

} catch (Exception e) {
log.error("设置缓存失败: {}", key, e);
}
}

/**
* 删除缓存
*/
public void evict(String key) {
try {
// 1. 删除本地缓存
localCache.invalidate(key);

// 2. 删除Redis缓存
redisTemplate.delete(key);

} catch (Exception e) {
log.error("删除缓存失败: {}", key, e);
}
}

/**
* 批量获取缓存
*/
public <T> Map<String, T> getBatch(List<String> keys, Class<T> clazz) {
Map<String, T> result = new HashMap<>();

for (String key : keys) {
T value = get(key, clazz);
if (value != null) {
result.put(key, value);
}
}

return result;
}

/**
* 批量设置缓存
*/
public void putBatch(Map<String, Object> keyValues, Duration duration) {
for (Map.Entry<String, Object> entry : keyValues.entrySet()) {
put(entry.getKey(), entry.getValue(), duration);
}
}

/**
* 清理过期缓存
*/
@Scheduled(fixedRate = 300000) // 5分钟
public void cleanupExpiredCache() {
try {
// 清理本地缓存
localCache.cleanUp();

// 清理Redis过期键
cleanupRedisExpiredKeys();

} catch (Exception e) {
log.error("清理过期缓存失败", e);
}
}

/**
* 清理Redis过期键
*/
private void cleanupRedisExpiredKeys() {
try {
// 获取所有二维码缓存键
Set<String> qrCodeKeys = redisTemplate.keys(QR_CODE_PREFIX + "*");

for (String key : qrCodeKeys) {
// 检查是否过期
Long ttl = redisTemplate.getExpire(key);
if (ttl != null && ttl <= 0) {
redisTemplate.delete(key);
}
}

} catch (Exception e) {
log.error("清理Redis过期键失败", e);
}
}

/**
* 转换值类型
*/
@SuppressWarnings("unchecked")
private <T> T convertValue(Object value, Class<T> clazz) {
if (value == null) {
return null;
}

if (clazz.isAssignableFrom(value.getClass())) {
return (T) value;
}

// 使用Jackson转换
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.convertValue(value, clazz);
} catch (Exception e) {
log.error("转换值类型失败", e);
return null;
}
}
}

四、二维码应用场景

4.1 支付场景应用

4.1.1 支付二维码生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* 支付二维码服务
*/
@Service
public class PaymentQRCodeService {

@Autowired
private QRCodeService qrCodeService;

@Autowired
private PaymentService paymentService;

/**
* 生成支付二维码
*/
public PaymentQRCodeResponse generatePaymentQRCode(PaymentQRCodeRequest request) {
try {
// 1. 创建支付订单
PaymentOrder order = paymentService.createOrder(request);

// 2. 构建支付内容
String paymentContent = buildPaymentContent(order);

// 3. 生成二维码请求
QRCodeRequest qrCodeRequest = new QRCodeRequest();
qrCodeRequest.setContent(paymentContent);
qrCodeRequest.setWidth(300);
qrCodeRequest.setHeight(300);
qrCodeRequest.setMargin(2);
qrCodeRequest.setErrorCorrection(ErrorCorrectionLevel.H);
qrCodeRequest.setForeColor(Color.BLACK);
qrCodeRequest.setBackColor(Color.WHITE);
qrCodeRequest.setExpireTime(order.getExpireTime());

// 4. 生成二维码
QRCodeResponse qrCodeResponse = qrCodeService.generateQRCode(qrCodeRequest);

// 5. 构建支付响应
PaymentQRCodeResponse response = new PaymentQRCodeResponse();
response.setOrderId(order.getId());
response.setQrCodeImage(qrCodeResponse.getImageUrl());
response.setQrCodeContent(paymentContent);
response.setAmount(order.getAmount());
response.setExpireTime(order.getExpireTime());
response.setStatus(PaymentStatus.PENDING);

return response;

} catch (Exception e) {
log.error("生成支付二维码失败", e);
throw new PaymentException("生成支付二维码失败", e);
}
}

/**
* 解析支付二维码
*/
public PaymentQRCodeParseResponse parsePaymentQRCode(String qrCodeContent) {
try {
// 1. 解析二维码内容
PaymentContent paymentContent = parsePaymentContent(qrCodeContent);

// 2. 验证支付信息
boolean isValid = validatePaymentContent(paymentContent);

// 3. 构建解析响应
PaymentQRCodeParseResponse response = new PaymentQRCodeParseResponse();
response.setValid(isValid);
response.setOrderId(paymentContent.getOrderId());
response.setAmount(paymentContent.getAmount());
response.setMerchantId(paymentContent.getMerchantId());
response.setProductName(paymentContent.getProductName());

return response;

} catch (Exception e) {
log.error("解析支付二维码失败", e);
throw new PaymentException("解析支付二维码失败", e);
}
}

/**
* 构建支付内容
*/
private String buildPaymentContent(PaymentOrder order) {
PaymentContent content = new PaymentContent();
content.setOrderId(order.getId());
content.setAmount(order.getAmount());
content.setMerchantId(order.getMerchantId());
content.setProductName(order.getProductName());
content.setTimestamp(System.currentTimeMillis());
content.setSignature(calculateSignature(content));

return JSON.toJSONString(content);
}

/**
* 解析支付内容
*/
private PaymentContent parsePaymentContent(String qrCodeContent) {
try {
return JSON.parseObject(qrCodeContent, PaymentContent.class);
} catch (Exception e) {
throw new PaymentException("解析支付内容失败", e);
}
}

/**
* 验证支付内容
*/
private boolean validatePaymentContent(PaymentContent content) {
try {
// 验证签名
String expectedSignature = calculateSignature(content);
if (!expectedSignature.equals(content.getSignature())) {
return false;
}

// 验证时间戳
long currentTime = System.currentTimeMillis();
long timeDiff = Math.abs(currentTime - content.getTimestamp());
if (timeDiff > 5 * 60 * 1000) { // 5分钟
return false;
}

// 验证订单状态
PaymentOrder order = paymentService.getOrder(content.getOrderId());
if (order == null || order.getStatus() != PaymentStatus.PENDING) {
return false;
}

return true;

} catch (Exception e) {
log.error("验证支付内容失败", e);
return false;
}
}

/**
* 计算签名
*/
private String calculateSignature(PaymentContent content) {
try {
StringBuilder data = new StringBuilder();
data.append(content.getOrderId());
data.append(content.getAmount());
data.append(content.getMerchantId());
data.append(content.getTimestamp());

Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec("payment-secret".getBytes(), "HmacSHA256");
mac.init(secretKeySpec);

byte[] signature = mac.doFinal(data.toString().getBytes());
return Base64.encode(signature);

} catch (Exception e) {
throw new PaymentException("计算签名失败", e);
}
}
}

4.2 登录场景应用

4.2.1 扫码登录二维码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* 扫码登录二维码服务
*/
@Service
public class LoginQRCodeService {

@Autowired
private QRCodeService qrCodeService;

@Autowired
private LoginSessionService sessionService;

/**
* 生成登录二维码
*/
public LoginQRCodeResponse generateLoginQRCode(LoginQRCodeRequest request) {
try {
// 1. 创建登录会话
LoginSession session = sessionService.createSession(request);

// 2. 构建登录内容
String loginContent = buildLoginContent(session);

// 3. 生成二维码请求
QRCodeRequest qrCodeRequest = new QRCodeRequest();
qrCodeRequest.setContent(loginContent);
qrCodeRequest.setWidth(300);
qrCodeRequest.setHeight(300);
qrCodeRequest.setMargin(2);
qrCodeRequest.setErrorCorrection(ErrorCorrectionLevel.H);
qrCodeRequest.setForeColor(Color.BLACK);
qrCodeRequest.setBackColor(Color.WHITE);
qrCodeRequest.setExpireTime(session.getExpireTime());

// 4. 生成二维码
QRCodeResponse qrCodeResponse = qrCodeService.generateQRCode(qrCodeRequest);

// 5. 构建登录响应
LoginQRCodeResponse response = new LoginQRCodeResponse();
response.setSessionId(session.getId());
response.setQrCodeImage(qrCodeResponse.getImageUrl());
response.setQrCodeContent(loginContent);
response.setExpireTime(session.getExpireTime());
response.setStatus(LoginStatus.PENDING);

return response;

} catch (Exception e) {
log.error("生成登录二维码失败", e);
throw new LoginException("生成登录二维码失败", e);
}
}

/**
* 解析登录二维码
*/
public LoginQRCodeParseResponse parseLoginQRCode(String qrCodeContent) {
try {
// 1. 解析二维码内容
LoginContent loginContent = parseLoginContent(qrCodeContent);

// 2. 验证登录信息
boolean isValid = validateLoginContent(loginContent);

// 3. 构建解析响应
LoginQRCodeParseResponse response = new LoginQRCodeParseResponse();
response.setValid(isValid);
response.setSessionId(loginContent.getSessionId());
response.setClientId(loginContent.getClientId());
response.setRedirectUrl(loginContent.getRedirectUrl());

return response;

} catch (Exception e) {
log.error("解析登录二维码失败", e);
throw new LoginException("解析登录二维码失败", e);
}
}

/**
* 构建登录内容
*/
private String buildLoginContent(LoginSession session) {
LoginContent content = new LoginContent();
content.setSessionId(session.getId());
content.setClientId(session.getClientId());
content.setRedirectUrl(session.getRedirectUrl());
content.setTimestamp(System.currentTimeMillis());
content.setSignature(calculateLoginSignature(content));

return JSON.toJSONString(content);
}

/**
* 解析登录内容
*/
private LoginContent parseLoginContent(String qrCodeContent) {
try {
return JSON.parseObject(qrCodeContent, LoginContent.class);
} catch (Exception e) {
throw new LoginException("解析登录内容失败", e);
}
}

/**
* 验证登录内容
*/
private boolean validateLoginContent(LoginContent content) {
try {
// 验证签名
String expectedSignature = calculateLoginSignature(content);
if (!expectedSignature.equals(content.getSignature())) {
return false;
}

// 验证时间戳
long currentTime = System.currentTimeMillis();
long timeDiff = Math.abs(currentTime - content.getTimestamp());
if (timeDiff > 5 * 60 * 1000) { // 5分钟
return false;
}

// 验证会话状态
LoginSession session = sessionService.getSession(content.getSessionId());
if (session == null || session.getStatus() != LoginStatus.PENDING) {
return false;
}

return true;

} catch (Exception e) {
log.error("验证登录内容失败", e);
return false;
}
}

/**
* 计算登录签名
*/
private String calculateLoginSignature(LoginContent content) {
try {
StringBuilder data = new StringBuilder();
data.append(content.getSessionId());
data.append(content.getClientId());
data.append(content.getTimestamp());

Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec("login-secret".getBytes(), "HmacSHA256");
mac.init(secretKeySpec);

byte[] signature = mac.doFinal(data.toString().getBytes());
return Base64.encode(signature);

} catch (Exception e) {
throw new LoginException("计算登录签名失败", e);
}
}
}

4.3 营销场景应用

4.3.1 营销二维码生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
* 营销二维码服务
*/
@Service
public class MarketingQRCodeService {

@Autowired
private QRCodeService qrCodeService;

@Autowired
private MarketingCampaignService campaignService;

/**
* 生成营销二维码
*/
public MarketingQRCodeResponse generateMarketingQRCode(MarketingQRCodeRequest request) {
try {
// 1. 创建营销活动
MarketingCampaign campaign = campaignService.createCampaign(request);

// 2. 构建营销内容
String marketingContent = buildMarketingContent(campaign);

// 3. 生成二维码请求
QRCodeRequest qrCodeRequest = new QRCodeRequest();
qrCodeRequest.setContent(marketingContent);
qrCodeRequest.setWidth(300);
qrCodeRequest.setHeight(300);
qrCodeRequest.setMargin(2);
qrCodeRequest.setErrorCorrection(ErrorCorrectionLevel.M);
qrCodeRequest.setForeColor(Color.BLACK);
qrCodeRequest.setBackColor(Color.WHITE);
qrCodeRequest.setLogoPath(campaign.getLogoPath());
qrCodeRequest.setExpireTime(campaign.getExpireTime());

// 4. 生成二维码
QRCodeResponse qrCodeResponse = qrCodeService.generateQRCode(qrCodeRequest);

// 5. 构建营销响应
MarketingQRCodeResponse response = new MarketingQRCodeResponse();
response.setCampaignId(campaign.getId());
response.setQrCodeImage(qrCodeResponse.getImageUrl());
response.setQrCodeContent(marketingContent);
response.setCampaignName(campaign.getName());
response.setDiscount(campaign.getDiscount());
response.setExpireTime(campaign.getExpireTime());
response.setStatus(CampaignStatus.ACTIVE);

return response;

} catch (Exception e) {
log.error("生成营销二维码失败", e);
throw new MarketingException("生成营销二维码失败", e);
}
}

/**
* 解析营销二维码
*/
public MarketingQRCodeParseResponse parseMarketingQRCode(String qrCodeContent) {
try {
// 1. 解析二维码内容
MarketingContent marketingContent = parseMarketingContent(qrCodeContent);

// 2. 验证营销信息
boolean isValid = validateMarketingContent(marketingContent);

// 3. 构建解析响应
MarketingQRCodeParseResponse response = new MarketingQRCodeParseResponse();
response.setValid(isValid);
response.setCampaignId(marketingContent.getCampaignId());
response.setCampaignName(marketingContent.getCampaignName());
response.setDiscount(marketingContent.getDiscount());
response.setProductId(marketingContent.getProductId());
response.setRedirectUrl(marketingContent.getRedirectUrl());

return response;

} catch (Exception e) {
log.error("解析营销二维码失败", e);
throw new MarketingException("解析营销二维码失败", e);
}
}

/**
* 构建营销内容
*/
private String buildMarketingContent(MarketingCampaign campaign) {
MarketingContent content = new MarketingContent();
content.setCampaignId(campaign.getId());
content.setCampaignName(campaign.getName());
content.setDiscount(campaign.getDiscount());
content.setProductId(campaign.getProductId());
content.setRedirectUrl(campaign.getRedirectUrl());
content.setTimestamp(System.currentTimeMillis());
content.setSignature(calculateMarketingSignature(content));

return JSON.toJSONString(content);
}

/**
* 解析营销内容
*/
private MarketingContent parseMarketingContent(String qrCodeContent) {
try {
return JSON.parseObject(qrCodeContent, MarketingContent.class);
} catch (Exception e) {
throw new MarketingException("解析营销内容失败", e);
}
}

/**
* 验证营销内容
*/
private boolean validateMarketingContent(MarketingContent content) {
try {
// 验证签名
String expectedSignature = calculateMarketingSignature(content);
if (!expectedSignature.equals(content.getSignature())) {
return false;
}

// 验证时间戳
long currentTime = System.currentTimeMillis();
long timeDiff = Math.abs(currentTime - content.getTimestamp());
if (timeDiff > 24 * 60 * 60 * 1000) { // 24小时
return false;
}

// 验证活动状态
MarketingCampaign campaign = campaignService.getCampaign(content.getCampaignId());
if (campaign == null || campaign.getStatus() != CampaignStatus.ACTIVE) {
return false;
}

return true;

} catch (Exception e) {
log.error("验证营销内容失败", e);
return false;
}
}

/**
* 计算营销签名
*/
private String calculateMarketingSignature(MarketingContent content) {
try {
StringBuilder data = new StringBuilder();
data.append(content.getCampaignId());
data.append(content.getProductId());
data.append(content.getTimestamp());

Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec("marketing-secret".getBytes(), "HmacSHA256");
mac.init(secretKeySpec);

byte[] signature = mac.doFinal(data.toString().getBytes());
return Base64.encode(signature);

} catch (Exception e) {
throw new MarketingException("计算营销签名失败", e);
}
}
}

五、企业级部署方案

5.1 容器化部署

5.1.1 Docker配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Dockerfile
FROM openjdk:8-jdk-alpine

# 安装必要的工具
RUN apk add --no-cache ttf-dejavu

# 设置工作目录
WORKDIR /app

# 复制应用文件
COPY target/qrcode-service-*.jar app.jar

# 设置环境变量
ENV JAVA_OPTS="-Xms512m -Xmx1g -XX:+UseG1GC"

# 暴露端口
EXPOSE 8080

# 启动命令
CMD ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# docker-compose.yml
version: '3.8'
services:
qrcode-service:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- REDIS_HOST=redis
- MYSQL_HOST=mysql
depends_on:
- redis
- mysql
restart: unless-stopped

redis:
image: redis:6.2-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped

mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root123
- MYSQL_DATABASE=qrcode
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
restart: unless-stopped

volumes:
redis_data:
mysql_data:

5.2 监控告警

5.2.1 监控指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 二维码监控指标
*/
@Component
public class QRCodeMetrics {

private final MeterRegistry meterRegistry;

public QRCodeMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

/**
* 记录二维码生成
*/
public void recordQRCodeGenerated() {
Counter.builder("qrcode.generated")
.description("二维码生成次数")
.register(meterRegistry)
.increment();
}

/**
* 记录二维码解析
*/
public void recordQRCodeParsed() {
Counter.builder("qrcode.parsed")
.description("二维码解析次数")
.register(meterRegistry)
.increment();
}

/**
* 记录二维码生成失败
*/
public void recordQRCodeGenerationFailed() {
Counter.builder("qrcode.generation.failed")
.description("二维码生成失败次数")
.register(meterRegistry)
.increment();
}

/**
* 记录二维码解析失败
*/
public void recordQRCodeParseFailed() {
Counter.builder("qrcode.parse.failed")
.description("二维码解析失败次数")
.register(meterRegistry)
.increment();
}

/**
* 记录响应时间
*/
public void recordResponseTime(String operation, long duration) {
Timer.builder("qrcode.response.time")
.description("响应时间")
.tag("operation", operation)
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
}
}

5.2.2 告警规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# prometheus-rules.yml
groups:
- name: qrcode_alerts
rules:
- alert: HighQRCodeGenerationFailureRate
expr: rate(qrcode_generation_failed[5m]) / rate(qrcode_generated[5m] + qrcode_generation_failed[5m]) > 0.1
for: 2m
labels:
severity: warning
annotations:
summary: "二维码生成失败率过高"
description: "二维码生成失败率超过10%,当前值: {{ $value }}"

- alert: HighQRCodeParseFailureRate
expr: rate(qrcode_parse_failed[5m]) / rate(qrcode_parsed[5m] + qrcode_parse_failed[5m]) > 0.1
for: 2m
labels:
severity: warning
annotations:
summary: "二维码解析失败率过高"
description: "二维码解析失败率超过10%,当前值: {{ $value }}"

- alert: HighResponseTime
expr: qrcode_response_time{quantile="0.95"} > 2000
for: 2m
labels:
severity: warning
annotations:
summary: "二维码服务响应时间过长"
description: "二维码服务响应时间P95超过2秒,当前值: {{ $value }}ms"

六、总结

使用Hutool生成与解析二维码,通过合理的架构设计和应用场景实现,能够为企业提供高效、稳定的二维码服务。本文从Hutool基础使用到企业级应用,从二维码生成解析到各种应用场景,系统梳理了基于Hutool的二维码应用完整解决方案。

6.1 关键要点

  1. 工具选择:Hutool提供了简洁易用的二维码生成和解析功能
  2. 架构设计:采用分层架构,支持模板管理和缓存优化
  3. 应用场景:支持支付、登录、营销等多种应用场景
  4. 安全机制:通过签名验证确保二维码内容的安全性
  5. 性能优化:通过缓存和批量处理提高系统性能

6.2 最佳实践

  1. 配置管理:使用模板管理二维码的样式和参数
  2. 缓存优化:使用多级缓存提高二维码生成和解析性能
  3. 安全防护:通过签名验证防止二维码内容被篡改
  4. 监控告警:建立完善的监控体系,及时发现和处理问题
  5. 容器化部署:使用Docker和Kubernetes实现服务的容器化部署

通过以上措施,可以构建一个高效、稳定、可扩展的二维码服务系统,为企业的各种业务场景提供支持。