mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-17 01:40:44 +08:00
修复资源搜索结果在大样本下超时后重复追加,导致重复渲染与计数膨胀的问题
- 前端:引入搜索 “会话号 + validating” 双重校验,超时立即取消当前会话,并在批处理/渲染前校验,阻断超时后的继续写入;保留稳定 v-for key 确保渲染一致性 - 后端:`get_detail` 增强容错,避免无 `code`/网络异常引发 KeyError;`/get_share_detail` 统一错误返回结构,前端稳定处理
This commit is contained in:
parent
3ccaeeae15
commit
5216fa981d
@ -1200,6 +1200,10 @@ def get_share_detail():
|
|||||||
if not is_sharing:
|
if not is_sharing:
|
||||||
return jsonify({"success": False, "data": {"error": stoken}})
|
return jsonify({"success": False, "data": {"error": stoken}})
|
||||||
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, _fetch_share=1)
|
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, _fetch_share=1)
|
||||||
|
# 统一错误返回,避免前端崩溃
|
||||||
|
if isinstance(share_detail, dict) and share_detail.get("error"):
|
||||||
|
return jsonify({"success": False, "data": {"error": share_detail.get("error")}})
|
||||||
|
|
||||||
share_detail["paths"] = paths
|
share_detail["paths"] = paths
|
||||||
share_detail["stoken"] = stoken
|
share_detail["stoken"] = stoken
|
||||||
|
|
||||||
|
|||||||
@ -1032,7 +1032,7 @@
|
|||||||
<div class="dropdown-item text-muted" v-else style="font-size:14px; padding-left: 8px; text-align: left;">
|
<div class="dropdown-item text-muted" v-else style="font-size:14px; padding-left: 8px; text-align: left;">
|
||||||
{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data && smart_param.taskSuggestions.data.length ? `以下资源由 ${(smart_param.taskSuggestions.source || '').replace(/,\s*/g, '、')} 搜索提供(仅显示有效链接,共 ${(smart_param.taskSuggestions.data || []).length} 个),如有侵权请联系资源发布方` : "未搜索到有效资源" }}
|
{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data && smart_param.taskSuggestions.data.length ? `以下资源由 ${(smart_param.taskSuggestions.source || '').replace(/,\s*/g, '、')} 搜索提供(仅显示有效链接,共 ${(smart_param.taskSuggestions.data || []).length} 个),如有侵权请联系资源发布方` : "未搜索到有效资源" }}
|
||||||
</div>
|
</div>
|
||||||
<div v-for="suggestion in smart_param.taskSuggestions.data || []" :key="suggestion.taskname" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(index, suggestion)" style="font-size: 14px;" :title="getSuggestionHoverTitle(suggestion)">
|
<div v-for="suggestion in smart_param.taskSuggestions.data || []" :key="(suggestion.shareurl || '') + '_' + (suggestion.taskname || '') + '_' + (suggestion.publish_date || '')" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(index, suggestion)" style="font-size: 14px;" :title="getSuggestionHoverTitle(suggestion)">
|
||||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
||||||
@ -1960,7 +1960,7 @@
|
|||||||
<div class="dropdown-item text-muted" v-else style="font-size:14px; padding-left: 8px; text-align: left;">
|
<div class="dropdown-item text-muted" v-else style="font-size:14px; padding-left: 8px; text-align: left;">
|
||||||
{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data && smart_param.taskSuggestions.data.length ? `以下资源由 ${(smart_param.taskSuggestions.source || '').replace(/,\s*/g, '、')} 搜索提供(仅显示有效链接,共 ${(smart_param.taskSuggestions.data || []).length} 个),如有侵权请联系资源发布方` : "未搜索到有效资源" }}
|
{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data && smart_param.taskSuggestions.data.length ? `以下资源由 ${(smart_param.taskSuggestions.source || '').replace(/,\s*/g, '、')} 搜索提供(仅显示有效链接,共 ${(smart_param.taskSuggestions.data || []).length} 个),如有侵权请联系资源发布方` : "未搜索到有效资源" }}
|
||||||
</div>
|
</div>
|
||||||
<div v-for="suggestion in smart_param.taskSuggestions.data || []" :key="suggestion.taskname" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(-1, suggestion)" style="font-size: 14px;" :title="getSuggestionHoverTitle(suggestion)">
|
<div v-for="suggestion in smart_param.taskSuggestions.data || []" :key="(suggestion.shareurl || '') + '_' + (suggestion.taskname || '') + '_' + (suggestion.publish_date || '')" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(-1, suggestion)" style="font-size: 14px;" :title="getSuggestionHoverTitle(suggestion)">
|
||||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
||||||
@ -2251,6 +2251,8 @@
|
|||||||
valid: 0
|
valid: 0
|
||||||
},
|
},
|
||||||
searchTimer: null,
|
searchTimer: null,
|
||||||
|
// 新增:搜索会话号用于取消上一次验证/渲染,避免卡死和重复
|
||||||
|
searchSessionId: 0
|
||||||
},
|
},
|
||||||
activeTab: 'config',
|
activeTab: 'config',
|
||||||
configModified: false,
|
configModified: false,
|
||||||
@ -4274,6 +4276,8 @@
|
|||||||
// 确保显示下拉菜单
|
// 确保显示下拉菜单
|
||||||
this.smart_param.showSuggestions = true;
|
this.smart_param.showSuggestions = true;
|
||||||
try {
|
try {
|
||||||
|
// 启动新的搜索会话,后续增量结果仅在会话一致时才渲染
|
||||||
|
const sessionId = ++this.smart_param.searchSessionId;
|
||||||
axios.get('/task_suggestions', {
|
axios.get('/task_suggestions', {
|
||||||
params: {
|
params: {
|
||||||
q: taskname,
|
q: taskname,
|
||||||
@ -4281,9 +4285,10 @@
|
|||||||
}
|
}
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
// 接收到数据后,过滤无效链接
|
// 接收到数据后,过滤无效链接
|
||||||
|
if (sessionId !== this.smart_param.searchSessionId) return; // 旧会话结果忽略
|
||||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||||
// 使用新增的方法验证链接有效性
|
// 使用新增的方法验证链接有效性
|
||||||
this.validateSearchResults(response.data);
|
this.validateSearchResults(response.data, sessionId);
|
||||||
} else {
|
} else {
|
||||||
this.smart_param.taskSuggestions = response.data;
|
this.smart_param.taskSuggestions = response.data;
|
||||||
// 重新确认设置为true
|
// 重新确认设置为true
|
||||||
@ -4294,6 +4299,7 @@
|
|||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.smart_param.isSearching = false;
|
this.smart_param.isSearching = false;
|
||||||
this.smart_param.validating = false; // 重置验证状态
|
this.smart_param.validating = false; // 重置验证状态
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.smart_param.taskSuggestions = {
|
this.smart_param.taskSuggestions = {
|
||||||
@ -4301,10 +4307,11 @@
|
|||||||
};
|
};
|
||||||
this.smart_param.isSearching = false;
|
this.smart_param.isSearching = false;
|
||||||
this.smart_param.validating = false; // 重置验证状态
|
this.smart_param.validating = false; // 重置验证状态
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 添加新方法来验证搜索结果
|
// 添加新方法来验证搜索结果
|
||||||
validateSearchResults(searchData) {
|
validateSearchResults(searchData, sessionId) {
|
||||||
const invalidTerms = [
|
const invalidTerms = [
|
||||||
"分享者用户封禁链接查看受限",
|
"分享者用户封禁链接查看受限",
|
||||||
"好友已取消了分享",
|
"好友已取消了分享",
|
||||||
@ -4347,6 +4354,7 @@
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (!link.shareurl) {
|
if (!link.shareurl) {
|
||||||
// 没有分享链接,直接跳过
|
// 没有分享链接,直接跳过
|
||||||
|
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4358,6 +4366,10 @@
|
|||||||
.then(response => {
|
.then(response => {
|
||||||
// 更新进度
|
// 更新进度
|
||||||
this.smart_param.validateProgress.current++;
|
this.smart_param.validateProgress.current++;
|
||||||
|
if (sessionId !== this.smart_param.searchSessionId) {
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
// 检查文件列表是否为空
|
// 检查文件列表是否为空
|
||||||
@ -4365,6 +4377,7 @@
|
|||||||
if (shareDetail.list && shareDetail.list.length > 0) {
|
if (shareDetail.list && shareDetail.list.length > 0) {
|
||||||
// 链接有效,添加到有效结果列表
|
// 链接有效,添加到有效结果列表
|
||||||
this.smart_param.validateProgress.valid++;
|
this.smart_param.validateProgress.valid++;
|
||||||
|
|
||||||
resolve(link);
|
resolve(link);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4390,18 +4403,21 @@
|
|||||||
// 如果不是已知的失效原因,保留该结果
|
// 如果不是已知的失效原因,保留该结果
|
||||||
if (!isInvalid) {
|
if (!isInvalid) {
|
||||||
this.smart_param.validateProgress.valid++;
|
this.smart_param.validateProgress.valid++;
|
||||||
|
|
||||||
resolve(link);
|
resolve(link);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 链接无效
|
// 链接无效
|
||||||
|
|
||||||
resolve(null);
|
resolve(null);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// 验证出错,保守处理为有效
|
// 验证出错,保守处理为有效
|
||||||
this.smart_param.validateProgress.current++;
|
this.smart_param.validateProgress.current++;
|
||||||
this.smart_param.validateProgress.valid++;
|
this.smart_param.validateProgress.valid++;
|
||||||
|
|
||||||
resolve(link);
|
resolve(link);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -4409,6 +4425,8 @@
|
|||||||
|
|
||||||
// 修改processBatch函数,增加快速显示功能
|
// 修改processBatch函数,增加快速显示功能
|
||||||
const processBatch = async () => {
|
const processBatch = async () => {
|
||||||
|
// 新会话已开始或已被取消(validating=false)则停止当前批次
|
||||||
|
if (sessionId !== this.smart_param.searchSessionId || !this.smart_param.validating) return;
|
||||||
// 取下一批处理
|
// 取下一批处理
|
||||||
const batch = toProcess.splice(0, batchSize);
|
const batch = toProcess.splice(0, batchSize);
|
||||||
if (batch.length === 0) {
|
if (batch.length === 0) {
|
||||||
@ -4431,6 +4449,7 @@
|
|||||||
validResults.sort((a, b) => getItemTs(b) - getItemTs(a));
|
validResults.sort((a, b) => getItemTs(b) - getItemTs(a));
|
||||||
|
|
||||||
// 每批次都增量更新到界面,显示当前有效数量并保持正在验证状态
|
// 每批次都增量更新到界面,显示当前有效数量并保持正在验证状态
|
||||||
|
if (sessionId !== this.smart_param.searchSessionId || !this.smart_param.validating) return; // 渲染前再次检查会话
|
||||||
this.smart_param.taskSuggestions = {
|
this.smart_param.taskSuggestions = {
|
||||||
success: searchData.success,
|
success: searchData.success,
|
||||||
source: searchData.source,
|
source: searchData.source,
|
||||||
@ -4449,7 +4468,10 @@
|
|||||||
// 设置超时,避免永久等待
|
// 设置超时,避免永久等待
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 如果验证还在进行中,强制完成
|
// 如果验证还在进行中,强制完成
|
||||||
if (this.smart_param.validating) {
|
if (this.smart_param.validating && sessionId === this.smart_param.searchSessionId) {
|
||||||
|
// 在收尾前立即取消会话,避免后续批次继续追加导致重复
|
||||||
|
this.smart_param.validating = false;
|
||||||
|
this.smart_param.searchSessionId++;
|
||||||
// 将剩余未验证的链接添加到结果中
|
// 将剩余未验证的链接添加到结果中
|
||||||
const remaining = toProcess.filter(item => item.shareurl);
|
const remaining = toProcess.filter(item => item.shareurl);
|
||||||
validResults.push(...remaining);
|
validResults.push(...remaining);
|
||||||
@ -4460,6 +4482,7 @@
|
|||||||
|
|
||||||
// 完成验证
|
// 完成验证
|
||||||
this.finishValidation(searchData, validResults);
|
this.finishValidation(searchData, validResults);
|
||||||
|
|
||||||
}
|
}
|
||||||
}, 30000); // 30秒超时
|
}, 30000); // 30秒超时
|
||||||
},
|
},
|
||||||
@ -4480,6 +4503,7 @@
|
|||||||
message: validResults.length === 0 ? "未找到有效的分享链接" : searchData.message
|
message: validResults.length === 0 ? "未找到有效的分享链接" : searchData.message
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this.smart_param.taskSuggestions = result;
|
this.smart_param.taskSuggestions = result;
|
||||||
this.smart_param.isSearching = false;
|
this.smart_param.isSearching = false;
|
||||||
this.smart_param.validating = false;
|
this.smart_param.validating = false;
|
||||||
|
|||||||
@ -1225,18 +1225,39 @@ class Quark:
|
|||||||
"_fetch_total": "1",
|
"_fetch_total": "1",
|
||||||
"_sort": "file_type:asc,updated_at:desc",
|
"_sort": "file_type:asc,updated_at:desc",
|
||||||
}
|
}
|
||||||
|
# 兼容网络错误或服务端异常
|
||||||
|
try:
|
||||||
response = self._send_request("GET", url, params=querystring).json()
|
response = self._send_request("GET", url, params=querystring).json()
|
||||||
if response["code"] != 0:
|
except Exception:
|
||||||
return {"error": response["message"]}
|
return {"error": "request error"}
|
||||||
if response["data"]["list"]:
|
|
||||||
list_merge += response["data"]["list"]
|
# 统一判错:某些情况下返回没有 code 字段
|
||||||
|
code = response.get("code")
|
||||||
|
status = response.get("status")
|
||||||
|
if code not in (0, None):
|
||||||
|
return {"error": response.get("message", "unknown error")}
|
||||||
|
if status not in (None, 200):
|
||||||
|
return {"error": response.get("message", "request error")}
|
||||||
|
|
||||||
|
data = response.get("data") or {}
|
||||||
|
metadata = response.get("metadata") or {}
|
||||||
|
|
||||||
|
if data.get("list"):
|
||||||
|
list_merge += data["list"]
|
||||||
page += 1
|
page += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if len(list_merge) >= response["metadata"]["_total"]:
|
# 防御性:metadata 或 _total 缺失时不再访问嵌套键
|
||||||
|
total = metadata.get("_total") if isinstance(metadata, dict) else None
|
||||||
|
if isinstance(total, int) and len(list_merge) >= total:
|
||||||
break
|
break
|
||||||
response["data"]["list"] = list_merge
|
# 统一输出结构,缺失字段时提供默认值
|
||||||
return response["data"]
|
if not isinstance(data, dict):
|
||||||
|
return {"error": response.get("message", "request error")}
|
||||||
|
data["list"] = list_merge
|
||||||
|
if "paths" not in data:
|
||||||
|
data["paths"] = []
|
||||||
|
return data
|
||||||
|
|
||||||
def get_fids(self, file_paths):
|
def get_fids(self, file_paths):
|
||||||
fids = []
|
fids = []
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user