|
|
@@ -744,155 +744,177 @@ public class ResumesServiceImpl extends ServiceImpl<ResumesDao, Resumes> impleme
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
+ * 发送简历邮件
|
|
|
+ * @param resumesId 简历ID
|
|
|
+ * @param to 收件人邮箱
|
|
|
+ * @param resumesName 简历名称
|
|
|
+ * @param resumesAttachmentId 简历附件ID
|
|
|
+ */
|
|
|
public void sendEmail(Long resumesId, String to, String resumesName, Long resumesAttachmentId) {
|
|
|
- // 入参非空校验
|
|
|
-// if (StringUtils.isEmpty(to) || StringUtils.isEmpty(resumesName)) {
|
|
|
-// log.error("邮件发送入参异常:收件人/简历名称为空,to={}, resumesName={}", to, resumesName);
|
|
|
-// return;
|
|
|
-// }
|
|
|
+ // to = "18064109218@163.com";
|
|
|
+ // 1. 入参校验优化
|
|
|
+ if (StringUtils.isEmpty(to)) {
|
|
|
+ log.error("邮件发送失败:收件人邮箱为空,resumesId={}", resumesId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // resumesName = resumesName + ".pdf";
|
|
|
+ if (StringUtils.isEmpty(resumesName)) {
|
|
|
+ log.error("邮件发送失败:简历名称为空,resumesId={}, to={}", resumesId, to);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!to.matches("^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$")) {
|
|
|
+ log.error("邮件发送失败:收件人邮箱格式不正确,resumesId={}, to={}", resumesId, to);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
-// to="18064109218@163.com";
|
|
|
- // 1. 读取配置(保留原有逻辑)
|
|
|
+ // 2. 读取配置
|
|
|
String from = commonInfoService.findOne(308).getValue();
|
|
|
String authCode = commonInfoService.findOne(309).getValue();
|
|
|
String host = commonInfoService.findOne(427).getValue();
|
|
|
|
|
|
- // 配置非空校验
|
|
|
+ // 配置校验
|
|
|
if (StringUtils.isEmpty(from) || StringUtils.isEmpty(authCode)) {
|
|
|
- log.error("发件人邮箱/授权码配置为空,from={}, authCode={}", from, authCode);
|
|
|
+ log.error("邮件发送失败:发件人邮箱/授权码配置为空,from={}", from);
|
|
|
return;
|
|
|
}
|
|
|
if (StringUtils.isEmpty(host)) {
|
|
|
- host = "smtp.163.com"; // 兜底默认值
|
|
|
+ host = "smtp.163.com"; // 默认值
|
|
|
log.warn("邮件服务器配置为空,使用默认值:smtp.163.com");
|
|
|
}
|
|
|
|
|
|
- // 2. 核心配置修复(解决465端口连接问题)
|
|
|
+ // 3. 邮件配置优化
|
|
|
Properties properties = new Properties();
|
|
|
properties.setProperty("mail.smtp.host", host);
|
|
|
properties.setProperty("mail.smtp.port", "465");
|
|
|
properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
|
|
|
- properties.setProperty("mail.smtp.socketFactory.port", "465"); // 新增:绑定SocketFactory端口
|
|
|
- properties.setProperty("mail.smtp.socketFactory.fallback", "false"); // 新增:禁止降级非SSL
|
|
|
- properties.setProperty("mail.smtp.ssl.enable", "true"); // 新增:强制开启SSL(解决isSSL false)
|
|
|
+ properties.setProperty("mail.smtp.socketFactory.port", "465");
|
|
|
+ properties.setProperty("mail.smtp.socketFactory.fallback", "false");
|
|
|
+ properties.setProperty("mail.smtp.ssl.enable", "true");
|
|
|
properties.setProperty("mail.transport.protocol", "smtp");
|
|
|
- properties.setProperty("mail.debug", "true");
|
|
|
+ properties.setProperty("mail.debug", "true"); // 生产环境关闭调试
|
|
|
properties.setProperty("mail.smtp.auth", "true");
|
|
|
- properties.setProperty("mail.mime.charset", "UTF-8"); // 新增:防止中文乱码
|
|
|
+ properties.setProperty("mail.mime.charset", "UTF-8");
|
|
|
|
|
|
- // 3. 创建Session(修复授权码重复读取问题)
|
|
|
- Session session = Session.getDefaultInstance(properties, new Authenticator() {
|
|
|
+ // 4. Session创建优化:使用getInstance提高线程安全性
|
|
|
+ Session session = Session.getInstance(properties, new Authenticator() {
|
|
|
@Override
|
|
|
public PasswordAuthentication getPasswordAuthentication() {
|
|
|
- return new PasswordAuthentication(from, authCode); // 直接用已读取的authCode,避免重复查库
|
|
|
+ return new PasswordAuthentication(from, authCode);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
try {
|
|
|
- // 4. 构建邮件(保留原有逻辑,仅修复附件处理)
|
|
|
+ // 5. 构建邮件
|
|
|
MimeMessage message = new MimeMessage(session);
|
|
|
|
|
|
- // 设置发件人(保留原有逻辑)
|
|
|
+ // 设置发件人
|
|
|
InternetAddress fromMail = new InternetAddress(from);
|
|
|
- String name = commonInfoService.findOne(12).getValue();
|
|
|
- fromMail.setPersonal(MimeUtility.encodeText((StringUtils.isEmpty(name) ? "系统管理员" : name) + "<" + from + ">"));
|
|
|
+ String senderName = commonInfoService.findOne(12).getValue();
|
|
|
+ fromMail.setPersonal(MimeUtility.encodeText((StringUtils.isEmpty(senderName) ? "系统管理员" : senderName) + "<" + from + ">"));
|
|
|
message.setFrom(fromMail);
|
|
|
|
|
|
- // 设置收件人(保留原有逻辑)
|
|
|
- InternetAddress toMail = new InternetAddress(to);
|
|
|
- Address[] allRecipients = {toMail};
|
|
|
- message.setRecipients(Message.RecipientType.TO, allRecipients);
|
|
|
+ // 设置收件人(优化:使用单个收件人方法)
|
|
|
+ message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
|
|
|
|
|
|
- // 设置主题(保留原有逻辑)
|
|
|
+ // 设置主题和发送时间
|
|
|
message.setSubject("收到一封投递简历:" + resumesName + ",请您查收!");
|
|
|
message.setSentDate(new Date());
|
|
|
|
|
|
- // 混合内容(保留原有逻辑)
|
|
|
+ // 混合内容
|
|
|
MimeMultipart msgMultipart = new MimeMultipart("mixed");
|
|
|
message.setContent(msgMultipart);
|
|
|
|
|
|
- // 文本内容(保留原有逻辑)
|
|
|
+ // 文本内容
|
|
|
MimeBodyPart htmlPart = new MimeBodyPart();
|
|
|
htmlPart.setContent("收到一封简历投递:" + resumesName + ",请您查收!", "text/html;charset=UTF-8");
|
|
|
msgMultipart.addBodyPart(htmlPart);
|
|
|
|
|
|
- // 5. 附件处理(核心修复:网络URL转本地文件)
|
|
|
+ // 6. 附件处理优化
|
|
|
if (resumesAttachmentId != null) {
|
|
|
ResumesAttachment resumesAttachment = resumesAttachmentService.getById(resumesAttachmentId);
|
|
|
if (resumesAttachment != null) {
|
|
|
String url = resumesAttachment.getAttachmentAddress();
|
|
|
if (StringUtils.isNotEmpty(url)) {
|
|
|
- // 修复:网络URL先下载到本地临时文件
|
|
|
- File localFile = downloadUrlToLocal(url);
|
|
|
- if (localFile != null && localFile.exists()) {
|
|
|
+ try {
|
|
|
+ // 直接使用URLDataSource,避免本地下载
|
|
|
+ URL attachmentUrl = new URL(url);
|
|
|
MimeBodyPart filePart = new MimeBodyPart();
|
|
|
- FileDataSource file_datasource = new FileDataSource(localFile);
|
|
|
- DataHandler dh = new DataHandler(file_datasource);
|
|
|
+ URLDataSource dataSource = new URLDataSource(attachmentUrl);
|
|
|
+ DataHandler dh = new DataHandler(dataSource);
|
|
|
filePart.setDataHandler(dh);
|
|
|
- // 附件名编码(保留原有逻辑)
|
|
|
- filePart.setFileName(MimeUtility.encodeText(dh.getName()));
|
|
|
+
|
|
|
+ // 优化文件名处理
|
|
|
+ String fileName = dh.getName();
|
|
|
+ if (StringUtils.isEmpty(fileName)) {
|
|
|
+ fileName = "简历-" + resumesName + ".pdf";
|
|
|
+ } else if (!fileName.contains(".")) {
|
|
|
+ fileName += ".pdf";
|
|
|
+ }
|
|
|
+ filePart.setFileName(MimeUtility.encodeText(fileName, "UTF-8", null));
|
|
|
+
|
|
|
msgMultipart.addBodyPart(filePart);
|
|
|
- localFile.deleteOnExit(); // 新增:JVM退出时删除临时文件
|
|
|
- } else {
|
|
|
- log.error("附件下载失败,跳过附件发送:url={}", url);
|
|
|
+ log.debug("添加简历附件成功:resumesId={}, fileName={}", resumesId, fileName);
|
|
|
+ } catch (MalformedURLException e) {
|
|
|
+ log.error("附件URL格式错误:resumesId={}, url={}", resumesId, url, e);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("附件处理失败:resumesId={}, url={}", resumesId, url, e);
|
|
|
}
|
|
|
} else {
|
|
|
- log.error("简历附件地址为空:resumesAttachmentId={}", resumesAttachmentId);
|
|
|
+ log.warn("简历附件地址为空:resumesId={}, attachmentId={}", resumesId, resumesAttachmentId);
|
|
|
}
|
|
|
} else {
|
|
|
- log.error("未查询到简历附件:resumesAttachmentId={}", resumesAttachmentId);
|
|
|
+ log.warn("未查询到简历附件:resumesId={}, attachmentId={}", resumesId, resumesAttachmentId);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 6. 发送邮件(修复:真正调用发送方法)
|
|
|
+ // 7. 发送邮件(优化:使用try-finally确保资源释放)
|
|
|
message.saveChanges();
|
|
|
|
|
|
- // 使用Transport对象发送邮件
|
|
|
- Transport transport = session.getTransport("smtp");
|
|
|
+ Transport transport = null;
|
|
|
try {
|
|
|
+ transport = session.getTransport("smtp");
|
|
|
transport.connect(host, 465, from, authCode);
|
|
|
transport.sendMessage(message, message.getAllRecipients());
|
|
|
log.info("邮件发送成功:resumesId={}, to={}, resumesName={}", resumesId, to, resumesName);
|
|
|
} finally {
|
|
|
if (transport != null) {
|
|
|
- transport.close();
|
|
|
+ try {
|
|
|
+ transport.close();
|
|
|
+ } catch (MessagingException e) {
|
|
|
+ log.error("关闭邮件传输连接失败:{}", e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} catch (AuthenticationFailedException e) {
|
|
|
- log.error("邮件发送认证失败:检查授权码是否正确,from={}, to={}, error={}", from, to, e.getMessage(), e);
|
|
|
+ log.error("邮件发送认证失败:请检查授权码是否正确,from={}, to={}", from, to, e);
|
|
|
+ } catch (javax.mail.SendFailedException e) {
|
|
|
+ // 单独处理发送失败异常
|
|
|
+ String failedAddresses = "";
|
|
|
+ if (e.getInvalidAddresses() != null && e.getInvalidAddresses().length > 0) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (javax.mail.Address addr : e.getInvalidAddresses()) {
|
|
|
+ sb.append(addr.toString()).append(", ");
|
|
|
+ }
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ sb.setLength(sb.length() - 2);
|
|
|
+ }
|
|
|
+ failedAddresses = sb.toString();
|
|
|
+ }
|
|
|
+ log.error("邮件发送失败:收件人地址无效,无效地址={}, 错误信息={}, resumesId={}",
|
|
|
+ failedAddresses, e.getMessage(), resumesId, e);
|
|
|
} catch (MessagingException e) {
|
|
|
- log.error("邮件发送失败:from={}, to={}, error={}", from, to, e.getMessage(), e);
|
|
|
+ log.error("邮件发送失败:SMTP服务器错误,from={}, to={}", from, to, e);
|
|
|
} catch (Exception e) {
|
|
|
- log.error("发送邮件异常:resumesId={}, 异常信息={}", resumesId, e.getMessage(), e);
|
|
|
+ log.error("邮件发送异常:resumesId={}, to={}, resumesName={}", resumesId, to, resumesName, e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 辅助方法:下载网络URL到本地临时文件(仅新增此方法,不改动原有逻辑)
|
|
|
*/
|
|
|
- private File downloadUrlToLocal(String url) {
|
|
|
- try {
|
|
|
- // 创建临时文件
|
|
|
- File tempFile = File.createTempFile("resume_", url.contains(".") ? url.substring(url.lastIndexOf(".")) : ".pdf");
|
|
|
- tempFile.deleteOnExit();
|
|
|
-
|
|
|
- // 下载URL内容到本地
|
|
|
- URL netUrl = new URL(url);
|
|
|
- try (InputStream in = netUrl.openStream();
|
|
|
- FileOutputStream out = new FileOutputStream(tempFile)) {
|
|
|
- byte[] buffer = new byte[1024 * 8];
|
|
|
- int len;
|
|
|
- while ((len = in.read(buffer)) != -1) {
|
|
|
- out.write(buffer, 0, len);
|
|
|
- }
|
|
|
- }
|
|
|
- return tempFile;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("下载网络文件失败:url={}", url, e);
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
|
|
|
|
|
|
public String exportPdf(Long resumesId) {
|