Foxmail邮件会话模式实现逻辑

邮件信息的组成部分

  • folder: 位置信息收件箱 发件箱
  • Message_ID: 邮件的ID
  • subject: 主题
  • sessionSubject 会话主题。去除主题的前缀,如回复 Re 答复 自动回复 自动答复
  • References
    回复(全部回复)/转发一个邮件的时候,当前邮件回在头信息中添加References,参照Message_ID。多轮回复,邮件有多个References.Root引用在第一个位置

  • Default References

    (subject + 所有参与者)为组合生产一个定义的Default References,这个always有。

会话的聚合逻辑

聚合的原则:

将相同【会话主题】和具有【引用关系】邮件组成一个会话模式

* 引用关系包含邮件References和Default Reference

几种情况的说明:

  1. 无主题的不聚合

  2. 转发的虽然有引用,但是不聚合,从会话中脱离出来。因为主题发生了变化。“回复:你好”的会话主题是“你好”,而“转发:你好”的会话主题是“转发:你好”

具体步骤:

数据准备

  1. 拉取所有的收件箱邮件

  2. 按条件拉取发件箱,找跟收件箱的关系

    前提,必须 收件箱有的【会话主题】

    • 收件箱邮件引用
    • 引用收件箱
    • 收件箱有相同的邮件引用 & 收件时间 < 发件时间
    • 收件箱有相同的自定义引用 & 邮件引用不存在 & 收件时间 < 发件时间

Query.sql

SELECT
    SUBJECT,
    NAME,
    common_subject,
    message_id,
    session_id session_id,
    reference,
    send_time
FROM
    t_mail t
WHERE
    EXISTS (
        SELECT
            1
        FROM
            t_mail t2
        WHERE
            t. NAME = 'INBOX'  -- 【INBOX】所有邮件

            OR (    
                t.common_subject = t2.common_subject
                and (
                    (
                        t.message_id = t2.reference -- 被INBOX引用的【发件箱】邮件
                        AND t2.`name` = 'INBOX'
                    )
                    OR
                    (
                        t.session_id = t2.session_id -- INBOX时间之后,【发件箱】能组队的
                        AND t2.`name` = 'INBOX'
                        AND t.send_time > t2.send_time 
                        And t2.reference is null

                    )
                    or (
                        t.reference = t2.message_id
                        AND t2.`name` = 'INBOX'
                    )

                    OR (
                        t.reference = t2.reference -- INBOX时间之后,【发件箱】是引用的
                        AND t2.`name` = 'INBOX'
                        AND t.send_time > t2.send_time 
                    )
                )
            )
)

ORDER BY
    send_time DESC;
  1. 所有邮件按照发件顺序倒序排

数据聚合

遍历所有邮件

1. 有主题吗?没有主题自己单独一个会话。否则往下继续执行2

  1. 邮件引用,有没有引用其他邮件

    • 有引用,看有没有以【Reference ID+会话主题】为key的空间,如果有在尾部加入,没有自己开辟
    • 没有引用,继续3
  2. 邮件被引用

    • 有没有【Message_ID + 会话主题】的空间,有直接加入

– 没有继续详细执行4

  1. 自定义引用
    • 看有没有以【自定义引用+会话主题】为key的空间,如果有在尾部加入,没有自己开辟

foreach.java

list.forEach(mailEntity -> {

            if (StringUtils.isBlank(mailEntity.getCommonSubject())) { //无主题独占会话
                sessionMap.put(String.valueOf(IDUtils.genItemId()), Sets.newLinkedHashSet(mailEntity));
                return;
            }

            String reference = mailEntity.getReference();

            // 引用方
            if (reference != null) {
                Set<MailEntity> line = sessionMap.get(reference + ":" + mailEntity.getCommonSubject());

                if (line == null) {
                    line = Sets.newLinkedHashSet();
                    sessionMap.put(reference + ":" + mailEntity.getCommonSubject(), line);
                }

                line.add(mailEntity);
                return;
            }

            // 被引用方
            String messageId = mailEntity.getMessageId();

            Set<MailEntity> line = sessionMap.get(messageId + ":" + mailEntity.getCommonSubject());
            if (line != null) {
                line.add(mailEntity);
                return;
            }

            // 自建引用
            String sessionId = mailEntity.getSessionId();

            line = sessionMap.get(sessionId + ":" + mailEntity.getCommonSubject());

            if (line != null) {
                line.add(mailEntity);
                return;
            } else {
                line = Sets.newLinkedHashSet();
                sessionMap.put(sessionId + ":" + mailEntity.getCommonSubject(), line);
                line.add(mailEntity);
            }

        });