|
|
@@ -16,6 +16,7 @@ import com.sqx.modules.chat.entity.ChatContent;
|
|
|
import com.sqx.modules.chat.entity.ChatConversation;
|
|
|
import com.sqx.modules.chat.service.ChatContentService;
|
|
|
import com.sqx.modules.chat.service.ChatConversationService;
|
|
|
+import com.sqx.modules.common.entity.CommonInfo;
|
|
|
import com.sqx.modules.common.service.CommonInfoService;
|
|
|
import com.sqx.modules.firstLogin.entity.*;
|
|
|
import com.sqx.modules.firstLogin.service.*;
|
|
|
@@ -746,171 +747,159 @@ 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) {
|
|
|
- // to = "18064109218@163.com";
|
|
|
- // 1. 入参校验优化
|
|
|
- if (StringUtils.isEmpty(to)) {
|
|
|
+
|
|
|
+ // 1. 入参校验
|
|
|
+ if (StringUtils.isBlank(to)) {
|
|
|
log.error("邮件发送失败:收件人邮箱为空,resumesId={}", resumesId);
|
|
|
return;
|
|
|
}
|
|
|
- resumesName = resumesName + ".pdf";
|
|
|
- if (StringUtils.isEmpty(resumesName)) {
|
|
|
- log.error("邮件发送失败:简历名称为空,resumesId={}, to={}", resumesId, to);
|
|
|
+ if (!to.matches("^[\\w.-]+@[\\w.-]+\\.[A-Za-z]{2,}$")) {
|
|
|
+ log.error("邮件发送失败:收件人邮箱格式错误,resumesId={}, to={}", resumesId, to);
|
|
|
return;
|
|
|
}
|
|
|
- if (!to.matches("^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$")) {
|
|
|
- log.error("邮件发送失败:收件人邮箱格式不正确,resumesId={}, to={}", resumesId, to);
|
|
|
+ if (StringUtils.isBlank(resumesName)) {
|
|
|
+ log.error("邮件发送失败:简历名称为空,resumesId={}, to={}", resumesId, to);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- // 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={}", from);
|
|
|
+ resumesName += ".pdf";
|
|
|
+
|
|
|
+ // 2. 一次性读取配置
|
|
|
+ String from = Optional.ofNullable(commonInfoService.findOne(308))
|
|
|
+ .map(CommonInfo::getValue).orElse("");
|
|
|
+ String authCode = Optional.ofNullable(commonInfoService.findOne(309))
|
|
|
+ .map(CommonInfo::getValue).orElse("");
|
|
|
+ String host = Optional.ofNullable(commonInfoService.findOne(427))
|
|
|
+ .map(CommonInfo::getValue).orElse("smtp.163.com");
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(from) || StringUtils.isBlank(authCode)) {
|
|
|
+ log.error("邮件发送失败:发件人邮箱或授权码未配置");
|
|
|
return;
|
|
|
}
|
|
|
- if (StringUtils.isEmpty(host)) {
|
|
|
- host = "smtp.163.com"; // 默认值
|
|
|
- log.warn("邮件服务器配置为空,使用默认值:smtp.163.com");
|
|
|
- }
|
|
|
|
|
|
- // 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");
|
|
|
- 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.smtp.auth", "true");
|
|
|
- properties.setProperty("mail.mime.charset", "UTF-8");
|
|
|
-
|
|
|
- // 4. Session创建优化:使用getInstance提高线程安全性
|
|
|
- Session session = Session.getInstance(properties, new Authenticator() {
|
|
|
+ // 3. JavaMail 配置
|
|
|
+ Properties props = new Properties();
|
|
|
+ props.put("mail.smtp.host", host);
|
|
|
+ props.put("mail.smtp.port", "465");
|
|
|
+ props.put("mail.smtp.auth", "true");
|
|
|
+ props.put("mail.smtp.ssl.enable", "true");
|
|
|
+ props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
|
|
|
+ props.put("mail.mime.charset", "UTF-8");
|
|
|
+
|
|
|
+ Session session = Session.getInstance(props, new Authenticator() {
|
|
|
@Override
|
|
|
- public PasswordAuthentication getPasswordAuthentication() {
|
|
|
+ protected PasswordAuthentication getPasswordAuthentication() {
|
|
|
return new PasswordAuthentication(from, authCode);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
try {
|
|
|
- // 5. 构建邮件
|
|
|
MimeMessage message = new MimeMessage(session);
|
|
|
|
|
|
- // 设置发件人
|
|
|
- InternetAddress fromMail = new InternetAddress(from);
|
|
|
- String senderName = commonInfoService.findOne(12).getValue();
|
|
|
- fromMail.setPersonal(MimeUtility.encodeText((StringUtils.isEmpty(senderName) ? "系统管理员" : senderName) + "<" + from + ">"));
|
|
|
- message.setFrom(fromMail);
|
|
|
+ // 发件人名称
|
|
|
+ String senderName = Optional.ofNullable(commonInfoService.findOne(12))
|
|
|
+ .map(CommonInfo::getValue)
|
|
|
+ .filter(StringUtils::isNotBlank)
|
|
|
+ .orElse("系统管理员");
|
|
|
|
|
|
- // 设置收件人(优化:使用单个收件人方法)
|
|
|
- message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
|
|
|
+ // setFrom 正确写法(名称 + 邮箱)
|
|
|
+ message.setFrom(new InternetAddress(from, senderName, "UTF-8"));
|
|
|
|
|
|
- // 设置主题和发送时间
|
|
|
- message.setSubject("收到一封投递简历:" + resumesName + ",请您查收!");
|
|
|
+ // 收件人
|
|
|
+ message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
|
|
|
+
|
|
|
+ // 主题
|
|
|
+ message.setSubject("收到一封投递简历:" + resumesName + ",请查收!", "UTF-8");
|
|
|
message.setSentDate(new Date());
|
|
|
|
|
|
// 混合内容
|
|
|
- MimeMultipart msgMultipart = new MimeMultipart("mixed");
|
|
|
- message.setContent(msgMultipart);
|
|
|
+ MimeMultipart mixed = new MimeMultipart("mixed");
|
|
|
+ message.setContent(mixed);
|
|
|
|
|
|
// 文本内容
|
|
|
- MimeBodyPart htmlPart = new MimeBodyPart();
|
|
|
- htmlPart.setContent("收到一封简历投递:" + resumesName + ",请您查收!", "text/html;charset=UTF-8");
|
|
|
- msgMultipart.addBodyPart(htmlPart);
|
|
|
+ MimeBodyPart textPart = new MimeBodyPart();
|
|
|
+ textPart.setContent("收到一封简历投递:" + resumesName + ",请您查收!", "text/html;charset=UTF-8");
|
|
|
+ mixed.addBodyPart(textPart);
|
|
|
|
|
|
- // 6. 附件处理优化
|
|
|
+ // 4. 附件处理
|
|
|
if (resumesAttachmentId != null) {
|
|
|
- ResumesAttachment resumesAttachment = resumesAttachmentService.getById(resumesAttachmentId);
|
|
|
- if (resumesAttachment != null) {
|
|
|
- String url = resumesAttachment.getAttachmentAddress();
|
|
|
- if (StringUtils.isNotEmpty(url)) {
|
|
|
- try {
|
|
|
- // 直接使用URLDataSource,避免本地下载
|
|
|
- URL attachmentUrl = new URL(url);
|
|
|
- MimeBodyPart filePart = new MimeBodyPart();
|
|
|
- URLDataSource dataSource = new URLDataSource(attachmentUrl);
|
|
|
- DataHandler dh = new DataHandler(dataSource);
|
|
|
- filePart.setDataHandler(dh);
|
|
|
-
|
|
|
- // 优化文件名处理
|
|
|
- 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);
|
|
|
- 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);
|
|
|
+ ResumesAttachment att = resumesAttachmentService.getById(resumesAttachmentId);
|
|
|
+
|
|
|
+ if (att != null && StringUtils.isNotBlank(att.getAttachmentAddress())) {
|
|
|
+ try {
|
|
|
+ URL url = new URL(att.getAttachmentAddress());
|
|
|
+ URLDataSource dataSource = new URLDataSource(url);
|
|
|
+
|
|
|
+ MimeBodyPart filePart = new MimeBodyPart();
|
|
|
+ filePart.setDataHandler(new DataHandler(dataSource));
|
|
|
+
|
|
|
+ // 附件名
|
|
|
+ String attachmentName = dataSource.getName();
|
|
|
+ if (StringUtils.isBlank(attachmentName)) {
|
|
|
+ attachmentName = "附件-" + resumesName;
|
|
|
+ } else if (!attachmentName.contains(".")) {
|
|
|
+ attachmentName += ".pdf";
|
|
|
}
|
|
|
- } else {
|
|
|
- log.warn("简历附件地址为空:resumesId={}, attachmentId={}", resumesId, resumesAttachmentId);
|
|
|
+
|
|
|
+ filePart.setFileName(MimeUtility.encodeText(attachmentName, "UTF-8", null));
|
|
|
+ mixed.addBodyPart(filePart);
|
|
|
+
|
|
|
+ log.debug("简历附件已添加:resumesId={}, fileName={}", resumesId, attachmentName);
|
|
|
+
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.error("附件处理失败:resumesId={}, url={}", resumesId, att.getAttachmentAddress(), ex);
|
|
|
}
|
|
|
} else {
|
|
|
- log.warn("未查询到简历附件:resumesId={}, attachmentId={}", resumesId, resumesAttachmentId);
|
|
|
+ log.warn("未查询到有效附件:resumesId={}, attachmentId={}", resumesId, resumesAttachmentId);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 7. 发送邮件(优化:使用try-finally确保资源释放)
|
|
|
message.saveChanges();
|
|
|
-
|
|
|
+
|
|
|
+ // 5. 邮件发送(必须用 finally 手动关闭 Transport)
|
|
|
Transport transport = null;
|
|
|
try {
|
|
|
transport = session.getTransport("smtp");
|
|
|
- transport.connect(host, 465, from, authCode);
|
|
|
+
|
|
|
+ // 自动从 properties 读取 host & port
|
|
|
+ transport.connect(from, authCode);
|
|
|
+
|
|
|
transport.sendMessage(message, message.getAllRecipients());
|
|
|
- log.info("邮件发送成功:resumesId={}, to={}, resumesName={}", resumesId, to, resumesName);
|
|
|
+
|
|
|
+ log.info("邮件发送成功:resumesId={}, to={}", resumesId, to);
|
|
|
+
|
|
|
} finally {
|
|
|
if (transport != null) {
|
|
|
try {
|
|
|
transport.close();
|
|
|
} catch (MessagingException e) {
|
|
|
- log.error("关闭邮件传输连接失败:{}", e.getMessage(), e);
|
|
|
+ log.error("邮件连接关闭失败:{}", e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} catch (AuthenticationFailedException 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);
|
|
|
+ log.error("邮件认证失败(授权码错误):from={}, to={}", from, to, e);
|
|
|
+
|
|
|
+ } catch (SendFailedException e) {
|
|
|
+ log.error("收件人地址无效:invalid={}", Arrays.toString(e.getInvalidAddresses()), e);
|
|
|
+
|
|
|
} catch (MessagingException e) {
|
|
|
- log.error("邮件发送失败:SMTP服务器错误,from={}, to={}", from, to, e);
|
|
|
+ log.error("SMTP 服务器通信错误:resumesId={}, to={}", resumesId, to, e);
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
- log.error("邮件发送异常:resumesId={}, to={}, resumesName={}", resumesId, to, resumesName, e);
|
|
|
+ log.error("邮件发送异常:resumesId={}, to={}", resumesId, to, e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 辅助方法:下载网络URL到本地临时文件(仅新增此方法,不改动原有逻辑)
|
|
|
*/
|