├── crontab_create.png
├── crontab_list.png
├── crontab_log_list.png
├── crontab_log_detail.png
├── .gitignore
├── routes
└── web.php
├── src
├── Http
│ ├── Models
│ │ ├── Crontab.php
│ │ └── CrontabLog.php
│ └── Controllers
│ │ ├── CrontabLogController.php
│ │ └── CrontabController.php
├── Crontab.php
├── CrontabServiceProvider.php
└── autoTask.php
├── composer.json
├── LICENSE
├── README.md
└── migrations
└── 2019_03_18_083704_create_crontab_table.php
/crontab_create.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArrowJustDoIt/crontab/HEAD/crontab_create.png
--------------------------------------------------------------------------------
/crontab_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArrowJustDoIt/crontab/HEAD/crontab_list.png
--------------------------------------------------------------------------------
/crontab_log_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArrowJustDoIt/crontab/HEAD/crontab_log_list.png
--------------------------------------------------------------------------------
/crontab_log_detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArrowJustDoIt/crontab/HEAD/crontab_log_detail.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | phpunit.phar
3 | /vendor
4 | composer.phar
5 | composer.lock
6 | *.project
7 | .idea/
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | hasMany(CrontabLog::class, 'cid', 'id');
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Crontab.php:
--------------------------------------------------------------------------------
1 | '定时任务',
15 | 'path' => 'crontab',
16 | 'icon' => 'fa-gears',
17 | ];
18 | }
--------------------------------------------------------------------------------
/src/Http/Models/CrontabLog.php:
--------------------------------------------------------------------------------
1 | belongsTo(Crontab::class, 'cid', 'id');
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/CrontabServiceProvider.php:
--------------------------------------------------------------------------------
1 | migrations()) {
21 | $this->loadMigrationsFrom($migrations);
22 | }
23 |
24 | //命令
25 | if ($this->app->runningInConsole()) {
26 | $this->commands([
27 | autoTask::class,
28 | ]);
29 | }
30 |
31 | $this->app->booted(function () {
32 | //路由
33 | Crontab::routes(__DIR__.'/../routes/web.php');
34 | });
35 | }
36 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arrowjustdoit/crontab",
3 | "description": "Crontab extension for laravel-admin",
4 | "type": "library",
5 | "keywords": ["laravel-admin", "extension", "crontab"],
6 | "homepage": "https://github.com/ArrowJustDoIt/crontab",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "arrow",
11 | "email": "arrowylq@outlook.com"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=7.0.0",
16 | "encore/laravel-admin": "~1.6",
17 | "guzzlehttp/guzzle": "^6.3"
18 | },
19 | "require-dev": {
20 | "phpunit/phpunit": "~6.0"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "ArrowJustDoIt\\Crontab\\": "src/"
25 | }
26 | },
27 | "extra": {
28 | "laravel": {
29 | "providers": [
30 | "ArrowJustDoIt\\Crontab\\CrontabServiceProvider"
31 | ]
32 |
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jens Segers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Crontab extension for laravel-admin
2 | ======
3 |
4 | [Crontab](https://github.com/ArrowJustDoIt/Crontab)是一个laravel-admin后台的定时任务扩展插件,你可以通过此插件定时执行shell、sql以及访问指定链接
5 |
6 | [dcat-admin版本](https://github.com/ArrowJustDoIt/dcat-admin-crontab-extension)
7 |
8 | ## 截图
9 | 
10 |
11 | 
12 |
13 | 
14 |
15 | 
16 | ## 安装
17 |
18 | ```bash
19 | composer require arrowjustdoit/crontab
20 | php artisan migrate
21 | ```
22 |
23 | ## 配置
24 |
25 | 在`config/admin.php`文件的`extensions`配置部分,加上属于这个扩展的配置
26 | ```php
27 |
28 | 'extensions' => [
29 |
30 | 'crontab' => [
31 |
32 | // 如果要关掉这个扩展,设置为false
33 | 'enable' => true,
34 | ]
35 | ]
36 |
37 | ```
38 |
39 | 在服务器中配置crontab
40 |
41 | ```
42 | crontab -e //回车
43 | * * * * * php /your web dir/artisan autotask:run >>/home/crontab.log 2>&1 //>>后面为日志文件保存地址,可加可不加
44 | ```
45 |
46 | ## 访问
47 |
48 | ```
49 | https://your domain/admin/crontabs #定时任务列表
50 | https://your domain/admin/crontabLogs #定时任务日志列表
51 | ```
52 |
53 |
54 | ## License
55 |
56 | Licensed under [The MIT License (MIT)](LICENSE).
57 |
--------------------------------------------------------------------------------
/migrations/2019_03_18_083704_create_crontab_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->string('type',10)->comment('类型');
19 | $table->string('title',150)->comment('标题');
20 | $table->text('contents')->comment('内容');
21 | $table->string('schedule',100)->comment('Cron表达式');
22 | $table->tinyInteger('sleep')->default(0)->comment('延迟秒数执行');
23 | $table->integer('maximums')->default(0)->comment('最大执行次数 0为不限');
24 | $table->integer('executes')->default(0)->nullable()->comment('已经执行的次数');
25 | $table->dateTime('begin_at')->comment('开始时间');
26 | $table->dateTime('end_at')->comment('结束时间');
27 | $table->dateTime('execute_at')->nullable()->comment('最后执行时间');
28 | $table->integer('weigh')->default(0)->comment('权重');
29 | $table->enum('status',['completed','expired','disable','normal'])->default('normal')->comment('状态');
30 | $table->timestamps();
31 | });
32 | Schema::create('crontab_log', function (Blueprint $table) {
33 | $table->increments('id');
34 | $table->string('type',10)->comment('类型');
35 | $table->integer('cid')->comment('任务的ID');
36 | $table->string('title',150)->comment('标题');
37 | $table->mediumText('remark')->comment('备注');
38 | $table->tinyInteger('status')->comment('状态 0:失败 1:成功');
39 | $table->timestamps();
40 | });
41 | }
42 |
43 | /**
44 | * Reverse the migrations.
45 | *
46 | * @return void
47 | */
48 | public function down()
49 | {
50 | Schema::dropIfExists('crontab');
51 | Schema::dropIfExists('crontab_log');
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Http/Controllers/CrontabLogController.php:
--------------------------------------------------------------------------------
1 | breadcrumb(
26 | ['text' => '定时任务日志', 'url' => '/crontabLogs'],
27 | ['text' => '列表']
28 | );
29 | return $content
30 | ->header('列表')
31 | ->description('定时任务日志')
32 | ->body($this->grid());
33 | }
34 |
35 | /**
36 | * Show interface.
37 | *
38 | * @param mixed $id
39 | * @param Content $content
40 | * @return Content
41 | */
42 | public function show($id, Content $content)
43 | {
44 | $content->breadcrumb(
45 | ['text' => '定时任务日志', 'url' => '/crontabLogs'],
46 | ['text' => '详情']
47 | );
48 | return $content
49 | ->header('详情')
50 | ->description('定时任务日志')
51 | ->body($this->detail($id));
52 | }
53 |
54 | /**
55 | * Make a grid builder.
56 | *
57 | * @return Grid
58 | */
59 | protected function grid()
60 | {
61 | $grid = new Grid(new CrontabLog());
62 | $grid->disableCreateButton();
63 | $grid->id('Id')->sortable();
64 | $grid->type('类型')->using(CrontabController::CRONTAB_TYPE)->label('default');
65 | $grid->title('任务标题');
66 | $grid->created_at('执行时间');
67 | $grid->status('状态')->sortable()->using(['0'=>'失败','1'=>'成功'])->display(function ($status) {
68 | if($status == '失败'){
69 | return ''.$status.'';
70 | }else{
71 | return ''.$status.'';
72 | }
73 | });
74 | $grid->actions(function ($actions) {
75 | $actions->disableEdit();
76 | });
77 | $grid->filter(function($filter){
78 | $filter->disableIdFilter();
79 | $filter->like('title', '任务标题');
80 | $filter->equal('type', '类型')->select(CrontabController::CRONTAB_TYPE);
81 |
82 | });
83 | return $grid;
84 | }
85 |
86 |
87 | /**
88 | * Make a show builder.
89 | *
90 | * @param mixed $id
91 | * @return Show
92 | */
93 | protected function detail($id)
94 | {
95 | $show = new Show(CrontabLog::findOrFail($id));
96 |
97 | $show->type('类型')->using(CrontabController::CRONTAB_TYPE)->label();
98 | $show->cid('任务ID');
99 | $show->title('任务标题');
100 | $show->created_at('执行时间');
101 | $show->status('状态')->using([0 => '失败',1 => '成功']);
102 | $show->remark('执行结果');
103 |
104 | $show->panel()->tools(function ($tools) {
105 | $tools->disableEdit();
106 | });
107 |
108 | return $show;
109 | }
110 |
111 | /**
112 | * Make a form builder.
113 | *
114 | * @return Form
115 | */
116 | protected function form()
117 | {
118 | $form = new Form(new CrontabLog);
119 | return $form;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/autoTask.php:
--------------------------------------------------------------------------------
1 | 'normal'])->orderBy('weigh','desc')->orderBy('id','desc')->get();
50 | if (!$crontab_list) {
51 | return null;
52 | }
53 | $time = time();
54 | foreach ($crontab_list as $key => $crontab) {
55 | $value = $crontab->toArray();
56 | $execute = false; // 是否执行
57 |
58 | if ($time < strtotime($value['begin_at'])) { //任务未开始
59 | continue;
60 | }
61 |
62 | if ($value['maximums'] && $value['executes'] > $value['maximums']) { //任务已超过最大执行次数
63 | $crontab->status = 'completed';
64 | } else if (strtotime($value['end_at']) > 0 && $time > strtotime($value['end_at'])) { //任务已过期
65 | $crontab->status = 'expired';
66 | } else {
67 | $cron = CronExpression::factory($value['schedule']);
68 | /*
69 | * 根据当前时间判断是否该应该执行
70 | * 这个判断和秒数无关,其最小单位为分
71 | * 也就是说,如果处于该执行的这个分钟内如果多次调用都会判定为真
72 | * 所以我们在服务器上设置的定时任务最小单位应该是分
73 | */
74 | if ($cron->isDue()) {
75 | // 允许执行
76 | $execute = true;
77 | // 允许执行的时候更新状态
78 | $crontab->execute_at = date('Y-m-d H:i:s');
79 | $crontab->executes = $value['executes'] + 1;
80 | $crontab->status = ($value['maximums'] > 0 && $crontab->executes >= $value['maximums']) ? 'completed' : 'normal';
81 | } else { //如果未到执行时间则跳过本任务去判断下一个任务
82 | continue;
83 | }
84 | }
85 | // 更新状态
86 | $crontab->save();
87 | // 如果不允许执行,只是从当前开始已过期或者已超过最大执行次数的任务,只是更新状态就行了,不执行
88 | if (!$execute) {
89 | continue;
90 | }
91 |
92 | try {
93 | // 分类执行任务
94 | switch ($value['type']) {
95 | case 'url':
96 | try {
97 | $client = new Client();
98 | $response = $client->request('GET', $value['contents']);
99 | $this->saveLog('url', $value['id'], $value['title'], 1, $value['contents'] . ' 请求成功,HTTP状态码: ' . $response->getStatusCode());
100 | } catch (RequestException $e) {
101 | $this->saveLog('url', $value['id'], $value['title'], 0, $value['contents'] . ' 请求成功失败: ' . $e->getMessage());
102 | }
103 | break;
104 | case 'sql':
105 | /* 注释中的方法可以一次执行所有SQL语句
106 | // 执行SQL
107 | $count = DB::select($crontab['contents']);
108 | dump($count );*/
109 |
110 | // 解析成一条条的sql语句
111 | $sqls = str_replace("\r", "\n", $value['contents']);
112 | $sqls = explode(";\n", $sqls);
113 | $remark = '';
114 | $status = 1;
115 | foreach ($sqls as $sql) {
116 | $sql = trim($sql);
117 | if (empty($sql)) continue;
118 | if (substr($sql, 0, 2) == '--') continue; // SQL注释
119 | // 执行SQL并记录执行结果
120 | if (false !== DB::select($sql)) {
121 | $remark .= '执行成功: ' . $sql . "\r\n\r\n";
122 | } else {
123 | $remark .= '执行失败: ' . $sql . "\r\n\r\n";
124 | $status = 0;
125 | }
126 | }
127 | $this->saveLog('sql', $value['id'], $value['title'], $status, $remark);
128 | break;
129 | case 'shell':
130 | $status = 0;
131 | $request = 'fail';
132 |
133 | $process = new Process($value['contents']);
134 | $process->run();
135 | if ($process->isSuccessful()) {
136 | $status = 1;
137 | $request = $process->getOutput();
138 | }
139 | $this->saveLog('shell', $value['id'], $value['title'], $status, $request);
140 | break;
141 | }
142 | }
143 | catch (Exception $e)
144 | {
145 | $this->saveLog($value['type'], $value['id'], $value['title'], 0, "执行的内容发生异常:\r\n" . $e->getMessage());
146 | }
147 |
148 | print_r('执行完毕');
149 | }
150 | }
151 |
152 | // 保存运行日志
153 | private function saveLog($type, $cid, $title, $status, $remark = '')
154 | {
155 | $crontLog = new CrontabLog();
156 | $crontLog->type = $type;
157 | $crontLog->cid = $cid;
158 | $crontLog->title = $title;
159 | $crontLog->status = $status;
160 | $crontLog->remark = $remark;
161 | $crontLog->save();
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/Http/Controllers/CrontabController.php:
--------------------------------------------------------------------------------
1 | '执行sql',
21 | 'shell'=>'执行shell',
22 | 'url'=>'请求url'
23 | ];
24 | const CRONTAB_STATUS = [
25 | 'normal'=>'正常',
26 | 'disable'=>'禁用',
27 | 'completed'=>'完成',
28 | 'expired'=>'过期'
29 | ];
30 |
31 | /**
32 | * Index interface.
33 | *
34 | * @param Content $content
35 | * @return Content
36 | */
37 | public function index(Content $content)
38 | {
39 | $content->breadcrumb(
40 | ['text' => '定时任务', 'url' => '/crontabs'],
41 | ['text' => '列表']
42 | );
43 | return $content
44 | ->header('列表')
45 | ->description('定时任务')
46 | ->body($this->grid());
47 | }
48 |
49 | /**
50 | * Edit interface.
51 | *
52 | * @param mixed $id
53 | * @param Content $content
54 | * @return Content
55 | */
56 | public function edit($id, Content $content)
57 | {
58 | $content->breadcrumb(
59 | ['text' => '定时任务', 'url' => '/crontabs'],
60 | ['text' => '编辑']
61 | );
62 | return $content
63 | ->header('编辑')
64 | ->description('定时任务')
65 | ->body($this->form()->edit($id));
66 | }
67 |
68 | /**
69 | * Create interface.
70 | *
71 | * @param Content $content
72 | * @return Content
73 | */
74 | public function create(Content $content)
75 | {
76 | $content->breadcrumb(
77 | ['text' => '定时任务', 'url' => '/crontabs'],
78 | ['text' => '创建']
79 | );
80 | return $content
81 | ->header('创建')
82 | ->description('定时任务')
83 | ->body($this->form());
84 | }
85 |
86 | /**
87 | * Make a grid builder.
88 | *
89 | * @return Grid
90 | */
91 | protected function grid()
92 | {
93 | $grid = new Grid(new Crontab);
94 | $grid->id('Id')->sortable();
95 | $grid->type('类型')->using(self::CRONTAB_TYPE)->label('default');
96 | $grid->title('任务标题');
97 | $grid->maximums('最大次数');
98 | $grid->executes('已执行次数')->sortable();
99 | $grid->execute_at('下次预计时间');
100 | $grid->end_at('最后执行时间')->sortable();
101 | $grid->status('状态')->sortable()->using(self::CRONTAB_STATUS)->display(function ($status) {
102 | switch ($status){
103 | case '正常':
104 | return ''.$status.'';
105 | break;
106 | case '禁用':
107 | return ''.$status.'';
108 | break;
109 | case '完成':
110 | return ''.$status.'';
111 | break;
112 | default :
113 | return ''.$status.'';
114 | break;
115 | }
116 | });
117 | $grid->created_at('创建时间');
118 | $grid->actions(function ($actions) {
119 | $actions->disableView();
120 | });
121 | $grid->filter(function($filter){
122 | $filter->disableIdFilter();
123 | $filter->like('title', '任务标题');
124 | $filter->equal('type', '类型')->select(self::CRONTAB_TYPE);
125 |
126 | });
127 |
128 | return $grid;
129 | }
130 |
131 |
132 | /**
133 | * Make a form builder.
134 | *
135 | * @return Form
136 | */
137 | protected function form()
138 | {
139 | $form = new Form(new Crontab);
140 |
141 | $form->text('title', '任务标题')->rules('required',['required'=>'任务标题不能为空']);
142 | $form->select('type','任务类型')->options(self::CRONTAB_TYPE)->help("1. URL类型是完整的URL地址,如: http://www.baidu.com/ ;
2. 如果你的服务器 php.ini 未开启 shell_exec() 函数,则不能使用URL类型和Shell类型模式!")->rules('required|in:url,sql,shell',['required'=>'任务类型不能为空','in'=>'参数错误']);
143 | $form->textarea('contents', '内容')->rows(3)->rules('required',['required'=>'内容不能为空']);
144 | $form->text('schedule', '执行周期')->default('* * * * *')->help("请使用Cron表达式")->rules(function ($form) {
145 | $value = $form->model()->schedule;
146 | if (empty($value)){
147 | return 'required';
148 | }
149 | if (!CronExpression::isValidExpression($value)){
150 | // return 'max:0';
151 | }
152 | },['required'=>'执行周期不能为空','max'=>'执行周期 Cron 表达式错误']);
153 |
154 | $form->html("
* * * * *
155 | - - - - -
156 | | | | | +--- day of week (0 - 7) (Sunday=0 or 7)
157 | | | | +-------- month (1 - 12)
158 | | | +------------- day of month (1 - 31)
159 | | +------------------ hour (0 - 23)
160 | +----------------------- min (0 - 59)");
161 | $checkSchedule_url = url('admin/crontabs/checkSchedule');
162 | $js = <<