""" 定时任务管理服务模块 ============================================================= 该模块实现了定时任务管理相关的所有业务逻辑。 """ import logging from flask import request from application.constants.common import PAGE_SIZE from application.forms.job import JobForm, JobStatusForm from application.models.job import Job from application.services.scheduler import ( add_job_to_scheduler, remove_job_from_scheduler, pause_job_in_scheduler, run_job_once as scheduler_run_job_once, reload_scheduler ) from utils import R, regular from utils.model_utils import models_to_list, model_to_dict from utils.request import parse_request_body, parse_id_list, like_contains from utils.security import get_username logger = logging.getLogger(__name__) # ============================================================ # 任务分页查询 # ============================================================ def get_job_page(): """查询定时任务分页数据""" try: page_no = int(request.args.get('pageNo', 1)) page_size = int(request.args.get('pageSize', PAGE_SIZE)) query = Job.query.filter(Job.is_delete == 0) # 筛选条件 job_name = request.args.get('jobName') if job_name: query = query.filter(Job.job_name.like(like_contains(job_name), escape='\\')) status = request.args.get('status') if status: query = query.filter(Job.status == status) job_group = request.args.get('jobGroup') if job_group: query = query.filter(Job.job_group == job_group) total = query.count() pages = (total + page_size - 1) // page_size if total > 0 else 0 job_list = query.order_by(Job.create_time.desc()) \ .limit(page_size) \ .offset((page_no - 1) * page_size) \ .all() records = models_to_list(job_list) # 添加状态文本 for record in records: record['statusText'] = _get_status_text(record.get('status')) record['executePolicyText'] = _get_execute_policy_text(record.get('execute_policy')) page_data = { 'records': records, 'total': total, 'size': page_size, 'current': page_no, 'pages': pages } return R.ok(data=page_data) except Exception as e: logger.error(f"查询任务分页异常: {str(e)}") return R.failed(msg="查询失败") def _get_status_text(status): """获取状态文本""" status_map = {0: '未发布', 1: '运行中', 2: '暂停', 3: '删除'} return status_map.get(status, '') def _get_execute_policy_text(policy): """获取执行策略文本""" policy_map = {1: '立即执行', 2: '执行一次', 3: '放弃执行'} return policy_map.get(policy, '') # ============================================================ # 任务详情查询 # ============================================================ def get_job_detail(job_id): """查询定时任务详情""" try: job = Job.query.filter(Job.id == job_id, Job.is_delete == 0).first() if not job: return None return model_to_dict(job) except Exception as e: logger.error(f"查询任务详情异常: {str(e)}") return None # ============================================================ # 任务添加 # ============================================================ def add_job(): """添加定时任务""" try: json_data, error = parse_request_body() if error: return R.failed(msg=error) form = JobForm(data=json_data) if not form.validate(): return R.failed(msg=regular.get_err(form)) job = Job(**form.data) job.create_user = get_username() job.save() # 添加到调度器 if job.status == 1 and job.cron_expression: add_job_to_scheduler(job) return R.ok(msg="添加成功") except Exception as e: logger.error(f"添加任务异常: {str(e)}") return R.failed(msg="添加任务失败,请稍后重试") # ============================================================ # 任务更新 # ============================================================ def update_job(): """更新定时任务""" try: json_data, error = parse_request_body() if error: return R.failed(msg=error) form = JobForm(data=json_data) if not form.validate(): return R.failed(msg=regular.get_err(form)) job_id = form.data.get('id') if not job_id: return R.failed("任务ID无效") job = Job.query.filter(Job.id == job_id, Job.is_delete == 0).first() if not job: return R.failed("任务不存在") old_status = job.status old_cron = job.cron_expression # 更新字段 for key, value in form.data.items(): if key != 'id' and hasattr(job, key): setattr(job, key, value) job.update_user = get_username() job.save() # 处理调度器 _handle_scheduler_update(job, old_status, old_cron) return R.ok(msg="更新成功") except Exception as e: logger.error(f"更新任务异常: {str(e)}") return R.failed(msg="更新任务失败,请稍后重试") def _handle_scheduler_update(job, old_status, old_cron): """处理调度器更新""" # 变为运行中 if old_status != 1 and job.status == 1 and job.cron_expression: add_job_to_scheduler(job) # 从运行中变为其他状态 elif old_status == 1 and job.status != 1: remove_job_from_scheduler(job.id) # 运行中但Cron变化 elif job.status == 1 and old_cron != job.cron_expression: if job.cron_expression: add_job_to_scheduler(job) else: remove_job_from_scheduler(job.id) # ============================================================ # 任务删除 # ============================================================ def delete_jobs(job_ids): """删除定时任务(批量)""" if not job_ids: return R.failed("记录ID不存在") id_list = parse_id_list(job_ids) if not id_list: return R.failed("无效的记录ID") for job_id in id_list: remove_job_from_scheduler(job_id) return Job.batch_delete_r(job_ids) # ============================================================ # 任务状态设置 # ============================================================ def update_job_status(): """设置任务状态""" try: json_data, error = parse_request_body() if error: return R.failed(msg=error) form = JobStatusForm(data=json_data) if not form.validate(): return R.failed(msg=regular.get_err(form)) job_id = int(form.data['id']) target_status = int(form.data['status']) job = Job.query.filter(Job.id == job_id, Job.is_delete == 0).first() if not job: return R.failed("任务不存在") old_status = job.status job.status = target_status job.update_user = get_username() job.save() # 处理调度器 if target_status == 1 and job.cron_expression: add_job_to_scheduler(job) elif target_status == 2: pause_job_in_scheduler(job_id) elif old_status == 1 and target_status != 1: remove_job_from_scheduler(job_id) return R.ok(msg="设置成功") except Exception as e: logger.error(f"设置任务状态异常: {str(e)}") return R.failed(msg="设置任务状态失败,请稍后重试") # ============================================================ # 暂停任务 # ============================================================ def pause_job(job_id): """暂停任务""" try: job = Job.query.filter(Job.id == job_id, Job.is_delete == 0).first() if not job: return R.failed("任务不存在") if job.status == 2: return R.ok(msg="任务已处于暂停状态") job.status = 2 job.update_user = get_username() job.save() pause_job_in_scheduler(job_id) return R.ok(msg="暂停成功") except Exception as e: logger.error(f"暂停任务异常: {str(e)}") return R.failed(msg="暂停任务失败,请稍后重试") # ============================================================ # 恢复任务 # ============================================================ def resume_job(job_id): """恢复任务""" try: job = Job.query.filter(Job.id == job_id, Job.is_delete == 0).first() if not job: return R.failed("任务不存在") if job.status == 1: return R.ok(msg="任务已处于运行中状态") if not job.cron_expression: return R.failed("Cron表达式为空,无法恢复") job.status = 1 job.update_user = get_username() job.save() # 直接传入模型对象,与其它方法保持一致 add_job_to_scheduler(job) return R.ok(msg="恢复成功") except Exception as e: logger.error(f"恢复任务异常: {str(e)}") return R.failed(msg="恢复任务失败,请稍后重试") # ============================================================ # 立即执行 # ============================================================ def run_job_once(job_id): """立即执行一次任务""" try: result = scheduler_run_job_once(job_id) if result.get('success'): return R.ok(data=result, msg="执行成功") else: return R.failed(msg=result.get('error', '执行失败')) except Exception as e: logger.error(f"立即执行异常: {str(e)}") return R.failed(msg="执行失败") # ============================================================ # 重新加载调度器 # ============================================================ def reload_all_jobs(): """重新加载所有任务""" try: count = reload_scheduler() return R.ok(data=count, msg=f"已重新加载 {count} 个任务") except Exception as e: logger.error(f"重新加载异常: {str(e)}") return R.failed(msg="重新加载失败")