调整部分前端布局和后端增删改逻辑
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from .service import JOB_RUNTIME, JobRuntimeService
|
||||
|
||||
__all__ = ['JOB_RUNTIME', 'JobRuntimeService']
|
||||
@@ -0,0 +1,74 @@
|
||||
from importlib import import_module
|
||||
from typing import Any
|
||||
|
||||
|
||||
class JobInfrastructureGateway:
|
||||
"""
|
||||
定时任务基础设施网关。
|
||||
|
||||
该对象负责延迟加载数据库会话、Redis、调度器和任务服务依赖,
|
||||
供定时任务运行时 facade 与其协作对象统一复用。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_page_model() -> Any:
|
||||
"""
|
||||
获取分页模型类型。
|
||||
|
||||
:return: 分页模型类型
|
||||
"""
|
||||
return import_module('common.vo').PageModel
|
||||
|
||||
@staticmethod
|
||||
def get_async_session_local() -> Any:
|
||||
"""
|
||||
获取异步数据库会话工厂。
|
||||
|
||||
:return: 异步数据库会话工厂
|
||||
"""
|
||||
return import_module('config.database').AsyncSessionLocal
|
||||
|
||||
@staticmethod
|
||||
def get_redis_util() -> Any:
|
||||
"""
|
||||
获取 Redis 工具类。
|
||||
|
||||
:return: Redis 工具类
|
||||
"""
|
||||
return import_module('config.get_redis').RedisUtil
|
||||
|
||||
@staticmethod
|
||||
def get_scheduler_util() -> Any:
|
||||
"""
|
||||
获取调度器工具类。
|
||||
|
||||
:return: 调度器工具类
|
||||
"""
|
||||
return import_module('config.get_scheduler').SchedulerUtil
|
||||
|
||||
@staticmethod
|
||||
def get_job_vo_module() -> Any:
|
||||
"""
|
||||
获取定时任务 VO 模块。
|
||||
|
||||
:return: 定时任务 VO 模块
|
||||
"""
|
||||
return import_module('module_admin.entity.vo.job_vo')
|
||||
|
||||
@staticmethod
|
||||
def get_job_service() -> Any:
|
||||
"""
|
||||
获取定时任务服务类。
|
||||
|
||||
:return: 定时任务服务类
|
||||
"""
|
||||
return import_module('module_admin.service.job_service').JobService
|
||||
|
||||
@staticmethod
|
||||
def get_job_log_service() -> Any:
|
||||
"""
|
||||
获取定时任务日志服务类。
|
||||
|
||||
:return: 定时任务日志服务类
|
||||
"""
|
||||
return import_module('module_admin.service.job_log_service').JobLogService
|
||||
@@ -0,0 +1,246 @@
|
||||
from typing import Any
|
||||
|
||||
from cli.exit_codes import DATABASE_ERROR, RUNTIME_ERROR
|
||||
|
||||
from .gateway import JobInfrastructureGateway
|
||||
from .support import JobDomainSupport, JobSchedulerSupport
|
||||
|
||||
|
||||
class JobRuntimeService:
|
||||
"""
|
||||
定时任务运行时服务。
|
||||
|
||||
该服务作为定时任务运行时 facade,对外统一暴露任务列表、详情、日志、
|
||||
执行一次、暂停与恢复入口。
|
||||
|
||||
:param infrastructure_gateway: 定时任务基础设施网关
|
||||
:param domain_support: 定时任务领域支持对象
|
||||
:param scheduler_support: 定时任务调度上下文支持对象
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
infrastructure_gateway: JobInfrastructureGateway | None = None,
|
||||
domain_support: JobDomainSupport | None = None,
|
||||
scheduler_support: JobSchedulerSupport | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
初始化定时任务运行时服务。
|
||||
|
||||
:param infrastructure_gateway: 定时任务基础设施网关
|
||||
:param domain_support: 定时任务领域支持对象
|
||||
:param scheduler_support: 定时任务调度上下文支持对象
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway or JobInfrastructureGateway()
|
||||
self.domain_support = domain_support or JobDomainSupport(self.infrastructure_gateway)
|
||||
self.scheduler_support = scheduler_support or JobSchedulerSupport(self.infrastructure_gateway)
|
||||
|
||||
async def list_jobs(
|
||||
self,
|
||||
*,
|
||||
job_name: str = '',
|
||||
job_group: str = '',
|
||||
status: str | None = None,
|
||||
paged: bool = False,
|
||||
page_num: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
查询定时任务列表。
|
||||
|
||||
:param job_name: 任务名称过滤条件
|
||||
:param job_group: 任务组过滤条件
|
||||
:param status: 状态过滤条件
|
||||
:param paged: 是否启用分页
|
||||
:param page_num: 页码
|
||||
:param page_size: 每页数量
|
||||
:return: 定时任务列表结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
job_vo_module = self.infrastructure_gateway.get_job_vo_module()
|
||||
job_service = self.infrastructure_gateway.get_job_service()
|
||||
query_model = job_vo_module.JobPageQueryModel(
|
||||
jobName=job_name or None,
|
||||
jobGroup=job_group or None,
|
||||
status=status,
|
||||
pageNum=page_num,
|
||||
pageSize=page_size,
|
||||
)
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
result = await job_service.get_job_list_services(session, query_model, is_page=paged)
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '读取定时任务列表失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
filters = self.domain_support.build_filters(
|
||||
job_name=job_name,
|
||||
job_group=job_group,
|
||||
status=status,
|
||||
paged=paged,
|
||||
page_num=page_num,
|
||||
page_size=page_size,
|
||||
)
|
||||
return self.domain_support.build_list_payload(result, filters=filters, paged=paged)
|
||||
|
||||
async def get_job_detail(self, job_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
读取单个定时任务详情。
|
||||
|
||||
:param job_id: 任务 ID
|
||||
:return: 定时任务详情结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
job_service = self.infrastructure_gateway.get_job_service()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
job_model = await job_service.job_detail_services(session, job_id)
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '读取定时任务详情失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
job_payload = self.domain_support.serialize_job_item(job_model)
|
||||
if not job_payload.get('jobId'):
|
||||
return {
|
||||
'ok': False,
|
||||
'message': f'定时任务不存在:{job_id}',
|
||||
'jobId': job_id,
|
||||
'exit_code': RUNTIME_ERROR,
|
||||
}
|
||||
return {'ok': True, 'job': job_payload}
|
||||
|
||||
async def list_job_logs(
|
||||
self,
|
||||
*,
|
||||
job_name: str = '',
|
||||
job_group: str = '',
|
||||
status: str | None = None,
|
||||
begin_date: str = '',
|
||||
end_date: str = '',
|
||||
paged: bool = False,
|
||||
page_num: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
查询定时任务执行日志列表。
|
||||
|
||||
:param job_name: 任务名称过滤条件
|
||||
:param job_group: 任务组过滤条件
|
||||
:param status: 执行状态过滤条件
|
||||
:param begin_date: 开始日期,格式 `YYYY-MM-DD`
|
||||
:param end_date: 结束日期,格式 `YYYY-MM-DD`
|
||||
:param paged: 是否启用分页
|
||||
:param page_num: 页码
|
||||
:param page_size: 每页数量
|
||||
:return: 定时任务日志列表结果
|
||||
"""
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
job_log_service = self.infrastructure_gateway.get_job_log_service()
|
||||
job_vo_module = self.infrastructure_gateway.get_job_vo_module()
|
||||
query_model = job_vo_module.JobLogPageQueryModel(
|
||||
jobName=job_name or None,
|
||||
jobGroup=job_group or None,
|
||||
status=status,
|
||||
beginTime=begin_date or None,
|
||||
endTime=end_date or None,
|
||||
pageNum=page_num,
|
||||
pageSize=page_size,
|
||||
)
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
result = await job_log_service.get_job_log_list_services(session, query_model, is_page=paged)
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '读取定时任务日志列表失败', 'error': str(exc), 'exit_code': DATABASE_ERROR}
|
||||
|
||||
filters = self.domain_support.build_filters(
|
||||
job_name=job_name,
|
||||
job_group=job_group,
|
||||
status=status,
|
||||
begin_date=begin_date,
|
||||
end_date=end_date,
|
||||
paged=paged,
|
||||
page_num=page_num,
|
||||
page_size=page_size,
|
||||
)
|
||||
return self.domain_support.build_list_payload(result, filters=filters, paged=paged)
|
||||
|
||||
async def run_with_scheduler_context(
|
||||
self,
|
||||
operation: str,
|
||||
job_id: int,
|
||||
*,
|
||||
status: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
在调度器上下文中执行任务操作。
|
||||
|
||||
:param operation: 操作名称
|
||||
:param job_id: 任务ID
|
||||
:param status: 目标状态
|
||||
:return: 任务操作结果
|
||||
"""
|
||||
operation_metadata = self.domain_support.build_job_operation_metadata(operation)
|
||||
redis = None
|
||||
async_session_local = self.infrastructure_gateway.get_async_session_local()
|
||||
redis_util = self.infrastructure_gateway.get_redis_util()
|
||||
scheduler_util = self.infrastructure_gateway.get_scheduler_util()
|
||||
job_vo_module = self.infrastructure_gateway.get_job_vo_module()
|
||||
job_service = self.infrastructure_gateway.get_job_service()
|
||||
try:
|
||||
async with async_session_local() as session:
|
||||
redis = await redis_util.create_redis_pool(log_enabled=False)
|
||||
await scheduler_util.init_system_scheduler(redis)
|
||||
|
||||
if operation == 'run-once':
|
||||
result = await job_service.execute_job_once_services(session, job_vo_module.JobModel(jobId=job_id))
|
||||
elif operation in {'pause', 'resume'}:
|
||||
result = await job_service.edit_job_services(
|
||||
session,
|
||||
job_vo_module.EditJobModel(jobId=job_id, status=status, type='status'),
|
||||
)
|
||||
else:
|
||||
raise ValueError(f'不支持的任务操作:{operation}')
|
||||
except Exception as exc:
|
||||
return {'ok': False, 'message': '执行定时任务操作失败', 'error': str(exc), 'exit_code': 22}
|
||||
finally:
|
||||
await self.scheduler_support.close_scheduler_context(redis)
|
||||
|
||||
return {
|
||||
'ok': bool(result.is_success),
|
||||
'jobId': job_id,
|
||||
'operation': operation,
|
||||
'operationLabel': operation_metadata['operationLabel'],
|
||||
'targetStatus': status,
|
||||
'message': operation_metadata['successMessage'] if result.is_success else '定时任务操作执行失败',
|
||||
'serviceMessage': result.message,
|
||||
}
|
||||
|
||||
async def run_job_once(self, job_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
执行一次指定定时任务。
|
||||
|
||||
:param job_id: 任务ID
|
||||
:return: 执行结果
|
||||
"""
|
||||
return await self.run_with_scheduler_context('run-once', job_id)
|
||||
|
||||
async def pause_job(self, job_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
暂停指定定时任务。
|
||||
|
||||
:param job_id: 任务ID
|
||||
:return: 暂停结果
|
||||
"""
|
||||
return await self.run_with_scheduler_context('pause', job_id, status='1')
|
||||
|
||||
async def resume_job(self, job_id: int) -> dict[str, Any]:
|
||||
"""
|
||||
恢复指定定时任务。
|
||||
|
||||
:param job_id: 任务ID
|
||||
:return: 恢复结果
|
||||
"""
|
||||
return await self.run_with_scheduler_context('resume', job_id, status='0')
|
||||
|
||||
|
||||
JOB_RUNTIME = JobRuntimeService()
|
||||
@@ -0,0 +1,173 @@
|
||||
from typing import Any
|
||||
|
||||
from .gateway import JobInfrastructureGateway
|
||||
|
||||
|
||||
class JobDomainSupport:
|
||||
"""
|
||||
定时任务领域支持对象。
|
||||
|
||||
该对象负责任务序列化、分页结果规整和任务操作元数据定义,
|
||||
避免主运行时服务继续承载细碎领域规则。
|
||||
|
||||
:param infrastructure_gateway: 定时任务基础设施网关
|
||||
"""
|
||||
|
||||
def __init__(self, infrastructure_gateway: JobInfrastructureGateway) -> None:
|
||||
"""
|
||||
初始化定时任务领域支持对象。
|
||||
|
||||
:param infrastructure_gateway: 定时任务基础设施网关
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway
|
||||
|
||||
@staticmethod
|
||||
def serialize_job_item(job_item: Any) -> dict[str, Any]:
|
||||
"""
|
||||
序列化单个定时任务模型。
|
||||
|
||||
:param job_item: 原始定时任务模型或字典
|
||||
:return: 可输出的定时任务字典
|
||||
"""
|
||||
if hasattr(job_item, 'model_dump'):
|
||||
return dict(job_item.model_dump(by_alias=True, exclude_none=True))
|
||||
return dict(job_item)
|
||||
|
||||
def serialize_job_items(self, job_items: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""
|
||||
序列化任务列表结果。
|
||||
|
||||
:param job_items: 原始任务列表
|
||||
:return: 可输出的任务列表
|
||||
"""
|
||||
return [self.serialize_job_item(item) for item in job_items]
|
||||
|
||||
@staticmethod
|
||||
def build_job_operation_metadata(operation: str) -> dict[str, str]:
|
||||
"""
|
||||
构建任务操作的统一元数据。
|
||||
|
||||
:param operation: 操作名称
|
||||
:return: 操作元数据字典
|
||||
:raises ValueError: 操作名称不受支持时抛出异常
|
||||
"""
|
||||
metadata_mapping = {
|
||||
'run-once': {
|
||||
'operationLabel': '执行一次任务',
|
||||
'successMessage': '定时任务已触发一次执行',
|
||||
},
|
||||
'pause': {
|
||||
'operationLabel': '暂停任务',
|
||||
'successMessage': '定时任务已暂停',
|
||||
},
|
||||
'resume': {
|
||||
'operationLabel': '恢复任务',
|
||||
'successMessage': '定时任务已恢复',
|
||||
},
|
||||
}
|
||||
if operation not in metadata_mapping:
|
||||
raise ValueError(f'不支持的任务操作:{operation}')
|
||||
return metadata_mapping[operation]
|
||||
|
||||
def build_filters(
|
||||
self,
|
||||
*,
|
||||
job_name: str = '',
|
||||
job_group: str = '',
|
||||
status: str | None = None,
|
||||
begin_date: str = '',
|
||||
end_date: str = '',
|
||||
paged: bool = False,
|
||||
page_num: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
构建任务列表或日志查询过滤条件。
|
||||
|
||||
:param job_name: 任务名称
|
||||
:param job_group: 任务分组
|
||||
:param status: 状态
|
||||
:param begin_date: 开始日期
|
||||
:param end_date: 结束日期
|
||||
:param paged: 是否分页
|
||||
:param page_num: 页码
|
||||
:param page_size: 每页数量
|
||||
:return: 过滤条件字典
|
||||
"""
|
||||
filters = {
|
||||
'jobName': job_name,
|
||||
'jobGroup': job_group,
|
||||
'status': status,
|
||||
'paged': paged,
|
||||
'pageNum': page_num,
|
||||
'pageSize': page_size,
|
||||
}
|
||||
if begin_date or end_date:
|
||||
filters['beginDate'] = begin_date
|
||||
filters['endDate'] = end_date
|
||||
return filters
|
||||
|
||||
def build_list_payload(
|
||||
self,
|
||||
result: Any,
|
||||
*,
|
||||
filters: dict[str, Any],
|
||||
paged: bool,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
统一构建任务列表或日志列表返回结构。
|
||||
|
||||
:param result: 原始结果对象
|
||||
:param filters: 查询过滤条件
|
||||
:param paged: 是否分页
|
||||
:return: 可输出结果
|
||||
"""
|
||||
if paged and isinstance(result, self.infrastructure_gateway.get_page_model()):
|
||||
page_payload = result.model_dump(by_alias=True)
|
||||
page_payload['rows'] = self.serialize_job_items(page_payload.get('rows', []))
|
||||
return {'ok': True, 'filters': filters, 'page': page_payload}
|
||||
|
||||
items = self.serialize_job_items(result)
|
||||
return {'ok': True, 'filters': filters, 'count': len(items), 'items': items}
|
||||
|
||||
|
||||
class JobSchedulerSupport:
|
||||
"""
|
||||
定时任务调度上下文支持对象。
|
||||
|
||||
该对象负责统一关闭调度器上下文资源,避免调度型操作在 facade 中
|
||||
持续堆叠资源清理细节。
|
||||
|
||||
:param infrastructure_gateway: 定时任务基础设施网关
|
||||
"""
|
||||
|
||||
def __init__(self, infrastructure_gateway: JobInfrastructureGateway) -> None:
|
||||
"""
|
||||
初始化定时任务调度上下文支持对象。
|
||||
|
||||
:param infrastructure_gateway: 定时任务基础设施网关
|
||||
:return: None
|
||||
"""
|
||||
self.infrastructure_gateway = infrastructure_gateway
|
||||
|
||||
async def close_scheduler_context(self, redis: Any) -> None:
|
||||
"""
|
||||
安全关闭任务调度相关资源。
|
||||
|
||||
:param redis: Redis 客户端实例
|
||||
:return: None
|
||||
"""
|
||||
scheduler_util = self.infrastructure_gateway.get_scheduler_util()
|
||||
try:
|
||||
await scheduler_util.close_system_scheduler()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if redis is None:
|
||||
return
|
||||
|
||||
try:
|
||||
await redis.close()
|
||||
except Exception:
|
||||
pass
|
||||
Reference in New Issue
Block a user