fix(u115): handle missing path response

This commit is contained in:
jxxghp
2026-08-03 08:16:45 +08:00
parent 51d2ed1200
commit d8adb4fbfe
2 changed files with 92 additions and 8 deletions

View File

@@ -29,6 +29,8 @@ lock = Lock()
MIN_U115_UPLOAD_PART_SIZE = 1 * 1024 * 1024
U115_UPLOAD_PART_COUNT_TARGET = 96
U115_DEFAULT_ACCEPTED_CODES = (0, 20004)
U115_GET_INFO_ACCEPTED_CODES = (*U115_DEFAULT_ACCEPTED_CODES, 430004)
U115_UPLOAD_PART_SIZE_STEPS = (
10 * 1024 * 1024,
16 * 1024 * 1024,
@@ -298,10 +300,18 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
return result.get("data")
def _request_api(
self, method: str, endpoint: str, result_key: Optional[str] = None, **kwargs
self,
method: str,
endpoint: str,
result_key: Optional[str] = None,
*,
accepted_codes: Tuple[int, ...] = U115_DEFAULT_ACCEPTED_CODES,
**kwargs,
) -> Optional[Union[dict, list]]:
"""
带错误处理和速率限制的API请求
:param accepted_codes: 当前接口可确认处理的业务码
"""
# 检查会话
self._check_session()
@@ -358,7 +368,13 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
time.sleep(self.limit_sleep_seconds)
kwargs["retry_limit"] = retry_times - 1
kwargs["no_error_log"] = no_error_log
return self._request_api(method, endpoint, result_key, **kwargs)
return self._request_api(
method,
endpoint,
result_key,
accepted_codes=accepted_codes,
**kwargs,
)
# 处理请求错误
try:
@@ -376,11 +392,17 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
f"【115】{method} 请求 {endpoint} 错误 {e},等待 {sleep_duration} 秒后重试..."
)
time.sleep(sleep_duration)
return self._request_api(method, endpoint, result_key, **kwargs)
return self._request_api(
method,
endpoint,
result_key,
accepted_codes=accepted_codes,
**kwargs,
)
# 返回数据
ret_data = resp.json()
if ret_data.get("code") not in (0, 20004):
if ret_data.get("code") not in accepted_codes:
error_msg = ret_data.get("message", "")
if not no_error_log:
logger.warn(f"【115】{method} 请求 {endpoint} 出错:{error_msg}")
@@ -402,7 +424,13 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
time.sleep(self.limit_sleep_seconds)
kwargs["retry_limit"] = retry_times - 1
kwargs["no_error_log"] = no_error_log
return self._request_api(method, endpoint, result_key, **kwargs)
return self._request_api(
method,
endpoint,
result_key,
accepted_codes=accepted_codes,
**kwargs,
)
return None
if result_key:
@@ -910,20 +938,22 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
def __get_info_item(self, path: Path) -> Optional[schemas.FileItem]:
"""
查询指定路径的文件/目录项,无法确认状态时抛出 StorageQueryError。
接口业务码 20004记录不存在与 0 一样视为确认结果,其余错误
(网络失败、限流重试用尽、未知业务错误)均无法确认目标状态。
接口业务码 20004记录不存在、430004路径不存在与 0 一样
视为确认结果,其余错误(网络失败、限流重试用尽、未知业务错误)
均无法确认目标状态。
"""
resp = self._request_api(
"POST",
"/open/folder/get_info",
data={"path": path.as_posix()},
no_error_log=True,
accepted_codes=U115_GET_INFO_ACCEPTED_CODES,
)
if resp is None:
raise StorageQueryError(f"【115】无法确认文件状态请求失败或接口错误: {path}")
data = resp.get("data") if isinstance(resp, dict) else None
if not data or not data.get("file_id"):
# code 20004记录不存在等场景确认目标不存在
# 115 对记录不存在和路径不存在返回不同业务码,两者都可确认目标不存在
return None
return schemas.FileItem(
storage=self.schema.value,

View File

@@ -1,4 +1,5 @@
from pathlib import Path
from threading import Lock
from unittest.mock import MagicMock
import pytest
@@ -24,6 +25,24 @@ def _u115() -> U115Pan:
return object.__new__(U115Pan)
def _u115_with_api_payload(payload: dict) -> U115Pan:
"""
构造返回固定业务响应的 115 存储实例。
"""
storage = _u115()
response = MagicMock(status_code=200)
response.json.return_value = payload
storage.session = MagicMock()
storage.session.request.return_value = response
storage._check_session = MagicMock()
storage._download_limiter = MagicMock()
storage._api_limiter = MagicMock()
storage._rate_stats = MagicMock()
storage._limit_lock = Lock()
storage._limit_until = 0.0
return storage
def _alipan(monkeypatch) -> AliPan:
"""
构造阿里云盘存储实例跳过初始化_default_drive_id 为只读属性需在类级替换)。
@@ -111,6 +130,41 @@ def test_u115_strict_confirmed_absent_returns_none():
assert storage.get_item_strict(Path("/movie.mkv")) is None
def test_u115_strict_path_not_found_returns_none():
"""
115 查询路径返回 430004 时应确认为不存在,允许首次整理。
"""
storage = _u115_with_api_payload(
{"state": False, "code": 430004, "message": "路径不存在", "data": {}}
)
assert storage.get_item_strict(Path("/movie.mkv")) is None
def test_u115_strict_empty_list_returns_none():
"""
115 查询不存在目标返回空列表时也应确认为不存在。
"""
storage = _u115_with_api_payload(
{"state": True, "code": 0, "message": "", "data": []}
)
assert storage.get_item_strict(Path("/movie.mkv")) is None
def test_u115_path_not_found_code_is_not_globally_accepted():
"""
非路径查询接口返回 430004 时仍应视为业务错误。
"""
storage = _u115_with_api_payload(
{"state": False, "code": 430004, "message": "路径不存在", "data": {}}
)
result = storage._request_api("POST", "/open/folder/add", data={"file_name": "TV"})
assert result is None
def test_u115_strict_returns_item():
"""
115 返回有效文件数据时应构造文件项。