feat: complete T-534 generated reset enhancements
This commit is contained in:
+176
-37
@@ -21,6 +21,11 @@ class GenerateTab(QWidget):
|
||||
("略过", "skipped"),
|
||||
("已更新", "applied"),
|
||||
]
|
||||
RESET_MODE_LABELS = {
|
||||
"title": "标题",
|
||||
"cover": "封面",
|
||||
"all": "标题和封面",
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -162,7 +167,7 @@ class GenerateTab(QWidget):
|
||||
self.model = GenerateTaskTableModel(self.task_table, db_path=self.db_path, status_callback=self._set_status)
|
||||
self.task_table.setModel(self.model)
|
||||
self.task_table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.task_table.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.task_table.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.task_table.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed)
|
||||
self.task_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
self.task_table.verticalHeader().setVisible(False)
|
||||
@@ -531,57 +536,191 @@ class GenerateTab(QWidget):
|
||||
if self.generate_thread is not None:
|
||||
self._set_status("AI 生成正在进行,不能重置")
|
||||
return
|
||||
task = self._selected_task()
|
||||
if task is None:
|
||||
self._set_status("请选择要重置生成结果的任务")
|
||||
scope, tasks = self._reset_scope_tasks()
|
||||
if not tasks:
|
||||
self._set_status("当前没有可重置生成结果的任务")
|
||||
return
|
||||
has_ai_result = bool(getattr(task, "new_title", None) or getattr(task, "new_cover_path", None))
|
||||
if not has_ai_result and getattr(task, "stage", None) not in {"generated", "applied"}:
|
||||
self._set_status("选中任务没有可重置的生成结果")
|
||||
if not self._resettable_tasks(tasks, reset_title=True, reset_cover=True):
|
||||
self._set_status("当前范围没有可重置的生成结果")
|
||||
return
|
||||
lines = [
|
||||
"确定重置当前选中任务的本地生成结果吗?",
|
||||
"",
|
||||
f"商品ID:{task.item_id}",
|
||||
f"店铺:{self.model.account_name_for(task)}",
|
||||
"",
|
||||
"将清空新标题、新封面路径和错误信息,并退回到已采集状态。",
|
||||
"默认不删除本地新封面文件,不触碰 Shopee,也不会自动回写 Excel。",
|
||||
]
|
||||
if getattr(task, "new_cover_path", None):
|
||||
lines.append(f"本地新封面文件保留:{task.new_cover_path}")
|
||||
if getattr(task, "committed", 0):
|
||||
lines.extend([
|
||||
"",
|
||||
"注意:该记录曾经提交过线上。本地重置不会回滚 Shopee,后续重新生成/更新可能再次提交线上。",
|
||||
])
|
||||
answer = QMessageBox.question(
|
||||
self,
|
||||
"重置生成结果",
|
||||
"\n".join(lines),
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.No,
|
||||
)
|
||||
if answer != QMessageBox.Yes:
|
||||
mode = self._ask_reset_generated_mode(tasks, scope)
|
||||
if mode is None:
|
||||
self._set_status("已取消重置生成结果")
|
||||
return
|
||||
reset_title, reset_cover = self._reset_mode_flags(mode)
|
||||
target_tasks = self._resettable_tasks(
|
||||
tasks,
|
||||
reset_title=reset_title,
|
||||
reset_cover=reset_cover,
|
||||
)
|
||||
if not target_tasks:
|
||||
self._set_status(f"当前范围没有可重置的{self.RESET_MODE_LABELS[mode]}")
|
||||
return
|
||||
label = self.RESET_MODE_LABELS[mode]
|
||||
try:
|
||||
db.reset_generated(task.id, path=self.db_path)
|
||||
message = (
|
||||
"action=reset_generated step=db_write result=success "
|
||||
f"detail=清空AI生成结果 task_id={task.id}"
|
||||
for task in target_tasks:
|
||||
db.reset_generated(
|
||||
task.id,
|
||||
reset_title=reset_title,
|
||||
reset_cover=reset_cover,
|
||||
path=self.db_path,
|
||||
)
|
||||
run_id = self._write_generated_reset_run_log(
|
||||
target_tasks,
|
||||
scope,
|
||||
mode,
|
||||
reset_title=reset_title,
|
||||
reset_cover=reset_cover,
|
||||
)
|
||||
run_id = _write_reset_run_log(self.db_path, task, "reset_generated", message)
|
||||
except Exception as exc:
|
||||
QMessageBox.warning(self, "重置生成结果", str(exc))
|
||||
self._set_status(f"重置生成结果失败:{exc}")
|
||||
return
|
||||
message = (
|
||||
"action=reset_generated step=db_write result=success "
|
||||
f"detail=重置{label} scope={scope} total={len(target_tasks)}"
|
||||
)
|
||||
self.refresh_tasks()
|
||||
self._append_generate_log(message)
|
||||
self._set_status(
|
||||
f"已重置生成结果:商品 {task.item_id},run_id={run_id}"
|
||||
f"已重置生成结果:{len(target_tasks)} 条,内容:{label},run_id={run_id}"
|
||||
)
|
||||
|
||||
def _reset_scope_tasks(self):
|
||||
selection = self.task_table.selectionModel()
|
||||
selected_rows = []
|
||||
if selection is not None:
|
||||
selected_rows = sorted({index.row() for index in selection.selectedRows()})
|
||||
if selected_rows:
|
||||
tasks = [self.model.task_at(row) for row in selected_rows]
|
||||
return "selected", [task for task in tasks if task is not None]
|
||||
return "filtered", list(self.model.tasks)
|
||||
|
||||
def _ask_reset_generated_mode(self, tasks, scope):
|
||||
scope_text = "选中任务" if scope == "selected" else "当前筛选结果"
|
||||
title_count = sum(1 for task in tasks if getattr(task, "new_title", None))
|
||||
cover_count = sum(1 for task in tasks if getattr(task, "new_cover_path", None))
|
||||
committed_count = sum(1 for task in tasks if int(getattr(task, "committed", 0) or 0))
|
||||
running_count = sum(1 for task in tasks if getattr(task, "status", None) == "running")
|
||||
sample_ids = [str(getattr(task, "item_id", "") or "") for task in tasks[:5]]
|
||||
sample_ids = [item_id for item_id in sample_ids if item_id]
|
||||
sample_text = "、".join(sample_ids)
|
||||
if len(tasks) > 5:
|
||||
sample_text += f" 等 {len(tasks)} 条"
|
||||
lines = [
|
||||
"请选择要重置的本地生成内容。",
|
||||
"",
|
||||
f"作用范围:{scope_text} {len(tasks)} 条",
|
||||
f"包含新标题:{title_count} 条",
|
||||
f"包含新封面:{cover_count} 条",
|
||||
]
|
||||
if sample_text:
|
||||
lines.append(f"商品ID:{sample_text}")
|
||||
if running_count:
|
||||
lines.append(f"运行中任务会跳过:{running_count} 条")
|
||||
lines.extend([
|
||||
"",
|
||||
"重置标题:只清空新标题,保留新封面。",
|
||||
"重置封面:只清空新封面,保留新标题和手动微调标题。",
|
||||
"重置全部:清空新标题和新封面。",
|
||||
"",
|
||||
"不会触碰 Shopee,也不会自动回写 Excel;后续重新生成会消耗 cmhub 点数。",
|
||||
"默认不删除本地新封面文件。",
|
||||
])
|
||||
if committed_count:
|
||||
lines.extend([
|
||||
"",
|
||||
f"注意:其中 {committed_count} 条已经提交过线上。本地重置不会回滚 Shopee,重生成后再更新会再次提交线上。",
|
||||
])
|
||||
box = QMessageBox(self)
|
||||
box.setWindowTitle("重置生成结果")
|
||||
box.setText("\n".join(lines))
|
||||
title_button = box.addButton("重置标题", QMessageBox.AcceptRole)
|
||||
cover_button = box.addButton("重置封面", QMessageBox.AcceptRole)
|
||||
all_button = box.addButton("重置全部", QMessageBox.DestructiveRole)
|
||||
cancel_button = box.addButton("取消", QMessageBox.RejectRole)
|
||||
box.setDefaultButton(cancel_button)
|
||||
box.exec()
|
||||
clicked = box.clickedButton()
|
||||
if clicked is title_button:
|
||||
return "title"
|
||||
if clicked is cover_button:
|
||||
return "cover"
|
||||
if clicked is all_button:
|
||||
return "all"
|
||||
return None
|
||||
|
||||
def _reset_mode_flags(self, mode):
|
||||
if mode == "title":
|
||||
return True, False
|
||||
if mode == "cover":
|
||||
return False, True
|
||||
return True, True
|
||||
|
||||
def _resettable_tasks(self, tasks, reset_title=True, reset_cover=True):
|
||||
resettable = []
|
||||
for task in tasks:
|
||||
if getattr(task, "status", None) == "running":
|
||||
continue
|
||||
has_title = bool(getattr(task, "new_title", None))
|
||||
has_cover = bool(getattr(task, "new_cover_path", None))
|
||||
if reset_title and has_title:
|
||||
resettable.append(task)
|
||||
continue
|
||||
if reset_cover and has_cover:
|
||||
resettable.append(task)
|
||||
return resettable
|
||||
|
||||
def _write_generated_reset_run_log(
|
||||
self,
|
||||
tasks,
|
||||
scope,
|
||||
mode,
|
||||
reset_title=True,
|
||||
reset_cover=True,
|
||||
):
|
||||
task_ids = [getattr(task, "id", None) for task in tasks]
|
||||
committed_count = sum(1 for task in tasks if int(getattr(task, "committed", 0) or 0))
|
||||
options = {
|
||||
"action": "reset_generated",
|
||||
"scope": scope,
|
||||
"mode": mode,
|
||||
"reset_title": bool(reset_title),
|
||||
"reset_cover": bool(reset_cover),
|
||||
"task_ids": task_ids,
|
||||
"committed_count": committed_count,
|
||||
}
|
||||
run_id = db.create_run_log(
|
||||
"reset",
|
||||
total=len(tasks),
|
||||
options=options,
|
||||
path=self.db_path,
|
||||
)
|
||||
label = self.RESET_MODE_LABELS[mode]
|
||||
for task in tasks:
|
||||
message = (
|
||||
"action=reset_generated step=db_write result=success "
|
||||
f"detail=重置{label} task_id={getattr(task, 'id', None)}"
|
||||
)
|
||||
db.add_run_log_event(
|
||||
run_id,
|
||||
message,
|
||||
task_id=getattr(task, "id", None),
|
||||
alias=getattr(task, "alias", None),
|
||||
item_id=getattr(task, "item_id", None),
|
||||
path=self.db_path,
|
||||
)
|
||||
db.finish_run_log(
|
||||
run_id,
|
||||
status="done",
|
||||
done=len(tasks),
|
||||
success_count=len(tasks),
|
||||
failed_count=0,
|
||||
summary_json=options,
|
||||
path=self.db_path,
|
||||
)
|
||||
return run_id
|
||||
|
||||
def show_task_images(self, index):
|
||||
if index.isValid() and index.column() == 3:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user