Harden patrol delete logging
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -38,6 +39,15 @@ def _new_trace_id(prefix: str) -> str:
|
|||||||
return f"{prefix}-{int(time.time() * 1000) % 1000000:06d}"
|
return f"{prefix}-{int(time.time() * 1000) % 1000000:06d}"
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_log_text(value: Any, limit: int = 3000) -> str:
|
||||||
|
text = "" if value is None else str(value)
|
||||||
|
text = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f]", " ", text)
|
||||||
|
if len(text) > limit:
|
||||||
|
text = text[: limit - 3] + "..."
|
||||||
|
encoding = getattr(getattr(sys, "stderr", None), "encoding", None) or "utf-8"
|
||||||
|
return text.encode(encoding, errors="backslashreplace").decode(encoding, errors="replace")
|
||||||
|
|
||||||
|
|
||||||
def _load_patrol_delete_script(name: str) -> str:
|
def _load_patrol_delete_script(name: str) -> str:
|
||||||
return (SCRIPT_DIR / name).read_text(encoding="utf-8")
|
return (SCRIPT_DIR / name).read_text(encoding="utf-8")
|
||||||
|
|
||||||
@@ -207,7 +217,10 @@ class InventoryManage(AmamzonBase):
|
|||||||
rows = self._wait_listing_status_section_rows()
|
rows = self._wait_listing_status_section_rows()
|
||||||
parsed_rows = self._parse_listing_status_section_rows(rows)
|
parsed_rows = self._parse_listing_status_section_rows(rows)
|
||||||
if not parsed_rows:
|
if not parsed_rows:
|
||||||
logger.info(f"trace_id={trace_id} 商品状态统计读取为空: raw_rows={json.dumps(rows, ensure_ascii=False)}")
|
logger.info(
|
||||||
|
f"trace_id={trace_id} 商品状态统计读取为空: "
|
||||||
|
f"raw_rows={_safe_log_text(json.dumps(rows, ensure_ascii=False))}"
|
||||||
|
)
|
||||||
raise RuntimeError(f"未读取到商品状态统计标签: {rows}")
|
raise RuntimeError(f"未读取到商品状态统计标签: {rows}")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"trace_id={trace_id} 商品状态统计读取完成: "
|
f"trace_id={trace_id} 商品状态统计读取完成: "
|
||||||
@@ -356,13 +369,16 @@ class InventoryManage(AmamzonBase):
|
|||||||
if not option_rows:
|
if not option_rows:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"trace_id={trace_id} 商品状态下拉选项为空: "
|
f"trace_id={trace_id} 商品状态下拉选项为空: "
|
||||||
f"open_result={json.dumps(open_result, ensure_ascii=False)}"
|
f"open_result={_safe_log_text(json.dumps(open_result, ensure_ascii=False))}"
|
||||||
)
|
)
|
||||||
raise RuntimeError(f"商品状态下拉框已打开但未读取到选项: {open_result}")
|
raise RuntimeError(f"商品状态下拉框已打开但未读取到选项: {open_result}")
|
||||||
|
|
||||||
result = self._run_js(LISTING_STATUS_SELECT_OPTION_SCRIPT, {"status": normalized_status}) or {}
|
result = self._run_js(LISTING_STATUS_SELECT_OPTION_SCRIPT, {"status": normalized_status}) or {}
|
||||||
if not result.get("ok"):
|
if not result.get("ok"):
|
||||||
logger.info(f"trace_id={trace_id} 商品状态选项未命中: status={normalized_status}, result={result}")
|
logger.info(
|
||||||
|
f"trace_id={trace_id} 商品状态选项未命中: "
|
||||||
|
f"status={normalized_status}, result={_safe_log_text(result)}"
|
||||||
|
)
|
||||||
raise RuntimeError(f"未找到商品状态选项[{normalized_status}]: {result}")
|
raise RuntimeError(f"未找到商品状态选项[{normalized_status}]: {result}")
|
||||||
time.sleep(LISTING_STATUS_SELECT_DELAY)
|
time.sleep(LISTING_STATUS_SELECT_DELAY)
|
||||||
self._wait_inventory_loader()
|
self._wait_inventory_loader()
|
||||||
@@ -413,7 +429,7 @@ class InventoryManage(AmamzonBase):
|
|||||||
target = self._summarize_inventory_row(row)
|
target = self._summarize_inventory_row(row)
|
||||||
select_result = self._run_js(INVENTORY_SELECT_ALL_SCRIPT) or {}
|
select_result = self._run_js(INVENTORY_SELECT_ALL_SCRIPT) or {}
|
||||||
if not select_result.get("ok"):
|
if not select_result.get("ok"):
|
||||||
logger.info(f"trace_id={trace_id} 全选商品失败: status={status}, result={select_result}")
|
logger.info(f"trace_id={trace_id} 全选商品失败: status={status}, result={_safe_log_text(select_result)}")
|
||||||
raise RuntimeError(f"状态[{status}]全选商品失败: {select_result}")
|
raise RuntimeError(f"状态[{status}]全选商品失败: {select_result}")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"trace_id={trace_id} 当前筛选结果已全选: status={status}, "
|
f"trace_id={trace_id} 当前筛选结果已全选: status={status}, "
|
||||||
@@ -427,21 +443,27 @@ class InventoryManage(AmamzonBase):
|
|||||||
if delete_result.get("ok"):
|
if delete_result.get("ok"):
|
||||||
break
|
break
|
||||||
if delete_result.get("menuOpened"):
|
if delete_result.get("menuOpened"):
|
||||||
logger.info(f"trace_id={trace_id} 状态[{status}]已点击选择组操作,等待删除菜单展开: {delete_result}")
|
logger.info(
|
||||||
|
f"trace_id={trace_id} 状态[{status}]已点击选择组操作,等待删除菜单展开: "
|
||||||
|
f"{_safe_log_text(delete_result)}"
|
||||||
|
)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
logger.info(f"trace_id={trace_id} 状态[{status}]第{attempt + 1}次点击批量删除未命中: {delete_result}")
|
logger.info(
|
||||||
|
f"trace_id={trace_id} 状态[{status}]第{attempt + 1}次点击批量删除未命中: "
|
||||||
|
f"{_safe_log_text(delete_result)}"
|
||||||
|
)
|
||||||
if not delete_result.get("ok"):
|
if not delete_result.get("ok"):
|
||||||
logger.info(f"trace_id={trace_id} 点击批量删除失败: status={status}, result={delete_result}")
|
logger.info(f"trace_id={trace_id} 点击批量删除失败: status={status}, result={_safe_log_text(delete_result)}")
|
||||||
raise RuntimeError(f"状态[{status}]点击批量删除失败: {delete_result}")
|
raise RuntimeError(f"状态[{status}]点击批量删除失败: {delete_result}")
|
||||||
logger.info(f"trace_id={trace_id} 批量删除按钮点击成功: status={status}, result={delete_result}")
|
logger.info(f"trace_id={trace_id} 批量删除按钮点击成功: status={status}, result={_safe_log_text(delete_result)}")
|
||||||
|
|
||||||
self._confirm_delete_modal(status, trace_id=trace_id)
|
self._confirm_delete_modal(status, trace_id=trace_id)
|
||||||
success_message = self._wait_delete_success_message(status, trace_id=trace_id)
|
success_message = self._wait_delete_success_message(status, trace_id=trace_id)
|
||||||
deleted_count = self._extract_delete_success_count(success_message) or int(select_result.get("rowCount") or 1)
|
deleted_count = self._extract_delete_success_count(success_message) or int(select_result.get("rowCount") or 1)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"trace_id={trace_id} 批量删除确认完成: status={status}, "
|
f"trace_id={trace_id} 批量删除确认完成: status={status}, "
|
||||||
f"deleted_count={deleted_count}, success_message={success_message}"
|
f"deleted_count={deleted_count}, success_message={_safe_log_text(success_message)}"
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
"deletedCount": deleted_count,
|
"deletedCount": deleted_count,
|
||||||
@@ -472,10 +494,13 @@ class InventoryManage(AmamzonBase):
|
|||||||
trace_id = trace_id or _new_trace_id("modal")
|
trace_id = trace_id or _new_trace_id("modal")
|
||||||
js_result = self._click_confirm_delete_modal_with_js()
|
js_result = self._click_confirm_delete_modal_with_js()
|
||||||
if js_result.get("ok"):
|
if js_result.get("ok"):
|
||||||
logger.info(f"trace_id={trace_id} 已通过 JS 确认删除弹窗: status={status}, result={js_result}")
|
logger.info(f"trace_id={trace_id} 已通过 JS 确认删除弹窗: status={status}, result={_safe_log_text(js_result)}")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"trace_id={trace_id} JS 确认删除弹窗未命中,尝试驱动点击: status={status}, result={js_result}")
|
logger.info(
|
||||||
|
f"trace_id={trace_id} JS 确认删除弹窗未命中,尝试驱动点击: "
|
||||||
|
f"status={status}, result={_safe_log_text(js_result)}"
|
||||||
|
)
|
||||||
confirm_button = self.tab.ele(
|
confirm_button = self.tab.ele(
|
||||||
'xpath://kat-modal[@data-testid="action-modal"]//kat-button[@variant="primary"]',
|
'xpath://kat-modal[@data-testid="action-modal"]//kat-button[@variant="primary"]',
|
||||||
timeout=5,
|
timeout=5,
|
||||||
@@ -507,10 +532,13 @@ class InventoryManage(AmamzonBase):
|
|||||||
if text not in seen_messages:
|
if text not in seen_messages:
|
||||||
seen_messages.append(text)
|
seen_messages.append(text)
|
||||||
if self._is_delete_success_message(text):
|
if self._is_delete_success_message(text):
|
||||||
logger.info(f"trace_id={trace_id} 读取到删除成功提示: status={status}, message={text}")
|
logger.info(f"trace_id={trace_id} 读取到删除成功提示: status={status}, message={_safe_log_text(text)}")
|
||||||
return text
|
return text
|
||||||
time.sleep(0.3)
|
time.sleep(0.3)
|
||||||
logger.info(f"trace_id={trace_id} 删除后未读取到成功提示: status={status}, seen_messages={seen_messages}")
|
logger.info(
|
||||||
|
f"trace_id={trace_id} 删除后未读取到成功提示: "
|
||||||
|
f"status={status}, seen_messages={_safe_log_text(seen_messages)}"
|
||||||
|
)
|
||||||
raise RuntimeError("删除后未读取到成功提示")
|
raise RuntimeError("删除后未读取到成功提示")
|
||||||
|
|
||||||
def _collect_delete_success_messages(self) -> List[str]:
|
def _collect_delete_success_messages(self) -> List[str]:
|
||||||
@@ -1260,7 +1288,7 @@ class PatrolDeleteTask:
|
|||||||
|
|
||||||
def log(self, message: str, level: str = "INFO"):
|
def log(self, message: str, level: str = "INFO"):
|
||||||
level_name = str(level or "INFO").upper()
|
level_name = str(level or "INFO").upper()
|
||||||
logger.log(level_name, f"[ProductTask] {message}")
|
logger.log(level_name, f"[PatrolDeleteTask] {_safe_log_text(message)}")
|
||||||
|
|
||||||
def process_task(self, task_data: dict):
|
def process_task(self, task_data: dict):
|
||||||
"""处理巡店删除任务主入口。"""
|
"""处理巡店删除任务主入口。"""
|
||||||
@@ -1658,7 +1686,7 @@ class PatrolDeleteTask:
|
|||||||
try:
|
try:
|
||||||
self.log(f"trace_id={trace_id} 尝试回传巡店删除结果 (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"trace_id={trace_id} 尝试回传巡店删除结果 (第 {retry + 1}/{max_retries} 次)")
|
||||||
self.log(f"trace_id={trace_id} 回传URL: {url}")
|
self.log(f"trace_id={trace_id} 回传URL: {url}")
|
||||||
self.log(f"trace_id={trace_id} 回传数据: {payload}")
|
self.log(f"trace_id={trace_id} 回传数据: {_safe_log_text(payload)}")
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url,
|
url,
|
||||||
json=payload,
|
json=payload,
|
||||||
@@ -1666,7 +1694,7 @@ class PatrolDeleteTask:
|
|||||||
timeout=30,
|
timeout=30,
|
||||||
verify=False,
|
verify=False,
|
||||||
)
|
)
|
||||||
self.log(f"trace_id={trace_id} 回传结果: {response.text}")
|
self.log(f"trace_id={trace_id} 回传结果: {_safe_log_text(response.text)}")
|
||||||
try:
|
try:
|
||||||
response_data = response.json()
|
response_data = response.json()
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
@@ -1674,13 +1702,14 @@ class PatrolDeleteTask:
|
|||||||
api_success = not (isinstance(response_data, dict) and response_data.get("success") is False)
|
api_success = not (isinstance(response_data, dict) and response_data.get("success") is False)
|
||||||
self.log(
|
self.log(
|
||||||
f"trace_id={trace_id} 巡店删除结果回传响应: status_code={response.status_code}, "
|
f"trace_id={trace_id} 巡店删除结果回传响应: status_code={response.status_code}, "
|
||||||
f"api_success={api_success}, response={response.text}"
|
f"api_success={api_success}, response={_safe_log_text(response.text)}"
|
||||||
)
|
)
|
||||||
if response.status_code == 200 and api_success:
|
if response.status_code == 200 and api_success:
|
||||||
self.log(f"trace_id={trace_id} 巡店删除结果回传成功: {shop_name}")
|
self.log(f"trace_id={trace_id} 巡店删除结果回传成功: {shop_name}")
|
||||||
return
|
return
|
||||||
self.log(
|
self.log(
|
||||||
f"trace_id={trace_id} 巡店删除结果回传失败,状态码: {response.status_code}, 响应: {response.text}",
|
f"trace_id={trace_id} 巡店删除结果回传失败,状态码: {response.status_code}, "
|
||||||
|
f"响应: {_safe_log_text(response.text)}",
|
||||||
"WARNING",
|
"WARNING",
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
Reference in New Issue
Block a user