diff --git a/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java b/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java index 6bf3748895..94a6c9753d 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java +++ b/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java @@ -35,6 +35,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/core/core-backend/src/main/java/io/dataease/commons/utils/ExcelWatermarkUtils.java b/core/core-backend/src/main/java/io/dataease/commons/utils/ExcelWatermarkUtils.java new file mode 100644 index 0000000000..6708ea2394 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/commons/utils/ExcelWatermarkUtils.java @@ -0,0 +1,112 @@ +package io.dataease.commons.utils; + +import io.dataease.api.permissions.user.vo.UserFormVO; +import io.dataease.visualization.dto.WatermarkContentDTO; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.awt.Color; +import java.awt.Font; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; + +public class ExcelWatermarkUtils { + + public static String transContent(String content, UserFormVO userInfo) { + content = content.replaceAll("\\$\\{ip}", userInfo.getIp() == null?"127.0.0.1":userInfo.getIp()); + content = content.replaceAll("\\$\\{username}", userInfo.getAccount()); + content = content.replaceAll("\\$\\{nickName}", userInfo.getName()); + content = content.replaceAll("\\$\\{time}", new Date().toString()); + return content; + } + + /** + * 添加水印图片到工作簿并返回图片 ID + */ + public static int addWatermarkImage(XSSFWorkbook wb, WatermarkContentDTO watermarkContent, UserFormVO userInfo) { + byte[] imageBytes = createTextImage(transContent(watermarkContent.getContent(),userInfo), 300, 100, 12); // 生成文字水印图片 + return wb.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); // 添加到工作簿并返回 ID + } + + public static void addWatermarkToSheet(Sheet sheet, XSSFWorkbook wb, int pictureIdx) { + XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch(); + + // 获取工作表的总列数和行数 + int lastRowNum = sheet.getLastRowNum(); + int totalColumns = 0; + for (int i = 0; i <= lastRowNum; i++) { + Row row = sheet.getRow(i); + if (row != null) { + totalColumns = Math.max(totalColumns, row.getLastCellNum()); + } + } + + // 如果没有内容,则假设默认覆盖100行和50列 + if (lastRowNum == 0 && totalColumns == 0) { + lastRowNum = 100; + totalColumns = 50; + } + + // 根据总行列数循环绘制水印 + for (int row = 0; row <= lastRowNum; row += 15) { // 每15行绘制一行水印 + for (int col = 0; col <= totalColumns; col += 8) { // 每8列绘制一列水印 + ClientAnchor anchor = wb.getCreationHelper().createClientAnchor(); + // 设置图片位置 + anchor.setCol1(col); // 起始列 + anchor.setRow1(row); // 起始行 + anchor.setCol2(col + 5); // 终止列 + anchor.setRow2(row + 10); // 终止行 + anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); // 防止移动和调整大小 + XSSFPicture picture = drawing.createPicture(anchor, pictureIdx); + // 锁定图片(不可移动、不可调整大小) + picture.getCTPicture().getNvPicPr().getCNvPicPr().addNewPicLocks().setNoChangeAspect(true); + picture.getCTPicture().getNvPicPr().getCNvPicPr().addNewPicLocks().setNoMove(true); + picture.getCTPicture().getNvPicPr().getCNvPicPr().addNewPicLocks().setNoResize(true); + } + } + } + + public static byte[] createTextImage(String text, int width, int height, int fontSize) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = image.createGraphics(); + + // 设置透明背景 + image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT); + g2d.dispose(); + g2d = image.createGraphics(); + + // 设置抗锯齿 + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 设置字体 + g2d.setFont(new Font("Arial", Font.BOLD, fontSize)); + g2d.setColor(new Color(0, 0, 0, 50)); // 半透明颜色 + g2d.rotate(Math.toRadians(25), width / 2.0, height / 2.0); // 旋转文字 + + // 绘制文字 + FontMetrics fontMetrics = g2d.getFontMetrics(); + int textWidth = fontMetrics.stringWidth(text); + int textHeight = fontMetrics.getHeight(); + int x = (width - textWidth) / 2; + int y = (height + textHeight) / 2 - fontMetrics.getDescent(); + g2d.drawString(text, x, y); + + g2d.dispose(); + + // 转为字节数组 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "png", baos); + } catch (IOException e) { + e.printStackTrace(); + } + return baos.toByteArray(); + } +} diff --git a/core/core-backend/src/main/java/io/dataease/exportCenter/manage/ExportCenterManage.java b/core/core-backend/src/main/java/io/dataease/exportCenter/manage/ExportCenterManage.java index 707327abda..cc896c7eb1 100644 --- a/core/core-backend/src/main/java/io/dataease/exportCenter/manage/ExportCenterManage.java +++ b/core/core-backend/src/main/java/io/dataease/exportCenter/manage/ExportCenterManage.java @@ -12,11 +12,13 @@ import io.dataease.api.dataset.union.DatasetGroupInfoDTO; import io.dataease.api.dataset.union.UnionDTO; import io.dataease.api.export.BaseExportApi; import io.dataease.api.permissions.dataset.dto.DataSetRowPermissionsTreeDTO; +import io.dataease.api.permissions.user.vo.UserFormVO; import io.dataease.api.xpack.dataFilling.DataFillingApi; import io.dataease.api.xpack.dataFilling.dto.DataFillFormTableDataRequest; import io.dataease.auth.bo.TokenUserBO; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.chart.server.ChartDataServer; +import io.dataease.commons.utils.ExcelWatermarkUtils; import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup; import io.dataease.dataset.dao.auto.mapper.CoreDatasetGroupMapper; import io.dataease.dataset.manage.*; @@ -49,9 +51,13 @@ import io.dataease.model.ExportTaskDTO; import io.dataease.system.manage.CoreUserManage; import io.dataease.system.manage.SysParameterManage; import io.dataease.utils.*; +import io.dataease.visualization.dao.auto.entity.VisualizationWatermark; +import io.dataease.visualization.dao.auto.mapper.VisualizationWatermarkMapper; +import io.dataease.visualization.dto.WatermarkContentDTO; import io.dataease.visualization.server.DataVisualizationServer; import io.dataease.websocket.WsMessage; import io.dataease.websocket.WsService; +import io.dataease.xpack.permissions.user.manage.UserPageManage; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; @@ -60,6 +66,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; @@ -100,8 +107,13 @@ public class ExportCenterManage implements BaseExportApi { private int core; @Value("${dataease.export.max.size:10}") private int max; + @Resource + private VisualizationWatermarkMapper watermarkMapper; + @Resource(name = "userPageManage") + private UserPageManage userPageManage; + private final static String DATA_URL_TITLE = "data:image/jpeg;base64,"; private static final String exportData_path = "/opt/dataease2.0/data/exportData/"; @@ -644,7 +656,7 @@ public class ExportCenterManage implements BaseExportApi { try { exportTask.setExportStatus("IN_PROGRESS"); exportTaskMapper.updateById(exportTask); - Workbook wb = new SXSSFWorkbook(); + XSSFWorkbook wb = new XSSFWorkbook(); CellStyle cellStyle = wb.createCellStyle(); Font font = wb.createFont(); font.setFontHeightInPoints((short) 12); @@ -683,12 +695,22 @@ public class ExportCenterManage implements BaseExportApi { } else { downloadNotTableInfoData(request, wb); } - + VisualizationWatermark watermark = watermarkMapper.selectById("system_default"); + WatermarkContentDTO watermarkContent = JsonUtil.parseObject(watermark.getSettingContent(), WatermarkContentDTO.class); + if (watermarkContent.getExcelEnable()) { + UserFormVO userInfo = userPageManage.queryForm(AuthUtils.getUser().getUserId()); + // 在主逻辑中添加水印 + int watermarkPictureIdx = ExcelWatermarkUtils.addWatermarkImage(wb, watermarkContent,userInfo); // 生成水印图片并获取 ID + for (Sheet sheet : wb) { + ExcelWatermarkUtils.addWatermarkToSheet(sheet, wb, watermarkPictureIdx); // 为每个 Sheet 添加水印 + } + } + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(wb); try (FileOutputStream outputStream = new FileOutputStream(dataPath + "/" + exportTask.getId() + ".xlsx")) { - wb.write(outputStream); + sxssfWorkbook.write(outputStream); outputStream.flush(); } - wb.close(); + sxssfWorkbook.close(); exportTask.setExportProgress("100"); exportTask.setExportStatus("SUCCESS"); setFileSize(dataPath + "/" + exportTask.getId() + ".xlsx", exportTask); diff --git a/core/core-backend/src/main/java/io/dataease/visualization/dto/WatermarkContentDTO.java b/core/core-backend/src/main/java/io/dataease/visualization/dto/WatermarkContentDTO.java new file mode 100644 index 0000000000..091b4d8e78 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/visualization/dto/WatermarkContentDTO.java @@ -0,0 +1,26 @@ +package io.dataease.visualization.dto; + +import lombok.Data; + +@Data +public class WatermarkContentDTO { + + private Boolean enable; + + private Boolean excelEnable = false; + + private Boolean enablePanelCustom; + + private String type; + + private String content; + + private String watermark_color; + + private Integer watermark_x_space; + + private Integer watermark_y_space; + + private Integer watermark_fontsize; + +}