package com.zteits.pay.util;
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 com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.lang.Base64;
import com.zteits.clouds.api.dto.pay.dto.TdCRechargeCardDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jmx.support.RegistrationPolicy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
/**
* 充值卡图片生成器(逐字搬自老门户
* {@code zteits-bcp-portal/src/main/java/com/zteits/bcp/portal/util/ChargeCardPicWrapper.java},
* 只改了包名。第二百八十二切片)。
*
*
⚠️ 三个字体文件({@code simhei.ttf} / {@code 汉仪书宋一简.ttf} / {@code 长城新艺体.ttf})
* 一并从老门户 {@code src/main/resources} 搬进本模块的 {@code src/main/resources}
* —— {@code new ClassPathResource("simhei.ttf")} 是**类路径根**,不是本包的相对路径。
* 少了任何一个,生成图片那一步会在 {@code Font.createFont} 处抛异常(被 catch 吞掉)⇒
* 图片生成失败但接口仍回 {@code true}(存量写法)。
*
* ⚠️ 存量写法照搬(红线⑥),不要"顺手修":
*
* - `@Async("myTaskAsyncPool")` 的三个方法**自己 catch 掉所有异常**、只在 finally
* `countDown()` ⇒ 生成失败对调用方完全不可见。
* - 字体是**静态字段 + 懒加载**(首次 `null` 才 `createFont`),多线程下可能重复加载几次。
* - `deleteAnyone` 上也挂了 `@Async` ⇒ 调用方拿到的返回值恒 `false`,删除其实是异步的
* (所以紧接着删 zip 与删目录的顺序在日志里看起来是乱的)。
*
*
* @author xiejianpeng(存量作者)
*/
@Component
// 解决jmx重复注册bean的问题
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
@EnableAsync
public class ChargeCardPicWrapper {
private final Logger logger = LoggerFactory.getLogger(ChargeCardPicWrapper.class);
private static Font fontStaticB = null;
private static Font fontStaticH = null;
private static Font fontStaticC = null;
//文字显示
private static final int QRCOLOR = 0x000000; //二维码颜色
private static final int BGWHITE = 0xFFFFFF; //二维码背景颜色:白色
// 设置QR二维码参数信息
private static Map hints = new HashMap() {
private static final long serialVersionUID = 1L;
{
put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);// 设置QR二维码的纠错级别(H为最高级别)
put(EncodeHintType.CHARACTER_SET, "utf-8");// 设置编码方式
put(EncodeHintType.MARGIN, 1);// 白边
}
};
public File base64ToFile(String base64) throws IOException {
String[] picInfo = base64.split(",");
base64 = picInfo[1];
if (base64 == null || "".equals(base64)) {
return null;
}
byte[] buff = Base64.decode(base64);
File file = null;
FileOutputStream fout = null;
try {
file = File.createTempFile("tmp", null);
fout = new FileOutputStream(file);
fout.write(buff);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fout != null) {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
private String handelPicExtend(String content) {
String suffIx = content.replace("data:image/", "").replace(";base64", "");
return suffIx;
}
private byte[] handelBase64PicContent(String content) throws IOException {
java.util.Base64.Decoder decoder = java.util.Base64.getMimeDecoder();
byte[] bytes = decoder.decode(content);
for (int i = 0; i < bytes.length; ++i) {
// 调整异常数据
if (bytes[i] < 0) {
bytes[i] += 256;
}
}
return bytes;
}
/**
* 生成二维码图片+背景+文字描述
*
* @param codeFile 生成图地址
* @param bgImgFile 背景图地址
*/
@Async("myTaskAsyncPool")
public Future CreatQRCodeTopOne(File codeFile, File bgImgFile, TdCRechargeCardDTO cRechargeCardDTO, CountDownLatch countDownLatch) {
Integer WIDTH = 320;
Integer HEIGHT = 320;
String cardNo = cRechargeCardDTO.getCardNo();
try {
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
// 1.参数顺序分别为: 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix bm = multiFormatWriter.encode(cardNo, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// 2.开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF) 白(0xFF000000)两色
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGWHITE);
}
}
/**
* 3.添加背景图片
*/
BufferedImage backgroundImage = ImageIO.read(bgImgFile);
// int qrWidth=image.getWidth();
//距离背景图片x边的距离,居中显示
int disx = 12;
//距离y边距离 * * * *
int disy = 316;
Graphics2D rng = backgroundImage.createGraphics();
// rng.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
//
// rng.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_DEFAULT);
//消除文字锯齿
rng.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//消除画图锯
rng.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rng.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP));
rng.drawImage(image, disx, disy, WIDTH, HEIGHT, null);
/**
* 4. 文字描述参数设置
*/
Color textColor = Color.white;
rng.setColor(textColor);
rng.drawImage(backgroundImage, 0, 0, null);
//设置字体类型和大小(BOLD加粗/ PLAIN平常)
float size2 = 18;
Font font2 = null;
if (fontStaticB == null) {
fontStaticB = Font.createFont(Font.TRUETYPE_FONT, new ClassPathResource("simhei.ttf").getInputStream());
}
font2 = fontStaticB.deriveFont(size2);
rng.setFont(font2);
//设置字体颜色
rng.setColor(Color.black);
if (2 == cRechargeCardDTO.getCardType()) {
//文字1显示位置--兑换码
String cardSecret = "兑换码:" + cRechargeCardDTO.getCardSecret();
rng.drawString(cardSecret, 260, 580);//上下
}
//文字1显示位置--兑换码
rng.drawString("扫我充值", 122, 360);//上下
//文字1显示位置--兑换码
String data = "截止日期:" + DateUtil.format(cRechargeCardDTO.getEndTime(), "yyyy年MM月dd日");
rng.drawString(data, 753, 580);//上下
float size = 48;
Font font = null;
if (fontStaticH == null) {
fontStaticH = Font.createFont(Font.TRUETYPE_FONT, new ClassPathResource("汉仪书宋一简.ttf").getInputStream());
}
font = fontStaticH.deriveFont(size);
rng.setFont(font);
//设置字体颜色
rng.setColor(Color.black);
//文字2显示位置
String regex = "(.{4})";
String result = cardNo.replaceAll(regex, "$1 ");
rng.drawString(result, 60, 100);//上下
size = 48;
font = fontStaticH.deriveFont(size);
rng.setFont(font);
//设置字体颜色
rng.setColor(Color.black);
//文字3显示位置
String no = "NO." + cRechargeCardDTO.getId();
rng.drawString(no, 690, 550);//上下
float size1 = 60;
Font font1 = null;
if (fontStaticC == null) {
fontStaticC = Font.createFont(Font.TRUETYPE_FONT, new ClassPathResource("长城新艺体.ttf").getInputStream());
}
font1 = fontStaticC.deriveFont(size1);
font1.deriveFont(Font.ITALIC);
rng.setFont(font1);
//设置字体颜色
rng.setColor(Color.black);
String money = cRechargeCardDTO.getTotalFee() / 100 + "元";
//文字2显示位置
rng.drawString(money, 850, 109);//上下
rng.dispose();
image = backgroundImage;
image.flush();
ImageIO.write(image, "png", codeFile);
} catch (Exception e) {
e.printStackTrace();
logger.error("生成图片错误:{}", e);
} finally {
countDownLatch.countDown();
}
return new AsyncResult<>(cRechargeCardDTO.getCardNo());
}
@Async("myTaskAsyncPool")
public void creatQRCodeTop(File codeFile, File bgImgFile, TdCRechargeCardDTO cRechargeCardDTO, CountDownLatch countDownLatch) {
String cardNo = cRechargeCardDTO.getCardNo();
try {
// 1.参数顺序分别为: 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BufferedImage image;
/**
* 3.添加背景图片
*/
BufferedImage backgroundImage = ImageIO.read(bgImgFile);
//距离背景图片x边的距离,居中显示
Graphics2D rng = backgroundImage.createGraphics();
//消除文字锯齿
rng.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//消除画图锯
rng.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rng.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP));
/**
* 4. 文字描述参数设置
*/
Color textColor = Color.white;
rng.setColor(textColor);
rng.drawImage(backgroundImage, 0, 0, null);
//设置字体类型和大小(BOLD加粗/ PLAIN平常)
float size = 48;
Font font = null;
if (fontStaticH == null) {
fontStaticH = Font.createFont(Font.TRUETYPE_FONT, new ClassPathResource("汉仪书宋一简.ttf").getInputStream());
}
font = fontStaticH.deriveFont(size);
rng.setFont(font);
//设置字体颜色
rng.setColor(Color.black);
//文字2显示位置
String regex = "(.{4})";
String result = cardNo.replaceAll(regex, "$1 ");
rng.drawString(result, 438, 546);//上下
size = 48;
font = fontStaticH.deriveFont(size);
rng.setFont(font);
//设置字体颜色
rng.setColor(Color.black);
//文字3显示位置
String no = "NO." + cRechargeCardDTO.getId();
rng.drawString(no, 690, 100);//上下
rng.dispose();
image = backgroundImage;
image.flush();
ImageIO.write(image, "png", codeFile);
} catch (Exception e) {
e.printStackTrace();
logger.error("生成图片错误:{}", e);
} finally {
countDownLatch.countDown();
}
}
@Async("myTaskAsyncPool")
public void creatQRCodeBottom(File codeFile, File bgImgFile, TdCRechargeCardDTO cRechargeCardDTO, CountDownLatch countDownLatch) {
Integer WIDTH = 321;
Integer HEIGHT = 321;
String cardNo = cRechargeCardDTO.getCardNo();
try {
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
// 1.参数顺序分别为: 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix bm = multiFormatWriter.encode(cardNo, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// 2.开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF) 白(0xFF000000)两色
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGWHITE);
}
}
/**
* 3.添加背景图片
*/
BufferedImage backgroundImage = ImageIO.read(bgImgFile);
// int qrWidth=image.getWidth();
//距离背景图片x边的距离,居中显示
int disx = 28;
//距离y边距离 * * * *
int disy = 160;
Graphics2D rng = backgroundImage.createGraphics();
//消除文字锯齿
rng.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//消除画图锯
rng.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rng.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP));
rng.drawImage(image, disx, disy, WIDTH, HEIGHT, null);
/**
* 4. 文字描述参数设置
*/
Color textColor = Color.white;
rng.setColor(textColor);
rng.drawImage(backgroundImage, 0, 0, null);
//设置字体类型和大小(BOLD加粗/ PLAIN平常)
float size2 = 26;
Font font2 = null;
if (fontStaticB == null) {
fontStaticB = Font.createFont(Font.TRUETYPE_FONT, new ClassPathResource("simhei.ttf").getInputStream());
}
font2 = fontStaticB.deriveFont(size2);
rng.setFont(font2);
//设置字体颜色
rng.setColor(Color.black);
//文字1显示位置--兑换码
if (2 == cRechargeCardDTO.getCardType()) {
//文字1显示位置--兑换码
String cardSecret = "兑换码:" + cRechargeCardDTO.getCardSecret();
rng.drawString(cardSecret, 108, 590);//上下
}
// String data = "截止日期:" + DateUtil.format(cRechargeCardDTO.getEndTime(), "yyyy年MM月dd日");
// rng.drawString(data, 639, 570);//上下
float size3 = 22;
font2 = fontStaticB.deriveFont(size3);
rng.setFont(font2);
rng.drawString("充值二维码", 133, 505);//上下
float size1 = 60;
Font font1 = null;
if (fontStaticC == null) {
fontStaticC = Font.createFont(Font.TRUETYPE_FONT, new ClassPathResource("长城新艺体.ttf").getInputStream());
}
font1 = fontStaticC.deriveFont(size1);
font1.deriveFont(Font.ITALIC);
rng.setFont(font1);
//设置字体颜色
rng.setColor(Color.black);
String money = cRechargeCardDTO.getTotalFee() / 100 + "元";
//文字2显示位置
rng.drawString(money, 738, 109);//上下
rng.dispose();
image = backgroundImage;
image.flush();
ImageIO.write(image, "png", codeFile);
} catch (Exception e) {
e.printStackTrace();
logger.error("生成图片错误:{}", e);
} finally {
countDownLatch.countDown();
}
}
/**
* 判断指定的文件或文件夹删除是否成功
*
* @param FileName 文件或文件夹的路径
* @return true or false 成功返回true,失败返回false
*/
@Async
public boolean deleteAnyone(String FileName) {
File file = new File(FileName);//根据指定的文件名创建File对象
if (!file.exists()) { //要删除的文件不存在
logger.info("文件" + FileName + "不存在,删除失败!");
return false;
} else { //要删除的文件存在
if (file.isFile()) { //如果目标文件是文件
return deleteFile(FileName);
} else { //如果目标文件是目录
return deleteDir(FileName);
}
}
}
/**
* 判断指定的文件删除是否成功
*
* @param
* @return true or false 成功返回true,失败返回false
*/
public boolean deleteFile(String fileName) {
File file = new File(fileName);//根据指定的文件名创建File对象
if (file.exists() && file.isFile()) { //要删除的文件存在且是文件
if (file.delete()) {
logger.info("文件" + fileName + "删除成功!");
return true;
} else {
logger.info("文件" + fileName + "删除失败!");
return false;
}
} else {
logger.info("文件" + fileName + "不存在,删除失败!");
return false;
}
}
/**
* 删除指定的目录以及目录下的所有子文件
*
* @param dirName is 目录路径
* @return true or false 成功返回true,失败返回false
*/
public boolean deleteDir(String dirName) {
if (dirName.endsWith(File.separator)) {//dirName不以分隔符结尾则自动添加分隔符
dirName = dirName + File.separator;
}
File file = new File(dirName);//根据指定的文件名创建File对象
if (!file.exists() || (!file.isDirectory())) { //目录不存在或者
logger.info("目录删除失败" + dirName + "目录不存在!");
return false;
}
File[] fileArrays = file.listFiles();//列出源文件下所有文件,包括子目录
for (int i = 0; i < fileArrays.length; i++) {//将源文件下的所有文件逐个删除
this.deleteAnyone(fileArrays[i].getAbsolutePath());
}
if (file.delete()) {//删除当前目录
logger.info("目录" + dirName + "删除成功!");
}
return true;
}
}