fix(imap): 序号按到达顺序(id ASC)分配,INTERNALDATE 使用到达时间
- ListAllByUserAndFolder 由 date DESC 改为 id ASC:新邮件永远追加到末尾 (seq = EXISTS 数),与 Dovecot/Courier 一致;带旧 Date 头的新邮件不再 插入中间导致既有邮件序号位移,seq 增量同步客户端不会漏收/标错 - INTERNALDATE 改用 CreatedAt(服务器接收时间, RFC 3501 §2.3.4), 零值降级用 Date 头;不再返回可被伪造的 Date 头 - Web 分页排序补 id DESC tiebreak,与 IMAP 全链路一致 - ListDeletedByUserAndFolder 同步改为 id ASC - 测试:TestSeqStoreClientSelfNumbered 替换为 TestSeqOrderArrival, 断言到达顺序序号映射、旧 Date 头新邮件落在末尾、INTERNALDATE=CreatedAt
This commit is contained in:
@@ -36,7 +36,7 @@ type MailStore interface {
|
||||
// SetDeletedStates 批量设置多封邮件的 \Deleted 标记(单条 UPDATE ... IN)。
|
||||
SetDeletedStates(ids []uint, deleted bool) error
|
||||
// ListDeletedByUserAndFolder 列出某文件夹中所有已标记 \Deleted 的邮件
|
||||
// (按 date DESC, id DESC 排序,与全量列表一致,序号映射全链路相同)。
|
||||
// (按 id ASC 排序,与全量列表一致,序号映射全链路相同)。
|
||||
ListDeletedByUserAndFolder(userID uint, folder string) ([]db.Message, error)
|
||||
// DeleteMany 批量硬删除多封邮件(单条 DELETE ... IN)。
|
||||
DeleteMany(ids []uint) error
|
||||
@@ -93,7 +93,7 @@ func (s *mailStoreGorm) ListByUserAndFolder(userID uint, folder string, page, si
|
||||
}
|
||||
|
||||
offset := (page - 1) * size
|
||||
if err := query.Order("date DESC").Offset(offset).Limit(size).Find(&messages).Error; err != nil {
|
||||
if err := query.Order("date DESC, id DESC").Offset(offset).Limit(size).Find(&messages).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return messages, total, nil
|
||||
@@ -145,11 +145,12 @@ func (s *mailStoreGorm) SetDeletedStates(ids []uint, deleted bool) error {
|
||||
return s.db.Model(&db.Message{}).Where("id IN ?", ids).Update("is_deleted", deleted).Error
|
||||
}
|
||||
|
||||
// ListDeletedByUserAndFolder 列出某文件夹中所有已标记 \Deleted 的邮件。
|
||||
// ListDeletedByUserAndFolder 列出某文件夹中所有已标记 \Deleted 的邮件
|
||||
// (按 id ASC 排序,与全量列表一致,序号映射全链路相同)。
|
||||
func (s *mailStoreGorm) ListDeletedByUserAndFolder(userID uint, folder string) ([]db.Message, error) {
|
||||
var messages []db.Message
|
||||
if err := s.db.Where("user_id = ? AND folder = ? AND is_deleted = ?", userID, folder, true).
|
||||
Order("date DESC, id DESC").Find(&messages).Error; err != nil {
|
||||
Order("id ASC").Find(&messages).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return messages, nil
|
||||
@@ -180,14 +181,16 @@ func (s *mailStoreGorm) CountUnread(userID uint, folder string) (int64, error) {
|
||||
}
|
||||
|
||||
// ListAllByUserAndFolder retrieves all messages for a user in a folder without pagination.
|
||||
// 按 date DESC, id DESC 排序(最新在前):与主流邮件客户端(Thunderbird、
|
||||
// 手机客户端等)默认视图一致,客户端自行按日期编号的 seq 式 STORE 不会
|
||||
// 错位标错邮件。所有 IMAP 序号相关路径(Status/ListMessages/推送/seqOf)
|
||||
// 共用本排序,保证序号全链路一致。
|
||||
// 按 id ASC(到达顺序,最早在前)排序:新邮件永远获得最大序号(seq =
|
||||
// EXISTS 数),与主流服务器(Dovecot/Courier)行为一致,依赖「新邮件 =
|
||||
// seq N+1」做增量同步的客户端不会漏收或标错邮件;新邮件到达不会使既有
|
||||
// 邮件序号位移(只有 EXPUNGE 才会,属正常行为)。所有 IMAP 序号相关路径
|
||||
// (Status/Fetch/Search/Store/Copy/Move/Expunge/推送/seqOf)共用本排序,
|
||||
// 保证序号全链路一致。INTERNALDATE 使用 CreatedAt(到达时间),与排序一致。
|
||||
func (s *mailStoreGorm) ListAllByUserAndFolder(userID uint, folder string) ([]db.Message, error) {
|
||||
var messages []db.Message
|
||||
if err := s.db.Where("user_id = ? AND folder = ?", userID, folder).
|
||||
Order("date DESC, id DESC").Find(&messages).Error; err != nil {
|
||||
Order("id ASC").Find(&messages).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return messages, nil
|
||||
|
||||
Reference in New Issue
Block a user