zhenxun_bot/zhenxun/services/scheduler/repository.py
Rumio 70bde00757
feat(core): 增强定时任务与群组标签管理,重构调度核心 (#2068)
*  feat(core): 更新群组信息、Markdown 样式与 Pydantic 兼容层

- 【group】添加更新所有群组信息指令,并同步群组控制台数据
- 【markdown】支持合并 Markdown 的 CSS 来源
- 【pydantic-compat】提供 model_validate 兼容函数

*  feat(core): 增强定时任务与群组标签管理,重构调度核心

 新功能

* **标签 (tags)**: 引入群组标签服务。
    * 支持静态标签和动态标签 (基于 Alconna 规则自动匹配群信息)。
    * 支持黑名单模式及 `@all` 特殊标签。
    * 提供 `tag_manage` 超级用户插件 (list, create, edit, delete 等)。
    * 群成员变动时自动失效动态标签缓存。
* **调度 (scheduler)**: 增强定时任务。
    * 重构 `ScheduledJob` 模型,支持 `TAG`, `ALL_GROUPS` 等多种目标类型。
    * 新增任务别名 (`name`)、创建者、权限、来源等字段。
    * 支持一次性任务 (`schedule_once`) 和 Alconna 命令行参数 (`--params-cli`)。
    * 新增执行选项 (`jitter`, `spread`) 和并发策略 (`ALLOW`, `SKIP`, `QUEUE`)。
    * 支持批量获取任务状态。

♻️ 重构优化

* **调度器核心**:
    * 拆分 `service.py` 为 `manager.py` (API) 和 `types.py` (模型)。
    * 合并 `adapter.py` / `job.py` 至 `engine.py` (统一调度引擎)。
    * 引入 `targeting.py` 模块管理任务目标解析。
* **调度器插件 (scheduler_admin)**:
    * 迁移命令参数校验逻辑至 `ArparmaBehavior`。
    * 引入 `dependencies.py` 和 `data_source.py` 解耦业务逻辑与依赖注入。
    * 适配新的任务目标类型展示。

* 🐛 fix(tag): 修复黑名单标签解析逻辑并优化标签详情展示

*  feat(scheduler): 为多目标定时任务添加固定间隔串行执行选项

*  feat(schedulerAdmin): 允许定时任务删除、暂停、恢复命令支持多ID操作

* 🚨 auto fix by pre-commit hooks

---------

Co-authored-by: webjoin111 <455457521@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-11-03 10:53:40 +08:00

109 lines
3.3 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.

"""
数据持久层 (Repository)
封装所有对 ScheduledJob 模型的数据库操作,将数据访问逻辑与业务逻辑分离。
"""
from typing import Any
from tortoise.queryset import QuerySet
from zhenxun.models.scheduled_job import ScheduledJob
class ScheduleRepository:
"""封装 ScheduledJob 模型的数据库操作"""
@staticmethod
async def get_by_id(schedule_id: int) -> ScheduledJob | None:
"""
通过ID获取任务
参数:
schedule_id: 任务ID。
返回:
ScheduledJob | None: 任务对象不存在时返回None。
"""
return await ScheduledJob.get_or_none(id=schedule_id)
@staticmethod
async def get_all_enabled() -> list[ScheduledJob]:
"""
获取所有启用的任务
返回:
list[ScheduledJob]: 所有启用状态的任务列表。
"""
return await ScheduledJob.filter(is_enabled=True).all()
@staticmethod
async def get_all(plugin_name: str | None = None) -> list[ScheduledJob]:
"""获取所有任务,可按插件名过滤"""
if plugin_name:
return await ScheduledJob.filter(plugin_name=plugin_name).all()
return await ScheduledJob.all()
@staticmethod
async def save(schedule: ScheduledJob, update_fields: list[str] | None = None):
"""
保存任务
参数:
schedule: 要保存的任务对象。
update_fields: 要更新的字段列表None表示更新所有字段。
"""
await schedule.save(update_fields=update_fields)
@staticmethod
async def exists(**kwargs: Any) -> bool:
"""检查任务是否存在"""
return await ScheduledJob.exists(**kwargs)
@staticmethod
async def get_by_plugin_and_group(
plugin_name: str, group_ids: list[str]
) -> list[ScheduledJob]:
"""[DEPRECATED] 根据插件和群组ID列表获取任务"""
return await ScheduledJob.filter(
plugin_name=plugin_name, target_descriptor__in=group_ids
).all()
@staticmethod
async def update_or_create(
defaults: dict, **kwargs: Any
) -> tuple[ScheduledJob, bool]:
"""更新或创建任务"""
return await ScheduledJob.update_or_create(defaults=defaults, **kwargs)
@staticmethod
async def query_schedules(
page: int | None = None, page_size: int | None = None, **filters: Any
) -> tuple[list[ScheduledJob], int]:
"""
根据任意条件查询任务列表
参数:
page: 页码从1开始
page_size: 每页数量
**filters: 过滤条件,如 group_id="123", plugin_name="abc"
返回:
tuple[list[ScheduledJob], int]: (任务列表, 总数)
"""
cleaned_filters = {k: v for k, v in filters.items() if v is not None}
query = ScheduledJob.filter(**cleaned_filters)
total_count = await query.count()
if page is not None and page_size is not None:
offset = (page - 1) * page_size
query = query.offset(offset).limit(page_size)
return await query.all(), total_count
@staticmethod
def filter(**kwargs: Any) -> QuerySet[ScheduledJob]:
"""提供一个通用的过滤查询接口供Targeter使用"""
return ScheduledJob.filter(**kwargs)