1、后台菜单增加定时任务和银行交易

2、增加后台配置银行账户和请求网址
This commit is contained in:
2026-08-13 15:12:16 +08:00
parent 33a5cc4f94
commit 7dcedf974d
22 changed files with 1741 additions and 3 deletions
+80
View File
@@ -40,6 +40,7 @@ CELERY_TASK_IMPORTS = (
"app.tasks.api_generation_tasks",
"app.tasks.api_recovery_tasks",
"app.tasks.api_upscale_tasks",
"app.tasks.scheduled_tasks",
)
@@ -292,6 +293,85 @@ else:
celery_app = None
def _parse_schedule_to_celery(schedule_str: str):
"""将 schedule 字符串解析为 Celery 可识别的调度值。
- 纯数字:视为间隔秒数(返回 int)
- cron 表达式 (5 字段空格分隔):返回 crontab 对象
"""
from celery.schedules import crontab
s = (schedule_str or "").strip()
if not s:
return None
# 纯数字 → 间隔秒数
if s.isdigit():
return int(s)
# cron 表达式 (分 时 日 月 周)
parts = s.split()
if len(parts) == 5:
try:
return crontab(
minute=parts[0],
hour=parts[1],
day_of_month=parts[2],
month_of_year=parts[3],
day_of_week=parts[4],
)
except Exception:
logger.exception("解析 cron 表达式失败: %s", s)
return None
logger.warning("无法解析 schedule 表达式: %s", s)
return None
@celery_app.on_after_configure.connect # type: ignore
def _setup_dynamic_beat_tasks(sender, **kwargs):
"""从数据库加载活跃定时任务并注册到 Beat 调度。
通过 @celery_app.on_after_configure.connect 在 Celery 配置完成后执行,
适用于 Worker 和 Beat 启动场景。
"""
if celery_app is None:
return
async def _load():
from sqlalchemy import select
from app.models.base import async_session
from app.models.scheduled_task import ScheduledTask
async with async_session() as db:
result = await db.execute(
select(ScheduledTask).where(ScheduledTask.is_active.is_(True))
)
tasks = result.scalars().all()
return tasks
try:
active_tasks = run_async(_load())
except Exception:
logger.exception("加载定时任务失败,跳过动态 Beat 注册")
return
for task in active_tasks:
schedule_val = _parse_schedule_to_celery(task.schedule)
if schedule_val is None:
logger.warning("定时任务 %s schedule 无效,跳过注册: %s", task.id, task.schedule)
continue
beat_key = f"dynamic-scheduled-task-{task.id}"
sender.conf.beat_schedule[beat_key] = {
"task": "execute_scheduled_task",
"schedule": schedule_val,
"args": (task.id,),
"options": {"queue": RECOVERY_QUEUE},
}
logger.info(
"动态注册定时任务到 Beat: %s (%s) schedule=%s",
task.name, task.id, task.schedule,
)
async def _try_acquire_startup_recovery_lock() -> bool:
"""任意 worker 启动时都可尝试抢恢复投递锁,避免依赖 hostname 命名。"""
from app.services.redis_registry_service import redis_acquire_lock