From 4daa6f80a26d8c5fd5df2f2fc8e74929f5da7820 Mon Sep 17 00:00:00 2001 From: vapao Date: Sat, 13 Jun 2020 09:44:06 +0800 Subject: [PATCH] =?UTF-8?q?=20A=20=E4=BB=BB=E5=8A=A1=E8=AE=A1=E5=88=92Cron?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=9E=E6=97=B6=E6=98=BE=E7=A4=BA=E9=A2=84?= =?UTF-8?q?=E4=BC=B0=E6=89=A7=E8=A1=8C=E6=97=B6=E9=97=B4=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spug_api/apps/schedule/urls.py | 1 + spug_api/apps/schedule/views.py | 28 ++++++++++++++++++++++ spug_web/src/pages/schedule/Form.js | 36 +++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/spug_api/apps/schedule/urls.py b/spug_api/apps/schedule/urls.py index 8abdb36..fc26420 100644 --- a/spug_api/apps/schedule/urls.py +++ b/spug_api/apps/schedule/urls.py @@ -8,4 +8,5 @@ from .views import * urlpatterns = [ path('', Schedule.as_view()), path('/', HistoryView.as_view()), + path('run_time/', next_run_time), ] diff --git a/spug_api/apps/schedule/views.py b/spug_api/apps/schedule/views.py index 9a888c6..55e0994 100644 --- a/spug_api/apps/schedule/views.py +++ b/spug_api/apps/schedule/views.py @@ -3,7 +3,9 @@ # Released under the MIT License. from django.views.generic import View from django_redis import get_redis_connection +from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger +from apps.schedule.scheduler import Scheduler from apps.schedule.models import Task, History from apps.schedule.executors import dispatch from apps.host.models import Host @@ -135,3 +137,29 @@ class HistoryView(View): 'output': out}) data['duration'] = f"{data['duration'] / len(outputs):.3f}" return data + + +def next_run_time(request): + form, error = JsonParser( + Argument('rule', help='参数错误'), + Argument('start', required=False), + Argument('stop', required=False) + ).parse(request.body) + if error is None: + try: + minute, hour, day, month, week = form.rule.split() + week = Scheduler.week_map[week] + trigger = CronTrigger(minute=minute, hour=hour, day=day, month=month, day_of_week=week, + start_date=form.start, end_date=form.stop) + except (ValueError, KeyError): + return json_response({'success': False, 'msg': '无效的执行规则'}) + scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE) + scheduler.start() + job = scheduler.add_job(lambda: None, trigger) + run_time = job.next_run_time + scheduler.shutdown() + if run_time: + return json_response({'success': True, 'msg': run_time.strftime('%Y-%m-%d %H:%M:%S')}) + else: + return json_response({'success': False, 'msg': '无法被触发'}) + return json_response(error=error) diff --git a/spug_web/src/pages/schedule/Form.js b/spug_web/src/pages/schedule/Form.js index 444d9f3..882d1fa 100644 --- a/spug_web/src/pages/schedule/Form.js +++ b/spug_web/src/pages/schedule/Form.js @@ -20,10 +20,13 @@ class ComForm extends React.Component { constructor(props) { super(props); this.isFirstRender = true; + this.lastFetchId = 0; + this._fetchNextRunTime = lds.debounce(this._fetchNextRunTime, 500); this.state = { loading: false, type: null, page: 0, + nextRunTime: null, args: {[store.record['trigger']]: store.record['trigger_args']}, command: store.record['command'], } @@ -100,7 +103,35 @@ class ComForm extends React.Component { handleCronArgs = (key, value) => { let args = this.state.args['cron'] || {}; args = Object.assign(args, {[key]: value}); - this.setState({args: Object.assign(this.state.args, {cron: args})}) + this.setState({args: Object.assign(this.state.args, {cron: args})}, () => { + if (key === 'rule') { + value = value.trim(); + if (value.split(' ').length === 5) { + this.setState({nextRunTime: }); + this._fetchNextRunTime() + } else { + this.setState({nextRunTime: null}) + } + } else { + this.setState({nextRunTime: }); + this._fetchNextRunTime() + } + }); + }; + + _fetchNextRunTime = () => { + this.lastFetchId += 1; + const fetchId = this.lastFetchId; + const args = this._parse_args('cron'); + http.post('/api/schedule/run_time/', JSON.parse(args)) + .then(({success, msg}) => { + if (fetchId !== this.lastFetchId) return; + if (success) { + this.setState({nextRunTime: {msg}}) + } else { + this.setState({nextRunTime: {msg}}) + } + }) }; verifyButtonStatus = () => { @@ -118,7 +149,7 @@ class ComForm extends React.Component { render() { const info = store.record; const {getFieldDecorator} = this.props.form; - const {page, args, loading, showTmp} = this.state; + const {page, args, loading, showTmp, nextRunTime} = this.state; const [b1, b2, b3] = this.verifyButtonStatus(); return ( } value={lds.get(args, 'cron.rule')} placeholder="例如每天凌晨1点执行:0 1 * * *" onChange={e => this.handleCronArgs('rule', e.target.value)}/>