内嵌查询代替LEFT JOIN导致的重复问题以及修改邮件不能发送图片和附件的问题

This commit is contained in:
2025-07-12 15:54:13 +08:00
parent 20edf904bc
commit 43642eeb4d
8 changed files with 559 additions and 74 deletions

View File

@@ -0,0 +1,84 @@
package com.ruoyi.oa.config;
import com.ruoyi.oa.domain.OaEmailAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import java.util.Properties;
/**
* 动态邮件配置类
* 支持根据不同的邮箱账号动态配置邮件发送器
*
* @author ruoyi
*/
@Configuration
public class DynamicMailConfig {
/**
* 根据邮箱账号创建JavaMailSender
*
* @param account 邮箱账号信息
* @return JavaMailSender
*/
public JavaMailSender createMailSender(OaEmailAccount account) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
// 设置SMTP服务器
mailSender.setHost(account.getSmtpHost());
mailSender.setPort(account.getSmtpPort() == null ? 465 : account.getSmtpPort().intValue());
mailSender.setUsername(account.getEmail());
mailSender.setPassword(account.getPassword());
// 设置邮件属性
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.connectiontimeout", "10000");
props.put("mail.smtp.timeout", "10000");
props.put("mail.smtp.writetimeout", "10000");
props.put("mail.debug", "false");
return mailSender;
}
/**
* 根据邮箱类型获取默认配置
*
* @param type 邮箱类型 0=网易 1=QQ 2=阿里云 3=飞书
* @return 默认配置
*/
public OaEmailAccount getDefaultConfig(int type) {
OaEmailAccount config = new OaEmailAccount();
switch (type) {
case 0: // 网易邮箱
config.setSmtpHost("smtp.163.com");
config.setSmtpPort(465L);
break;
case 1: // QQ邮箱
config.setSmtpHost("smtp.qq.com");
config.setSmtpPort(465L);
break;
case 2: // 阿里云邮箱
config.setSmtpHost("smtp.aliyun.com");
config.setSmtpPort(465L);
break;
case 3: // 飞书邮箱
config.setSmtpHost("smtp.feishu.cn");
config.setSmtpPort(465L);
break;
default:
config.setSmtpHost("smtp.163.com");
config.setSmtpPort(465L);
break;
}
return config;
}
}

View File

@@ -16,4 +16,8 @@ public class EmailSendRequest {
private String subject;
/** 邮件正文 */
private String content;
/** 附件文件路径列表 */
private List<String> attachmentPaths;
/** 内嵌图片路径列表 */
private List<String> inlineImagePaths;
}

View File

@@ -7,6 +7,7 @@ import com.ruoyi.oa.domain.SysOaTask;
import com.ruoyi.oa.domain.UserMonthlyData;
import com.ruoyi.oa.domain.bo.SysOaTaskBo;
import com.ruoyi.oa.domain.vo.SysOaTaskVo;
import com.ruoyi.oa.domain.vo.SysOaTaskItemVo;
import com.ruoyi.common.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
@@ -50,4 +51,9 @@ public interface SysOaTaskMapper extends BaseMapperPlus<SysOaTaskMapper, SysOaTa
*/
List<SysOaTaskVo> queryListPlus(SysOaTaskBo bo);
/**
* 嵌套查询根据任务ID获取报工单元列表
*/
List<SysOaTaskItemVo> selectTaskItemsByTaskId(Long taskId);
}

View File

@@ -26,8 +26,8 @@ import com.ruoyi.oa.domain.OaEmailAccount;
import com.ruoyi.oa.mapper.OaEmailAccountMapper;
import com.ruoyi.oa.service.IOaEmailAccountService;
import com.ruoyi.oa.domain.request.EmailSendRequest;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import com.ruoyi.oa.utils.EmailUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@@ -45,6 +45,9 @@ import java.util.Collection;
public class OaEmailAccountServiceImpl implements IOaEmailAccountService {
private final OaEmailAccountMapper baseMapper;
@Autowired
private EmailUtil emailUtil;
/**
* 查询发件人邮箱账号管理
@@ -138,26 +141,60 @@ public class OaEmailAccountServiceImpl implements IOaEmailAccountService {
int success = 0, fail = 0;
StringBuilder failList = new StringBuilder();
if (type == 0 || type == 1) {
// 网易/QQ邮箱 SMTP方式(已实现)
MailAccount mailAccount = new MailAccount();
mailAccount.setHost(account.getSmtpHost());
mailAccount.setPort(account.getSmtpPort() == null ? 465 : account.getSmtpPort().intValue());
mailAccount.setAuth(true);
mailAccount.setFrom(account.getEmail());
mailAccount.setUser(account.getEmail());
mailAccount.setPass(account.getPassword());
mailAccount.setSslEnable(true);
if (type == 0 || type == 1) {
// 网易/QQ邮箱 SMTP方式
for (String to : request.getToList()) {
try {
MailUtil.send(mailAccount, to, request.getSubject(), request.getContent(), false);
// 判断是否为HTML内容
boolean isHtml = request.getContent() != null &&
(request.getContent().contains("<html") ||
request.getContent().contains("<div") ||
request.getContent().contains("<p>"));
// 检查是否有附件
boolean hasAttachments = request.getAttachmentPaths() != null && !request.getAttachmentPaths().isEmpty();
// 检查是否有内嵌图片
boolean hasInlineImages = request.getInlineImagePaths() != null && !request.getInlineImagePaths().isEmpty();
if (hasAttachments && hasInlineImages) {
// 既有附件又有内嵌图片
if (isHtml) {
String processedContent = emailUtil.processHtmlWithInlineImages(request.getContent(), request.getInlineImagePaths());
emailUtil.sendHtmlMailWithAttachmentsAndDynamicConfig(account, to, request.getSubject(), processedContent, request.getAttachmentPaths());
} else {
// 纯文本不支持内嵌图片,只发送附件
emailUtil.sendMailWithAttachmentAndDynamicConfig(account, to, request.getSubject(), request.getContent(), request.getAttachmentPaths().get(0));
}
} else if (hasAttachments) {
// 只有附件
if (isHtml) {
emailUtil.sendHtmlMailWithAttachmentsAndDynamicConfig(account, to, request.getSubject(), request.getContent(), request.getAttachmentPaths());
} else {
emailUtil.sendMailWithAttachmentAndDynamicConfig(account, to, request.getSubject(), request.getContent(), request.getAttachmentPaths().get(0));
}
} else if (hasInlineImages) {
// 只有内嵌图片
if (isHtml) {
emailUtil.sendHtmlMailWithInlineImagesAndDynamicConfig(account, to, request.getSubject(), request.getContent(), request.getInlineImagePaths());
} else {
// 纯文本不支持内嵌图片
emailUtil.sendMailWithDynamicConfig(account, to, request.getSubject(), request.getContent());
}
} else {
// 无附件无内嵌图片
if (isHtml) {
emailUtil.sendHtmlMailWithDynamicConfig(account, to, request.getSubject(), request.getContent());
} else {
emailUtil.sendMailWithDynamicConfig(account, to, request.getSubject(), request.getContent());
}
}
success++;
} catch (Exception e) {
fail++;
failList.append(to).append(", ");
}
}
}
}
else if (type == 2) {
// 阿里云邮件推送API
for (String to : request.getToList()) {

View File

@@ -0,0 +1,358 @@
package com.ruoyi.oa.utils;
import com.ruoyi.oa.domain.OaEmailAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import com.ruoyi.oa.config.DynamicMailConfig;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.List;
/**
* 邮件发送工具类
*
* @author ruoyi
*/
@Component
public class EmailUtil {
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private DynamicMailConfig dynamicMailConfig;
/**
* 发送纯文本邮件
*
* @param from 发件人邮箱
* @param to 收件人邮箱
* @param subject 邮件主题
* @param text 纯文本内容
*/
public void sendMail(String from, String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(text);
javaMailSender.send(message);
}
/**
* 使用动态配置发送纯文本邮件
*
* @param account 邮箱账号信息
* @param to 收件人邮箱
* @param subject 邮件主题
* @param text 纯文本内容
*/
public void sendMailWithDynamicConfig(OaEmailAccount account, String to, String subject, String text) {
JavaMailSender dynamicMailSender = dynamicMailConfig.createMailSender(account);
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(account.getEmail());
message.setTo(to);
message.setSubject(subject);
message.setText(text);
dynamicMailSender.send(message);
}
/**
* 发送纯文本邮件(批量)
*
* @param from 发件人邮箱
* @param toList 收件人邮箱列表
* @param subject 邮件主题
* @param text 纯文本内容
*/
public void sendMailBatch(String from, List<String> toList, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(toList.toArray(new String[0]));
message.setSubject(subject);
message.setText(text);
javaMailSender.send(message);
}
/**
* 发送富文本邮件
*
* @param from 发件人邮箱
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
*/
public void sendHtmlMail(String from, String to, String subject, String htmlContent) throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true); // 第二个参数true表示这是HTML内容
javaMailSender.send(mimeMessage);
}
/**
* 使用动态配置发送富文本邮件
*
* @param account 邮箱账号信息
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
*/
public void sendHtmlMailWithDynamicConfig(com.ruoyi.oa.domain.OaEmailAccount account, String to, String subject, String htmlContent) throws MessagingException {
JavaMailSender dynamicMailSender = dynamicMailConfig.createMailSender(account);
MimeMessage mimeMessage = dynamicMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(account.getEmail());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
dynamicMailSender.send(mimeMessage);
}
/**
* 发送富文本邮件(批量)
*
* @param from 发件人邮箱
* @param toList 收件人邮箱列表
* @param subject 邮件主题
* @param htmlContent HTML内容
*/
public void sendHtmlMailBatch(String from, List<String> toList, String subject, String htmlContent) throws MessagingException {
for (String to : toList) {
sendHtmlMail(from, to, subject, htmlContent);
}
}
/**
* 发送带附件的邮件
*
* @param from 发件人邮箱
* @param to 收件人邮箱
* @param subject 邮件主题
* @param text 邮件内容
* @param filePath 附件文件路径
*/
public void sendMailWithAttachment(String from, String to, String subject, String text, String filePath) throws MessagingException {
File attachment = new File(filePath);
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text);
helper.addAttachment(attachment.getName(), attachment);
javaMailSender.send(mimeMessage);
}
/**
* 使用动态配置发送带附件的邮件
*
* @param account 邮箱账号信息
* @param to 收件人邮箱
* @param subject 邮件主题
* @param text 邮件内容
* @param filePath 附件文件路径
*/
public void sendMailWithAttachmentAndDynamicConfig(com.ruoyi.oa.domain.OaEmailAccount account, String to, String subject, String text, String filePath) throws MessagingException {
JavaMailSender dynamicMailSender = dynamicMailConfig.createMailSender(account);
File attachment = new File(filePath);
MimeMessage mimeMessage = dynamicMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(account.getEmail());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text);
helper.addAttachment(attachment.getName(), attachment);
dynamicMailSender.send(mimeMessage);
}
/**
* 发送带附件的富文本邮件
*
* @param from 发件人邮箱
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
* @param filePath 附件文件路径
*/
public void sendHtmlMailWithAttachment(String from, String to, String subject, String htmlContent, String filePath) throws MessagingException {
File attachment = new File(filePath);
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
helper.addAttachment(attachment.getName(), attachment);
javaMailSender.send(mimeMessage);
}
/**
* 使用动态配置发送带附件的富文本邮件
*
* @param account 邮箱账号信息
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
* @param filePath 附件文件路径
*/
public void sendHtmlMailWithAttachmentAndDynamicConfig(com.ruoyi.oa.domain.OaEmailAccount account, String to, String subject, String htmlContent, String filePath) throws MessagingException {
JavaMailSender dynamicMailSender = dynamicMailConfig.createMailSender(account);
File attachment = new File(filePath);
MimeMessage mimeMessage = dynamicMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(account.getEmail());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
helper.addAttachment(attachment.getName(), attachment);
dynamicMailSender.send(mimeMessage);
}
/**
* 发送带多个附件的邮件
*
* @param from 发件人邮箱
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
* @param filePaths 附件文件路径列表
*/
public void sendHtmlMailWithAttachments(String from, String to, String subject, String htmlContent, List<String> filePaths) throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
for (String filePath : filePaths) {
File attachment = new File(filePath);
if (attachment.exists()) {
helper.addAttachment(attachment.getName(), attachment);
}
}
javaMailSender.send(mimeMessage);
}
/**
* 使用动态配置发送带多个附件的邮件
*
* @param account 邮箱账号信息
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
* @param filePaths 附件文件路径列表
*/
public void sendHtmlMailWithAttachmentsAndDynamicConfig(com.ruoyi.oa.domain.OaEmailAccount account, String to, String subject, String htmlContent, List<String> filePaths) throws MessagingException {
JavaMailSender dynamicMailSender = dynamicMailConfig.createMailSender(account);
MimeMessage mimeMessage = dynamicMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(account.getEmail());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
for (String filePath : filePaths) {
File attachment = new File(filePath);
if (attachment.exists()) {
helper.addAttachment(attachment.getName(), attachment);
}
}
dynamicMailSender.send(mimeMessage);
}
/**
* 发送带内嵌图片的邮件
*
* @param from 发件人邮箱
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容包含cid:xxx的图片引用
* @param imagePaths 内嵌图片路径列表
*/
public void sendHtmlMailWithInlineImages(String from, String to, String subject, String htmlContent, List<String> imagePaths) throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
for (int i = 0; i < imagePaths.size(); i++) {
String imagePath = imagePaths.get(i);
File imageFile = new File(imagePath);
if (imageFile.exists()) {
String cid = "image" + i;
helper.addInline(cid, imageFile);
}
}
javaMailSender.send(mimeMessage);
}
/**
* 使用动态配置发送带内嵌图片的邮件
*
* @param account 邮箱账号信息
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容包含cid:xxx的图片引用
* @param imagePaths 内嵌图片路径列表
*/
public void sendHtmlMailWithInlineImagesAndDynamicConfig(OaEmailAccount account, String to, String subject, String htmlContent, List<String> imagePaths) throws MessagingException {
JavaMailSender dynamicMailSender = dynamicMailConfig.createMailSender(account);
MimeMessage mimeMessage = dynamicMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(account.getEmail());
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
for (int i = 0; i < imagePaths.size(); i++) {
String imagePath = imagePaths.get(i);
File imageFile = new File(imagePath);
if (imageFile.exists()) {
String cid = "image" + i;
helper.addInline(cid, imageFile);
}
}
dynamicMailSender.send(mimeMessage);
}
/**
* 处理HTML内容中的图片将外链图片转为内嵌
*
* @param htmlContent 原始HTML内容
* @param imagePaths 图片路径列表
* @return 处理后的HTML内容
*/
public String processHtmlWithInlineImages(String htmlContent, List<String> imagePaths) {
if (imagePaths == null || imagePaths.isEmpty()) {
return htmlContent;
}
String processedHtml = htmlContent;
for (int i = 0; i < imagePaths.size(); i++) {
String imagePath = imagePaths.get(i);
String cid = "image" + i;
// 这里可以根据需要替换图片URL为cid引用
// 例如:将 <img src="path/to/image.jpg"> 替换为 <img src="cid:image0">
processedHtml = processedHtml.replace("image" + i + ".jpg", "cid:" + cid);
processedHtml = processedHtml.replace("image" + i + ".png", "cid:" + cid);
}
return processedHtml;
}
}