273 lines
11 KiB
Java
273 lines
11 KiB
Java
package com.campus.activity.util;
|
||
|
||
import com.campus.activity.entity.Activity;
|
||
import com.campus.activity.entity.Registration;
|
||
import com.campus.activity.entity.User;
|
||
import com.google.zxing.BarcodeFormat;
|
||
import com.google.zxing.EncodeHintType;
|
||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||
import com.google.zxing.common.BitMatrix;
|
||
import com.google.zxing.qrcode.QRCodeWriter;
|
||
import com.itextpdf.io.font.PdfEncodings;
|
||
import com.itextpdf.io.image.ImageDataFactory;
|
||
import com.itextpdf.kernel.colors.ColorConstants;
|
||
import com.itextpdf.kernel.font.PdfFont;
|
||
import com.itextpdf.kernel.font.PdfFontFactory;
|
||
import com.itextpdf.kernel.pdf.PdfDocument;
|
||
import com.itextpdf.kernel.pdf.PdfWriter;
|
||
import com.itextpdf.kernel.pdf.canvas.draw.SolidLine;
|
||
import com.itextpdf.layout.Document;
|
||
import com.itextpdf.layout.element.*;
|
||
import com.itextpdf.layout.properties.HorizontalAlignment;
|
||
import com.itextpdf.layout.properties.TextAlignment;
|
||
import com.itextpdf.layout.properties.UnitValue;
|
||
import com.itextpdf.layout.properties.VerticalAlignment;
|
||
|
||
import java.io.ByteArrayOutputStream;
|
||
import java.io.IOException;
|
||
import java.time.LocalDateTime;
|
||
import java.time.format.DateTimeFormatter;
|
||
import java.util.HashMap;
|
||
import java.util.Map;
|
||
|
||
public class PdfUtil {
|
||
|
||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
|
||
|
||
public static byte[] generateTicketPdf(User user, Activity activity, Registration registration) {
|
||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||
PdfWriter writer = new PdfWriter(outputStream);
|
||
PdfDocument pdf = new PdfDocument(writer);
|
||
Document document = new Document(pdf);
|
||
|
||
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
|
||
PdfFont boldChineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
|
||
|
||
createHeader(document, chineseFont, boldChineseFont);
|
||
createDivider(document);
|
||
createActivityInfo(document, activity, chineseFont, boldChineseFont);
|
||
createParticipantAndQrCode(document, user, registration, chineseFont, boldChineseFont);
|
||
createFooter(document, chineseFont);
|
||
|
||
document.close();
|
||
|
||
return outputStream.toByteArray();
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("生成电子票失败", e);
|
||
}
|
||
}
|
||
|
||
private static void createHeader(Document document, PdfFont font, PdfFont boldFont) {
|
||
Paragraph title = new Paragraph("校园活动电子票")
|
||
.setFont(boldFont)
|
||
.setFontSize(20)
|
||
.setBold()
|
||
.setTextAlignment(TextAlignment.CENTER)
|
||
.setMarginTop(15)
|
||
.setMarginBottom(5);
|
||
|
||
Paragraph subtitle = new Paragraph("Campus Activity E-Ticket")
|
||
.setFont(font)
|
||
.setFontSize(9)
|
||
.setTextAlignment(TextAlignment.CENTER)
|
||
.setFontColor(ColorConstants.GRAY)
|
||
.setMarginBottom(10);
|
||
|
||
document.add(title);
|
||
document.add(subtitle);
|
||
}
|
||
|
||
private static void createDivider(Document document) {
|
||
SolidLine line = new SolidLine(1f);
|
||
line.setColor(ColorConstants.LIGHT_GRAY);
|
||
LineSeparator separator = new LineSeparator(line)
|
||
.setMarginBottom(15)
|
||
.setWidth(UnitValue.createPercentValue(100));
|
||
document.add(separator);
|
||
}
|
||
|
||
private static void createActivityInfo(Document document, Activity activity, PdfFont font, PdfFont boldFont) {
|
||
Paragraph sectionTitle = new Paragraph("活动信息")
|
||
.setFont(boldFont)
|
||
.setFontSize(12)
|
||
.setBold()
|
||
.setMarginBottom(8);
|
||
|
||
document.add(sectionTitle);
|
||
|
||
Table infoTable = new Table(UnitValue.createPercentArray(new float[]{25, 75}))
|
||
.setMarginBottom(15)
|
||
.setWidth(UnitValue.createPercentValue(90))
|
||
.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||
|
||
addTableRow(infoTable, "活动名称", activity.getTitle(), font, boldFont, true);
|
||
addTableRow(infoTable, "活动时间", formatDateTime(activity.getStartTime()) + " ~ " + formatDateTime(activity.getEndTime()), font, boldFont, false);
|
||
addTableRow(infoTable, "活动地点", activity.getLocation(), font, boldFont, false);
|
||
|
||
document.add(infoTable);
|
||
}
|
||
|
||
private static void createParticipantAndQrCode(Document document, User user, Registration registration, PdfFont font, PdfFont boldFont) {
|
||
Table mainTable = new Table(UnitValue.createPercentArray(new float[]{60, 40}))
|
||
.setWidth(UnitValue.createPercentValue(90))
|
||
.setHorizontalAlignment(HorizontalAlignment.CENTER)
|
||
.setMarginBottom(15);
|
||
|
||
Cell leftCell = new Cell()
|
||
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER)
|
||
.setPadding(0);
|
||
|
||
Cell rightCell = new Cell()
|
||
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER)
|
||
.setPadding(0)
|
||
.setVerticalAlignment(VerticalAlignment.MIDDLE);
|
||
|
||
Paragraph participantTitle = new Paragraph("参与者信息")
|
||
.setFont(boldFont)
|
||
.setFontSize(12)
|
||
.setBold()
|
||
.setMarginBottom(8);
|
||
|
||
Table participantTable = new Table(UnitValue.createPercentArray(new float[]{25, 75}))
|
||
.setMarginBottom(0);
|
||
|
||
addTableRow(participantTable, "学生姓名", user.getName(), font, boldFont, true);
|
||
addTableRow(participantTable, "学号", user.getStudentId(), font, boldFont, false);
|
||
addTableRow(participantTable, "电子票号", registration.getTicketCode(), font, boldFont, false);
|
||
addTableRow(participantTable, "报名时间", formatDateTime(registration.getCreatedAt()), font, boldFont, false);
|
||
|
||
leftCell.add(participantTitle);
|
||
leftCell.add(participantTable);
|
||
|
||
try {
|
||
byte[] qrCodeBytes = generateQrCode(registration.getTicketCode());
|
||
Image qrCodeImage = new Image(ImageDataFactory.create(qrCodeBytes))
|
||
.setWidth(120)
|
||
.setHeight(120)
|
||
.setHorizontalAlignment(HorizontalAlignment.CENTER)
|
||
.setMarginBottom(5);
|
||
|
||
Paragraph qrTitle = new Paragraph("签到二维码")
|
||
.setFont(font)
|
||
.setFontSize(12)
|
||
.setBold()
|
||
.setTextAlignment(TextAlignment.CENTER)
|
||
.setMarginBottom(8);
|
||
|
||
Paragraph ticketCode = new Paragraph("票号:" + registration.getTicketCode())
|
||
.setFont(font)
|
||
.setFontSize(9)
|
||
.setTextAlignment(TextAlignment.CENTER)
|
||
.setFontColor(ColorConstants.GRAY)
|
||
.setMarginBottom(0);
|
||
|
||
rightCell.add(qrTitle);
|
||
rightCell.add(qrCodeImage);
|
||
rightCell.add(ticketCode);
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("生成二维码失败", e);
|
||
}
|
||
|
||
mainTable.addCell(leftCell);
|
||
mainTable.addCell(rightCell);
|
||
|
||
document.add(mainTable);
|
||
}
|
||
|
||
private static void createFooter(Document document, PdfFont font) {
|
||
SolidLine line = new SolidLine(1f);
|
||
line.setColor(ColorConstants.LIGHT_GRAY);
|
||
LineSeparator separator = new LineSeparator(line)
|
||
.setMarginTop(15)
|
||
.setMarginBottom(10)
|
||
.setWidth(UnitValue.createPercentValue(100));
|
||
document.add(separator);
|
||
|
||
Paragraph notice = new Paragraph("温馨提示:请妥善保管此电子票,活动当天凭此票签到入场。请提前15分钟到达活动现场,出示电子票二维码签到。")
|
||
.setFont(font)
|
||
.setFontSize(9)
|
||
.setTextAlignment(TextAlignment.CENTER)
|
||
.setMarginBottom(10);
|
||
|
||
document.add(notice);
|
||
|
||
Paragraph footer = new Paragraph("本电子票由校园活动系统自动生成 | 生成时间:" + LocalDateTime.now().format(DATE_TIME_FORMATTER))
|
||
.setFont(font)
|
||
.setFontSize(7)
|
||
.setTextAlignment(TextAlignment.CENTER)
|
||
.setFontColor(ColorConstants.GRAY)
|
||
.setMarginBottom(15);
|
||
|
||
document.add(footer);
|
||
}
|
||
|
||
private static void addTableRow(Table table, String label, String value, PdfFont font, PdfFont boldFont, boolean isFirstRow) {
|
||
Cell labelCell = new Cell()
|
||
.add(new Paragraph(label).setFont(boldFont).setFontSize(10))
|
||
.setPadding(4)
|
||
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER);
|
||
|
||
Cell valueCell = new Cell()
|
||
.add(new Paragraph(value != null ? value : "").setFont(font).setFontSize(10))
|
||
.setPadding(4)
|
||
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER);
|
||
|
||
if (isFirstRow) {
|
||
labelCell.setBackgroundColor(new com.itextpdf.kernel.colors.DeviceRgb(240, 240, 240));
|
||
valueCell.setBackgroundColor(new com.itextpdf.kernel.colors.DeviceRgb(240, 240, 240));
|
||
}
|
||
|
||
table.addCell(labelCell);
|
||
table.addCell(valueCell);
|
||
}
|
||
|
||
private static String formatDateTime(LocalDateTime dateTime) {
|
||
if (dateTime == null) {
|
||
return "待定";
|
||
}
|
||
return dateTime.format(DATE_TIME_FORMATTER);
|
||
}
|
||
|
||
private static String formatDescription(String description) {
|
||
if (description == null || description.isEmpty()) {
|
||
return "暂无描述";
|
||
}
|
||
if (description.length() > 100) {
|
||
return description.substring(0, 100) + "...";
|
||
}
|
||
return description;
|
||
}
|
||
|
||
private static String formatRegistrationStatus(Integer status) {
|
||
if (status == null) {
|
||
return "未知";
|
||
}
|
||
switch (status) {
|
||
case 0:
|
||
return "已取消";
|
||
case 1:
|
||
return "已报名";
|
||
case 2:
|
||
return "已签到";
|
||
default:
|
||
return "未知";
|
||
}
|
||
}
|
||
|
||
private static byte[] generateQrCode(String content) throws IOException {
|
||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
|
||
hints.put(EncodeHintType.MARGIN, 2);
|
||
|
||
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
|
||
|
||
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
|
||
|
||
return outputStream.toByteArray();
|
||
} catch (com.google.zxing.WriterException e) {
|
||
throw new RuntimeException("生成二维码失败", e);
|
||
}
|
||
}
|
||
} |