主管理员 2 weeks ago
parent
commit
e45b6b286d
6 changed files with 153 additions and 15 deletions
  1. +13
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml
  2. +2
    -2
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiPromotionController.java
  3. +1
    -1
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiWaterService.java
  4. +23
    -11
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiWaterServiceImpl.java
  5. +113
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/util/QRCodeUtil.java
  6. +1
    -1
      jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml

+ 13
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml View File

@ -40,6 +40,19 @@
<version>3.8.1</version> <version>3.8.1</version>
</dependency> </dependency>
<!-- zxing生成二维码 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
<dependency> <dependency>
<groupId>com.tencentcloudapi</groupId> <groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-tts</artifactId> <artifactId>tencentcloud-sdk-java-tts</artifactId>


+ 2
- 2
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiPromotionController.java View File

@ -32,8 +32,8 @@ public class AppletApiPromotionController {
@Operation(summary = "获取推广二维码", description = "获取推广二维码") @Operation(summary = "获取推广二维码", description = "获取推广二维码")
@GetMapping(value = "/qrCode", produces = MediaType.IMAGE_PNG_VALUE) @GetMapping(value = "/qrCode", produces = MediaType.IMAGE_PNG_VALUE)
public byte[] getInviteCode() {
return appletApiWaterService.getInviteCode();
public byte[] getInviteCode(@RequestParam(required = false, defaultValue = LoginType.APPLET) String type) {
return appletApiWaterService.getInviteCode(type);
} }
@Operation(summary = "我的团队", description = "我的团队") @Operation(summary = "我的团队", description = "我的团队")


+ 1
- 1
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiWaterService.java View File

@ -11,7 +11,7 @@ import java.util.List;
public interface AppletApiWaterService { public interface AppletApiWaterService {
byte[] getInviteCode();
byte[] getInviteCode(String type);
/** /**
* 流水列表 * 流水列表


+ 23
- 11
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiWaterServiceImpl.java View File

@ -11,8 +11,10 @@ import org.jeecg.common.system.util.AppletUserUtil;
import org.jeecg.common.system.vo.AppletUser; import org.jeecg.common.system.vo.AppletUser;
import org.jeecg.common.util.RedisUtil; import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oss.OssBootUtil; import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecg.modules.applet.Final.LoginType;
import org.jeecg.modules.applet.entity.StatisticsVo; import org.jeecg.modules.applet.entity.StatisticsVo;
import org.jeecg.modules.applet.service.AppletApiWaterService; import org.jeecg.modules.applet.service.AppletApiWaterService;
import org.jeecg.modules.applet.util.QRCodeUtil;
import org.jeecg.modules.common.IdUtils; import org.jeecg.modules.common.IdUtils;
import org.jeecg.modules.common.wxUtils.WxHttpUtils; import org.jeecg.modules.common.wxUtils.WxHttpUtils;
import org.jeecg.modules.demo.appletConfig.service.IAppletConfigService; import org.jeecg.modules.demo.appletConfig.service.IAppletConfigService;
@ -67,7 +69,7 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService {
private final ExecutorService asyncExecutor = Executors.newFixedThreadPool(5); private final ExecutorService asyncExecutor = Executors.newFixedThreadPool(5);
@Override @Override
public byte[] getInviteCode(){
public byte[] getInviteCode(String type){
AppletUser user = AppletUserUtil.getCurrentAppletUser(); AppletUser user = AppletUserUtil.getCurrentAppletUser();
// 获取环境配置 // 获取环境配置
@ -84,21 +86,23 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService {
// 获取必要的配置信息 // 获取必要的配置信息
String xcxSharePage = appletConfigService.getContentByCode("xcxSharePage"); String xcxSharePage = appletConfigService.getContentByCode("xcxSharePage");
String backgroundImageUrl = appletConfigService.getContentByCode("qr_code_bg"); String backgroundImageUrl = appletConfigService.getContentByCode("qr_code_bg");
String webUrl = appletConfigService.getContentByCode("webUrl");
int webUrlCode = (webUrl != null && LoginType.OFFICIAL.equals(type)) ? webUrl.hashCode() : 0;
// 获取二维码位置配置参数 // 获取二维码位置配置参数
int qrCodeX = appletConfigService.getContentByCodeAsInt("qr_code_x"); int qrCodeX = appletConfigService.getContentByCodeAsInt("qr_code_x");
int qrCodeY = appletConfigService.getContentByCodeAsInt("qr_code_y"); int qrCodeY = appletConfigService.getContentByCodeAsInt("qr_code_y");
// 优化缓存策略使用更精确的缓存key包含所有影响因素包括背景图片URL和二维码位置配置 // 优化缓存策略使用更精确的缓存key包含所有影响因素包括背景图片URL和二维码位置配置
String cacheKey = String.format("inviteCode:final:%s:%s:%s:%s:%s:%s",
user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY);
// String cacheKey = String.format("inviteCode:final:%s:%s:%s:%s:%s:%s",
// user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY);
// 移除图片数据的Redis缓存避免类型转换错误 // 移除图片数据的Redis缓存避免类型转换错误
// 直接检查OSS中是否已存在最终图片 // 直接检查OSS中是否已存在最终图片
// 检查OSS中是否已存在最终图片 // 检查OSS中是否已存在最终图片
String finalPath = String.format("invite/final/%s_%s_%s_%s_%s_%s.jpg",
user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY);
String finalPath = String.format("invite/final/%s_%s_%s_%s_%s_%s_%s_%s.jpg",
user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY, type, webUrlCode);
try { try {
InputStream ossFile = OssBootUtil.getOssFile(finalPath, null); InputStream ossFile = OssBootUtil.getOssFile(finalPath, null);
if (ossFile != null) { if (ossFile != null) {
@ -112,7 +116,7 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService {
} }
buffer.flush(); buffer.flush();
byte[] ossImageBytes = buffer.toByteArray(); byte[] ossImageBytes = buffer.toByteArray();
// 直接返回OSS中的图片不再缓存到Redis // 直接返回OSS中的图片不再缓存到Redis
log.info("从OSS返回邀请码图片,用户ID: {}", user.getId()); log.info("从OSS返回邀请码图片,用户ID: {}", user.getId());
return ossImageBytes; return ossImageBytes;
@ -127,10 +131,18 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService {
try { try {
// 直接生成小程序码移除Redis缓存避免类型转换错误 // 直接生成小程序码移除Redis缓存避免类型转换错误
log.info("生成小程序码,用户ID: {}", user.getId()); log.info("生成小程序码,用户ID: {}", user.getId());
byte[] qrCodeBytes = generateWxQrCode(user, xcxSharePage, trial);
// byte[] qrCodeBytes = null;
BufferedImage qrCodeImage = null;
if (LoginType.APPLET.equals(type)){
qrCodeImage = ImageIO.read(new ByteArrayInputStream(generateWxQrCode(user, xcxSharePage, trial)));
}else {
qrCodeImage = QRCodeUtil.getBufferedImage(webUrl + "?inviter=" + user.getId());
}
// 生成最终合成图片传递已获取的配置参数避免重复调用 // 生成最终合成图片传递已获取的配置参数避免重复调用
byte[] finalImage = this.generateAndCombineImagesFromUrl2(qrCodeBytes, backgroundImageUrl, qrCodeX, qrCodeY);
byte[] finalImage = this.generateAndCombineImagesFromUrl2(qrCodeImage, backgroundImageUrl, qrCodeX, qrCodeY);
// 异步上传到OSS移除Redis缓存 // 异步上传到OSS移除Redis缓存
uploadToOssAsync(finalImage, finalPath); uploadToOssAsync(finalImage, finalPath);
@ -192,7 +204,7 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService {
} }
public byte[] generateAndCombineImagesFromUrl2(byte[] qrCodeImageByte, String backgroundUrl, int qr_code_x, int qr_code_y) {
public byte[] generateAndCombineImagesFromUrl2(BufferedImage qrCodeImage, String backgroundUrl, int qr_code_x, int qr_code_y) {
File file = null; File file = null;
try { try {
// 直接从URL加载背景图像移除Redis缓存避免类型转换问题 // 直接从URL加载背景图像移除Redis缓存避免类型转换问题
@ -201,7 +213,7 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService {
log.debug("从URL加载背景图片: {}", backgroundUrl); log.debug("从URL加载背景图片: {}", backgroundUrl);
// 从字节数组加载小程序码图像 // 从字节数组加载小程序码图像
BufferedImage qrCodeImage = ImageIO.read(new ByteArrayInputStream(qrCodeImageByte));
// BufferedImage qrCodeImage = ImageIO.read(new ByteArrayInputStream(qrCodeImageByte));
// 使用传入的配置参数避免重复调用配置服务 // 使用传入的配置参数避免重复调用配置服务


+ 113
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/util/QRCodeUtil.java View File

@ -0,0 +1,113 @@
package org.jeecg.modules.applet.util;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.log4j.Log4j2;
import javax.imageio.ImageIO;
import javax.swing.filechooser.FileSystemView;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
@Log4j2
public class QRCodeUtil {
//CODE_WIDTH二维码宽度单位像素
private static final int CODE_WIDTH = 256;
//CODE_HEIGHT二维码高度单位像素
private static final int CODE_HEIGHT = 256;
//FRONT_COLOR二维码前景色0x000000 表示黑色
private static final int FRONT_COLOR = 0x000000;
//BACKGROUND_COLOR二维码背景色0xFFFFFF 表示白色
//演示用 16 进制表示和前端页面 CSS 的取色是一样的注意前后景颜色应该对比明显如常见的黑白
private static final int BACKGROUND_COLOR = 0xF4FCFF;//rgb(244 252 255)
public static void createCodeToFile(String content, File codeImgFileSaveDir, String fileName) {
try {
if (StringUtils.isBlank(content) || StringUtils.isBlank(fileName)) {
return;
}
content = content.trim();
if (codeImgFileSaveDir==null || codeImgFileSaveDir.isFile()) {
//二维码图片存在目录为空默认放在桌面...
codeImgFileSaveDir = FileSystemView.getFileSystemView().getHomeDirectory();
}
if (!codeImgFileSaveDir.exists()) {
//二维码图片存在目录不存在开始创建...
codeImgFileSaveDir.mkdirs();
}
//核心代码-生成二维码
BufferedImage bufferedImage = getBufferedImage(content);
File codeImgFile = new File(codeImgFileSaveDir, fileName);
ImageIO.write(bufferedImage, "png", codeImgFile);
log.info("二维码图片生成成功:" + codeImgFile.getPath());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成二维码并输出到输出流, 通常用于输出到网页上进行显示输出到网页与输出到磁盘上的文件中区别在于最后一句 ImageIO.write
* write(RenderedImage im,String formatName,File output)写到文件中
* write(RenderedImage im,String formatName,OutputStream output)输出到输出流中
* @param content 二维码内容
* @param outputStream 输出流比如 HttpServletResponse getOutputStream
*/
public static void createCodeToOutputStream(String content, OutputStream outputStream) {
try {
if (StringUtils.isBlank(content)) {
return;
}
content = content.trim();
//核心代码-生成二维码
BufferedImage bufferedImage = getBufferedImage(content);
//区别就是这一句输出到输出流中如果第三个参数是 File则输出到文件中
ImageIO.write(bufferedImage, "png", outputStream);
log.info("二维码图片生成到输出流成功...");
} catch (Exception e) {
e.printStackTrace();
}
}
//核心代码-生成二维码
public static BufferedImage getBufferedImage(String content) throws WriterException {
//com.google.zxing.EncodeHintType编码提示类型,枚举类型
Map<EncodeHintType, Object> hints = new HashMap();
//EncodeHintType.CHARACTER_SET设置字符编码类型
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//EncodeHintType.ERROR_CORRECTION设置误差校正
//ErrorCorrectionLevel误差校正等级L = ~7% correctionM = ~15% correctionQ = ~25% correctionH = ~30% correction
//不设置时默认为 L 等级等级不一样生成的图案不同但扫描的结果是一样的
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
//EncodeHintType.MARGIN设置二维码边距单位像素值越小二维码距离四周越近
hints.put(EncodeHintType.MARGIN, 1);
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints);
BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR);
for (int x = 0; x < CODE_WIDTH; x++) {
for (int y = 0; y < CODE_HEIGHT; y++) {
bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
}
}
return bufferedImage;
}
}

+ 1
- 1
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml View File

@ -1,5 +1,5 @@
server: server:
port: 8002
port: 8003
undertow: undertow:
# max-http-post-size: 10MB # 平替 tomcat server.tomcat.max-swallow-siz, undertow该值默认为-1 # max-http-post-size: 10MB # 平替 tomcat server.tomcat.max-swallow-siz, undertow该值默认为-1
worker-threads: 16 # 4核CPU标准配置 worker-threads: 16 # 4核CPU标准配置


Loading…
Cancel
Save