|
@@ -39,9 +39,11 @@ import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
+import javax.imageio.ImageIO;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.validation.Valid;
|
|
|
import java.awt.*;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
import java.io.File;
|
|
|
import java.io.FileInputStream;
|
|
|
import java.io.IOException;
|
|
@@ -128,14 +130,27 @@ public class UserScrmController extends AppBaseController {
|
|
|
@GetMapping("/getTuiImg")
|
|
|
public R getTuiImg(HttpServletRequest request){
|
|
|
log.info("获取推荐海报 headers: {}", ServletUtil.getHeaderMap(request));
|
|
|
-
|
|
|
log.info("开始获取推荐海报");
|
|
|
+
|
|
|
+ InputStream templateStream = null;
|
|
|
+ InputStream fontStream = null;
|
|
|
+
|
|
|
try {
|
|
|
String userId = getUserId();
|
|
|
log.info("获取用户ID: {}", userId);
|
|
|
+
|
|
|
+ if (StringUtils.isEmpty(userId)) {
|
|
|
+ log.error("用户ID为空");
|
|
|
+ return R.error("用户ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
FsUserScrm user = userService.selectFsUserById(Long.parseLong(userId));
|
|
|
- log.info("查询到用户信息: 用户ID={}, 昵称={}", userId, user.getNickname());
|
|
|
+ if (user == null) {
|
|
|
+ log.error("未找到用户信息,用户ID: {}", userId);
|
|
|
+ return R.error("用户信息不存在");
|
|
|
+ }
|
|
|
|
|
|
+ log.info("查询到用户信息: 用户ID={}, 昵称={}", userId, user.getNickname());
|
|
|
if(StringUtils.isEmpty(user.getUserCode())){
|
|
|
log.info("用户邀请码为空,开始生成新的邀请码");
|
|
|
FsUserScrm userMap = new FsUserScrm();
|
|
@@ -145,104 +160,223 @@ public class UserScrmController extends AppBaseController {
|
|
|
user.setUserCode(userMap.getUserCode());
|
|
|
log.info("成功生成并更新用户邀请码: {}", userMap.getUserCode());
|
|
|
}
|
|
|
+ File templateSourceFile = new File(getTuiImg);
|
|
|
+ File fontSourceFile = new File(getTuiFont);
|
|
|
+
|
|
|
+ if (!templateSourceFile.exists() || !templateSourceFile.isFile()) {
|
|
|
+ log.error("海报模板文件不存在: {}", getTuiImg);
|
|
|
+ return R.error("海报模板文件不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!fontSourceFile.exists() || !fontSourceFile.isFile()) {
|
|
|
+ log.error("字体文件不存在: {}", getTuiFont);
|
|
|
+ return R.error("字体文件不存在");
|
|
|
+ }
|
|
|
+ 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");
|
|
|
|
|
|
- File newFile = new File(getTuiImg);
|
|
|
- File newFileT = new File(getTuiFont);
|
|
|
try {
|
|
|
log.info("开始加载海报模板图片");
|
|
|
- InputStream stream = new FileInputStream(getTuiImg);
|
|
|
- FileUtils.copyInputStreamToFile(stream, newFile);
|
|
|
- log.info("海报模板图片加载成功: {}", newFile.getAbsolutePath());
|
|
|
-
|
|
|
- if(!newFileT.exists()){
|
|
|
- log.info("开始加载字体文件");
|
|
|
- InputStream streamT = new FileInputStream(getTuiFont);
|
|
|
- FileUtils.copyInputStreamToFile(streamT, newFileT);
|
|
|
- log.info("字体文件加载成功: {}", newFileT.getAbsolutePath());
|
|
|
+ templateStream = new FileInputStream(templateSourceFile);
|
|
|
+ FileUtils.copyInputStreamToFile(templateStream, tempTemplateFile);
|
|
|
+ log.info("海报模板图片加载成功: {}", tempTemplateFile.getAbsolutePath());
|
|
|
+ if (!isValidImageFile(tempTemplateFile)) {
|
|
|
+ log.error("模板图片文件无效或损坏");
|
|
|
+ return R.error("模板图片文件无效");
|
|
|
}
|
|
|
+ log.info("开始加载字体文件");
|
|
|
+ fontStream = new FileInputStream(fontSourceFile);
|
|
|
+ FileUtils.copyInputStreamToFile(fontStream, tempFontFile);
|
|
|
+ log.info("字体文件加载成功: {}", tempFontFile.getAbsolutePath());
|
|
|
+
|
|
|
} catch (IOException e) {
|
|
|
log.error("加载资源文件失败: {}", e.getMessage(), e);
|
|
|
- throw new CustomException(e.getMessage());
|
|
|
+ return R.error("加载资源文件失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ return generatePoster(user, tempTemplateFile, tempFontFile, userId);
|
|
|
+
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.error("用户ID格式错误: {}", e.getMessage(), e);
|
|
|
+ return R.error("用户ID格式错误");
|
|
|
+ } catch (Exception e){
|
|
|
+ log.error("获取推荐海报失败: {}", e.getMessage(), e);
|
|
|
+ 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;
|
|
|
+
|
|
|
+ try {
|
|
|
+ String outputDir = fsConfig.getTuiImgPath();
|
|
|
+ File outputDirFile = new File(outputDir);
|
|
|
+ if (!outputDirFile.exists()) {
|
|
|
+ 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 {
|
|
|
- String url = fsConfig.getTuiImgPath()+"/tui-"+getUserId()+".jpg";
|
|
|
- log.info("开始生成用户海报,输出路径: {}", url);
|
|
|
-
|
|
|
- File outputFile = new File(url);
|
|
|
- if(!outputFile.exists()) {
|
|
|
- try {
|
|
|
- outputFile.createNewFile();
|
|
|
- log.info("创建海报输出文件成功");
|
|
|
- } catch (IOException e) {
|
|
|
- log.error("创建海报输出文件失败: {}", e.getMessage(), e);
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
+ FileUtils.copyFile(templateFile, outputFile);
|
|
|
+
|
|
|
+ BufferedImage templateImage = ImageIO.read(templateFile);
|
|
|
+ if (templateImage == null) {
|
|
|
+ log.error("无法读取模板图片");
|
|
|
+ return R.error("模板图片读取失败");
|
|
|
}
|
|
|
|
|
|
- Font font = Font.createFont(Font.TRUETYPE_FONT, newFileT);
|
|
|
- Font f = font.deriveFont(Font.PLAIN,50);
|
|
|
- log.info("开始向海报添加用户昵称文本");
|
|
|
- ImgUtil.pressText(
|
|
|
- newFile,
|
|
|
- outputFile,
|
|
|
- user.getNickname()+"邀您加入",
|
|
|
- Color.BLACK,
|
|
|
- f,
|
|
|
- -60,
|
|
|
- 900,
|
|
|
- 0.8f
|
|
|
+ BufferedImage resultImage = new BufferedImage(
|
|
|
+ templateImage.getWidth(),
|
|
|
+ templateImage.getHeight(),
|
|
|
+ BufferedImage.TYPE_INT_RGB
|
|
|
);
|
|
|
|
|
|
- log.info("开始向海报添加邀请码文本: {}", user.getUserCode());
|
|
|
- ImgUtil.pressText(
|
|
|
- outputFile,
|
|
|
- outputFile,
|
|
|
- "邀请码:"+user.getUserCode(),
|
|
|
- Color.BLACK,
|
|
|
- f,
|
|
|
- -40,
|
|
|
- 1000,
|
|
|
- 0.8f
|
|
|
- );
|
|
|
+ Graphics2D g2d = resultImage.createGraphics();
|
|
|
+ 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 qrPath = fsConfig.getTuiImgPath()+"/qr-"+getUserId()+".png";
|
|
|
- File qr = new File(qrPath);
|
|
|
- log.info("开始生成二维码图片: {}", qrPath);
|
|
|
- if(!qr.exists()) {
|
|
|
- try {
|
|
|
- qr.createNewFile();
|
|
|
- log.info("创建二维码输出文件成功");
|
|
|
- } catch (IOException e) {
|
|
|
- log.error("创建二维码输出文件失败: {}", e.getMessage(), e);
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
+ 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();
|
|
|
}
|
|
|
|
|
|
- String qrContent = fsConfig.getUrl()+"/distribution?userCode="+user.getUserCode();
|
|
|
- log.info("生成二维码内容: {}", qrContent);
|
|
|
- QrCodeUtil.generate(qrContent, 300, 300, FileUtil.file(qrPath));
|
|
|
+ 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("开始将二维码图片合并到海报");
|
|
|
- ImgUtil.pressImage(
|
|
|
- outputFile,
|
|
|
- outputFile,
|
|
|
- ImgUtil.read(qr),
|
|
|
- -400,
|
|
|
- 900,
|
|
|
- 1f
|
|
|
- );
|
|
|
+ BufferedImage posterImage = ImageIO.read(outputFile);
|
|
|
+ BufferedImage qrImage = ImageIO.read(qrFile);
|
|
|
+
|
|
|
+ if (posterImage == null || qrImage == null) {
|
|
|
+ log.error("读取图片失败,无法合并二维码");
|
|
|
+ return R.error("图片读取失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ Graphics2D g2d = posterImage.createGraphics();
|
|
|
+ try {
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+
|
|
|
+ int qrX = posterImage.getWidth() - qrImage.getWidth() - 50;
|
|
|
+ int qrY = 900;
|
|
|
+
|
|
|
+ g2d.drawImage(qrImage, qrX, qrY, null);
|
|
|
+ } finally {
|
|
|
+ g2d.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ ImageIO.write(posterImage, "jpg", outputFile);
|
|
|
+ log.info("二维码合并完成");
|
|
|
|
|
|
- String resultUrl = "profile/tui/tui-"+getUserId()+".jpg";
|
|
|
- log.info("海报生成完成,返回URL: {}", resultUrl);
|
|
|
- return R.ok().put("url", resultUrl);
|
|
|
} catch (Exception e) {
|
|
|
- log.error("生成海报过程出现异常: {}", e.getMessage(), e);
|
|
|
- e.printStackTrace();
|
|
|
- return R.error("操作异常");
|
|
|
+ log.error("合并二维码时出现异常: {}", e.getMessage(), e);
|
|
|
+ return R.error("合并二维码失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ String resultUrl = "profile/tui/tui-" + userId + ".jpg";
|
|
|
+ log.info("海报生成完成,返回URL: {}", resultUrl);
|
|
|
+ return R.ok().put("url", resultUrl);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("生成海报过程出现异常: {}", e.getMessage(), e);
|
|
|
+ return R.error("生成海报失败: " + e.getMessage());
|
|
|
+ } finally {
|
|
|
+ cleanupTempFiles(qrFile);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private boolean isValidImageFile(File imageFile) {
|
|
|
+ try {
|
|
|
+ BufferedImage image = ImageIO.read(imageFile);
|
|
|
+ return image != null && image.getWidth() > 0 && image.getHeight() > 0;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("验证图片文件失败: {}", e.getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private void closeQuietly(InputStream stream) {
|
|
|
+ if (stream != null) {
|
|
|
+ try {
|
|
|
+ stream.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.warn("关闭流时出现异常: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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());
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (Exception e){
|
|
|
- log.error("获取推荐海报失败: {}", e.getMessage(), e);
|
|
|
- return R.error("操作异常");
|
|
|
}
|
|
|
}
|
|
|
|