添加生成课程码的功能
This commit is contained in:
7
pom.xml
7
pom.xml
@ -207,6 +207,13 @@
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
|
||||
<!--图片合成-->
|
||||
<dependency>
|
||||
<groupId>com.freewayso</groupId>
|
||||
<artifactId>image-combiner</artifactId>
|
||||
<version>2.6.9</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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<String> getCourseQrcode(@Valid @RequestBody CommonStringRequest commonStringRequest) throws Exception {
|
||||
String inviteCode = commonStringRequest.getTemplateString();
|
||||
String view = wechatGetQrcodeService.getWxCourseQrCode(inviteCode);
|
||||
return ResultUtils.success(view);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -22,4 +22,10 @@ public interface WechatGetQrcodeService {
|
||||
* 微信小程序获取二维码
|
||||
*/
|
||||
String getWxQrCode(String inviteCode) throws IOException;
|
||||
|
||||
|
||||
/**
|
||||
* 微信小程序获取课程码
|
||||
*/
|
||||
String getWxCourseQrCode(String inviteCode) throws Exception;
|
||||
}
|
||||
|
@ -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<String, Object> 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ spring:
|
||||
data:
|
||||
redis:
|
||||
port: 6379
|
||||
host: 154.8.193.216
|
||||
host: 27.30.77.229
|
||||
database: 7
|
||||
password: Cksys6509
|
||||
|
||||
|
@ -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/
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -13,7 +13,7 @@ spring:
|
||||
data:
|
||||
redis:
|
||||
port: 6379
|
||||
host: 154.8.193.216
|
||||
host: 27.30.77.229
|
||||
database: 8
|
||||
password: Cksys6509
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: test
|
||||
active: dev
|
||||
|
||||
|
BIN
src/main/resources/fonts/SourceHanSansCN-Bold.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-Bold.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSansCN-ExtraLight.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSansCN-Heavy.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-Heavy.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSansCN-Light.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-Light.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSansCN-Medium.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-Medium.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSansCN-Normal.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-Normal.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSansCN-Regular.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSansCN-Regular.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-Bold.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-Bold.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-ExtraLight.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-Heavy.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-Heavy.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-Light.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-Light.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-Medium.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-Medium.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-Regular.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-Regular.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/fonts/SourceHanSerifCN-SemiBold.ttf
Normal file
BIN
src/main/resources/fonts/SourceHanSerifCN-SemiBold.ttf
Normal file
Binary file not shown.
Reference in New Issue
Block a user