fix(product-suite): allow completed source image removal

This commit is contained in:
chengma
2026-07-22 17:51:37 +08:00
parent 70c4323c52
commit 4550369bc1
6 changed files with 234 additions and 15 deletions
+67 -10
View File
@@ -871,11 +871,68 @@ def remove_asset_if_unused(asset_id, path=None, conn=None):
return asset
def remove_original_assets_if_unused(project_id, asset_ids, path=None, conn=None):
"""Atomically remove unreferenced original assets from one project.
def _job_requires_original_assets(status, recovery_action):
"""Return whether a job can still need its frozen source/reference assets."""
if status in {"pending", "submitted", "running"}:
return True
return recovery_action in {JOB_RECOVERY_RESUME, JOB_RECOVERY_REGENERATE}
def _original_asset_delete_blocker(database, project_id, asset_ids):
"""Return a user-facing blocker for deleting project original assets, if any."""
selected_ids = {int(asset_id) for asset_id in asset_ids}
placeholders = ",".join("?" for _ in selected_ids)
selection = database.execute(
f"""
SELECT 1 FROM image_studio_selections
WHERE project_id = ? AND asset_id IN ({placeholders})
LIMIT 1
""",
[int(project_id), *selected_ids],
).fetchone()
if selection is not None:
return "选中的图片已被终选引用,请先从终选中移除后再删除"
jobs = database.execute(
"""
SELECT id, status, recovery_action, source_asset_id, reference_asset_ids
FROM image_studio_jobs
WHERE project_id = ?
ORDER BY id
""",
(int(project_id),),
).fetchall()
for job in jobs:
status = str(job["status"] or "")
recovery_action = str(job["recovery_action"] or "")
if status not in JOB_STATUSES or recovery_action not in JOB_RECOVERY_ACTIONS:
return "生成任务状态无效,无法确认图片引用,不能删除"
try:
reference_ids = _parse_reference_asset_ids(
job["reference_asset_ids"],
job["source_asset_id"],
)
except db.DbError:
return "生成任务参考图快照无效,无法确认图片引用,不能删除"
if not _job_requires_original_assets(status, recovery_action):
continue
source_asset_id = job["source_asset_id"]
source_selected = (
source_asset_id is not None and int(source_asset_id) in selected_ids
)
if source_selected or selected_ids.intersection(reference_ids):
return "选中的图片仍被可继续处理的生成任务引用,请先完成、停止或放弃该任务后再删除"
return None
def remove_original_assets_if_unused(project_id, asset_ids, path=None, conn=None):
"""Atomically remove original assets that no runnable job or selection needs.
Completed, non-retryable jobs remain as history but do not block removal.
Local files are intentionally retained. If any requested asset is invalid or
referenced, no asset row is removed.
still needed, no asset row is removed.
"""
project_id = int(project_id)
@@ -906,13 +963,13 @@ def remove_original_assets_if_unused(project_id, asset_ids, path=None, conn=None
if set(by_id) != set(ordered_ids):
raise db.DbError("选中的商品原图不存在或不属于当前项目")
referenced_ids = [
asset_id
for asset_id in ordered_ids
if asset_reference_counts(asset_id, conn=database)["total"]
]
if referenced_ids:
raise db.DbError("选中的图片正在被生成任务或终选引用,不能移除")
blocker = _original_asset_delete_blocker(
database,
project_id,
ordered_ids,
)
if blocker:
raise db.DbError(blocker)
database.execute(
f"""