补采新增设备并限制次日截止
This commit is contained in:
@@ -840,13 +840,22 @@ def _poll_screenshot_results(public_id: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def retry_failed_acceptance_devices(public_id: str) -> dict[str, Any]:
|
def retry_failed_acceptance_devices(public_id: str) -> dict[str, Any]:
|
||||||
"""仅对截图失败的“关键词 × 设备”组合重新立即下发。"""
|
"""补采失败及批次开始后新增的设备,仍写入原自然日批次。"""
|
||||||
with connection() as conn:
|
with connection() as conn:
|
||||||
task = conn.execute(
|
task = conn.execute(
|
||||||
"SELECT * FROM acceptance_tasks WHERE public_task_id=?", (public_id,),
|
"SELECT * FROM acceptance_tasks WHERE public_task_id=?", (public_id,),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
if not task:
|
if not task:
|
||||||
raise TaskError("ACCEPTANCE_TASK_NOT_FOUND", "验收任务不存在", 404)
|
raise TaskError("ACCEPTANCE_TASK_NOT_FOUND", "验收任务不存在", 404)
|
||||||
|
if task["batch_date"]:
|
||||||
|
batch_day = datetime.strptime(task["batch_date"], "%Y-%m-%d").date()
|
||||||
|
today = datetime.now().astimezone().date()
|
||||||
|
if today > batch_day + timedelta(days=1):
|
||||||
|
raise TaskError(
|
||||||
|
"ACCEPTANCE_RETRY_WINDOW_EXPIRED",
|
||||||
|
f"{task['batch_date']} 批次仅允许在批次当天或次日重采",
|
||||||
|
409,
|
||||||
|
)
|
||||||
if task["acceptance_method"] not in {"screenshot", "combined"}:
|
if task["acceptance_method"] not in {"screenshot", "combined"}:
|
||||||
raise TaskError("SCREENSHOT_RETRY_NOT_SUPPORTED", "该任务不包含截图采集", 409)
|
raise TaskError("SCREENSHOT_RETRY_NOT_SUPPORTED", "该任务不包含截图采集", 409)
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
@@ -878,8 +887,24 @@ def retry_failed_acceptance_devices(public_id: str) -> dict[str, Any]:
|
|||||||
)
|
)
|
||||||
if status in terminal_failures or (status == "completed" and not has_screenshot):
|
if status in terminal_failures or (status == "completed" and not has_screenshot):
|
||||||
failed_by_keyword.setdefault(keyword, []).append(account_id)
|
failed_by_keyword.setdefault(keyword, []).append(account_id)
|
||||||
|
active_device_ids = {
|
||||||
|
str(item.get("xhs_account_id") or "").strip()
|
||||||
|
for item in list_xhs_wen_xapi_devices()
|
||||||
|
if str(item.get("xhs_account_id") or "").strip()
|
||||||
|
}
|
||||||
|
for keyword in keyword_runs:
|
||||||
|
collected_ids = {
|
||||||
|
account_id for (row_keyword, account_id) in latest if row_keyword == keyword
|
||||||
|
}
|
||||||
|
missing_ids = active_device_ids - collected_ids
|
||||||
|
if missing_ids:
|
||||||
|
failed_by_keyword.setdefault(keyword, []).extend(sorted(missing_ids))
|
||||||
|
failed_by_keyword = {
|
||||||
|
keyword: list(dict.fromkeys(account_ids))
|
||||||
|
for keyword, account_ids in failed_by_keyword.items() if account_ids
|
||||||
|
}
|
||||||
if not failed_by_keyword:
|
if not failed_by_keyword:
|
||||||
raise TaskError("NO_FAILED_SCREENSHOT_DEVICES", "当前没有可重新采集的失败设备", 409)
|
raise TaskError("NO_FAILED_SCREENSHOT_DEVICES", "当前没有失败或新增设备需要补采", 409)
|
||||||
|
|
||||||
dispatched_devices: set[str] = set()
|
dispatched_devices: set[str] = set()
|
||||||
dispatched_pairs = 0
|
dispatched_pairs = 0
|
||||||
@@ -1705,6 +1730,10 @@ def _task_dict(conn, task) -> dict[str, Any]:
|
|||||||
"dashboard_batch_key": task["dashboard_batch_key"],
|
"dashboard_batch_key": task["dashboard_batch_key"],
|
||||||
"use_all_devices": bool(task["use_all_devices"]),
|
"use_all_devices": bool(task["use_all_devices"]),
|
||||||
"batch_date": task["batch_date"],
|
"batch_date": task["batch_date"],
|
||||||
|
"retry_deadline": (
|
||||||
|
(datetime.strptime(task["batch_date"], "%Y-%m-%d").date() + timedelta(days=1)).isoformat()
|
||||||
|
if task["batch_date"] else None
|
||||||
|
),
|
||||||
"schedule_enabled": bool(task["schedule_enabled"]),
|
"schedule_enabled": bool(task["schedule_enabled"]),
|
||||||
"schedule_weekday": task["schedule_weekday"],
|
"schedule_weekday": task["schedule_weekday"],
|
||||||
"schedule_weekdays": _decode_json(task["schedule_weekdays"], None) or (
|
"schedule_weekdays": _decode_json(task["schedule_weekdays"], None) or (
|
||||||
|
|||||||
@@ -99,8 +99,8 @@
|
|||||||
},
|
},
|
||||||
"/api/v1/acceptance-tasks/{task_id}/retry-failed-devices": {
|
"/api/v1/acceptance-tasks/{task_id}/retry-failed-devices": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "重新采集全部失败截图设备",
|
"summary": "补采失败及新增截图设备",
|
||||||
"description": "按每个关键词读取最新 XAPI 结果,仅将失败、取消、异常或已完成但无截图的设备精确重新下发。成功设备不会重复执行,新批次继续归入原验收任务。",
|
"description": "按关键词补采失败设备,以及批次完成后新加入设备库但尚未参与该批次的设备。结果仍归原日期批次;只允许在批次当天和次日执行。",
|
||||||
"tags": ["项目验收"],
|
"tags": ["项目验收"],
|
||||||
"parameters": [{"$ref": "#/components/parameters/TaskId"}],
|
"parameters": [{"$ref": "#/components/parameters/TaskId"}],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -174,9 +174,14 @@ class AcceptanceCreationTest(unittest.TestCase):
|
|||||||
|
|
||||||
@patch("acceptance_service.threading.Thread", _ThreadWithoutStart)
|
@patch("acceptance_service.threading.Thread", _ThreadWithoutStart)
|
||||||
@patch("acceptance_service.schedule_xapi_screenshots")
|
@patch("acceptance_service.schedule_xapi_screenshots")
|
||||||
|
@patch("acceptance_service.list_xhs_wen_xapi_devices")
|
||||||
@patch("acceptance_service.get_wen_resources")
|
@patch("acceptance_service.get_wen_resources")
|
||||||
def test_retry_failed_devices_dispatches_only_failed_accounts(self, resources, schedule):
|
def test_retry_failed_devices_dispatches_only_failed_accounts(self, resources, devices, schedule):
|
||||||
resources.return_value = {"cookie_count": 0, "device_count": 2}
|
resources.return_value = {"cookie_count": 0, "device_count": 2}
|
||||||
|
devices.return_value = [
|
||||||
|
{"xhs_account_id": "failed-account"},
|
||||||
|
{"xhs_account_id": "successful-account"},
|
||||||
|
]
|
||||||
schedule.return_value = {
|
schedule.return_value = {
|
||||||
"execution_mode": "immediate",
|
"execution_mode": "immediate",
|
||||||
"batches": [{
|
"batches": [{
|
||||||
|
|||||||
Reference in New Issue
Block a user