feat(settings): support gateway source switching

This commit is contained in:
chengma
2026-07-20 16:29:28 +08:00
parent ed1d8ee764
commit 6a0cd1c763
18 changed files with 746 additions and 77 deletions
+145 -6
View File
@@ -81,6 +81,7 @@ from ..workers import (
CMHubModelCatalogWorker,
ImageStudioDownloadOriginalWorker,
ImageStudioPullImagesWorker,
ImageStudioResumeJobsWorker,
ProductSuiteAiWriteWorker,
ProductSuiteGenerateWorker,
ProductSuiteHistoryExportWorker,
@@ -723,13 +724,20 @@ class SuiteResultCard(QFrame):
status_label.setWordWrap(False)
status_label.setToolTip(status_text)
footer.addWidget(status_label, 1)
self.retry_button = None
if status in {"failed", "expired", "cancelled"}:
retry_button = QPushButton("重试")
retry_button.setMinimumWidth(52)
retry_button.clicked.connect(lambda: self.retryRequested.emit(self.job))
footer.addWidget(retry_button)
self.retry_button = QPushButton("重试")
self.retry_button.setMinimumWidth(52)
self.retry_button.clicked.connect(lambda: self.retryRequested.emit(self.job))
footer.addWidget(self.retry_button)
layout.addLayout(footer)
def set_retry_enabled(self, enabled, tooltip=""):
if self.retry_button is None:
return
self.retry_button.setEnabled(bool(enabled))
self.retry_button.setToolTip(str(tooltip or ""))
class SuiteHistoryImageCard(QFrame):
"""Read-only image card used by the product-suite history dialog."""
@@ -1884,6 +1892,8 @@ class SuiteTaskState:
generation_price_worker: object = None
generation_price_thread: object = None
generation_confirmation_open: bool = False
resume_worker: object = None
resume_thread: object = None
download_queue: list = field(default_factory=list)
downloads: dict = field(default_factory=dict)
download_tokens: dict = field(default_factory=dict)
@@ -1962,6 +1972,10 @@ class ProductSuiteTab(QWidget):
if self._prompt_template_init_error:
self._status(self._prompt_template_init_error, "danger")
def showEvent(self, event):
super().showEvent(event)
self.refresh_gateway_state()
def _build_ui(self):
root = QVBoxLayout(self)
root.setContentsMargins(10, 8, 10, 8)
@@ -2111,6 +2125,11 @@ class ProductSuiteTab(QWidget):
"QPushButton:hover { background: #245fce; }"
)
layout.addWidget(self.generate_button)
self.resume_submitted_button = QPushButton("继续查询已提交图片")
self.resume_submitted_button.setObjectName("suiteResumeSubmittedButton")
self.resume_submitted_button.setToolTip("继续查询已提交到默认网关的图片,不会重新生成或再次扣点")
self.resume_submitted_button.setVisible(False)
layout.addWidget(self.resume_submitted_button)
self.generate_helper_label = QLabel("建议填写产品名称、核心卖点、目标人群、使用场景与禁用元素")
self.generate_helper_label.setWordWrap(True)
self.generate_helper_label.setStyleSheet("color: #6b7280;")
@@ -2396,6 +2415,7 @@ class ProductSuiteTab(QWidget):
self.custom_category_edit.returnPressed.connect(self._commit_custom_category)
self.custom_category_edit.editingFinished.connect(self._finish_custom_category_edit)
self.generate_button.clicked.connect(self.toggle_generation)
self.resume_submitted_button.clicked.connect(self.resume_submitted_jobs)
self.history_button.clicked.connect(self.open_history_dialog)
self.undo_button.clicked.connect(self.undo_delete)
self.more_button.clicked.connect(self._show_more_menu)
@@ -4155,7 +4175,105 @@ class ProductSuiteTab(QWidget):
if not state.generation_running():
self.generate_button.setText("生成套图(%d)" % total)
def _is_default_gateway(self):
try:
return appconfig.ai_backend(self.config) == "cmhub"
except Exception:
return False
@staticmethod
def _is_default_gateway_job(job):
return (
str(getattr(job, "generation_source", "") or "").strip().lower() == "cmhub"
and str(getattr(job, "provider", "") or "").strip().lower() == "cmhub"
and bool(str(getattr(job, "task_id", "") or "").strip())
)
def _resumable_default_gateway_jobs(self, state):
if state is None or state.project_id is None:
return []
try:
jobs = image_studio.list_resumable_jobs(
project_id=state.project_id,
include_failed_downloads=True,
path=self.db_path,
)
except Exception:
return []
return [job for job in jobs if self._is_default_gateway_job(job)]
def refresh_gateway_state(self):
state = self._displayed_state
if state is not None:
self._apply_running_state(state)
self._refresh_results(state)
def _update_resume_submitted_action(self, state):
jobs = self._resumable_default_gateway_jobs(state)
self.resume_submitted_button.setVisible(bool(jobs))
self.resume_submitted_button.setEnabled(
bool(jobs)
and state.resume_worker is None
and not state.generation_running()
and state.generation_price_worker is None
)
def resume_submitted_jobs(self, checked=False):
state = self._displayed_state
if state is None or state.project_id is None:
return
if state.resume_worker is not None:
self._status("正在继续查询已提交图片", "info")
return
jobs = self._resumable_default_gateway_jobs(state)
if not jobs:
self._status("当前没有可继续查询的默认网关图片", "info")
self._update_resume_submitted_action(state)
return
worker = ImageStudioResumeJobsWorker(
project_id=state.project_id,
aspect_ratio=state.settings.get("ratio", "1:1"),
db_path=self.db_path,
config=self.config,
cmhub_config_path=self.cmhub_config_path,
)
state.resume_worker = worker
worker.finished.connect(
lambda result, state=state: self._on_resume_submitted_finished(state, result)
)
worker.cancelled.connect(
lambda result, state=state: self._on_resume_submitted_finished(state, result)
)
state.resume_thread = self._start_thread(worker, "商品套图继续查询")
self._apply_running_state(state)
self._status("开始继续查询%d张已提交图片,不会重新生成或再次扣点" % len(jobs), "info")
def _on_resume_submitted_finished(self, state, result):
state.resume_worker = None
state.resume_thread = None
self._refresh_results(state)
self._update_resume_submitted_action(state)
if result.get("ok") is False:
error = str(result.get("error") or "")
if "cmhub_not_configured" in error or "缺少" in error:
self._message(
"继续查询失败",
"默认网关配置不可用,请恢复原默认网关配置后继续查询已提交图片。",
)
else:
self._message("继续查询失败", _user_error(error))
return
self._status("已完成已提交图片查询,请查看生成结果", "success")
def _require_default_gateway(self, action):
if self._is_default_gateway():
return True
self._message("当前不可用", "%s仅支持默认网关,请到⑤设置切换后再使用。" % action)
return False
def start_ai_write(self, checked=False):
if not self._require_default_gateway("商品套图AI帮写"):
return
state = self._displayed_state
if state is None:
return
@@ -4482,6 +4600,8 @@ class ProductSuiteTab(QWidget):
self.start_generation(state)
def start_generation(self, state, specs=None, *, retry_job_id=None):
if not self._require_default_gateway("商品套图生成"):
return False
if state.generation_running():
self._status("当前套图任务仍在生成", "warning")
return False
@@ -5414,6 +5534,7 @@ class ProductSuiteTab(QWidget):
generation_running = state.generation_running()
generation_price_pending = state.generation_price_worker is not None
pull_running = state.pull_running()
default_gateway = self._is_default_gateway()
self.pull_button.setText(
"正在停止..."
if pull_running and state.pull_stop_requested
@@ -5439,7 +5560,9 @@ class ProductSuiteTab(QWidget):
widget.setEnabled(not generation_running)
for row in self.category_rows.values():
row.set_controls_enabled(not generation_running)
self.generate_button.setEnabled(True)
self.generate_button.setEnabled(
generation_running or generation_price_pending or default_gateway
)
if generation_running:
self.generate_button.setText(
"正在停止..."
@@ -5465,8 +5588,17 @@ class ProductSuiteTab(QWidget):
)
self._refresh_totals(state)
ai_running = state.ai_worker is not None or state.ai_price_worker is not None
self.ai_write_button.setEnabled(not ai_running and not generation_running)
self.ai_write_button.setEnabled(
default_gateway and not ai_running and not generation_running
)
if not default_gateway and not ai_running:
self.ai_write_button.setToolTip("商品套图AI帮写仅支持默认网关")
self.generate_button.setToolTip("商品套图生成仅支持默认网关")
else:
self.ai_write_button.setToolTip("")
self.generate_button.setToolTip("")
self.ai_cancel_button.setVisible(ai_running)
self._update_resume_submitted_action(state)
self._update_context_actions(state)
def _refresh_elapsed(self):
@@ -5541,6 +5673,8 @@ class ProductSuiteTab(QWidget):
card.retryRequested.connect(self.retry_job)
card.menuRequested.connect(self._show_job_menu)
card.deleteRequested.connect(self.delete_job_asset)
if not self._is_default_gateway():
card.set_retry_enabled(False, "商品套图重新生成仅支持默认网关")
self.result_grid.addWidget(card, index // columns, index % columns)
self.result_summary_label.setText("共 %d 张 · 成功 %d 张" % (len(jobs), success))
self.undo_button.setVisible(bool(state.undo_records))
@@ -5611,6 +5745,8 @@ class ProductSuiteTab(QWidget):
ProductSuitePreviewDialog(asset.local_path, "%s预览" % job.job_type, self).exec()
def retry_job(self, job):
if not self._require_default_gateway("商品套图重新生成"):
return
state = self._displayed_state
if state is None:
return
@@ -5643,6 +5779,9 @@ class ProductSuiteTab(QWidget):
copy_action = menu.addAction("复制路径")
folder_action = menu.addAction("打开文件夹")
retry_action = menu.addAction("重新生成")
if not self._is_default_gateway():
retry_action.setEnabled(False)
retry_action.setToolTip("商品套图重新生成仅支持默认网关")
delete_action = menu.addAction("删除")
action = menu.exec(global_position)
asset = image_studio.get_asset(job.output_asset_id, path=self.db_path) if job.output_asset_id else None