docs: add BUG-00009 scroll reset and push interruption analysis
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
d4f8f609cf
commit
f11096f30a
1 file changed
+165
@@ -0,0 +1,165 @@
|
||||
# BUG-00009 Telegram 推送交互与推送中断问题分析
|
||||
|
||||
> 关联功能: TODO-00007 / FD-00007
|
||||
> 前置修复: BUG-00007, BUG-00008
|
||||
> 分析时间: 2026-03-05
|
||||
|
||||
---
|
||||
|
||||
## 一、问题概述
|
||||
|
||||
| 编号 | 标题 | 严重程度 | 类型 |
|
||||
|------|------|---------|------|
|
||||
| **D-1** | 开关推送后账号列表滚动到顶部,体验差 | 🟡 中 | 前端交互 |
|
||||
| **D-2** | 收到一次推送后不再推送新邮件 | 🔴 高 | 推送逻辑 |
|
||||
|
||||
---
|
||||
|
||||
## 二、D-1:开关推送后账号列表自动回到顶部
|
||||
|
||||
### 2.1 现象
|
||||
|
||||
用户在某个分组中滚动到第 N 个账号,点击 🔔 开启/关闭推送后,列表瞬间回到顶部,用户丢失当前浏览位置。
|
||||
|
||||
### 2.2 根因分析
|
||||
|
||||
`toggleTelegramPush` 成功后调用 `loadAccountsByGroup(currentGroupId, true)`,该函数的核心逻辑:
|
||||
|
||||
```javascript
|
||||
// groups.js:166 — 立即替换为加载动画
|
||||
container.innerHTML = '<div class="loading-overlay">...</div>';
|
||||
|
||||
// groups.js:175 — API 返回后完整替换
|
||||
renderAccountList(data.accounts);
|
||||
// → container.innerHTML = accounts.map(...).join(''); // 第230行
|
||||
```
|
||||
|
||||
**`innerHTML` 赋值会销毁所有 DOM 子元素并重建**,浏览器滚动位置随之清零。
|
||||
|
||||
### 2.3 影响范围
|
||||
|
||||
**不仅限于推送切换**,以下操作都有同样问题:
|
||||
|
||||
| 操作 | 调用位置 | 同样回滚? |
|
||||
|------|---------|-----------|
|
||||
| `toggleTelegramPush` | accounts.js:557 | ✅ |
|
||||
| `deleteAccount` | accounts.js:534 | ✅ |
|
||||
| `toggleAccountStatus` | accounts.js:486 | ✅ |
|
||||
| `batchDeleteAccounts` | main.js:2280 | ✅ |
|
||||
| `confirmBatchMoveGroup` | main.js:2437 | ✅ |
|
||||
| `confirmBatchTag` | main.js:2361 | ✅ |
|
||||
|
||||
### 2.4 修复方案对比
|
||||
|
||||
| 方案 | 优点 | 缺点 | 推荐 |
|
||||
|------|------|------|------|
|
||||
| **A: 保存/恢复滚动位置** | 简单,改动小 | 列表重建时会闪烁 | ✅ 推荐 |
|
||||
| B: 局部 DOM 更新 | 无闪烁,最佳体验 | 改动大,需重构渲染逻辑 | P2 |
|
||||
| C: 虚拟滚动 | 大列表性能好 | 过度设计 | 不推荐 |
|
||||
|
||||
#### 方案 A 实现(推荐)
|
||||
|
||||
在 `loadAccountsByGroup` 中保存/恢复滚动位置:
|
||||
|
||||
```javascript
|
||||
async function loadAccountsByGroup(groupId, forceRefresh = false) {
|
||||
const container = document.getElementById('accountList');
|
||||
const scrollTop = container.scrollTop; // ← 保存滚动位置
|
||||
|
||||
// ... 原有加载逻辑 ...
|
||||
|
||||
renderAccountList(data.accounts);
|
||||
container.scrollTop = scrollTop; // ← 恢复滚动位置
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:需要去掉加载时的 `container.innerHTML = '加载中…'` 占位(forceRefresh 时直接原地刷新,避免闪烁)。
|
||||
|
||||
更精细的实现:仅在"单账号更新"场景(toggle、状态切换等)保留滚动,在"整组刷新"场景(切换分组)允许重置。
|
||||
|
||||
---
|
||||
|
||||
## 三、D-2:收到一次推送后不再推送新邮件
|
||||
|
||||
### 3.1 现象
|
||||
|
||||
用户在 Telegram 收到一次邮件推送通知后,后续即使有新邮件到达,也不再收到推送。
|
||||
|
||||
### 3.2 时间线还原
|
||||
|
||||
| 时间 (CST) | 事件 | `sent` |
|
||||
|------------|------|--------|
|
||||
| 17:04 | 修复前 Job 运行 | **sent=1**(时区 Bug,重复推送旧邮件) |
|
||||
| 17:06 | 修复前 Job | sent=1 |
|
||||
| 17:08 | 修复前 Job | sent=1 |
|
||||
| 17:16 | **部署时区修复** | - |
|
||||
| 17:18 | 修复后首次 Job | **sent=0** ✅(旧邮件被正确过滤) |
|
||||
|
||||
### 3.3 根因分析
|
||||
|
||||
**这是"看起来像 Bug 但其实是修复生效"的场景。**
|
||||
|
||||
修复前:
|
||||
- Gmail 邮箱的一封旧邮件(CST 某时间)的 `received_iso` 为本地时间 `17:xx:xx`
|
||||
- 游标为 UTC `09:xx:xx`
|
||||
- 本地 17:xx > UTC 09:xx → 每次都通过过滤 → **每轮都推送同一封邮件**
|
||||
- 用户误以为"推送在工作"
|
||||
|
||||
修复后:
|
||||
- 同一封邮件转换为 UTC 后 `received_iso = 09:xx:xx`
|
||||
- 游标已推进到 `09:26` → `09:xx <= 09:26` → **正确过滤**
|
||||
- `sent=0` → 用户以为"推送不工作了"
|
||||
|
||||
**实际上推送功能正常,只是当前环境的网络问题导致无法测试:**
|
||||
1. Outlook 账号 → Graph API 超时(无代理)
|
||||
2. QQ 账号 → IMAP 超时(无法连接 imap.qq.com)
|
||||
3. Gmail 账号 → IMAP 成功,但无新邮件到达
|
||||
|
||||
### 3.4 验证方法
|
||||
|
||||
要确认推送功能是否正常,可以:
|
||||
|
||||
1. **发送测试邮件**:向 Gmail 账号 `h15002710886@gmail.com` 发送一封新邮件,等待 1-2 分钟观察 Telegram 是否收到推送
|
||||
2. **检查日志**:重新启动后监控日志,等待 `sent=1` 出现
|
||||
3. **手动测试**:使用已有的 "📨 发送测试消息" 按钮确认 Telegram Bot API 仍然可用
|
||||
|
||||
### 3.5 潜在的隐藏问题
|
||||
|
||||
即使推送逻辑正确,以下情况仍会导致"收不到推送":
|
||||
|
||||
| 场景 | 原因 | 状态 |
|
||||
|------|------|------|
|
||||
| 所有 Graph API 超时 | 无代理,无法连接 login.microsoftonline.com | 已知(BUG-TG-004) |
|
||||
| QQ IMAP 超时 | 无法连接 imap.qq.com | 已知 |
|
||||
| Job 被跳过 | 上一轮还在运行(max_instances=1) | 已知 |
|
||||
| Job 耗时过长 | 6 个账号×30s 超时 = 最长 180s | 可优化 |
|
||||
| 错误时游标仍推进 | `finally` 块无条件推进游标 | **待讨论** |
|
||||
|
||||
#### 关于"错误时游标是否应推进"
|
||||
|
||||
**当前行为**: 无论拉取成功或失败,游标都推进到 `job_start_time`(在 `finally` 中)。
|
||||
|
||||
**优缺点**:
|
||||
- ✅ 优点:防止失败账号积累大量待推送邮件(如网络恢复后突然推送几百封)
|
||||
- ❌ 缺点:拉取失败期间的邮件永远不会被推送(被游标跳过了)
|
||||
|
||||
**MailAggregator_Pro 的做法**: 基于 Message-ID 去重,不依赖游标推进。失败后重试时,已推送的不会重复。
|
||||
|
||||
**建议**:
|
||||
- 短期:保持当前行为(防止轰炸)
|
||||
- 中期:仅在成功拉取后推进游标(失败时保留旧游标,但加 `PUSH_RECENCY_HOURS` 兜底)
|
||||
|
||||
---
|
||||
|
||||
## 四、修复建议
|
||||
|
||||
### 立即可做(本轮)
|
||||
|
||||
- [ ] D-1: `loadAccountsByGroup` 增加滚动位置保存/恢复
|
||||
- [ ] 确认 D-2: 向 Gmail 发送测试邮件验证推送
|
||||
|
||||
### 后续优化(P1)
|
||||
|
||||
- 仅成功时推进游标 + `PUSH_RECENCY_HOURS = 12` 兜底
|
||||
- 消息间延迟 `TELEGRAM_PUSH_DELAY_SEC = 1.5`
|
||||
- Job 执行日志中增加 per-account 结果(成功/失败/跳过)
|
||||
Reference in new issue
Block a user