Commit 53082c5f by 王祖波

合成图片并生成mediaId

parent 324b30d6
package com.gic.haoban.manage.api.qdto.combined;
import java.io.Serializable;
public class CombinedQDTO implements Serializable {
private static final long serialVersionUID = -28454620622119889L;
/**
* 主图url
*/
private String imageUrl;
/**
* 小程序码url
*/
private String qrCodeUrl;
/**
* 展示样式
*/
private Integer showStyle;
/**
* 第一行文字
*/
private String lineOne;
/**
* 第二行文字
*/
private String lineTwo;
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getQrCodeUrl() {
return qrCodeUrl;
}
public void setQrCodeUrl(String qrCodeUrl) {
this.qrCodeUrl = qrCodeUrl;
}
public Integer getShowStyle() {
return showStyle;
}
public void setShowStyle(Integer showStyle) {
this.showStyle = showStyle;
}
public String getLineOne() {
return lineOne;
}
public void setLineOne(String lineOne) {
this.lineOne = lineOne;
}
public String getLineTwo() {
return lineTwo;
}
public void setLineTwo(String lineTwo) {
this.lineTwo = lineTwo;
}
public String getUnionString() {
return this.getImageUrl() + this.getQrCodeUrl() + this.getShowStyle() + this.getLineOne() + this.getLineTwo();
}
}
...@@ -7,6 +7,7 @@ import com.gic.haoban.manage.api.dto.BatchAddMaterialDTO; ...@@ -7,6 +7,7 @@ import com.gic.haoban.manage.api.dto.BatchAddMaterialDTO;
import com.gic.haoban.manage.api.dto.ContentMaterialDTO; import com.gic.haoban.manage.api.dto.ContentMaterialDTO;
import com.gic.haoban.manage.api.dto.MaterialCategoryDTO; import com.gic.haoban.manage.api.dto.MaterialCategoryDTO;
import com.gic.haoban.manage.api.dto.MaterialDTO; import com.gic.haoban.manage.api.dto.MaterialDTO;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import java.util.List; import java.util.List;
...@@ -85,4 +86,6 @@ public interface MaterialApiService { ...@@ -85,4 +86,6 @@ public interface MaterialApiService {
com.gic.api.base.commons.ServiceResponse<String> getMaterialIdByUrl(String wxEnterpriseId, ContentMaterialDTO dto) ; com.gic.api.base.commons.ServiceResponse<String> getMaterialIdByUrl(String wxEnterpriseId, ContentMaterialDTO dto) ;
com.gic.api.base.commons.ServiceResponse<String> getMaterialIdByCombined(String wxEnterpriseId, CombinedQDTO combinedQDTO);
} }
...@@ -229,6 +229,18 @@ ...@@ -229,6 +229,18 @@
<artifactId>gic-member-ext-api</artifactId> <artifactId>gic-member-ext-api</artifactId>
<version>${gic-member-ext-api}</version> <version>${gic-member-ext-api}</version>
</dependency> </dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
package com.gic.haoban.manage.service.context.draw;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import com.gic.haoban.manage.service.context.chat.BusinessManager;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Created by wangzubo on 2024/10/27.
*/
public class ImageDraw {
private static final Logger logger = LoggerFactory.getLogger(BusinessManager.class);
private static final Cache<String, BufferedImage> imageCache = Caffeine.newBuilder()
.maximumWeight(100 * 1024 * 1024) // 100MB
.weigher((String key, BufferedImage image) -> image.getWidth() * image.getHeight() / 4)
.expireAfterAccess(30, TimeUnit.MINUTES) // 可设置失效时间
.build();
public static final String IMAGE_REDIS_KEY = "haoban-manage3-service:combined_image:";
private static final int MAX_HEIGHT_NO_WHITE_SPACE = 1500;
private static final int MAX_HEIGHT_WITH_WHITE_SPACE = 1340;
private static final int MIN_WIDTH = 500;
private static final int WHITE_SPACE_HEIGHT = 160;
private static final int QR_WIDTH_REFERENCE = 136;
private static final String LARGE_IMAGE_URL = "https://jhdmyx-1251519181.cos.ap-shanghai.myqcloud.com/image/material_content-2e81265ed1bf4c4d8a623d6b04942767.jpeg?imageView2/2/w/1080/h/10800/format/jpg";
private static final String QR_CODE_URL = "https://gicinner-1251519181.cos.ap-shanghai.myqcloud.com/image/material_content-4ffc77073ca1476fb264bf1be9f11383.png";
private static final String STORE_NAME = "门店CCA";
private static final String GUIDE_NAME = "宇智222为您推荐";
private static final String OUTPUT_PATH = "/Users/wang/Downloads/output_image3.jpg";
public static void main(String[] args) {
try {
BufferedImage largeImage = loadImageWithCache(LARGE_IMAGE_URL);
BufferedImage qrCodeImage = ImageIO.read(new URL(QR_CODE_URL));
int showStyle = 0;
BufferedImage combinedImage = generateImage(largeImage, qrCodeImage, showStyle,STORE_NAME, GUIDE_NAME);
ImageIO.write(combinedImage, "jpg", new File(OUTPUT_PATH));
System.out.println("图像生成成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] getCombinedImage(CombinedQDTO combinedQDTO) throws Exception{
String imageUrl = combinedQDTO.getImageUrl();
String qrCodeUrl = combinedQDTO.getQrCodeUrl();
Integer showStyle = combinedQDTO.getShowStyle();
String lineOne = combinedQDTO.getLineOne();
String lineTwo = combinedQDTO.getLineTwo();
if(imageUrl.contains("?")) {
logger.info("url有参数={}",imageUrl);
imageUrl = imageUrl.split("\\?")[0] ;
}
//压缩图片的参数
imageUrl = imageUrl + "?imageView2/2/w/1080/h/10800/format/jpg";
BufferedImage largeImage = loadImageWithCache(imageUrl);
BufferedImage qrCodeImage = ImageIO.read(new URL(qrCodeUrl));
if (largeImage == null || qrCodeImage == null) {
return null;
}
BufferedImage combinedImage = generateImage(largeImage, qrCodeImage, showStyle, lineOne, lineTwo);
ImageIO.write(combinedImage, "jpg", new File(OUTPUT_PATH));
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(combinedImage, "jpg", baos);
// 返回字节数组
return baos.toByteArray();
}
}
/**
* 从缓存中加载图像
*/
private static BufferedImage loadImageWithCache(String url) {
return imageCache.get(url, key -> {
try {
logger.info("加载图片buffered");
return ImageIO.read(new URL(key));
} catch (Exception e) {
logger.info("获取图片异常",e);
return null;
}
});
}
private static BufferedImage generateImage(BufferedImage image, BufferedImage qrCode, int showStyle,String lineOne,String lineTwo) throws Exception {
int width = image.getWidth();
int height = image.getHeight();
if (showStyle == 1) {
if (height > MAX_HEIGHT_NO_WHITE_SPACE) {
width = width * MAX_HEIGHT_NO_WHITE_SPACE / height;
height = MAX_HEIGHT_NO_WHITE_SPACE;
}
} else {
if (height > MAX_HEIGHT_WITH_WHITE_SPACE) {
width = width * MAX_HEIGHT_WITH_WHITE_SPACE / height;
height = MAX_HEIGHT_WITH_WHITE_SPACE;
}
width = Math.max(width, MIN_WIDTH);
}
BufferedImage resizedImage = Thumbnails.of(image).size(width, height).asBufferedImage();
int finalHeight = height + (showStyle == 1 ? 0 : WHITE_SPACE_HEIGHT);
BufferedImage combinedImage = new BufferedImage(width, finalHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = combinedImage.createGraphics();
g.drawImage(resizedImage, 0, 0, null);
int qrWidth = width * QR_WIDTH_REFERENCE / 750;
if (width <= MIN_WIDTH) {
qrWidth = 136;
}
BufferedImage resizedQRCode = Thumbnails.of(qrCode).size(qrWidth, qrWidth).asBufferedImage();
if (showStyle == 1) {
// 不需要留白时,QR码距离底部20px
g.drawImage(resizedQRCode, width - qrWidth - 20, finalHeight - qrWidth - 20, null);
} else {
// 需要留白时
addWhiteSpace(g, width, finalHeight, resizedQRCode, qrWidth, lineOne, lineTwo);
}
g.dispose();
return combinedImage;
}
private static void addWhiteSpace(Graphics2D g, int width, int finalHeight, BufferedImage qrCode, int qrWidth,String lineOne,String lineTwo) {
// 添加白色背景
g.setColor(Color.WHITE);
g.fillRect(0, finalHeight - WHITE_SPACE_HEIGHT, width, WHITE_SPACE_HEIGHT);
// 添加门店名称,距底部88px
g.setColor(new Color(36, 40, 53));
g.setFont(new Font("Arial", Font.PLAIN, 30));
g.drawString(lineOne, 30, finalHeight - WHITE_SPACE_HEIGHT + 72);
// 添加推荐人信息,距底部40px
g.setColor(new Color(151, 155, 165));
g.setFont(new Font("Arial", Font.PLAIN, 26));
g.drawString(lineTwo, 30, finalHeight - WHITE_SPACE_HEIGHT + 120);
// 添加二维码,距底部12px
int qrBottomPadding = 12;
g.drawImage(qrCode, width - qrWidth - qrBottomPadding, finalHeight - qrWidth - qrBottomPadding, null);
}
}
...@@ -3,6 +3,7 @@ package com.gic.haoban.manage.service.service; ...@@ -3,6 +3,7 @@ package com.gic.haoban.manage.service.service;
import com.gic.api.base.commons.JSONResponse; import com.gic.api.base.commons.JSONResponse;
import com.gic.haoban.manage.api.dto.ContentMaterialDTO; import com.gic.haoban.manage.api.dto.ContentMaterialDTO;
import com.gic.haoban.manage.api.dto.MaterialDTO; import com.gic.haoban.manage.api.dto.MaterialDTO;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import java.util.List; import java.util.List;
...@@ -59,4 +60,7 @@ public interface MaterialService { ...@@ -59,4 +60,7 @@ public interface MaterialService {
public List<String> getImageMediaId(String wxEnterpriseId, List<ContentMaterialDTO> imageList, int mediaType) ; public List<String> getImageMediaId(String wxEnterpriseId, List<ContentMaterialDTO> imageList, int mediaType) ;
List<MaterialDTO> listContentMaterialByIds(List<String> materialIds,Integer mediaType); List<MaterialDTO> listContentMaterialByIds(List<String> materialIds,Integer mediaType);
JSONResponse getMaterialIdByCombined(String wxEnterpriseId, CombinedQDTO combinedQDTO);
} }
...@@ -9,6 +9,8 @@ import java.util.stream.Collectors; ...@@ -9,6 +9,8 @@ import java.util.stream.Collectors;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.gic.commons.util.HttpClient; import com.gic.commons.util.HttpClient;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import com.gic.haoban.manage.service.context.draw.ImageDraw;
import com.gic.redis.data.util.RedisUtil; import com.gic.redis.data.util.RedisUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
...@@ -345,4 +347,30 @@ public class MaterialServiceImpl implements MaterialService { ...@@ -345,4 +347,30 @@ public class MaterialServiceImpl implements MaterialService {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
return materialDTOS; return materialDTOS;
} }
@Override
public JSONResponse getMaterialIdByCombined(String wxEnterpriseId, CombinedQDTO combinedQDTO) {
WxEnterpriseQwDTO qwDTO = this.wxEnterpriseService.getQwInfo(wxEnterpriseId) ;
JSONResponse jsonResponse = new JSONResponse();
jsonResponse.setErrorCode(9999);
jsonResponse.setErrorMessage("获取媒体id失败");
try {
String[] arr = null ;
String imageUrl = combinedQDTO.getImageUrl();
//合成图片
byte[] combinedImageData = ImageDraw.getCombinedImage(combinedQDTO);
if (combinedImageData == null) {
return jsonResponse;
}
if(imageUrl.contains("?")) {
arr = imageUrl.split("\\?")[0].split("/") ;
}else {
arr = imageUrl.split("/");
}
JSONResponse json = qywxSuiteApiService.uploadAttachment(qwDTO.getThirdCorpid(), qwDTO.getSelf3thSecret(), combinedImageData, arr[arr.length - 1], QywxMediaTypeEnum.IMAGE.getCode(), qwDTO.isSelf(), qwDTO.getUrlHost());
return json;
} catch (Exception e) {
return jsonResponse;
}
}
} }
...@@ -6,10 +6,14 @@ import java.util.Collections; ...@@ -6,10 +6,14 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.collection.ListUtil;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import com.gic.haoban.manage.service.context.draw.ImageDraw;
import com.gic.redis.data.util.RedisUtil;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
...@@ -516,4 +520,25 @@ public class MaterialApiServiceImpl implements MaterialApiService { ...@@ -516,4 +520,25 @@ public class MaterialApiServiceImpl implements MaterialApiService {
String materialId = materialIdList.get(0) ; String materialId = materialIdList.get(0) ;
return com.gic.api.base.commons.ServiceResponse.success(materialId) ; return com.gic.api.base.commons.ServiceResponse.success(materialId) ;
} }
@Override
public com.gic.api.base.commons.ServiceResponse<String> getMaterialIdByCombined(String wxEnterpriseId, CombinedQDTO combinedQDTO) {
if (StringUtils.isAnyBlank(combinedQDTO.getImageUrl(), combinedQDTO.getQrCodeUrl())) {
return com.gic.api.base.commons.ServiceResponse.failure("9999","参数异常") ;
}
String unionString = combinedQDTO.getUnionString();
String key = ImageDraw.IMAGE_REDIS_KEY + unionString;
String cache = (String)RedisUtil.getCache(key);
if (StringUtils.isNotBlank(cache)) {
return com.gic.api.base.commons.ServiceResponse.success(cache);
}
JSONResponse json = materialService.getMaterialIdByCombined(wxEnterpriseId, combinedQDTO);
if (json.getErrorCode() == 0) {
String mediaId= json.getResult().toString() ;
RedisUtil.setCache(key, mediaId, 1L, TimeUnit.DAYS);
return com.gic.api.base.commons.ServiceResponse.success(mediaId) ;
}else {
return com.gic.api.base.commons.ServiceResponse.failure("9999",json.getErrorMessage()) ;
}
}
} }
import com.alibaba.fastjson.JSON;
import com.gic.api.base.commons.JSONResponse;
import com.gic.haoban.manage.api.dto.WxEnterpriseQwDTO;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import com.gic.haoban.manage.service.context.draw.ImageDraw;
import com.gic.haoban.manage.service.service.WxEnterpriseService;
import com.gic.wechat.api.enums.QywxMediaTypeEnum;
import com.gic.wechat.api.service.qywx.QywxSuiteApiService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext-conf.xml"})
public class QWmediaTest {
private static Logger logger = LoggerFactory.getLogger(QWmediaTest.class);
@Autowired
private QywxSuiteApiService qywxSuiteApiService ;
@Autowired
private WxEnterpriseService wxEnterpriseService;
private static final String LARGE_IMAGE_URL = "https://jhdm-1251519181.cos.ap-shanghai.myqcloud.com/image/material_content-4582434f589b494aa4f27df6e3aa3770.png?imageView2/2/w/1080/h/10800/format/jpg";
private static final String QR_CODE_URL = "https://gicinner-1251519181.cos.ap-shanghai.myqcloud.com/image/material_content-4ffc77073ca1476fb264bf1be9f11383.png";
private static final String STORE_NAME = "门店CCA";
private static final String GUIDE_NAME = "宇智222";
@Test
public void test() throws Exception{
WxEnterpriseQwDTO qwDTO = this.wxEnterpriseService.getQwInfo("b18ffdc9d0644912865a248859914d80") ;
CombinedQDTO combinedQDTO = new CombinedQDTO();
combinedQDTO.setImageUrl(LARGE_IMAGE_URL);
combinedQDTO.setQrCodeUrl(QR_CODE_URL);
combinedQDTO.setShowStyle(1);
combinedQDTO.setLineOne(STORE_NAME);
combinedQDTO.setLineTwo(GUIDE_NAME);
String[] arr = null ;
byte[] combinedImageData = ImageDraw.getCombinedImage(combinedQDTO);
// 将 BufferedImage 写入 ByteArrayOutputStream
if(LARGE_IMAGE_URL.contains("?")) {
arr = LARGE_IMAGE_URL.split("\\?")[0].split("/") ;
}else {
arr = LARGE_IMAGE_URL.split("/");
}
// JSONResponse test = qywxSuiteApiService.uploadAttachment(qwDTO.getThirdCorpid(), qwDTO.getSelf3thSecret(), combinedImageData, arr[arr.length - 1], QywxMediaTypeEnum.IMAGE.getCode(), qwDTO.isSelf(), qwDTO.getUrlHost());
// System.out.println(JSON.toJSONString(test));
}
}
...@@ -3,11 +3,14 @@ package com.gic.haoban.manage.web.controller.content; ...@@ -3,11 +3,14 @@ package com.gic.haoban.manage.web.controller.content;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.gic.haoban.manage.api.qdto.combined.CombinedQDTO;
import com.gic.haoban.manage.web.qo.combined.CombinedQO;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -171,4 +174,15 @@ public class QwMessageController extends WebBaseController { ...@@ -171,4 +174,15 @@ public class QwMessageController extends WebBaseController {
} }
return RestResponse.failure("-1", resp.getMessage()) ; return RestResponse.failure("-1", resp.getMessage()) ;
} }
@RequestMapping("combined-qw-materialid")
public RestResponse<Object> combinedMaterialId(@RequestBody CombinedQO combinedQO) {
String wxEnterpriseId = combinedQO.getWxEnterpriseId();
CombinedQDTO combinedQDTO = com.gic.commons.util.EntityUtil.changeEntityNew(CombinedQDTO.class, combinedQO);
ServiceResponse<String> resp = this.materialApiService.getMaterialIdByCombined(wxEnterpriseId, combinedQDTO) ;
if(resp.isSuccess()) {
return RestResponse.successResult(resp.getResult()) ;
}
return RestResponse.failure("-1", resp.getMessage()) ;
}
} }
package com.gic.haoban.manage.web.qo.combined;
import java.io.Serializable;
public class CombinedQO implements Serializable {
private static final long serialVersionUID = -28454620622119889L;
private String wxEnterpriseId;
/**
* 主图url
*/
private String imageUrl;
/**
* 小程序码url
*/
private String qrCodeUrl;
/**
* 展示样式
*/
private Integer showStyle;
/**
* 第一行文字
*/
private String lineOne;
/**
* 第二行文字
*/
private String lineTwo;
public String getWxEnterpriseId() {
return wxEnterpriseId;
}
public void setWxEnterpriseId(String wxEnterpriseId) {
this.wxEnterpriseId = wxEnterpriseId;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getQrCodeUrl() {
return qrCodeUrl;
}
public void setQrCodeUrl(String qrCodeUrl) {
this.qrCodeUrl = qrCodeUrl;
}
public Integer getShowStyle() {
return showStyle;
}
public void setShowStyle(Integer showStyle) {
this.showStyle = showStyle;
}
public String getLineOne() {
return lineOne;
}
public void setLineOne(String lineOne) {
this.lineOne = lineOne;
}
public String getLineTwo() {
return lineTwo;
}
public void setLineTwo(String lineTwo) {
this.lineTwo = lineTwo;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment