chenkq 3 днів тому
батько
коміт
849533c08c

+ 13 - 0
storlead-mail-api/src/main/java/com/storlead/mail/mkt/MktMailApiController.java

@@ -244,6 +244,19 @@ public class MktMailApiController {
         return Result.result(page);
     }
 
+    @PostMapping("/list_by_ids")
+    @ApiOperation("按邮件ID列表查询(不含正文 content)")
+    public Result<?> listByIds(@RequestBody List<Long> ids) {
+        Long userId = LoginUserUtil.getCurrentUserId();
+        if (Objects.isNull(userId)) {
+            return Result.error("未登录");
+        }
+        if (CollectionUtils.isEmpty(ids)) {
+            return Result.error("参数错误");
+        }
+        return Result.result(mktMailMessageService.listMetaByIds(ids));
+    }
+
     @PostMapping("/detail")
     @ApiOperation("营销邮件详情(含附件)")
     public Result<?> detail(@RequestParam Long id) {

+ 1 - 0
storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_recreate.sql

@@ -99,6 +99,7 @@ CREATE TABLE mkt_mail_status (
     first_reply_time  datetime     NULL COMMENT '首次回复时间',
     first_reply_email_id bigint    NULL COMMENT '首次回复邮件ID,对应 mkt_mail_message.id',
     first_reply_theme varchar(512) NULL COMMENT '首次回复邮件主题',
+    reply_email_ids   varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '全部回复邮件id,逗号隔开',
     status_updated_at datetime     NOT NULL COMMENT '业务状态变更时间(打开/回复/发送态等变更时刷新)',
     synced_at         datetime     NULL COMMENT '业务侧最后同步时间;null或小于status_updated_at表示待同步',
     owner_by          bigint       NULL COMMENT '数据归属人',

+ 1 - 0
storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_status.sql

@@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS mkt_mail_status (
     first_reply_time  datetime     NULL COMMENT '首次回复时间',
     first_reply_email_id bigint    NULL COMMENT '首次回复邮件ID,对应 mkt_mail_message.id',
     first_reply_theme varchar(512) NULL COMMENT '首次回复邮件主题',
+    reply_email_ids   varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '全部回复邮件id,逗号隔开',
 
     status_updated_at datetime     NOT NULL COMMENT '业务状态变更时间(打开/回复/发送态等变更时刷新)',
     synced_at         datetime     NULL COMMENT '业务侧最后同步时间;null或小于status_updated_at表示待同步',

+ 3 - 0
storlead-mail/storlead-mail-common/src/main/resources/sql/mkt_mail_status_add_reply_email_ids.sql

@@ -0,0 +1,3 @@
+-- mkt_mail_status:全部回复邮件 ID(逗号隔开)
+ALTER TABLE mkt_mail_status
+    ADD COLUMN reply_email_ids varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '全部回复邮件id,逗号隔开' AFTER first_reply_theme;

+ 9 - 0
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/entity/MktMailMessageEntity.java

@@ -1,15 +1,18 @@
 package com.storlead.sales.mail.mkt.entity;
 
+import com.alibaba.fastjson.annotation.JSONField;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.storlead.framework.mybatis.entity.SysBaseField;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.util.Date;
 import java.util.List;
@@ -81,10 +84,16 @@ public class MktMailMessageEntity extends SysBaseField {
     private String msgUid;
 
     @ApiModelProperty("收信时间")
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @JSONField(format ="yyyy-MM-dd HH:mm:ss")
     @TableField("recipient_date")
     private Date recipientDate;
 
     @ApiModelProperty("邮件头发送时间")
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @JSONField(format ="yyyy-MM-dd HH:mm:ss")
     @TableField("sent_date")
     private Date sentDate;
 

+ 4 - 0
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/entity/MktMailStatusEntity.java

@@ -93,6 +93,10 @@ public class MktMailStatusEntity extends SysBaseField {
     @TableField("first_reply_theme")
     private String firstReplyTheme;
 
+    @ApiModelProperty("全部回复邮件ID,逗号隔开")
+    @TableField("reply_email_ids")
+    private String replyEmailIds;
+
     @ApiModelProperty("业务状态变更时间(打开/回复/发送态等变更时刷新)")
     @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
     @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")

+ 1 - 1
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/receive/MktMailReceivePersist.java

@@ -534,7 +534,7 @@ public class MktMailReceivePersist implements MailReceivePersistSpi {
                         .or().like(MktMailMessageEntity::getRecipient, recipientEmail))
                 .orderByDesc(MktMailMessageEntity::getSentTime)
                 .orderByDesc(MktMailMessageEntity::getId)
-                .last("limit 50"));
+                .last("limit 10"));
         if (CollectionUtils.isEmpty(list)) {
             return list;
         }

+ 8 - 0
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/service/MktMailMessageService.java

@@ -9,6 +9,9 @@ import com.storlead.sales.mail.mkt.pojo.MktOpenStatsVO;
 import com.storlead.sales.mail.mkt.pojo.MktSendMailDTO;
 import com.storlead.sales.mail.mkt.pojo.MktSendMailResult;
 
+import java.util.Collection;
+import java.util.List;
+
 /**
  * 营销邮件投递业务(对齐 CRM:草稿、附件、延时、发送)。
  */
@@ -38,6 +41,11 @@ public interface MktMailMessageService extends MyBaseService<MktMailMessageEntit
 
     IPage<MktMailMessageEntity> pageList(MktMailQueryDTO query);
 
+    /**
+     * 按邮件 ID 列表查询元数据(不含 content 正文)。
+     */
+    List<MktMailMessageEntity> listMetaByIds(Collection<Long> ids);
+
     /**
      * 详情:草稿/延时/失败带临时附件;发送成功带 mkt_mail_attachment。
      */

+ 3 - 3
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/service/MktMailStatusService.java

@@ -28,11 +28,11 @@ public interface MktMailStatusService extends MyBaseService<MktMailStatusEntity>
 
     /**
      * 收到针对原信的回复:更新 replied / first_reply_time,
-     * 以及首次回复邮件 ID、主题(仅首次写入)。
+     * 首次回复邮件 ID、主题;追加 reply_email_ids;并刷新 status_updated_at。
      *
      * @param mailId            原信(发出信)ID
-     * @param firstReplyEmailId 回复邮件 ID(入站 mkt_mail_message.id)
-     * @param firstReplyTheme   回复邮件主题
+     * @param firstReplyEmailId 本次回复邮件 ID(入站 mkt_mail_message.id)
+     * @param firstReplyTheme   本次回复邮件主题
      */
     void markReplied(Long mailId, Long firstReplyEmailId, String firstReplyTheme);
 

+ 73 - 0
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/service/impl/MktMailMessageServiceImpl.java

@@ -60,6 +60,8 @@ import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMessage;
 import javax.mail.internet.MimeMultipart;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
@@ -355,10 +357,81 @@ public class MktMailMessageServiceImpl extends MyBaseServiceImpl<MktMailMessageM
         if (StringUtils.hasText(query.getBizBatchNo())) {
             wrapper.eq(MktMailMessageEntity::getBizBatchNo, query.getBizBatchNo());
         }
+        selectWithoutContent(wrapper);
         wrapper.orderByDesc(MktMailMessageEntity::getId);
         return this.page(page, wrapper);
     }
 
+    @Override
+    public List<MktMailMessageEntity> listMetaByIds(Collection<Long> ids) {
+        if (CollectionUtils.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        List<Long> idList = ids.stream()
+                .filter(Objects::nonNull)
+                .distinct()
+                .limit(200)
+                .collect(Collectors.toList());
+        if (idList.isEmpty()) {
+            return Collections.emptyList();
+        }
+        LambdaQueryWrapper<MktMailMessageEntity> wrapper = new LambdaQueryWrapper<>();
+        selectWithoutContent(wrapper);
+        wrapper.in(MktMailMessageEntity::getId, idList)
+                .eq(MktMailMessageEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .orderByDesc(MktMailMessageEntity::getId);
+        return this.list(wrapper);
+    }
+
+    /**
+     * 列表查询显式排除 content,避免正文大字段拖慢 SQL / 序列化。
+     */
+    private static void selectWithoutContent(LambdaQueryWrapper<MktMailMessageEntity> wrapper) {
+        wrapper.select(
+                MktMailMessageEntity::getId,
+                MktMailMessageEntity::getAccountId,
+                MktMailMessageEntity::getOrgId,
+                MktMailMessageEntity::getBizRef,
+                MktMailMessageEntity::getBizBatchNo,
+                MktMailMessageEntity::getMessageId,
+                MktMailMessageEntity::getTrackToken,
+                MktMailMessageEntity::getSubject,
+                MktMailMessageEntity::getFromAddr,
+                MktMailMessageEntity::getFromName,
+                MktMailMessageEntity::getRecipient,
+                MktMailMessageEntity::getRecipientCc,
+                MktMailMessageEntity::getRecipientBcc,
+                MktMailMessageEntity::getFolder,
+                MktMailMessageEntity::getMsgUid,
+                MktMailMessageEntity::getRecipientDate,
+                MktMailMessageEntity::getSentDate,
+                MktMailMessageEntity::getIsOnlyHead,
+                MktMailMessageEntity::getIsRead,
+                MktMailMessageEntity::getInOutMark,
+                MktMailMessageEntity::getEmailSize,
+                MktMailMessageEntity::getInReplyTo,
+                MktMailMessageEntity::getReplyMsgId,
+                MktMailMessageEntity::getStatus,
+                MktMailMessageEntity::getFailReason,
+                MktMailMessageEntity::getIsTrack,
+                MktMailMessageEntity::getOpened,
+                MktMailMessageEntity::getFirstOpenTime,
+                MktMailMessageEntity::getOpenCount,
+                MktMailMessageEntity::getReplied,
+                MktMailMessageEntity::getFirstReplyTime,
+                MktMailMessageEntity::getSentTime,
+                MktMailMessageEntity::getDelaySendTime,
+                MktMailMessageEntity::getOwnerBy,
+                MktMailMessageEntity::getCreateBy,
+                MktMailMessageEntity::getUpdateBy,
+                MktMailMessageEntity::getCreateTime,
+                MktMailMessageEntity::getUpdateTime,
+                MktMailMessageEntity::getIsDelete,
+                MktMailMessageEntity::getEnabled,
+                MktMailMessageEntity::getSort
+        );
+    }
+
     @Override
     public MktMailMessageEntity getDetail(Long id, Long operatorId) {
         if (id == null) {

+ 32 - 0
storlead-mail/storlead-mail-mkt/src/main/java/com/storlead/sales/mail/mkt/service/impl/MktMailStatusServiceImpl.java

@@ -128,11 +128,43 @@ public class MktMailStatusServiceImpl extends MyBaseServiceImpl<MktMailStatusMap
                 update.set(MktMailStatusEntity::getFirstReplyTheme, theme);
             }
         }
+        // 追加全部回复邮件 ID(去重),供多封回复同步
+        String mergedIds = appendReplyEmailId(
+                existing == null ? null : existing.getReplyEmailIds(), firstReplyEmailId);
+        if (StringUtils.hasText(mergedIds)
+                && (existing == null || !mergedIds.equals(existing.getReplyEmailIds()))) {
+            update.set(MktMailStatusEntity::getReplyEmailIds, mergedIds);
+        }
+        // 每次回复都刷新业务变更时间,便于外部按时间戳增量同步
         update.set(MktMailStatusEntity::getStatusUpdatedAt, now);
         update.set(MktMailStatusEntity::getUpdateTime, now);
         this.update(update);
     }
 
+    /**
+     * 将回复邮件 ID 追加到逗号串;已存在则原样返回;超过 200 字符则不再追加。
+     */
+    static String appendReplyEmailId(String existingIds, Long replyEmailId) {
+        if (replyEmailId == null) {
+            return existingIds;
+        }
+        String idStr = String.valueOf(replyEmailId);
+        if (!StringUtils.hasText(existingIds)) {
+            return idStr.length() <= 200 ? idStr : null;
+        }
+        String[] parts = existingIds.split(",");
+        for (String part : parts) {
+            if (idStr.equals(part.trim())) {
+                return existingIds;
+            }
+        }
+        String merged = existingIds.trim() + "," + idStr;
+        if (merged.length() > 200) {
+            return existingIds;
+        }
+        return merged;
+    }
+
     @Override
     public void markSynced(Long mailId) {
         if (mailId == null) {