Browse Source

集成邮件、stimulsoft生成

liyuan 3 weeks ago
commit
48a8b93b68

+ 38 - 0
.gitignore

@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 85 - 0
pom.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.echepei</groupId>
+    <artifactId>common-utils</artifactId>
+    <version>1.0</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.36</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>jakarta.mail</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>jakarta.activation</groupId>
+            <artifactId>jakarta.activation-api</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <version>5.3.39</version>
+        </dependency>
+
+
+        <!--   stimulsoft 依赖     -->
+        <dependency>
+            <groupId>com.stimulsoft</groupId>
+            <artifactId>stimulsoft-reports-report</artifactId>
+            <version>2025.1.6</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.xmlgraphics</groupId>
+                    <artifactId>batik-all</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.apache.xmlgraphics</groupId>
+            <artifactId>batik-all</artifactId>
+            <version>1.17</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-io</groupId>
+                    <artifactId>commons-io</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.14.0</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.5.18</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 57 - 0
src/main/java/com/echepei/dto/mail/MailDto.java

@@ -0,0 +1,57 @@
+package com.echepei.dto.mail;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Rain
+ */
+@Data
+public class MailDto {
+
+
+    /**
+     * 邮件类型 管理 mail type枚举
+     */
+    private Integer mailType;
+
+    /**
+     * 邮件用户名
+     */
+    private String mailUserName;
+
+    /**
+     * 邮箱授权码,非登录密码
+     */
+    private String mailPassword;
+
+    /**
+     * 邮件发送人
+     */
+    private String mailFrom;
+
+    /**
+     * 邮件接收人
+     */
+    private String mailTo;
+
+    /**
+     * 邮件标题
+     */
+    private String mailTitle;
+
+    /**
+     * 邮件内容
+     */
+    private String mailContent;
+
+
+    /**
+     * 邮件附件路径
+     */
+    private List<String> mailFilePath;
+
+
+
+}

+ 34 - 0
src/main/java/com/echepei/dto/stimulsoft/StimulsoftPdfDto.java

@@ -0,0 +1,34 @@
+package com.echepei.dto.stimulsoft;
+
+import lombok.Data;
+
+/**
+ * @author Rain
+ */
+@Data
+public class StimulsoftPdfDto {
+
+
+
+    /**
+     * 报表保存路径
+     */
+    private String saveFilePath;
+
+    /**
+     * 模板mrt地址
+     */
+    private String templatePath;
+
+    /**
+     * 模板内容
+     */
+    private String reportContent;
+
+    /**
+     *  报表名称,需要与模板中的名称一致
+     */
+    private String reportName;
+
+
+}

+ 56 - 0
src/main/java/com/echepei/enums/MailTypeEnum.java

@@ -0,0 +1,56 @@
+package com.echepei.enums;
+
+/**
+ * @author Rain
+ */
+
+public enum MailTypeEnum {
+
+
+    /**
+     * QQ
+     */
+    QQ(100, "QQ邮箱", "smtp.qq.com", 587),
+
+    /**
+     * 网易
+     */
+    NET_EASE(200, "网易邮箱", "smtp.163.com", 994);
+
+    /**
+     * 邮箱类型
+     */
+    public final Integer mailType;
+
+    /**
+     * 邮箱类型名称
+     */
+    public final String mailTypeName;
+
+    /**
+     * 邮箱smtp主机
+     */
+    public final String mailSmtpHost;
+
+    /**
+     * 邮箱smtp端口
+     */
+    public final Integer mailSmtpPort;
+
+
+    MailTypeEnum(Integer mailType, String mailTypeName, String mailSmtpHost, Integer mailSmtpPort) {
+        this.mailType = mailType;
+        this.mailTypeName = mailTypeName;
+        this.mailSmtpHost = mailSmtpHost;
+        this.mailSmtpPort = mailSmtpPort;
+    }
+
+    public static MailTypeEnum getMailTypeEnum(Integer mailType) {
+        for (MailTypeEnum mailTypeEnum : MailTypeEnum.values()) {
+            if (mailTypeEnum.mailType.equals(mailType)) {
+                return mailTypeEnum;
+            }
+        }
+        return null;
+    }
+}

+ 47 - 0
src/main/java/com/echepei/utils/mail/MailUtil.java

@@ -0,0 +1,47 @@
+package com.echepei.utils.mail;
+
+import com.echepei.dto.mail.MailDto;
+import com.echepei.enums.MailTypeEnum;
+import jakarta.mail.Authenticator;
+import jakarta.mail.PasswordAuthentication;
+import jakarta.mail.Session;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author Rain
+ */
+public class MailUtil {
+
+
+    private static final Map<String, Session> SESSION_MAP = new HashMap<>(20);
+
+
+    public static Session getSession(MailDto mailDto) {
+        // 如果存在则直接返回
+        if (SESSION_MAP.containsKey(mailDto.getMailFrom())) {
+            return SESSION_MAP.get(mailDto.getMailFrom());
+        }
+        MailTypeEnum mailTypeEnum = MailTypeEnum.getMailTypeEnum(mailDto.getMailType());
+        if (mailTypeEnum == null) {
+            throw new RuntimeException("邮箱类型错误");
+        }
+        Properties properties = new Properties();
+        properties.put("mail.smtp.host", mailTypeEnum.mailSmtpHost);
+        properties.put("mail.smtp.port", mailTypeEnum.mailSmtpPort);
+        properties.put("mail.smtp.auth", "true");
+        properties.put("mail.smtp.starttls.enable", "true");
+        Session session = Session.getInstance(properties, new Authenticator() {
+            @Override
+            protected PasswordAuthentication getPasswordAuthentication() {
+                return new PasswordAuthentication(mailDto.getMailUserName(), mailDto.getMailPassword());
+            }
+        });
+        SESSION_MAP.put(mailDto.getMailFrom(), session);
+        return session;
+    }
+
+
+}

+ 124 - 0
src/main/java/com/echepei/utils/mail/SendMailUtil.java

@@ -0,0 +1,124 @@
+package com.echepei.utils.mail;
+
+import com.echepei.dto.mail.MailDto;
+import jakarta.activation.DataHandler;
+import jakarta.activation.DataSource;
+import jakarta.activation.FileDataSource;
+import jakarta.mail.*;
+import jakarta.mail.internet.InternetAddress;
+import jakarta.mail.internet.MimeBodyPart;
+import jakarta.mail.internet.MimeMessage;
+import jakarta.mail.internet.MimeMultipart;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.File;
+
+/**
+ * @author Rain
+ */
+public class SendMailUtil {
+
+    /**
+     * 发送邮件(通用方法)
+     *
+     * @param mailDto        邮件信息
+     * @param contentType    邮件内容类型(如 "text/plain" 或 "text/html")
+     * @param hasAttachments 是否包含附件
+     */
+    private static void sendMail(MailDto mailDto, String contentType, boolean hasAttachments) {
+        try {
+            Session session = MailUtil.getSession(mailDto);
+            MimeMessage message = createMimeMessage(session, mailDto, contentType, hasAttachments);
+            Transport.send(message);
+        } catch (MessagingException e) {
+            handleMessagingException(e);
+        }
+    }
+
+    /**
+     * 创建 MimeMessage
+     */
+    private static MimeMessage createMimeMessage(Session session, MailDto mailDto, String contentType, boolean hasAttachments) throws MessagingException {
+        MimeMessage message = new MimeMessage(session);
+        message.setSubject(mailDto.getMailTitle());
+        message.setFrom(new InternetAddress(mailDto.getMailFrom()));
+        message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(mailDto.getMailTo().trim()));
+        Multipart multipart = new MimeMultipart();
+        // 添加邮件正文
+        MimeBodyPart textPart = new MimeBodyPart();
+        textPart.setContent(StringUtils.hasText(mailDto.getMailContent()) ? mailDto.getMailContent() : "", contentType);
+        multipart.addBodyPart(textPart);
+        // 添加附件
+        if (hasAttachments && !CollectionUtils.isEmpty(mailDto.getMailFilePath())) {
+            for (String filePath : mailDto.getMailFilePath()) {
+                multipart.addBodyPart(createAttachmentBodyPart(filePath));
+            }
+        }
+        message.setContent(multipart);
+        return message;
+    }
+
+    /**
+     * 创建附件 BodyPart
+     */
+    private static MimeBodyPart createAttachmentBodyPart(String filePath) throws MessagingException {
+        MimeBodyPart filePart = new MimeBodyPart();
+        DataSource source = new FileDataSource(filePath);
+        filePart.setFileName(new File(filePath).getName());
+        filePart.setDataHandler(new DataHandler(source));
+        return filePart;
+    }
+
+    /**
+     * 处理 MessagingException
+     */
+    private static void handleMessagingException(MessagingException e) {
+        if (e.getMessage().contains("554 Reject by")) {
+            throw new RuntimeException("发信行为被反垃圾拦截,投递失败。");
+        }
+        throw new RuntimeException(e);
+    }
+
+    /**
+     * 发送纯文本邮件
+     */
+    public static void sendTextMail(MailDto mailDto) {
+        sendMail(mailDto, "text/plain", false);
+    }
+
+    /**
+     * 发送 HTML 邮件
+     */
+    public static void sendHtmlMail(MailDto mailDto) {
+        sendMail(mailDto, "text/html;charset=utf-8", false);
+    }
+
+    /**
+     * 发送带附件的邮件
+     */
+    public static void sendFileMail(MailDto mailDto) {
+        if (CollectionUtils.isEmpty(mailDto.getMailFilePath())) {
+            throw new RuntimeException("邮件附件为空");
+        }
+        sendMail(mailDto, "text/plain", true);
+    }
+
+    /**
+     * 发送带附件的纯文本邮件
+     */
+    public static void sendTextFileMail(MailDto mailDto) {
+        sendFileMail(mailDto);
+    }
+
+    /**
+     * 发送带附件的 HTML 邮件
+     */
+    public static void sendHtmlFileMail(MailDto mailDto) {
+        if (CollectionUtils.isEmpty(mailDto.getMailFilePath())) {
+            throw new RuntimeException("邮件附件为空");
+        }
+        sendMail(mailDto, "text/html;charset=utf-8", true);
+    }
+
+}

+ 51 - 0
src/main/java/com/echepei/utils/stimulsoft/StimulsoftUtil.java

@@ -0,0 +1,51 @@
+package com.echepei.utils.stimulsoft;
+
+
+import com.echepei.dto.stimulsoft.StimulsoftPdfDto;
+import com.stimulsoft.base.licenses.StiLicense;
+import com.stimulsoft.report.StiExportManager;
+import com.stimulsoft.report.StiReport;
+import com.stimulsoft.report.StiSerializeManager;
+import com.stimulsoft.report.dictionary.databases.StiJsonDatabase;
+import com.stimulsoft.report.export.settings.StiPdfExportSettings;
+import com.stimulsoft.report.export.tools.pdf.StiPdfImageCompressionMethod;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * @author Rain
+ */
+public class StimulsoftUtil {
+
+
+    static {
+        StiLicense.loadFromString("6vJhGtLLLz2GNviWmUTrhSqnOItdDwjBylQzQcAOiHn0s4gy0Fr5YoUZ9V00Y0igCSFQzwEqYBh/N77k4f0fWXTHW5rqeBNLkaurJDenJ9o97TyqHs9HfvINK18Uwzsc/bG01Rq+x3H3Rf+g7AY92gvWmp7VA2Uxa30Q97f61siWz2dE5kdBVcCnSFzC6awE74JzDcJMj8OuxplqB1CYcpoPcOjKy1PiATlC3UsBaLEXsok1xxtRMQ283r282tkh8XQitsxtTczAJBxijuJNfziYhci2jResWXK51ygOOEbVAxmpflujkJ8oEVHkOA/CjX6bGx05pNZ6oSIu9H8deF94MyqIwcdeirCe60GbIQByQtLimfxbIZnO35X3fs/94av0ODfELqrQEpLrpU6FNeHttvlMc5UVrT4K+8lPbqR8Hq0PFWmFrbVIYSi7tAVFMMe2D1C59NWyLu3AkrD3No7YhLVh7LV0Tttr/8FrcZ8xirBPcMZCIGrRIesrHxOsZH2V8t/t0GXCnLLAWX+TNvdNXkB8cF2y9ZXf1enI064yE5dwMs2fQ0yOUG/xornE");
+    }
+
+
+    public static void createPdfByMrt(StimulsoftPdfDto stimulsoftPdfDto) {
+        File targetFile = new File(stimulsoftPdfDto.getSaveFilePath());
+        try (OutputStream os = Files.newOutputStream(targetFile.toPath())) {
+            InputStream ins = Files.newInputStream(Paths.get(stimulsoftPdfDto.getTemplatePath()));
+            StiReport report = StiSerializeManager.deserializeReport(ins);
+            // 注意模版中的字体支持中文,这里模版的字体是微软雅黑
+            report.setCulture("zh-CN");
+            StiJsonDatabase jsonDatabase = new StiJsonDatabase(stimulsoftPdfDto.getReportName());
+            jsonDatabase.setJsonData(stimulsoftPdfDto.getReportContent());
+            report.getDictionary().getDatabases().clear();
+            report.getDictionary().getDatabases().add(jsonDatabase);
+            report.render();
+            StiPdfExportSettings pdfSettings = new StiPdfExportSettings();
+            pdfSettings.setImageCompressionMethod(StiPdfImageCompressionMethod.Flate);
+            pdfSettings.setEmbeddedFonts(true);
+            StiExportManager.exportPdf(report, pdfSettings, os);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}