diff --git a/pom.xml b/pom.xml
index d1e0755..4282b7c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -207,6 +207,13 @@
2.6
+
+
+ com.freewayso
+ image-combiner
+ 2.6.9
+
+
diff --git a/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java b/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java
index 2ce8052..2e15abc 100644
--- a/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java
+++ b/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java
@@ -3,6 +3,8 @@ package com.greenorange.promotion.controller.wechat;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
+import com.freewayso.image.combiner.ImageCombiner;
+import com.freewayso.image.combiner.enums.OutputFormat;
import com.greenorange.promotion.annotation.RequiresPermission;
import com.greenorange.promotion.common.BaseResponse;
import com.greenorange.promotion.common.ErrorCode;
@@ -19,15 +21,18 @@ import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.FileSystemResource;
import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -78,6 +83,25 @@ public class WechatGetQrcodeController {
}
+ /**
+ * 微信小程序获取课程码
+ * @return
+ * @throws IOException
+ */
+// @Hidden
+ @PostMapping("/get/course/qrcode")
+ @Operation(summary = "微信小程序获取课程码", description = "参数:无, 权限:所有人, 方法名:getCourseQrcode")
+// @RequiresPermission(mustRole = UserConstant.DEFAULT_ROLE)
+ public BaseResponse getCourseQrcode(@Valid @RequestBody CommonStringRequest commonStringRequest) throws Exception {
+ String inviteCode = commonStringRequest.getTemplateString();
+ String view = wechatGetQrcodeService.getWxCourseQrCode(inviteCode);
+ return ResultUtils.success(view);
+ }
+
+
+
+
+
diff --git a/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java b/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java
index 3e0a7b2..362d89f 100644
--- a/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java
+++ b/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java
@@ -22,4 +22,10 @@ public interface WechatGetQrcodeService {
* 微信小程序获取二维码
*/
String getWxQrCode(String inviteCode) throws IOException;
+
+
+ /**
+ * 微信小程序获取课程码
+ */
+ String getWxCourseQrCode(String inviteCode) throws Exception;
}
diff --git a/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java b/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java
index 3b7c6aa..1a0e8ef 100644
--- a/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java
+++ b/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java
@@ -1,14 +1,16 @@
package com.greenorange.promotion.service.wechat.impl;
-
-import ch.qos.logback.core.util.MD5Util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
+import com.freewayso.image.combiner.ImageCombiner;
+import com.freewayso.image.combiner.element.TextElement;
+import com.freewayso.image.combiner.enums.BaseLine;
+import com.freewayso.image.combiner.enums.OutputFormat;
+import com.freewayso.image.combiner.enums.ZoomMode;
import com.google.gson.Gson;
import com.greenorange.promotion.common.ErrorCode;
-import com.greenorange.promotion.common.ResultUtils;
import com.greenorange.promotion.config.WxAccessToken;
import com.greenorange.promotion.exception.BusinessException;
import com.greenorange.promotion.model.entity.FileInfo;
@@ -19,16 +21,17 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
-import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.imageio.ImageIO;
+import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
-import java.util.Base64;
+import java.net.URL;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -185,4 +188,145 @@ public class WechatGetQrcodeServiceImpl implements WechatGetQrcodeService {
fileInfoService.save(fileInfo);
return biz + "-" + view;
}
+
+
+
+
+ /**
+ * 微信小程序获取课程码
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String getWxCourseQrCode(String inviteCode) throws Exception {
+ String accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY);
+ if (accessToken == null) {
+ accessToken = this.getAccessToken().getAccess_token();
+ }
+ Map param = new HashMap<>();
+ param.put("page", "pages/loginModule/register/register");
+ param.put("scene", "invitationCode=" + inviteCode);
+ param.put("width", 430);
+ param.put("check_path", false);
+ param.put("env_version", "develop");
+ String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;
+ String jsonParams = JSONUtil.toJsonStr(param);
+ byte[] responseBytes = HttpUtil.createPost(url)
+ .header("Content-Type", "application/json")
+ .body(jsonParams)
+ .execute()
+ .bodyBytes();
+
+ String courseTitle = "【早鸟42折】掌握CAD技能实战技能实战技能实战技能实战工作训练营";
+ String originalPrice = "2680元";
+ String discountPrice = "1680";
+ String discountText = "元券后价";
+ String FontType = "Microsoft YaHei UI";
+ int FontStyle = Font.PLAIN;
+
+ // 加载一张空白图像
+ String blankUrl = "https://img.picui.cn/free/2025/06/21/6856a072e9eea.png";
+ ImageCombiner combiner = new ImageCombiner(blankUrl, 341, 391, ZoomMode.WidthHeight, OutputFormat.PNG);
+
+ // 加载课程图片
+ String courseUrl = "https://img.picui.cn/free/2025/06/22/6856ef5908d4b.jpg";
+ BufferedImage courseImage = ImageIO.read(new URL(courseUrl));
+
+// Graphics2D graphics = courseImage.createGraphics();
+// graphics.setColor(Color.decode("#323232"));
+// graphics.setFont(new Font("阿里巴巴普惠体", Font.PLAIN, 60));
+// graphics.drawString("测试", 0, 80);
+// graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
+// graphics.drawString("测试", 0, 160);
+// graphics.dispose();
+
+ // 将二维码数据转换为 BufferedImage
+ BufferedImage qrImage = ImageIO.read(new ByteArrayInputStream(responseBytes));
+
+ // 缩放图片(不失真)
+ Image scaledImage = qrImage.getScaledInstance(134, 134, Image.SCALE_SMOOTH);
+ BufferedImage resizedImage = new BufferedImage(134, 134, BufferedImage.TYPE_INT_RGB);
+ resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
+
+ combiner.setQuality(1f);
+
+ // 将课程图片合并到组合器中
+ combiner.addImageElement(courseImage, 18, 16, 306, 158, ZoomMode.WidthHeight).setRoundCorner(5).setCenter(true);
+ // 将二维码图片合并到组合器中
+ combiner.addImageElement(resizedImage, 190, 240);
+
+ // 绘制文本
+ TextElement courseTitleElement = new TextElement(courseTitle, FontType, FontStyle, 18, 20, 186);
+ courseTitleElement.setColor(Color.decode("#323232"))
+ .setCenter(true)
+ .setAutoBreakLine(306);
+ combiner.addElement(courseTitleElement);
+
+ TextElement originalPriceElement = new TextElement(originalPrice, FontType, FontStyle, 24, 56, 275);
+ originalPriceElement.setColor(Color.decode("#8C8C8C")).setStrikeThrough(true);
+ combiner.addElement(originalPriceElement);
+
+ TextElement discountedPriceElement = new TextElement(discountPrice, FontType, Font.BOLD, 28, 31, 343);
+ discountedPriceElement.setColor(Color.decode("#F84947")).setBaseLine(BaseLine.Base).setSpace(0.01f);
+ combiner.addElement(discountedPriceElement);
+
+ TextElement discountedTextElement = new TextElement(discountText, FontType, FontStyle, 16, discountedPriceElement.getX() + discountedPriceElement.getWidth(), 341);
+ discountedTextElement.setColor(Color.decode("#F84947")).setBaseLine(BaseLine.Base);
+ combiner.addElement(discountedTextElement);
+
+ combiner.combine();
+
+ InputStream resultStream = combiner.getCombinedImageStream();
+ byte[] resultBytes = resultStream.readAllBytes();
+ // 创建上传目录,如果不存在
+ String biz = "default";
+ String fileName = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + "." + "png";
+ // 获取文件类型
+ String fileType = FileUtil.getSuffix(fileName);
+ // 获取view值
+ String view = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
+ File file = new File(uploadDir + fileName);
+ if (!file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();// 如果路径不存在则创建
+ }
+ // 将文件上传到目标位置
+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)) {
+ bos.write(resultBytes);
+ } catch (IOException e) {
+ throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败,失败原因:" + e.getMessage());
+ }
+
+ // 获取文件大小
+ Double fileSize = file.length() / 1024.0;
+ fileSize = Double.valueOf(String.format("%.2f", fileSize));
+ // 获取文件哈希值
+ InputStream inputStream = new FileInputStream(file);
+ String hashValue = DigestUtils.sha256Hex(inputStream);
+ // 保存文件
+ FileInfo fileInfo = FileInfo.builder()
+ .name(fileName)
+ .type(fileType)
+ .size(fileSize)
+ .fileView(view)
+ .biz(biz)
+ .hashValue(hashValue)
+ .build();
+ fileInfoService.save(fileInfo);
+ return biz + "-" + view;
+ }
+
+ public static void main(String[] args) {
+ // 获取本地图形环境
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ // 列出所有可用字体家族名
+ String[] families = ge.getAvailableFontFamilyNames();
+
+ // 打印全部字体(可选)
+ System.out.println("=== Available Font Families ===");
+ Arrays.stream(families).forEach(System.out::println);
+ }
+
+
+
+
+
}
diff --git a/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java b/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java
index 866362d..9abc64f 100644
--- a/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java
+++ b/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java
@@ -3,11 +3,14 @@ package com.greenorange.promotion.utils;
import javax.imageio.ImageIO;
import java.awt.*;
+import java.awt.font.LineBreakMeasurer;
+import java.awt.font.TextLayout;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
+import java.text.AttributedString;
public class QRCodeUtil {
@@ -128,4 +131,9 @@ public class QRCodeUtil {
}
+
+
+
+
+
}
diff --git a/src/main/resources/application-caozhe.yml b/src/main/resources/application-caozhe.yml
index cc0ef4c..b4e441e 100644
--- a/src/main/resources/application-caozhe.yml
+++ b/src/main/resources/application-caozhe.yml
@@ -13,7 +13,7 @@ spring:
data:
redis:
port: 6379
- host: 154.8.193.216
+ host: 27.30.77.229
database: 7
password: Cksys6509
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index adef54b..73dfe6d 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -13,7 +13,7 @@ spring:
data:
redis:
port: 6379
- host: 154.8.193.216
+ host: 27.30.77.229
database: 9
password: Cksys6509
@@ -28,8 +28,8 @@ spring:
#文件上传和下载地址
file:
- upload-dir: /www/wwwroot/fileUpload_qc/
-# upload-dir: D:/qingcheng/image/
+# upload-dir: /www/wwwroot/fileUpload_qc/
+ upload-dir: D:/qingcheng/image/
diff --git a/src/main/resources/application-practice.yml b/src/main/resources/application-practice.yml
index bac9b4e..69d6630 100644
--- a/src/main/resources/application-practice.yml
+++ b/src/main/resources/application-practice.yml
@@ -13,8 +13,8 @@ spring:
data:
redis:
port: 6379
- host: 154.8.193.216
- database: 9
+ host: 27.30.77.229
+ database: 10
password: Cksys6509
servlet:
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
index 8440964..d8ad409 100644
--- a/src/main/resources/application-test.yml
+++ b/src/main/resources/application-test.yml
@@ -13,7 +13,7 @@ spring:
data:
redis:
port: 6379
- host: 154.8.193.216
+ host: 27.30.77.229
database: 8
password: Cksys6509
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index a17ec4c..ad9a171 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,4 +1,4 @@
spring:
profiles:
- active: test
+ active: dev
diff --git a/src/main/resources/fonts/SourceHanSansCN-Bold.ttf b/src/main/resources/fonts/SourceHanSansCN-Bold.ttf
new file mode 100644
index 0000000..76842e5
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-Bold.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSansCN-ExtraLight.ttf b/src/main/resources/fonts/SourceHanSansCN-ExtraLight.ttf
new file mode 100644
index 0000000..740fa34
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-ExtraLight.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSansCN-Heavy.ttf b/src/main/resources/fonts/SourceHanSansCN-Heavy.ttf
new file mode 100644
index 0000000..541ffa1
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-Heavy.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSansCN-Light.ttf b/src/main/resources/fonts/SourceHanSansCN-Light.ttf
new file mode 100644
index 0000000..f509b54
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-Light.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSansCN-Medium.ttf b/src/main/resources/fonts/SourceHanSansCN-Medium.ttf
new file mode 100644
index 0000000..8d7a190
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-Medium.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSansCN-Normal.ttf b/src/main/resources/fonts/SourceHanSansCN-Normal.ttf
new file mode 100644
index 0000000..220caae
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-Normal.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSansCN-Regular.ttf b/src/main/resources/fonts/SourceHanSansCN-Regular.ttf
new file mode 100644
index 0000000..eb247e4
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSansCN-Regular.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-Bold.ttf b/src/main/resources/fonts/SourceHanSerifCN-Bold.ttf
new file mode 100644
index 0000000..9db8c20
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-Bold.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-ExtraLight.ttf b/src/main/resources/fonts/SourceHanSerifCN-ExtraLight.ttf
new file mode 100644
index 0000000..c2c05b8
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-ExtraLight.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-Heavy.ttf b/src/main/resources/fonts/SourceHanSerifCN-Heavy.ttf
new file mode 100644
index 0000000..fd25c8b
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-Heavy.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-Light.ttf b/src/main/resources/fonts/SourceHanSerifCN-Light.ttf
new file mode 100644
index 0000000..f911f70
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-Light.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-Medium.ttf b/src/main/resources/fonts/SourceHanSerifCN-Medium.ttf
new file mode 100644
index 0000000..02d7172
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-Medium.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-Regular.ttf b/src/main/resources/fonts/SourceHanSerifCN-Regular.ttf
new file mode 100644
index 0000000..3e8bdca
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-Regular.ttf differ
diff --git a/src/main/resources/fonts/SourceHanSerifCN-SemiBold.ttf b/src/main/resources/fonts/SourceHanSerifCN-SemiBold.ttf
new file mode 100644
index 0000000..b66ca38
Binary files /dev/null and b/src/main/resources/fonts/SourceHanSerifCN-SemiBold.ttf differ