✅ 数据库查询一致性: 已通过 ✅ WebSocket 推送一致性: 已修复 ✅ 前端数据同步: 已优化 ✅ 关联数据完整性: 已确保
- ✅ LEFT JOIN: 正确关联
tg_media和uploads表 - ✅ 多上传处理: 正确处理一个下载对应多个上传的情况
- ✅ 数据去重: 使用字典去重,避免重复记录
- ✅ 排序: 上传记录按创建时间正序排序
SQL 查询:
SELECT d.*, m.*, u.*
FROM downloads AS d
LEFT JOIN tg_media AS m ON d.file_unique_id = m.file_unique_id
LEFT JOIN uploads AS u ON u.download_id = d.id
ORDER BY d.created_at DESC, u.created_at DESC- ✅ LEFT JOIN: 正确关联
downloads和tg_media表 - ✅ 过滤条件: 支持按状态和上传目标过滤
- ✅ 关联数据: 包含下载状态、GID、文件名等信息
SQL 查询:
SELECT u.*, d.local_path, d.status as download_status, d.gid, m.file_name, m.file_size
FROM uploads AS u
LEFT JOIN downloads AS d ON u.download_id = d.id
LEFT JOIN tg_media AS m ON d.file_unique_id = m.file_unique_id
ORDER BY u.created_at DESC- ✅ 分组逻辑: 按
media_group_id或chat_id+message_id分组 - ✅ 完成状态判断:
is_truly_completed()函数正确检查上传状态 - ✅ 统计计算: 正确计算组内统计信息
修复前问题:
- ❌ 只推送下载信息,不包含上传信息
- ❌ 前端无法判断下载是否真正完成(需要检查上传状态)
修复后:
- ✅ 包含上传信息列表 (
uploads) - ✅ 每个上传记录包含关键字段:
id,status,uploaded_size,total_size,upload_speed,cleaned_at - ✅ 确保前端能正确判断完成状态
推送数据结构:
{
"gid": gid,
"download_id": download_id,
"status": download.get('status'),
"completed_length": download.get('completed_length'),
"total_length": download.get('total_length'),
"download_speed": download.get('download_speed'),
"uploads": [ # 新增:上传信息列表
{
"id": upload_id,
"upload_target": upload_target,
"status": status,
"uploaded_size": uploaded_size,
"total_size": total_size,
"upload_speed": upload_speed,
"cleaned_at": cleaned_at,
}
]
}- ✅ 推送完整的上传信息
- ✅ 包含
download_id,前端可同时更新下载记录中的上传信息 - ✅ 包含
cleaned_at,用于判断清理状态
- ✅ 推送清理状态更新
- ✅ 前端可更新上传记录的
cleaned_at字段
修复前问题:
- ❌ 只更新下载字段,不更新上传信息
- ❌ 可能导致上传状态不一致
修复后:
- ✅ 如果 WebSocket 推送包含上传信息,同步更新下载记录中的上传列表
- ✅ 支持更新现有上传记录和添加新上传记录
- ✅ 移除不在推送列表中的上传记录(确保数据一致性)
更新逻辑:
// 如果 WebSocket 推送了上传信息,更新上传列表
if (uploads_data && Array.isArray(uploads_data)) {
// 确保 uploads 数组存在
if (!download.uploads) {
download.uploads = []
}
// 更新或添加上传记录
for (const uploadUpdate of uploads_data) {
const existingUploadIndex = download.uploads.findIndex(u => u.id === uploadUpdate.id)
if (existingUploadIndex !== -1) {
Object.assign(download.uploads[existingUploadIndex], uploadUpdate)
} else {
download.uploads.push(uploadUpdate)
}
}
// 移除不在推送列表中的上传记录
download.uploads = download.uploads.filter(u =>
uploads_data.some(ud => ud.id === u.id)
)
}- ✅ 同时更新上传列表和下载记录中的上传信息
- ✅ 更新组的统计数据
- ✅ 确保数据一致性
- ✅ 基于下载和上传状态重新计算组统计
- ✅ 正确判断是否真正完成(
isTrulyCompleted) - ✅ 考虑上传状态和清理状态
- ✅ 外键约束:
uploads.download_id→downloads.id(ON DELETE CASCADE) - ✅ 事务管理: 使用
db_cursor()上下文管理器确保原子性 - ✅ WAL 模式: 提升并发性能,减少锁竞争
- ✅ WebSocket 推送: 每次数据库更新后推送
- ✅ 轮询同步: 定期同步 aria2 状态与数据库
- ✅ 前端同步: WebSocket 更新时同步关联数据
- ✅ 下载完成判断: 检查上传状态,只有所有上传完成才算真正完成
- ✅ 清理状态: 检查
cleaned_at字段判断是否已清理 - ✅ 统计计算: 基于实际状态计算,而非简单计数
- ✅ WebSocket 下载更新缺少上传信息: 已修复,现在包含上传信息
- ✅ 前端更新不完整: 已修复,现在同步更新上传信息
- 定期数据校验: 添加定期校验脚本,检查数据库一致性
- 错误恢复: 如果 WebSocket 更新失败,前端应自动刷新数据
- 日志记录: 记录所有数据更新操作,便于排查问题
- 性能优化: 如果上传记录很多,考虑只推送关键字段
- ✅ 创建下载任务,检查是否包含上传信息
- ✅ 更新下载状态,检查上传信息是否同步
- ✅ 更新上传状态,检查下载记录是否更新
- ✅ 删除上传记录,检查下载记录是否更新
- ✅ 检查数据库查询结果是否包含完整关联数据
- ✅ 检查 WebSocket 推送是否包含完整数据
- ✅ 检查前端显示是否与实际数据一致
- ✅ 检查统计信息是否准确
经过检查和修复,任务中心的上传下载记录数据一致性已得到保证:
- 数据库查询: ✅ 正确关联所有相关表
- WebSocket 推送: ✅ 包含完整的上传信息
- 前端同步: ✅ 正确更新关联数据
- 状态判断: ✅ 基于完整数据判断完成状态
所有数据同步机制已正常工作,数据一致性得到保证。