feat: 任务列表显示店铺名和执行用时 (#136)
This commit is contained in:
+13
-1
@@ -4,7 +4,7 @@
|
||||
``MIGRATIONS`` 末尾增加版本,不能修改已经发布的迁移。
|
||||
"""
|
||||
|
||||
SCHEMA_VERSION = 4
|
||||
SCHEMA_VERSION = 5
|
||||
|
||||
|
||||
MIGRATION_1 = (
|
||||
@@ -152,10 +152,22 @@ MIGRATION_4 = (
|
||||
""",
|
||||
)
|
||||
|
||||
MIGRATION_5 = (
|
||||
"ALTER TABLE pdd_tasks ADD COLUMN shop_name TEXT",
|
||||
"""
|
||||
UPDATE pdd_tasks
|
||||
SET shop_name = TRIM(json_extract(pdd_data, '$.shop_name'))
|
||||
WHERE pdd_data IS NOT NULL AND json_valid(pdd_data)
|
||||
AND json_type(pdd_data, '$.shop_name') = 'text'
|
||||
AND TRIM(json_extract(pdd_data, '$.shop_name')) <> ''
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
MIGRATIONS = {
|
||||
1: MIGRATION_1,
|
||||
2: MIGRATION_2,
|
||||
3: MIGRATION_3,
|
||||
4: MIGRATION_4,
|
||||
5: MIGRATION_5,
|
||||
}
|
||||
|
||||
+27
-12
@@ -67,12 +67,15 @@ class TaskRow:
|
||||
task_type: str
|
||||
goods_id: str = ""
|
||||
title: str = ""
|
||||
shop_name: str = ""
|
||||
color: str = ""
|
||||
size: str = ""
|
||||
price_cents: Optional[int] = None
|
||||
price_is_starting: bool = False
|
||||
quantity: Optional[int] = None
|
||||
status: str = "待执行"
|
||||
latest_run_status: str = ""
|
||||
duration_seconds: Optional[int] = None
|
||||
updated_at: str = ""
|
||||
|
||||
|
||||
@@ -86,11 +89,13 @@ class TaskTableModel(QAbstractTableModel):
|
||||
"选择",
|
||||
"任务类型",
|
||||
"商品标题",
|
||||
"店铺名",
|
||||
"颜色",
|
||||
"尺码",
|
||||
"价格",
|
||||
"数量",
|
||||
"状态",
|
||||
"用时",
|
||||
"更新时间",
|
||||
)
|
||||
|
||||
@@ -133,9 +138,9 @@ class TaskTableModel(QAbstractTableModel):
|
||||
return self._display_value(task, column)
|
||||
|
||||
if role == Qt.TextAlignmentRole:
|
||||
if column in (0, 1, 7):
|
||||
if column in (0, 1, 8, 9):
|
||||
return Qt.AlignCenter
|
||||
if column in (5, 6):
|
||||
if column in (6, 7):
|
||||
return Qt.AlignRight | Qt.AlignVCenter
|
||||
return Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
@@ -367,19 +372,27 @@ class TaskTableModel(QAbstractTableModel):
|
||||
if column == 2:
|
||||
return task.title or "尚未获取标题"
|
||||
if column == 3:
|
||||
return task.color or "—"
|
||||
return task.shop_name or "—"
|
||||
if column == 4:
|
||||
return task.size or "—"
|
||||
return task.color or "—"
|
||||
if column == 5:
|
||||
return task.size or "—"
|
||||
if column == 6:
|
||||
if task.price_cents is None:
|
||||
return "—"
|
||||
suffix = " 起" if task.price_is_starting else ""
|
||||
return f"¥{task.price_cents / 100:.2f}{suffix}"
|
||||
if column == 6:
|
||||
return "—" if task.quantity is None else str(task.quantity)
|
||||
if column == 7:
|
||||
return task.status
|
||||
return "—" if task.quantity is None else str(task.quantity)
|
||||
if column == 8:
|
||||
return task.status
|
||||
if column == 9:
|
||||
if task.latest_run_status == "running":
|
||||
return "进行中"
|
||||
if task.duration_seconds is None:
|
||||
return "—"
|
||||
return f"{max(0, task.duration_seconds)} 秒"
|
||||
if column == 10:
|
||||
return task.updated_at or "—"
|
||||
return ""
|
||||
|
||||
@@ -567,11 +580,13 @@ class PDDTaskPage(QWidget):
|
||||
self.taskTable.setColumnWidth(0, 64)
|
||||
self.taskTable.setColumnWidth(1, 82)
|
||||
self.taskTable.setColumnWidth(3, 108)
|
||||
self.taskTable.setColumnWidth(4, 82)
|
||||
self.taskTable.setColumnWidth(5, 96)
|
||||
self.taskTable.setColumnWidth(6, 70)
|
||||
self.taskTable.setColumnWidth(7, 116)
|
||||
self.taskTable.setColumnWidth(8, 156)
|
||||
self.taskTable.setColumnWidth(4, 108)
|
||||
self.taskTable.setColumnWidth(5, 82)
|
||||
self.taskTable.setColumnWidth(6, 96)
|
||||
self.taskTable.setColumnWidth(7, 70)
|
||||
self.taskTable.setColumnWidth(8, 116)
|
||||
self.taskTable.setColumnWidth(9, 82)
|
||||
self.taskTable.setColumnWidth(10, 156)
|
||||
self.taskTable.viewport().installEventFilter(self)
|
||||
|
||||
self.emptyStateCard = CardWidget(self)
|
||||
|
||||
@@ -1709,10 +1709,15 @@ def summary_to_row(summary: TaskSummary) -> TaskRow:
|
||||
task_type=TASK_TYPE_TEXT[summary.task_type],
|
||||
goods_id=summary.goods_id or "",
|
||||
title=summary.title or "",
|
||||
shop_name=summary.shop_name or "",
|
||||
color=summary.target_color or "",
|
||||
size=summary.target_size or "",
|
||||
price_cents=summary.price_cent,
|
||||
quantity=summary.quantity,
|
||||
status=TASK_STATUS_TEXT[summary.status],
|
||||
latest_run_status=(
|
||||
summary.latest_run_status.value if summary.latest_run_status else ""
|
||||
),
|
||||
duration_seconds=summary.duration_seconds,
|
||||
updated_at=summary.updated_at,
|
||||
)
|
||||
|
||||
@@ -138,6 +138,9 @@ class TaskSummary:
|
||||
quantity: Optional[int]
|
||||
status: TaskStatus
|
||||
updated_at: str
|
||||
shop_name: Optional[str] = None
|
||||
latest_run_status: Optional[RunStatus] = None
|
||||
duration_seconds: Optional[int] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -129,11 +129,20 @@ class TaskRepository:
|
||||
connection = open_database(self._db_path)
|
||||
try:
|
||||
rows = connection.execute(
|
||||
"SELECT id, remote_task_id, task_type, goods_id, title,"
|
||||
" target_color, target_size, price_cent, quantity, status, updated_at"
|
||||
"SELECT pdd_tasks.id, remote_task_id, task_type, goods_id, title,"
|
||||
" shop_name, target_color, target_size, price_cent, quantity,"
|
||||
" status, pdd_tasks.updated_at, latest_run.run_status AS latest_run_status,"
|
||||
" CASE WHEN latest_run.run_status = 'running' THEN NULL"
|
||||
" WHEN latest_run.started_at IS NOT NULL AND latest_run.finished_at IS NOT NULL"
|
||||
" THEN MAX(0, CAST(strftime('%s', latest_run.finished_at)"
|
||||
" - strftime('%s', latest_run.started_at) AS INTEGER))"
|
||||
" ELSE NULL END AS duration_seconds"
|
||||
" FROM pdd_tasks"
|
||||
" LEFT JOIN task_runs AS latest_run ON latest_run.id = ("
|
||||
" SELECT id FROM task_runs WHERE task_id = pdd_tasks.id"
|
||||
" ORDER BY attempt_no DESC LIMIT 1)"
|
||||
f"{where_sql}"
|
||||
" ORDER BY updated_at DESC, id DESC"
|
||||
" ORDER BY pdd_tasks.updated_at DESC, pdd_tasks.id DESC"
|
||||
" LIMIT ? OFFSET ?",
|
||||
parameters,
|
||||
).fetchall()
|
||||
@@ -1098,13 +1107,14 @@ class TaskRepository:
|
||||
task_cursor = connection.execute(
|
||||
"UPDATE pdd_tasks SET status = 'result_pending',"
|
||||
" current_step = 'purchase_order_matched_pending_report',"
|
||||
" pdd_data = ?,"
|
||||
" pdd_data = ?, shop_name = ?,"
|
||||
" price_cent = ?, last_error_code = NULL,"
|
||||
" last_error_message = NULL, finished_at = ?, updated_at = ?"
|
||||
" WHERE id = ? AND status = 'manual_review'"
|
||||
" AND current_step = 'reconcile_purchase'",
|
||||
(
|
||||
result_json,
|
||||
self._summary_shop_name(pdd_data),
|
||||
self._purchase_result_price(pdd_data),
|
||||
now,
|
||||
now,
|
||||
@@ -1188,11 +1198,12 @@ class TaskRepository:
|
||||
)
|
||||
connection.execute(
|
||||
"UPDATE pdd_tasks SET status = 'result_pending',"
|
||||
" current_step = 'submit_result', pdd_data = ?,"
|
||||
" current_step = 'submit_result', pdd_data = ?, shop_name = ?,"
|
||||
" price_cent = ?, finished_at = ?, updated_at = ?"
|
||||
" WHERE id = ?",
|
||||
(
|
||||
result_json,
|
||||
self._summary_shop_name(pdd_data),
|
||||
self._purchase_result_price(pdd_data),
|
||||
now,
|
||||
now,
|
||||
@@ -1373,11 +1384,13 @@ class TaskRepository:
|
||||
connection.execute(
|
||||
"UPDATE pdd_tasks SET status = 'result_pending',"
|
||||
" current_step = 'submit_result', pdd_data = ?, goods_id = ?,"
|
||||
" title = ?, price_cent = ?, finished_at = ?, updated_at = ?"
|
||||
" title = ?, shop_name = ?, price_cent = ?,"
|
||||
" finished_at = ?, updated_at = ?"
|
||||
" WHERE id = ?",
|
||||
(
|
||||
result_json,
|
||||
pdd_data.get("goods_id"), pdd_data.get("title"),
|
||||
self._summary_shop_name(pdd_data),
|
||||
self._summary_price(pdd_data), now, now, task["id"],
|
||||
),
|
||||
)
|
||||
@@ -1834,14 +1847,31 @@ class TaskRepository:
|
||||
task_type=TaskType(row["task_type"]),
|
||||
goods_id=row["goods_id"],
|
||||
title=row["title"],
|
||||
shop_name=row["shop_name"],
|
||||
target_color=row["target_color"],
|
||||
target_size=row["target_size"],
|
||||
price_cent=row["price_cent"],
|
||||
quantity=row["quantity"],
|
||||
status=TaskStatus(row["status"]),
|
||||
latest_run_status=(
|
||||
RunStatus(row["latest_run_status"])
|
||||
if row["latest_run_status"] is not None
|
||||
else None
|
||||
),
|
||||
duration_seconds=row["duration_seconds"],
|
||||
updated_at=row["updated_at"],
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _summary_shop_name(pdd_data: Dict[str, object]) -> Optional[str]:
|
||||
"""提取列表需要的店铺名,空值不写入摘要列。"""
|
||||
|
||||
value = pdd_data.get("shop_name")
|
||||
if not isinstance(value, str):
|
||||
return None
|
||||
value = value.strip()
|
||||
return value or None
|
||||
|
||||
@staticmethod
|
||||
def _to_detail(row: sqlite3.Row) -> TaskDetail:
|
||||
admin_payload = TaskRepository._load_json_object(row["admin_payload"])
|
||||
|
||||
Reference in New Issue
Block a user