Browse Source

重庆太医堂 推广人功能新增

xgb 1 week ago
parent
commit
dc1e172d0c

+ 21 - 8
fs-service/src/main/java/com/fs/utils/FileCacheService.java

@@ -1,5 +1,6 @@
 package com.fs.utils;
 
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -8,7 +9,7 @@ import javax.annotation.PostConstruct;
 import java.io.*;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-
+@Slf4j
 @Service
 public class FileCacheService {
 
@@ -23,17 +24,29 @@ public class FileCacheService {
 
     private byte[] templateBytes;
     private byte[] fontBytes;
-
     @PostConstruct
-    public void init() throws IOException {
-        if(StringUtils.equals("1",tuiImgCache)) {
-            // 缓存模板图片文件
-            this.templateBytes = Files.readAllBytes(Paths.get(getTuiImg));
-            // 缓存字体文件
-            this.fontBytes = Files.readAllBytes(Paths.get(getTuiFont));
+    public void init() {
+        if(StringUtils.equals("1", tuiImgCache)) {
+            try {
+                // 检查配置是否为空
+                if (StringUtils.isNotBlank(getTuiImg) && Files.exists(Paths.get(getTuiImg))) {
+                    this.templateBytes = Files.readAllBytes(Paths.get(getTuiImg));
+                } else {
+                    log.warn("模板图片路径为空或文件不存在: {}", getTuiImg);
+                }
+
+                if (StringUtils.isNotBlank(getTuiFont) && Files.exists(Paths.get(getTuiFont))) {
+                    this.fontBytes = Files.readAllBytes(Paths.get(getTuiFont));
+                } else {
+                    log.warn("字体文件路径为空或文件不存在: {}", getTuiFont);
+                }
+            } catch (Exception e) {
+                log.error("文件缓存初始化异常,应用将继续启动: {}", e.getMessage(), e);
+            }
         }
     }
 
+
     /**
      * 获取模板图片的 InputStream
      */

+ 5 - 8
fs-service/src/main/resources/application-config-druid-cqtyt.yml

@@ -10,13 +10,8 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid:
-        secret:
-        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
-        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
-        msgDataFormat: JSON
-      - appid:
-        secret:
+      - appid: wxb2d969ad2a2e71f3
+        secret: d1fe2a8939f41aa875b3fe68db6b216d
         token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON
@@ -96,5 +91,7 @@ ipad:
 wx_miniapp_temp:
   pay_order_temp_id: V
   inquiry_temp_id: 9
-
+getTuiImg: 'C:\Tools\ylrm_his_scrm\images\fx.jpg'
+getTuiFont: 'C:\Tools\ylrm_his_scrm\images\simsunb.ttf'
+tuiImgCache: '1'
 

+ 247 - 2
fs-user-app/src/main/java/com/fs/app/controller/store/UserScrmController.java

@@ -131,8 +131,8 @@ public class UserScrmController extends AppBaseController {
 
     @Login
     @ApiOperation("获取推荐海报")
-    @GetMapping("/getTuiImg")
-    public R getTuiImg(HttpServletRequest request){
+    @GetMapping("/getTuiImgOld")
+    public R getTuiImgOld(HttpServletRequest request){
         try {
             FsUserScrm user=userService.selectFsUserById(Long.parseLong(getUserId()));
             if(StringUtils.isEmpty(user.getUserCode())){
@@ -226,6 +226,251 @@ public class UserScrmController extends AppBaseController {
         }
     }
 
+    @Login
+    @ApiOperation("获取推荐海报")
+    @GetMapping("/getTuiImg")
+    public R getTuiImg(HttpServletRequest request){
+        log.info("获取推荐海报 headers: {}", ServletUtil.getHeaderMap(request));
+
+        try {
+            String userId = getUserId();
+            log.info("获取用户ID: {}", userId);
+            if (StringUtils.isEmpty(userId)) {
+                log.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));
+            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();
+                userMap.setUserId(user.getUserId());
+                userMap.setUserCode(OrderUtils.genUserCode());
+                userService.updateFsUser(userMap);
+                user.setUserCode(userMap.getUserCode());
+                log.info("成功生成并更新用户邀请码: {}", userMap.getUserCode());
+            }
+            return generatePosterOptimized(user, 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());
+        }
+    }
+
+    /**
+     * 优化的海报生成方法 - 在内存中完成所有图片操作
+     */
+    private R generatePosterOptimized(FsUserScrm user, String userId) {
+        log.info("开始生成用户海报,用户ID: {}", userId);
+
+        try {
+            // 确保输出目录存在
+            String outputDir = fsConfig.getTuiImgPath();
+            File outputDirFile = new File(outputDir);
+            if (!outputDirFile.exists()) {
+                outputDirFile.mkdirs();
+            }
+            // 并行加载资源
+            CompletableFuture<BufferedImage> templateFuture = CompletableFuture.supplyAsync(() -> {
+                try {
+                    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);
+                }
+            });
+            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);
+                }
+            });
+            CompletableFuture<BufferedImage> qrFuture = CompletableFuture.supplyAsync(() -> {
+                try {
+                    // 改为小程序码生成
+                    WxMaService wxService = WxMaConfiguration.getMaService(maProperties.getConfigs().get(0).getAppid());
+                    byte[] qrCodeBytes = wxService.getQrcodeService().createWxaCodeUnlimitBytes(
+                            user.getUserCode(),           // scene - 分销用户码
+                            "pages_shopping/promotion/distribution",   // page - 目标页面(需要你确认)
+                            false,                        // checkPath - 检查页面是否存在
+                            "release",                   // envVersion - 版本(release/trial/develop)
+                            300,                         // width - 宽度
+                            true,                        // autoColor - 自动配置线条颜色
+                            null,                        // lineColor - 线条颜色(null使用默认)
+                            false                        // isHyaline - 是否需要透明底色
+                    );
+
+                    return ImageIO.read(new ByteArrayInputStream(qrCodeBytes));
+
+                } 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);
+
+            // 使用高质量的JPEG编码
+            writeHighQualityJPEG(finalImage, outputFile);
+
+            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());
+        }
+    }
+
+    /**
+     * 在内存中完成所有图片合成操作
+     */
+    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 {
+            // 设置高质量渲染
+            setupHighQualityRendering(g2d);
+
+            // 绘制模板图片
+            g2d.drawImage(templateImage, 0, 0, null);
+
+            // 绘制文本
+            drawUserText(g2d, font, user, templateImage.getWidth());
+
+            // 绘制二维码
+            drawQRCode(g2d, qrImage, templateImage);
+
+            log.info("海报合成完成");
+            return finalImage;
+
+        } finally {
+            g2d.dispose();
+        }
+    }
+
+    /**
+     * 设置高质量渲染参数
+     */
+    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 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, BufferedImage templateImage) {
+        int qrX =  100;
+        int qrY = templateImage.getHeight()-qrImage.getHeight()-100;
+        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
     @ApiOperation("获取我的推荐人")
     @GetMapping("/getMyTuiList")

BIN
fs-user-app/src/main/resources/fx-old.jpg


BIN
fs-user-app/src/main/resources/fx.jpg