|
|
@@ -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")
|