zhenxun_bot/zhenxun/services/scheduler/targeter.py
Rumio fcb385cf01
♻️ refactor(scheduler): 重构定时任务服务架构并增强用户体验 (#1967)
**架构重构**
- 拆分为 Service、Repository、Adapter 三层架构,提升模块化
- 统一 APScheduler Job ID 生成方式,优化 ScheduleTargeter 逻辑

**新增功能**
- 支持定时任务时区配置
- 新增"运行中"任务状态显示
- 为"所有群组"任务增加随机延迟,分散并发压力

**用户体验优化**
- 重构操作反馈消息,提供详细的成功提示卡片
- 优化任务查看命令的筛选逻辑
- 统一删除、暂停、恢复、执行、更新操作的响应格式

Co-authored-by: webjoin111 <455457521@qq.com>
2025-07-10 22:20:08 +08:00

110 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
目标选择器 (Targeter)
提供链式API用于构建和执行对多个定时任务的批量操作。
"""
from collections.abc import Callable, Coroutine
from typing import Any
from .adapter import APSchedulerAdapter
from .repository import ScheduleRepository
class ScheduleTargeter:
"""
一个用于构建和执行定时任务批量操作的目标选择器。
"""
def __init__(self, manager: Any, **filters: Any):
"""初始化目标选择器"""
self._manager = manager
self._filters = {k: v for k, v in filters.items() if v is not None}
async def _get_schedules(self):
"""根据过滤器获取任务"""
query = ScheduleRepository.filter(**self._filters)
return await query.all()
def _generate_target_description(self) -> str:
"""根据过滤条件生成友好的目标描述"""
if "id" in self._filters:
return f"任务 ID {self._filters['id']}"
parts = []
if "group_id" in self._filters:
group_id = self._filters["group_id"]
if group_id == self._manager.ALL_GROUPS:
parts.append("所有群组中")
else:
parts.append(f"{group_id}")
if "plugin_name" in self._filters:
parts.append(f"插件 '{self._filters['plugin_name']}'")
if not parts:
return "所有"
return "".join(parts)
async def _apply_operation(
self,
operation_func: Callable[[int], Coroutine[Any, Any, tuple[bool, str]]],
operation_name: str,
) -> tuple[int, str]:
"""通用的操作应用模板"""
schedules = await self._get_schedules()
if not schedules:
target_desc = self._generate_target_description()
return 0, f"没有找到{target_desc}可供{operation_name}的任务。"
success_count = 0
for schedule in schedules:
success, _ = await operation_func(schedule.id)
if success:
success_count += 1
target_desc = self._generate_target_description()
return (
success_count,
f"成功{operation_name}{target_desc} {success_count} 个任务。",
)
async def pause(self) -> tuple[int, str]:
"""
暂停匹配的定时任务
返回:
tuple[int, str]: (成功暂停的任务数量, 操作结果消息)。
"""
return await self._apply_operation(self._manager.pause_schedule, "暂停")
async def resume(self) -> tuple[int, str]:
"""
恢复匹配的定时任务
返回:
tuple[int, str]: (成功恢复的任务数量, 操作结果消息)。
"""
return await self._apply_operation(self._manager.resume_schedule, "恢复")
async def remove(self) -> tuple[int, str]:
"""
移除匹配的定时任务
返回:
tuple[int, str]: (成功移除的任务数量, 操作结果消息)。
"""
schedules = await self._get_schedules()
if not schedules:
target_desc = self._generate_target_description()
return 0, f"没有找到{target_desc}可供移除的任务。"
for schedule in schedules:
APSchedulerAdapter.remove_job(schedule.id)
query = ScheduleRepository.filter(**self._filters)
count = await query.delete()
target_desc = self._generate_target_description()
return count, f"成功移除了{target_desc} {count} 个任务。"