|
@@ -30,6 +30,11 @@ import com.fs.hisStore.vo.FsUserTuiVO;
|
|
import com.fs.utils.FileCacheService;
|
|
import com.fs.utils.FileCacheService;
|
|
import com.github.pagehelper.PageHelper;
|
|
import com.github.pagehelper.PageHelper;
|
|
import com.github.pagehelper.PageInfo;
|
|
import com.github.pagehelper.PageInfo;
|
|
|
|
+import com.google.zxing.BarcodeFormat;
|
|
|
|
+import com.google.zxing.EncodeHintType;
|
|
|
|
+import com.google.zxing.MultiFormatWriter;
|
|
|
|
+import com.google.zxing.common.BitMatrix;
|
|
|
|
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
|
import io.swagger.annotations.Api;
|
|
import io.swagger.annotations.Api;
|
|
import io.swagger.annotations.ApiOperation;
|
|
import io.swagger.annotations.ApiOperation;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@@ -40,7 +45,11 @@ import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
|
|
+import javax.imageio.IIOImage;
|
|
import javax.imageio.ImageIO;
|
|
import javax.imageio.ImageIO;
|
|
|
|
+import javax.imageio.ImageWriteParam;
|
|
|
|
+import javax.imageio.ImageWriter;
|
|
|
|
+import javax.imageio.stream.FileImageOutputStream;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.validation.Valid;
|
|
import javax.validation.Valid;
|
|
import java.awt.*;
|
|
import java.awt.*;
|
|
@@ -51,7 +60,12 @@ import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStream;
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
import java.net.URL;
|
|
import java.net.URL;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
|
|
@Api("个人中心")
|
|
@Api("个人中心")
|
|
@@ -129,27 +143,29 @@ public class UserScrmController extends AppBaseController {
|
|
@GetMapping("/getTuiImg")
|
|
@GetMapping("/getTuiImg")
|
|
public R getTuiImg(HttpServletRequest request){
|
|
public R getTuiImg(HttpServletRequest request){
|
|
log.info("获取推荐海报 headers: {}", ServletUtil.getHeaderMap(request));
|
|
log.info("获取推荐海报 headers: {}", ServletUtil.getHeaderMap(request));
|
|
- log.info("开始获取推荐海报");
|
|
|
|
-
|
|
|
|
- InputStream templateStream = null;
|
|
|
|
- InputStream fontStream = null;
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
String userId = getUserId();
|
|
String userId = getUserId();
|
|
log.info("获取用户ID: {}", userId);
|
|
log.info("获取用户ID: {}", userId);
|
|
-
|
|
|
|
if (StringUtils.isEmpty(userId)) {
|
|
if (StringUtils.isEmpty(userId)) {
|
|
log.error("用户ID为空");
|
|
log.error("用户ID为空");
|
|
return R.error("用户ID不能为空");
|
|
return R.error("用户ID不能为空");
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+ // 检查是否已存在海报,避免重复生成
|
|
|
|
+ String resultUrl = "profile/tui/tui-" + userId + ".jpg";
|
|
|
|
+ File existingPoster = new File(fsConfig.getTuiImgPath() + "/tui-" + userId + ".jpg");
|
|
|
|
+ if (existingPoster.exists() && existingPoster.lastModified() > System.currentTimeMillis() - 24 * 60 * 60 * 1000) {
|
|
|
|
+ log.info("使用已存在的海报: {}", resultUrl);
|
|
|
|
+ return R.ok().put("url", resultUrl);
|
|
|
|
+ }
|
|
FsUserScrm user = userService.selectFsUserById(Long.parseLong(userId));
|
|
FsUserScrm user = userService.selectFsUserById(Long.parseLong(userId));
|
|
if (user == null) {
|
|
if (user == null) {
|
|
log.error("未找到用户信息,用户ID: {}", userId);
|
|
log.error("未找到用户信息,用户ID: {}", userId);
|
|
return R.error("用户信息不存在");
|
|
return R.error("用户信息不存在");
|
|
}
|
|
}
|
|
-
|
|
|
|
log.info("查询到用户信息: 用户ID={}, 昵称={}", userId, user.getNickname());
|
|
log.info("查询到用户信息: 用户ID={}, 昵称={}", userId, user.getNickname());
|
|
|
|
+
|
|
|
|
+ // 确保用户有邀请码
|
|
if(StringUtils.isEmpty(user.getUserCode())){
|
|
if(StringUtils.isEmpty(user.getUserCode())){
|
|
log.info("用户邀请码为空,开始生成新的邀请码");
|
|
log.info("用户邀请码为空,开始生成新的邀请码");
|
|
FsUserScrm userMap = new FsUserScrm();
|
|
FsUserScrm userMap = new FsUserScrm();
|
|
@@ -159,213 +175,203 @@ public class UserScrmController extends AppBaseController {
|
|
user.setUserCode(userMap.getUserCode());
|
|
user.setUserCode(userMap.getUserCode());
|
|
log.info("成功生成并更新用户邀请码: {}", userMap.getUserCode());
|
|
log.info("成功生成并更新用户邀请码: {}", userMap.getUserCode());
|
|
}
|
|
}
|
|
-
|
|
|
|
- String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "poster_temp";
|
|
|
|
- File tempDirFile = new File(tempDir);
|
|
|
|
- if (!tempDirFile.exists()) {
|
|
|
|
- tempDirFile.mkdirs();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- File tempTemplateFile = new File(tempDir, "template_" + userId + "_" + System.currentTimeMillis() + ".jpg");
|
|
|
|
- File tempFontFile = new File(tempDir, "font_" + userId + "_" + System.currentTimeMillis() + ".ttf");
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- log.info("开始加载海报模板图片");
|
|
|
|
- templateStream = fileCacheService.getTemplateStream();
|
|
|
|
- FileUtils.copyInputStreamToFile(templateStream, tempTemplateFile);
|
|
|
|
- log.info("海报模板图片加载成功: {}", tempTemplateFile.getAbsolutePath());
|
|
|
|
- if (!isValidImageFile(tempTemplateFile)) {
|
|
|
|
- log.error("模板图片文件无效或损坏");
|
|
|
|
- return R.error("模板图片文件无效");
|
|
|
|
- }
|
|
|
|
- log.info("开始加载字体文件");
|
|
|
|
- fontStream = fileCacheService.getFontStream();
|
|
|
|
- FileUtils.copyInputStreamToFile(fontStream, tempFontFile);
|
|
|
|
- log.info("字体文件加载成功: {}", tempFontFile.getAbsolutePath());
|
|
|
|
-
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error("加载资源文件失败: {}", e.getMessage(), e);
|
|
|
|
- return R.error("加载资源文件失败: " + e.getMessage());
|
|
|
|
- }
|
|
|
|
- return generatePoster(user, tempTemplateFile, tempFontFile, userId);
|
|
|
|
-
|
|
|
|
|
|
+ return generatePosterOptimized(user, userId);
|
|
} catch (NumberFormatException e) {
|
|
} catch (NumberFormatException e) {
|
|
log.error("用户ID格式错误: {}", e.getMessage(), e);
|
|
log.error("用户ID格式错误: {}", e.getMessage(), e);
|
|
return R.error("用户ID格式错误");
|
|
return R.error("用户ID格式错误");
|
|
} catch (Exception e){
|
|
} catch (Exception e){
|
|
log.error("获取推荐海报失败: {}", e.getMessage(), e);
|
|
log.error("获取推荐海报失败: {}", e.getMessage(), e);
|
|
return R.error("操作异常: " + e.getMessage());
|
|
return R.error("操作异常: " + e.getMessage());
|
|
- } finally {
|
|
|
|
- closeQuietly(templateStream);
|
|
|
|
- closeQuietly(fontStream);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 生成海报的核心方法
|
|
|
|
|
|
+ * 优化的海报生成方法 - 在内存中完成所有图片操作
|
|
*/
|
|
*/
|
|
- private R generatePoster(FsUserScrm user, File templateFile, File fontFile, String userId) {
|
|
|
|
- File outputFile = null;
|
|
|
|
- File qrFile = null;
|
|
|
|
|
|
+ private R generatePosterOptimized(FsUserScrm user, String userId) {
|
|
|
|
+ log.info("开始生成用户海报,用户ID: {}", userId);
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
+ // 确保输出目录存在
|
|
String outputDir = fsConfig.getTuiImgPath();
|
|
String outputDir = fsConfig.getTuiImgPath();
|
|
File outputDirFile = new File(outputDir);
|
|
File outputDirFile = new File(outputDir);
|
|
if (!outputDirFile.exists()) {
|
|
if (!outputDirFile.exists()) {
|
|
outputDirFile.mkdirs();
|
|
outputDirFile.mkdirs();
|
|
}
|
|
}
|
|
-
|
|
|
|
- String outputPath = outputDir + "/tui-" + userId + ".jpg";
|
|
|
|
- outputFile = new File(outputPath);
|
|
|
|
- log.info("开始生成用户海报,输出路径: {}", outputPath);
|
|
|
|
- Font font;
|
|
|
|
- try {
|
|
|
|
- font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
|
|
|
|
- font = font.deriveFont(Font.PLAIN, 50f);
|
|
|
|
- log.info("字体加载成功");
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- log.error("字体加载失败: {}", e.getMessage(), e);
|
|
|
|
- font = new Font(Font.SANS_SERIF, Font.PLAIN, 50);
|
|
|
|
- log.info("使用系统默认字体作为备选");
|
|
|
|
- }
|
|
|
|
- String nickname = user.getNickname();
|
|
|
|
- if (StringUtils.isEmpty(nickname)) {
|
|
|
|
- nickname = "用户";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- log.info("开始向海报添加用户昵称文本: {}", nickname);
|
|
|
|
- try {
|
|
|
|
- FileUtils.copyFile(templateFile, outputFile);
|
|
|
|
-
|
|
|
|
- BufferedImage templateImage = ImageIO.read(templateFile);
|
|
|
|
- if (templateImage == null) {
|
|
|
|
- log.error("无法读取模板图片");
|
|
|
|
- return R.error("模板图片读取失败");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- BufferedImage resultImage = new BufferedImage(
|
|
|
|
- templateImage.getWidth(),
|
|
|
|
- templateImage.getHeight(),
|
|
|
|
- BufferedImage.TYPE_INT_RGB
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- Graphics2D g2d = resultImage.createGraphics();
|
|
|
|
|
|
+ // 并行加载资源
|
|
|
|
+ CompletableFuture<BufferedImage> templateFuture = CompletableFuture.supplyAsync(() -> {
|
|
try {
|
|
try {
|
|
- g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
|
- g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
|
|
|
-
|
|
|
|
- g2d.drawImage(templateImage, 0, 0, null);
|
|
|
|
-
|
|
|
|
- g2d.setFont(font);
|
|
|
|
- g2d.setColor(Color.BLACK);
|
|
|
|
-
|
|
|
|
- String nicknameText = nickname + "邀您加入";
|
|
|
|
- FontMetrics fm = g2d.getFontMetrics();
|
|
|
|
- int textWidth = fm.stringWidth(nicknameText);
|
|
|
|
- int x = (templateImage.getWidth() - textWidth) / 2;
|
|
|
|
- int y = 900;
|
|
|
|
-
|
|
|
|
- g2d.drawString(nicknameText, x, y);
|
|
|
|
-
|
|
|
|
- String inviteText = "邀请码:" + user.getUserCode();
|
|
|
|
- int inviteTextWidth = fm.stringWidth(inviteText);
|
|
|
|
- int inviteX = (templateImage.getWidth() - inviteTextWidth) / 2;
|
|
|
|
- int inviteY = 1000;
|
|
|
|
-
|
|
|
|
- g2d.drawString(inviteText, inviteX, inviteY);
|
|
|
|
-
|
|
|
|
- } finally {
|
|
|
|
- g2d.dispose();
|
|
|
|
|
|
+ log.info("开始加载海报模板图片");
|
|
|
|
+ try (InputStream templateStream = fileCacheService.getTemplateStream()) {
|
|
|
|
+ BufferedImage image = ImageIO.read(templateStream);
|
|
|
|
+ log.info("海报模板图片加载成功");
|
|
|
|
+ return image;
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("加载模板图片失败: {}", e.getMessage(), e);
|
|
|
|
+ throw new RuntimeException("加载模板图片失败", e);
|
|
}
|
|
}
|
|
-
|
|
|
|
- ImageIO.write(resultImage, "jpg", outputFile);
|
|
|
|
- log.info("文本添加完成");
|
|
|
|
-
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- log.error("添加文本时出现异常: {}", e.getMessage(), e);
|
|
|
|
- return R.error("添加文本失败: " + e.getMessage());
|
|
|
|
- }
|
|
|
|
- String qrPath = fsConfig.getTuiImgPath() + "/qr-" + userId + ".png";
|
|
|
|
- qrFile = new File(qrPath);
|
|
|
|
- log.info("开始生成二维码图片: {}", qrPath);
|
|
|
|
- try {
|
|
|
|
- String qrContent = fsConfig.getUrl() + "/distribution?userCode=" + user.getUserCode();
|
|
|
|
- log.info("生成二维码内容: {}", qrContent);
|
|
|
|
- QrCodeUtil.generate(qrContent, 300, 300, qrFile);
|
|
|
|
- log.info("二维码生成成功");
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- log.error("生成二维码失败: {}", e.getMessage(), e);
|
|
|
|
- return R.error("生成二维码失败: " + e.getMessage());
|
|
|
|
- }
|
|
|
|
- try {
|
|
|
|
- log.info("开始将二维码图片合并到海报");
|
|
|
|
- BufferedImage posterImage = ImageIO.read(outputFile);
|
|
|
|
- BufferedImage qrImage = ImageIO.read(qrFile);
|
|
|
|
-
|
|
|
|
- if (posterImage == null || qrImage == null) {
|
|
|
|
- log.error("读取图片失败,无法合并二维码");
|
|
|
|
- return R.error("图片读取失败");
|
|
|
|
|
|
+ });
|
|
|
|
+ CompletableFuture<Font> fontFuture = CompletableFuture.supplyAsync(() -> {
|
|
|
|
+ try {
|
|
|
|
+ log.info("开始加载字体文件");
|
|
|
|
+ try (InputStream fontStream = fileCacheService.getFontStream()) {
|
|
|
|
+ Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
|
|
|
|
+ font = font.deriveFont(Font.PLAIN, 50f);
|
|
|
|
+ log.info("字体文件加载成功");
|
|
|
|
+ return font;
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("字体加载失败,使用默认字体: {}", e.getMessage());
|
|
|
|
+ return new Font(Font.SANS_SERIF, Font.PLAIN, 50);
|
|
}
|
|
}
|
|
-
|
|
|
|
- Graphics2D g2d = posterImage.createGraphics();
|
|
|
|
|
|
+ });
|
|
|
|
+ CompletableFuture<BufferedImage> qrFuture = CompletableFuture.supplyAsync(() -> {
|
|
try {
|
|
try {
|
|
- g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
|
-
|
|
|
|
- int qrX = posterImage.getWidth() - qrImage.getWidth() - 50;
|
|
|
|
- int qrY = 900;
|
|
|
|
|
|
+ log.info("开始生成二维码");
|
|
|
|
+ String qrContent = fsConfig.getUrl() + "/distribution?userCode=" + user.getUserCode();
|
|
|
|
+ log.info("生成二维码内容: {}", qrContent);
|
|
|
|
|
|
- g2d.drawImage(qrImage, qrX, qrY, null);
|
|
|
|
- } finally {
|
|
|
|
- g2d.dispose();
|
|
|
|
|
|
+ // 直接生成BufferedImage,不保存临时文件
|
|
|
|
+ return QrCodeUtil.generate(qrContent, 300, 300);
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("生成二维码失败: {}", e.getMessage(), e);
|
|
|
|
+ throw new RuntimeException("生成二维码失败", e);
|
|
}
|
|
}
|
|
|
|
+ });
|
|
|
|
+ // 等待所有资源加载完成
|
|
|
|
+ BufferedImage templateImage = templateFuture.get(10, TimeUnit.SECONDS);
|
|
|
|
+ Font font = fontFuture.get(10, TimeUnit.SECONDS);
|
|
|
|
+ BufferedImage qrImage = qrFuture.get(10, TimeUnit.SECONDS);
|
|
|
|
+ if (templateImage == null) {
|
|
|
|
+ log.error("模板图片加载失败");
|
|
|
|
+ return R.error("模板图片加载失败");
|
|
|
|
+ }
|
|
|
|
+ // 在内存中完成所有图片合成操作
|
|
|
|
+ BufferedImage finalImage = createFinalPoster(templateImage, font, qrImage, user);
|
|
|
|
+ // 只进行一次文件写入
|
|
|
|
+ String outputPath = outputDir + "/tui-" + userId + ".jpg";
|
|
|
|
+ File outputFile = new File(outputPath);
|
|
|
|
|
|
- ImageIO.write(posterImage, "jpg", outputFile);
|
|
|
|
- log.info("二维码合并完成");
|
|
|
|
|
|
+ // 使用高质量的JPEG编码
|
|
|
|
+ writeHighQualityJPEG(finalImage, outputFile);
|
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- log.error("合并二维码时出现异常: {}", e.getMessage(), e);
|
|
|
|
- return R.error("合并二维码失败: " + e.getMessage());
|
|
|
|
- }
|
|
|
|
String resultUrl = "profile/tui/tui-" + userId + ".jpg";
|
|
String resultUrl = "profile/tui/tui-" + userId + ".jpg";
|
|
log.info("海报生成完成,返回URL: {}", resultUrl);
|
|
log.info("海报生成完成,返回URL: {}", resultUrl);
|
|
return R.ok().put("url", resultUrl);
|
|
return R.ok().put("url", resultUrl);
|
|
-
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
log.error("生成海报过程出现异常: {}", e.getMessage(), e);
|
|
log.error("生成海报过程出现异常: {}", e.getMessage(), e);
|
|
return R.error("生成海报失败: " + e.getMessage());
|
|
return R.error("生成海报失败: " + e.getMessage());
|
|
- } finally {
|
|
|
|
- cleanupTempFiles(qrFile);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- private boolean isValidImageFile(File imageFile) {
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 在内存中完成所有图片合成操作
|
|
|
|
+ */
|
|
|
|
+ private BufferedImage createFinalPoster(BufferedImage templateImage, Font font,
|
|
|
|
+ BufferedImage qrImage, FsUserScrm user) {
|
|
|
|
+ log.info("开始在内存中合成最终海报");
|
|
|
|
+
|
|
|
|
+ // 创建最终图片
|
|
|
|
+ BufferedImage finalImage = new BufferedImage(
|
|
|
|
+ templateImage.getWidth(),
|
|
|
|
+ templateImage.getHeight(),
|
|
|
|
+ BufferedImage.TYPE_INT_RGB
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ Graphics2D g2d = finalImage.createGraphics();
|
|
try {
|
|
try {
|
|
- BufferedImage image = ImageIO.read(imageFile);
|
|
|
|
- return image != null && image.getWidth() > 0 && image.getHeight() > 0;
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- log.error("验证图片文件失败: {}", e.getMessage());
|
|
|
|
- return false;
|
|
|
|
|
|
+ // 设置高质量渲染
|
|
|
|
+ setupHighQualityRendering(g2d);
|
|
|
|
+
|
|
|
|
+ // 绘制模板图片
|
|
|
|
+ g2d.drawImage(templateImage, 0, 0, null);
|
|
|
|
+
|
|
|
|
+ // 绘制文本
|
|
|
|
+ drawUserText(g2d, font, user, templateImage.getWidth());
|
|
|
|
+
|
|
|
|
+ // 绘制二维码
|
|
|
|
+ drawQRCode(g2d, qrImage, templateImage.getWidth());
|
|
|
|
+
|
|
|
|
+ log.info("海报合成完成");
|
|
|
|
+ return finalImage;
|
|
|
|
+
|
|
|
|
+ } finally {
|
|
|
|
+ g2d.dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- private void closeQuietly(InputStream stream) {
|
|
|
|
- if (stream != null) {
|
|
|
|
- try {
|
|
|
|
- stream.close();
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.warn("关闭流时出现异常: {}", e.getMessage());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 设置高质量渲染参数
|
|
|
|
+ */
|
|
|
|
+ private void setupHighQualityRendering(Graphics2D g2d) {
|
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
|
}
|
|
}
|
|
- private void cleanupTempFiles(File... files) {
|
|
|
|
- for (File file : files) {
|
|
|
|
- if (file != null && file.exists()) {
|
|
|
|
- try {
|
|
|
|
- file.delete();
|
|
|
|
- log.debug("删除临时文件: {}", file.getAbsolutePath());
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- log.warn("删除临时文件失败: {}", e.getMessage());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 绘制用户文本信息
|
|
|
|
+ */
|
|
|
|
+ private void drawUserText(Graphics2D g2d, Font font, FsUserScrm user, int imageWidth) {
|
|
|
|
+ g2d.setFont(font);
|
|
|
|
+ g2d.setColor(Color.BLACK);
|
|
|
|
+
|
|
|
|
+ String nickname = StringUtils.isEmpty(user.getNickname()) ? "用户" : user.getNickname();
|
|
|
|
+ FontMetrics fm = g2d.getFontMetrics();
|
|
|
|
+
|
|
|
|
+ // 绘制邀请文本
|
|
|
|
+ String nicknameText = nickname + "邀您加入";
|
|
|
|
+ int textWidth = fm.stringWidth(nicknameText);
|
|
|
|
+ int x = (imageWidth - textWidth) / 2;
|
|
|
|
+ int y = 900;
|
|
|
|
+ g2d.drawString(nicknameText, x, y);
|
|
|
|
+
|
|
|
|
+ // 绘制邀请码
|
|
|
|
+ String inviteText = "邀请码:" + user.getUserCode();
|
|
|
|
+ int inviteTextWidth = fm.stringWidth(inviteText);
|
|
|
|
+ int inviteX = (imageWidth - inviteTextWidth) / 2;
|
|
|
|
+ int inviteY = 1000;
|
|
|
|
+ g2d.drawString(inviteText, inviteX, inviteY);
|
|
|
|
+
|
|
|
|
+ log.info("用户文本绘制完成");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 绘制二维码
|
|
|
|
+ */
|
|
|
|
+ private void drawQRCode(Graphics2D g2d, BufferedImage qrImage, int imageWidth) {
|
|
|
|
+ int qrX = imageWidth - qrImage.getWidth() - 50;
|
|
|
|
+ int qrY = 900;
|
|
|
|
+ g2d.drawImage(qrImage, qrX, qrY, null);
|
|
|
|
+ log.info("二维码绘制完成");
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * 高质量JPEG图片写入
|
|
|
|
+ */
|
|
|
|
+ private void writeHighQualityJPEG(BufferedImage image, File outputFile) throws IOException {
|
|
|
|
+ Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
|
|
|
|
+ if (!writers.hasNext()) {
|
|
|
|
+ throw new IllegalStateException("No JPEG writers found");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ImageWriter writer = writers.next();
|
|
|
|
+ ImageWriteParam param = writer.getDefaultWriteParam();
|
|
|
|
+
|
|
|
|
+ if (param.canWriteCompressed()) {
|
|
|
|
+ param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
|
|
|
+ param.setCompressionQuality(0.9f); // 高质量压缩
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ try (FileImageOutputStream output = new FileImageOutputStream(outputFile)) {
|
|
|
|
+ writer.setOutput(output);
|
|
|
|
+ writer.write(null, new IIOImage(image, null, null), param);
|
|
|
|
+ } finally {
|
|
|
|
+ writer.dispose();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log.info("高质量JPEG图片写入完成: {}", outputFile.getAbsolutePath());
|
|
}
|
|
}
|
|
|
|
|
|
@Login
|
|
@Login
|