|
@@ -443,6 +443,11 @@ public class MktMailMessageServiceImpl extends MyBaseServiceImpl<MktMailMessageM
|
|
|
private MimeMessage buildMimeMessage(Session session,
|
|
private MimeMessage buildMimeMessage(Session session,
|
|
|
MktMailMessageEntity entity,
|
|
MktMailMessageEntity entity,
|
|
|
SmtpPopSettingsEntity account) throws Exception {
|
|
SmtpPopSettingsEntity account) throws Exception {
|
|
|
|
|
+ // 定时任务/直接插库场景:发送前补齐 track_token,并保证状态行存在
|
|
|
|
|
+ prepareTrackBeforeSend(entity);
|
|
|
|
|
+ // 开启追踪时必须能注入像素(避免只有 token、发出去却没有像素)
|
|
|
|
|
+ assertTrackReady(entity);
|
|
|
|
|
+
|
|
|
MimeMessage message = new MimeMessage(session);
|
|
MimeMessage message = new MimeMessage(session);
|
|
|
String fromName = StringUtils.hasText(account.getSenderNickName())
|
|
String fromName = StringUtils.hasText(account.getSenderNickName())
|
|
|
? account.getSenderNickName() : account.getEmailAddress();
|
|
? account.getSenderNickName() : account.getEmailAddress();
|
|
@@ -459,8 +464,17 @@ public class MktMailMessageServiceImpl extends MyBaseServiceImpl<MktMailMessageM
|
|
|
Multipart multipart = new MimeMultipart();
|
|
Multipart multipart = new MimeMultipart();
|
|
|
BodyPart htmlPart = new MimeBodyPart();
|
|
BodyPart htmlPart = new MimeBodyPart();
|
|
|
List<MimeBodyPart> imageParts = new ArrayList<>();
|
|
List<MimeBodyPart> imageParts = new ArrayList<>();
|
|
|
- String html = entity.getContent() == null ? "" : entity.getContent();
|
|
|
|
|
- html = injectTrackPixel(html, entity);
|
|
|
|
|
|
|
+ String originalHtml = entity.getContent() == null ? "" : entity.getContent();
|
|
|
|
|
+ String html = injectTrackPixel(originalHtml, entity);
|
|
|
|
|
+ // 像素写入 content 字段,便于库内核对;CID 转换只作用于发出去的 MIME
|
|
|
|
|
+ if (entity.getId() != null && !Objects.equals(originalHtml, html)) {
|
|
|
|
|
+ entity.setContent(html);
|
|
|
|
|
+ LambdaUpdateWrapper<MktMailMessageEntity> contentUpdate = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ contentUpdate.eq(MktMailMessageEntity::getId, entity.getId());
|
|
|
|
|
+ contentUpdate.set(MktMailMessageEntity::getContent, html);
|
|
|
|
|
+ this.update(contentUpdate);
|
|
|
|
|
+ log.info("mkt persist track pixel into content, mailId={}", entity.getId());
|
|
|
|
|
+ }
|
|
|
String updatedHtml = EmailHelper.convertImageUrlsToCidBodyPart(
|
|
String updatedHtml = EmailHelper.convertImageUrlsToCidBodyPart(
|
|
|
html, imageParts, mailFileProperties.getPath().getJpeg(), multipart);
|
|
html, imageParts, mailFileProperties.getPath().getJpeg(), multipart);
|
|
|
if (updatedHtml == null) {
|
|
if (updatedHtml == null) {
|
|
@@ -503,18 +517,49 @@ public class MktMailMessageServiceImpl extends MyBaseServiceImpl<MktMailMessageM
|
|
|
.eq(MailTempAttachmentEntity::getScene, MailSceneEnum.MKT.getCode()));
|
|
.eq(MailTempAttachmentEntity::getScene, MailSceneEnum.MKT.getCode()));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 实际 SMTP 发送前补齐追踪:支持定时任务 / 第三方直接插库。
|
|
|
|
|
+ * is_track=1 且无 track_token 时生成并回写库;同步 ensure 状态行。
|
|
|
|
|
+ */
|
|
|
|
|
+ private void prepareTrackBeforeSend(MktMailMessageEntity entity) {
|
|
|
|
|
+ if (entity == null || entity.getId() == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isTrackOn(entity) && !StringUtils.hasText(entity.getTrackToken())) {
|
|
|
|
|
+ String token = UUID.randomUUID().toString().replace("-", "");
|
|
|
|
|
+ entity.setTrackToken(token);
|
|
|
|
|
+ LambdaUpdateWrapper<MktMailMessageEntity> update = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ update.eq(MktMailMessageEntity::getId, entity.getId());
|
|
|
|
|
+ update.set(MktMailMessageEntity::getIsTrack, 1);
|
|
|
|
|
+ update.set(MktMailMessageEntity::getTrackToken, token);
|
|
|
|
|
+ this.update(update);
|
|
|
|
|
+ log.info("mkt prepare track_token before send, mailId={}", entity.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ mktMailStatusService.ensureStatus(entity);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("mkt ensureStatus before send error, mailId={}", entity.getId(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private String injectTrackPixel(String html, MktMailMessageEntity entity) {
|
|
private String injectTrackPixel(String html, MktMailMessageEntity entity) {
|
|
|
- if (!Integer.valueOf(1).equals(entity.getIsTrack()) || !StringUtils.hasText(entity.getTrackToken())) {
|
|
|
|
|
|
|
+ if (!isTrackOn(entity) || !StringUtils.hasText(entity.getTrackToken())) {
|
|
|
return html;
|
|
return html;
|
|
|
}
|
|
}
|
|
|
if (!StringUtils.hasText(trackBaseUrl)) {
|
|
if (!StringUtils.hasText(trackBaseUrl)) {
|
|
|
log.warn("storlead.mail.track-base-url 未配置,跳过打开追踪像素注入, mailId={}", entity.getId());
|
|
log.warn("storlead.mail.track-base-url 未配置,跳过打开追踪像素注入, mailId={}", entity.getId());
|
|
|
return html;
|
|
return html;
|
|
|
}
|
|
}
|
|
|
|
|
+ String body = html == null ? "" : html;
|
|
|
|
|
+ // 正文已含追踪像素则不再重复注入(直接插库可能已写过)
|
|
|
|
|
+ if (body.contains("/mail/mkt/track/")) {
|
|
|
|
|
+ return body;
|
|
|
|
|
+ }
|
|
|
String base = trackBaseUrl.endsWith("/") ? trackBaseUrl.substring(0, trackBaseUrl.length() - 1) : trackBaseUrl;
|
|
String base = trackBaseUrl.endsWith("/") ? trackBaseUrl.substring(0, trackBaseUrl.length() - 1) : trackBaseUrl;
|
|
|
- String pixel = "<img src=\"" + base + "/mail/mkt/track/" + entity.getTrackToken()
|
|
|
|
|
|
|
+ String pixelUrl = base + "/mail/mkt/track/" + entity.getTrackToken();
|
|
|
|
|
+ String pixel = "<img src=\"" + pixelUrl
|
|
|
+ "\" width=\"1\" height=\"1\" style=\"display:none!important;width:1px;height:1px;border:0;\" alt=\"\"/>";
|
|
+ "\" width=\"1\" height=\"1\" style=\"display:none!important;width:1px;height:1px;border:0;\" alt=\"\"/>";
|
|
|
- String body = html == null ? "" : html;
|
|
|
|
|
|
|
+ log.info("mkt inject track pixel, mailId={}, url={}", entity.getId(), pixelUrl);
|
|
|
int idx = body.toLowerCase().lastIndexOf("</body>");
|
|
int idx = body.toLowerCase().lastIndexOf("</body>");
|
|
|
if (idx >= 0) {
|
|
if (idx >= 0) {
|
|
|
return body.substring(0, idx) + pixel + body.substring(idx);
|
|
return body.substring(0, idx) + pixel + body.substring(idx);
|
|
@@ -523,7 +568,7 @@ public class MktMailMessageServiceImpl extends MyBaseServiceImpl<MktMailMessageM
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private void assertTrackReady(MktMailMessageEntity entity) {
|
|
private void assertTrackReady(MktMailMessageEntity entity) {
|
|
|
- if (!Integer.valueOf(1).equals(entity.getIsTrack())) {
|
|
|
|
|
|
|
+ if (!isTrackOn(entity)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (!StringUtils.hasText(trackBaseUrl)) {
|
|
if (!StringUtils.hasText(trackBaseUrl)) {
|
|
@@ -534,6 +579,10 @@ public class MktMailMessageServiceImpl extends MyBaseServiceImpl<MktMailMessageM
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private static boolean isTrackOn(MktMailMessageEntity entity) {
|
|
|
|
|
+ return entity != null && entity.getIsTrack() != null && entity.getIsTrack() == 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private void bindTempAttachments(Long mailId, List<Long> fileIds, Long operatorId) {
|
|
private void bindTempAttachments(Long mailId, List<Long> fileIds, Long operatorId) {
|
|
|
if (CollectionUtils.isEmpty(fileIds) || mailId == null) {
|
|
if (CollectionUtils.isEmpty(fileIds) || mailId == null) {
|
|
|
return;
|
|
return;
|