diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 0dfdfd6..94be61d 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -23,6 +23,11 @@ 8.0.4 pom + + com.itextpdf + html2pdf + 4.0.5 + org.apache.poi poi-ooxml diff --git a/ruoyi-admin/src/main/java/com/ruoyi/docking/controller/SmartRemindersController.java b/ruoyi-admin/src/main/java/com/ruoyi/docking/controller/SmartRemindersController.java index 78b7b4d..aa27227 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/docking/controller/SmartRemindersController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/docking/controller/SmartRemindersController.java @@ -126,13 +126,13 @@ public class SmartRemindersController extends BaseController { @PostMapping("/updateDq") public AjaxResult updateDq(@RequestBody @Valid SmartReminders dq) throws SchedulerException, TaskException { if (dq.getAlertType() == 2 || dq.getAlertType() == 3) { - if (dq.getAlertManner() == 1) { + if (dq.getAlertManner() == 2) { smartRemindersService.updateAndAdd(dq,false); }else { throw new ServiceException("参数错误!"); } }else { - if (dq.getAlertManner() == 2) { + if (dq.getAlertManner() == 1) { smartRemindersService.updateById(dq); }else { throw new ServiceException("参数错误!"); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/docking/entity/request/SREnterRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/docking/entity/request/SREnterRequest.java index e2f2b70..e44a4d5 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/docking/entity/request/SREnterRequest.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/docking/entity/request/SREnterRequest.java @@ -36,9 +36,9 @@ public class SREnterRequest { private LocalDateTime alertTime; /** - * 提醒分类 1.全局自定义通知 2.申报任务即将结束(企业) 3.项目即将建设完成(政务) 4.项目自定义通知 + * 提醒分类 1.全局自定义通知 2.项目即将结束(企业) 3.项目即将建设完成(政务) 4.项目自定义通知 */ - @ApiModelProperty("提醒分类 1.全局自定义通知 2.申报任务即将结束(企业) 3.项目即将建设完成(政务) 4.项目自定义通知") + @ApiModelProperty("提醒分类 1.全局自定义通知 2.项目即将结束(企业) 3.项目即将建设完成(政务) 4.项目自定义通知") private Integer alertType; /** diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/GyslProjectHandbookController.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/GyslProjectHandbookController.java index 30aa4ae..4b21b66 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/GyslProjectHandbookController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/GyslProjectHandbookController.java @@ -3,21 +3,16 @@ package com.ruoyi.gysl.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.itextpdf.kernel.pdf.PdfDocument; -import com.itextpdf.kernel.pdf.PdfWriter; -import com.itextpdf.layout.Document; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.gysl.entity.GyslProjectHandbook; import com.ruoyi.gysl.service.GyslProjectHandbookService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.io.File; import java.io.Serializable; import static com.ruoyi.common.core.domain.AjaxResult.success; @@ -112,6 +107,5 @@ public class GyslProjectHandbookController { throws Exception { gyslProjectHandbookService.generatePdfs(response,id); } - } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/GyslProjectHandbookService.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/GyslProjectHandbookService.java index 83f853c..c3a38d4 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/GyslProjectHandbookService.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/GyslProjectHandbookService.java @@ -2,12 +2,11 @@ package com.ruoyi.gysl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.itextpdf.layout.Document; -import com.ruoyi.gysl.entity.BasicInformation; import com.ruoyi.gysl.entity.GyslProjectHandbook; -import com.ruoyi.gysl.entity.response.BasicInformationResponse; import javax.servlet.http.HttpServletResponse; import java.io.FileNotFoundException; +import java.io.IOException; import java.net.MalformedURLException; /** @@ -22,25 +21,12 @@ public interface GyslProjectHandbookService extends IService basicInfoList); - - /** - * 内容页设计 - */ - void addProductPages(Document document, java.util.List basicInfoList) throws MalformedURLException; - - - } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/impl/GyslProjectHandbookServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/impl/GyslProjectHandbookServiceImpl.java index d52f3ec..6d0a9b6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/impl/GyslProjectHandbookServiceImpl.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/service/impl/GyslProjectHandbookServiceImpl.java @@ -1,20 +1,25 @@ package com.ruoyi.gysl.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.itextpdf.html2pdf.ConverterProperties; +import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.io.image.ImageData; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.colors.ColorConstants; -import com.itextpdf.kernel.colors.DeviceRgb; +import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.utils.PdfMerger; import com.itextpdf.layout.Document; -import com.itextpdf.layout.element.*; -import com.itextpdf.layout.properties.AreaBreakType; +import com.itextpdf.layout.element.AreaBreak; +import com.itextpdf.layout.element.Image; +import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.properties.HorizontalAlignment; import com.itextpdf.layout.properties.TextAlignment; -import com.itextpdf.layout.properties.UnitValue; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.file.FileUtils; @@ -27,9 +32,16 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; -import java.io.FileNotFoundException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.rmi.ServerError; import java.util.Arrays; +import java.util.List; /** * 项目手册(GyslProjectHandbook)表服务实现类 @@ -44,163 +56,80 @@ public class GyslProjectHandbookServiceImpl extends ServiceImpl basicInfoList = basicInformationMapper.idListToProject - (Arrays.asList(byId.getXmId().split(","))); + public void generatePdfs(HttpServletResponse response, Integer id) throws IOException { + // 获取数据 + GyslProjectHandbook handbook = getById(id); + List basicInfoList = basicInformationMapper.idListToProject( + Arrays.asList(handbook.getXmId().split(","))); + // 设置响应头 response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename=\"product_catalog.pdf\""); - try ( - // 直接绑定到 response 的输出流(不再生成本地文件) - PdfDocument pdf = new PdfDocument(new PdfWriter(response.getOutputStream())); - Document document = new Document(pdf) - ) { - addCoverPage(document, byId); - addTableOfContents(document, basicInfoList); - addProductPages(document, basicInfoList); + + try (OutputStream outputStream = response.getOutputStream(); + PdfWriter writer = new PdfWriter(outputStream); + PdfDocument mergedPdf = new PdfDocument(writer)) { // 主文档生命周期由外部try控制 + + // ========== 封面页 ========== + Document coverDoc = new Document(mergedPdf); + addCoverPage(coverDoc, handbook); + coverDoc.close(); // 关闭封面Document但不关闭PdfDocument + + // ========== 动态内容合并 ========== + String htmlContent = "动态内容"; + try (ByteArrayOutputStream tempStream = new ByteArrayOutputStream()) { + // HTML转PDF + HtmlConverter.convertToPdf(htmlContent, tempStream); + // 合并到主文档 + try (PdfReader tempReader = new PdfReader(new ByteArrayInputStream(tempStream.toByteArray())); + PdfDocument tempPdf = new PdfDocument(tempReader)) { + PdfMerger merger = new PdfMerger(mergedPdf); + merger.merge(tempPdf, 1, tempPdf.getNumberOfPages()); + } + } + } catch (Exception e) { - response.reset(); // 清除已写入的内容 + response.reset(); + response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - throw new RuntimeException("PDF生成失败", e); + throw new ServiceException("错误!"); } } - /** - * 封面页设计 - */ @Override - public void addCoverPage(Document document, GyslProjectHandbook gyslProjectHandbook) throws MalformedURLException { - //添加手册名称 - Paragraph coverTitle = new Paragraph(gyslProjectHandbook.getName()) - .setFontSize(30) - .setBold() - .setFontColor(ColorConstants.BLUE) + public void addCoverPage(Document document, GyslProjectHandbook handbook) throws MalformedURLException { + // 标题 + Paragraph title = new Paragraph(handbook.getName()) + .setFontSize(30).setBold().setFontColor(ColorConstants.BLUE) .setTextAlignment(TextAlignment.CENTER) - .setMarginTop(200); // 垂直居中 - document.add(coverTitle); - //添加副标题 - Paragraph ft = new Paragraph(gyslProjectHandbook.getSubtitle()) - .setFontSize(22) - .setFontColor(ColorConstants.BLUE) + .setMarginTop(60); + document.add(title); + + // 副标题 + Paragraph subtitle = new Paragraph(handbook.getSubtitle()) + .setFontSize(22).setFontColor(ColorConstants.BLUE) .setTextAlignment(TextAlignment.RIGHT) - .setMarginTop(300); // 垂直居中 - document.add(ft); - //添加封面图片 - try { - if (!FileUtils.checkAllowDownload(gyslProjectHandbook.getCoverImg())) { - throw new ServiceException("资源文件({})非法! "); - } - // 本地资源路径 - String localPath = RuoYiConfig.getProfile(); - // 数据库资源地址 - String downloadPath = localPath + StringUtils.substringAfter(gyslProjectHandbook.getCoverImg(), Constants.RESOURCE_PREFIX); - ImageData imageData = ImageDataFactory.create(downloadPath); - Image image = new Image(imageData).setWidth(300).setHorizontalAlignment(HorizontalAlignment.CENTER); - document.add(image); - } catch (Exception e) { - throw new ServiceException("下载文件失败! "); + .setMarginTop(70); + document.add(subtitle); + + // 封面图片 + String imgPath = RuoYiConfig.getProfile() + + StringUtils.substringAfter(handbook.getCoverImg(), Constants.RESOURCE_PREFIX); + + if (!FileUtils.checkAllowDownload(imgPath)) { + throw new ServiceException("非法文件路径: " + imgPath); } - document.add(new AreaBreak()); // 强制分页 - } - /** - * 目录页设计 - */ - @Override - public void addTableOfContents(Document document, java.util.List basicInfoList) { - Paragraph tocTitle = new Paragraph("目录") - .setFontSize(20) - .setBold() - .setTextAlignment(TextAlignment.CENTER); - document.add(tocTitle); - List tocList = new List() - .setSymbolIndent(20); - basicInfoList.forEach(x -> tocList.add(x.getBasicInformation().getName())); - document.add(tocList); - document.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); - } + ImageData imageData = ImageDataFactory.create(imgPath); + Image image = new Image(imageData) + .setWidth(400) + .setMarginTop(85) + .setHorizontalAlignment(HorizontalAlignment.CENTER); + document.add(image); - /** - * 内容页设计 - */ - @Override - public void addProductPages(Document document, java.util.List basicInfoList) throws MalformedURLException { - basicInfoList.forEach(x -> { - Paragraph chapter1 = new Paragraph(x.getBasicInformation().getName()) - .setFontSize(18) - .setBold(); - document.add(chapter1); - // 1. 添加园区信息表格 - Table parkTable = new Table(UnitValue.createPercentArray(new float[]{30, 70})) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginBottom(20) - .setKeepTogether(true); // 保持表格在同一页; - // 园区信息标题(蓝色背景) - Cell cell = new Cell(1, 2) - .setBackgroundColor(new DeviceRgb(33, 150, 243)) - .add(new Paragraph("园区信息") - .setFontColor(DeviceRgb.WHITE) - .setFontSize(16) - .setBold()) - .setTextAlignment(TextAlignment.CENTER) - .setPadding(8); - parkTable.addCell(cell); - // 添加园区信息行 - parkTable.addCell(new Cell().add(new Paragraph("总投资额")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("项目标签")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("项目标签")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("2.87万m²")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("现状分类")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("毛坯")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("重点发展产业")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("70个")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("建设模式")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("宿舍78间")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("所属功能区")).setPadding(5)); - parkTable.addCell(new Cell().add(new Paragraph("A栋18900m²,B栋5500m²整租价格可议")).setPadding(5)); - document.add(parkTable); - // 2. 添加厂房信息表格 - Table factoryTable = new Table(UnitValue.createPercentArray(new float[]{25, 35, 35})) - .setWidth(UnitValue.createPercentValue(100)).setKeepTogether(true); // 保持表格在同一页; - // 厂房信息标题(蓝色背景) - Cell cell2 = new Cell(1, 2) - .setBackgroundColor(new DeviceRgb(33, 150, 243)) - .add(new Paragraph("厂房信息") - .setFontColor(DeviceRgb.WHITE) - .setFontSize(16) - .setBold()) - .setTextAlignment(TextAlignment.CENTER) - .setPadding(8); - factoryTable.addCell(cell2); - // 子标题(浅灰色背景) - Cell z1 = new Cell() - .setBackgroundColor(new DeviceRgb(245, 245, 245)) - .add(new Paragraph("A栋具体参数").setBold()) - .setTextAlignment(TextAlignment.CENTER) - .setPadding(5); - factoryTable.addCell(z1); - Cell z2 = new Cell() - .setBackgroundColor(new DeviceRgb(245, 245, 245)) - .add(new Paragraph("B栋具体参数").setBold()) - .setTextAlignment(TextAlignment.CENTER) - .setPadding(5); - factoryTable.addCell(z2); - // 添加参数行 - factoryTable.addCell(new Cell().add(new Paragraph("租金")).setPadding(5)); - factoryTable.addCell(new Cell().add(new Paragraph("首层35元/m²,2-3层30元/m²,4-9层25元/m²")).setPadding(5)); - factoryTable.addCell(new Cell().add(new Paragraph("首层32元/m²,2-3层26元/m²,4-5层25元/m²")).setPadding(5)); - factoryTable.addCell(new Cell().add(new Paragraph("租金")).setPadding(5)); - factoryTable.addCell(new Cell().add(new Paragraph("首层35元/m²,2-3层30元/m²,4-9层25元/m²")).setPadding(5)); - factoryTable.addCell(new Cell().add(new Paragraph("首层32元/m²,2-3层26元/m²,4-5层25元/m²")).setPadding(5)); - document.add(factoryTable); - }); + // 分页控制(关键修改:直接操作PdfDocument) + document.getPdfDocument().addNewPage(); } } diff --git a/ruoyi-admin/src/main/resources/mapper/BasicInformationMapper.xml b/ruoyi-admin/src/main/resources/mapper/BasicInformationMapper.xml index c4e74d5..e39165a 100644 --- a/ruoyi-admin/src/main/resources/mapper/BasicInformationMapper.xml +++ b/ruoyi-admin/src/main/resources/mapper/BasicInformationMapper.xml @@ -357,6 +357,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + diff --git a/ruoyi-admin/src/main/resources/mapper/QyStatsMapper.xml b/ruoyi-admin/src/main/resources/mapper/QyStatsMapper.xml index f6f31ea..f1aefea 100644 --- a/ruoyi-admin/src/main/resources/mapper/QyStatsMapper.xml +++ b/ruoyi-admin/src/main/resources/mapper/QyStatsMapper.xml @@ -3,30 +3,6 @@