update:筛选

This commit is contained in:
Marsway 2026-01-13 03:05:32 +08:00
parent 80fbe0874b
commit 337091d8d1
2 changed files with 80 additions and 0 deletions

49
app/admin/filters.py Normal file
View File

@ -0,0 +1,49 @@
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Any
class RecentDateTimeFilter:
"""
最近时间筛选避免依赖 DateTime OperationColumnFilter 支持情况
- all: 全部
- 1h/24h/7d/30d: 最近 N
"""
def __init__(self, column: Any, *, title: str, parameter_name: str) -> None:
self.column = column
self.title = title
self.parameter_name = parameter_name
def lookups(self, request, model) -> list[tuple[str, str]]: # noqa: ARG002 (framework signature)
return [
("all", "全部"),
("1h", "最近 1 小时"),
("24h", "最近 24 小时"),
("7d", "最近 7 天"),
("30d", "最近 30 天"),
]
def get_filtered_query(self, query, value: str):
if not value or value == "all":
return query
now = datetime.utcnow()
if value == "1h":
threshold = now - timedelta(hours=1)
elif value == "24h":
threshold = now - timedelta(hours=24)
elif value == "7d":
threshold = now - timedelta(days=7)
elif value == "30d":
threshold = now - timedelta(days=30)
else:
return query
cond = self.column >= threshold
# SQLAlchemy Select 在不同版本下可能是 where/filter这里兼容两者
if hasattr(query, "where"):
return query.where(cond)
return query.filter(cond)

View File

@ -8,9 +8,11 @@ from zoneinfo import ZoneInfo
from croniter import croniter from croniter import croniter
from markupsafe import Markup from markupsafe import Markup
from sqladmin import ModelView, action from sqladmin import ModelView, action
from sqladmin.filters import BooleanFilter, OperationColumnFilter, StaticValuesFilter
from sqladmin.models import Request from sqladmin.models import Request
from starlette.responses import RedirectResponse from starlette.responses import RedirectResponse
from app.admin.filters import RecentDateTimeFilter
from app.db.models import Job, JobLog from app.db.models import Job, JobLog
from app.plugins.manager import load_job_class from app.plugins.manager import load_job_class
from app.security.fernet import encrypt_json from app.security.fernet import encrypt_json
@ -103,6 +105,16 @@ class JobAdmin(ModelView, model=Job):
Job.last_run_at: lambda m, a: _fmt_dt_seconds(m.last_run_at), Job.last_run_at: lambda m, a: _fmt_dt_seconds(m.last_run_at),
} }
# 搜索:顶部 Search 输入框
column_searchable_list = [Job.id, Job.handler_path, Job.cron_expr]
# 筛选:右侧 Filters 栏
column_filters = [
BooleanFilter(Job.enabled),
OperationColumnFilter(Job.handler_path),
RecentDateTimeFilter(Job.created_at, title="创建时间", parameter_name="created_at_recent"),
]
@action( @action(
name="run_now", name="run_now",
label="立即运行", label="立即运行",
@ -228,6 +240,25 @@ class JobLogAdmin(ModelView, model=JobLog):
"finished_at": "结束时间", "finished_at": "结束时间",
} }
# 搜索任务ID / message / celery_task_id
column_searchable_list = [JobLog.job_id, JobLog.message, JobLog.celery_task_id]
# 筛选:状态 / 重试次数 / 最近开始时间
column_filters = [
StaticValuesFilter(
JobLog.status,
options=(
("RUNNING", "运行中"),
("SUCCESS", "成功"),
("FAILURE", "失败"),
("RETRY", "重试"),
),
title="状态",
),
OperationColumnFilter(JobLog.attempt),
RecentDateTimeFilter(JobLog.started_at, title="开始时间", parameter_name="started_at_recent"),
]
column_formatters = { column_formatters = {
JobLog.started_at: lambda m, a: _fmt_dt_seconds(m.started_at), JobLog.started_at: lambda m, a: _fmt_dt_seconds(m.started_at),
JobLog.finished_at: lambda m, a: _fmt_dt_seconds(m.finished_at), JobLog.finished_at: lambda m, a: _fmt_dt_seconds(m.finished_at),