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