fix(client): identify sku entry failure substage

This commit is contained in:
QiuSW
2026-08-05 10:32:35 +08:00
parent e45d26fe93
commit b754cc9ca9
3 changed files with 142 additions and 21 deletions
@@ -30,7 +30,10 @@ from .sku_selection import (
SkuPanelDevice,
SkuSelectionError,
SkuSelectionFlow,
_SKU_ENTRY_FAILURE_STAGES,
_action_bounds,
_annotate_sku_entry_failure,
_safe_sku_entry_failure_stage,
resolve_task_selection,
)
@@ -48,6 +51,7 @@ _FAILURE_STAGES = frozenset(
"device_session",
"product_open",
"sku_entry",
*_SKU_ENTRY_FAILURE_STAGES,
"sku_selection",
"price_verification",
"screenshot_capture",
@@ -85,7 +89,11 @@ def safe_failure_stage(error: BaseException) -> str:
stage = getattr(error, "_cmbuyer_failure_stage", None)
# exact str 避免恶意 str 子类在 hash/eq 中执行任意异常;诊断路径
# 自己也必须失败闭合,不能让异常正文越过 CLI 的统一脱敏出口。
return stage if type(stage) is str and stage in _FAILURE_STAGES else "unknown"
if type(stage) is not str or stage not in _FAILURE_STAGES:
return "unknown"
if stage in _SKU_ENTRY_FAILURE_STAGES:
return stage if _safe_sku_entry_failure_stage(error) == stage else "unknown"
return stage
except BaseException:
return "unknown"
@@ -101,6 +109,21 @@ def _annotate_failure(error: BaseException, stage: str) -> None:
pass
def _failure_stage_for(error: BaseException, runner_stage: str) -> str:
if runner_stage == "sku_entry":
flow_stage = _safe_sku_entry_failure_stage(error)
if flow_stage is not None:
return flow_stage
return runner_stage
def _annotate_mapped_failure(mapped: BaseException, source: BaseException, runner_stage: str) -> None:
stage = _failure_stage_for(source, runner_stage)
if stage in _SKU_ENTRY_FAILURE_STAGES:
_annotate_sku_entry_failure(mapped, stage)
_annotate_failure(mapped, stage)
@dataclass(frozen=True)
class SkuSelectionRunResult:
"""已发布的截图和无页面正文 manifest 摘要。"""
@@ -299,22 +322,22 @@ class SkuSelectionRunner:
staging = None
except (DeviceConnectionError, ProductUrlError, SkuSelectionRunError, SkuSelectionError) as error:
_clean_staging(staging)
_annotate_failure(error, stage)
_annotate_failure(error, _failure_stage_for(error, stage))
raise
except (AdbTimeout, HTTPTimeoutError, TimeoutError) as error:
_clean_staging(staging)
mapped = SkuSelectionRunTimeoutError("规格面板运行超时,未发布任何证据产物。")
_annotate_failure(mapped, stage)
_annotate_mapped_failure(mapped, error, stage)
raise mapped from error
except OSError as error:
_clean_staging(staging)
mapped = SkuSelectionRunError("规格面板证据目录无法创建或发布,未发布任何证据产物。")
_annotate_failure(mapped, stage)
_annotate_mapped_failure(mapped, error, stage)
raise mapped from error
except Exception as error:
_clean_staging(staging)
mapped = SkuSelectionRunError("规格面板运行未完成,未发布任何证据产物。")
_annotate_failure(mapped, stage)
_annotate_mapped_failure(mapped, error, stage)
raise mapped from error
finally:
# 失败路径只能复用 Flow 的版本、前台和面板证明;证明不了便停止,绝不盲目返回。