feat(collect): support soft deletion of one task

This commit is contained in:
chengma
2026-07-20 10:27:21 +08:00
parent a92f1c86f4
commit 9852b1c9c1
8 changed files with 333 additions and 10 deletions
+49
View File
@@ -161,6 +161,8 @@ class Task:
generated_at: Optional[str]
applied_at: Optional[str]
updated_at: str
deleted_at: Optional[str]
deleted_reason: Optional[str]
@dataclass(frozen=True)
@@ -261,6 +263,8 @@ CREATE TABLE IF NOT EXISTS tasks (
generated_at TEXT,
applied_at TEXT,
updated_at TEXT NOT NULL,
deleted_at TEXT,
deleted_reason TEXT,
UNIQUE(batch_id, source_file_abs, source_sheet, source_row)
);
@@ -495,6 +499,7 @@ def init_db(path=None, conn=None) -> None:
_ensure_task_image_task_columns(database)
_ensure_task_cover_reset_columns(database)
_ensure_task_product_status_columns(database)
_ensure_task_delete_columns(database)
_migrate_legacy_product_status_defaults(database)
_ensure_image_studio_project_suite_columns(database)
_ensure_image_studio_project_draft_columns(database)
@@ -537,6 +542,15 @@ def _ensure_task_product_status_columns(database):
database.execute("ALTER TABLE tasks ADD COLUMN product_status_at TEXT")
def _ensure_task_delete_columns(database):
columns = {row["name"] for row in database.execute("PRAGMA table_info(tasks)").fetchall()}
if "deleted_at" not in columns:
database.execute("ALTER TABLE tasks ADD COLUMN deleted_at TEXT")
if "deleted_reason" not in columns:
database.execute("ALTER TABLE tasks ADD COLUMN deleted_reason TEXT")
database.execute("CREATE INDEX IF NOT EXISTS idx_tasks_deleted_at ON tasks(deleted_at)")
def _migrate_legacy_product_status_defaults(database):
"""Treat pre-status-feature active batches as historically on-shelf once."""
@@ -548,6 +562,7 @@ def _migrate_legacy_product_status_defaults(database):
product_status_at = NULL,
updated_at = ?
WHERE (product_status IS NULL OR TRIM(product_status) = '')
AND deleted_at IS NULL
AND batch_id IN (
SELECT id
FROM batches
@@ -880,6 +895,7 @@ def list_tasks(
params.append(value)
if not include_deleted:
clauses.append("b.deleted_at IS NULL")
clauses.append("t.deleted_at IS NULL")
sql = "SELECT t.* FROM tasks t JOIN batches b ON b.id = t.batch_id"
if clauses:
sql += " WHERE " + " AND ".join(clauses)
@@ -920,11 +936,44 @@ def delete_batch(batch_id, reason=None, path=None, conn=None) -> dict:
"image_paths": image_paths,
}
def delete_task(task_id, reason=None, path=None, conn=None) -> dict:
"""Soft delete one inactive task without touching its source files or images."""
task_id = int(task_id)
with _connection(conn, path) as database:
task = get_task(task_id, conn=database)
if task is None:
raise DbError(f"任务不存在或已删除: {task_id}")
if task.status == "running":
raise DbError("任务正在处理,不能删除")
now = _now()
with database:
cursor = database.execute(
"""
UPDATE tasks
SET deleted_at = ?, deleted_reason = ?, updated_at = ?
WHERE id = ? AND deleted_at IS NULL AND status <> 'running'
""",
(now, str(reason or ""), now, task_id),
)
if cursor.rowcount != 1:
raise DbError("任务已删除或正在处理,不能删除")
return {
"task_id": task_id,
"batch_id": task.batch_id,
"item_id": task.item_id,
"committed": int(task.committed or 0),
"deleted_at": now,
}
def get_task(task_id, path=None, conn=None, include_deleted=False):
clauses = ["t.id = ?"]
params = [int(task_id)]
if not include_deleted:
clauses.append("b.deleted_at IS NULL")
clauses.append("t.deleted_at IS NULL")
sql = "SELECT t.* FROM tasks t JOIN batches b ON b.id = t.batch_id"
sql += " WHERE " + " AND ".join(clauses)
with _connection(conn, path) as database: