This commit is contained in:
Александр Рыбкин 2024-11-20 16:40:08 +03:00
commit c24d319159
13 changed files with 918 additions and 0 deletions

75
Module.php Normal file
View File

@ -0,0 +1,75 @@
<?php
namespace dominion\cron;
use yii\base\BootstrapInterface;
use yii\i18n\PhpMessageSource;
use dominion\cron\models\MoleTask;
/**
* cron module definition class
*/
class Module extends \yii\base\Module implements BootstrapInterface
{
public $admins = [];
public $project = 'yii';
/**
* {@inheritdoc}
*/
public $controllerNamespace = 'dominion\cron\controllers';
public function bootstrap($app)
{
if ($app instanceof \yii\web\Application)
{
$app->getUrlManager()->addRules([
['class' => 'yii\web\UrlRule', 'pattern' => $this->id, 'route' => $this->id . '/default/index'],
['class' => 'yii\web\UrlRule', 'pattern' => $this->id . '/<id:\w+>', 'route' => $this->id . '/default/view'],
['class' => 'yii\web\UrlRule', 'pattern' => $this->id . '/<controller:[\w\-]+>/<action:[\w\-]+>', 'route' => $this->id . '/<controller>/<action>'],
], false);
}
elseif ($app instanceof \yii\console\Application)
{
$app->controllerMap[$this->id] = [
'class' => 'dominion\cron\console\MoleController',
// 'module' => $this,
];
}
if (!isset($app->get('i18n')->translations['mole*']))
{
$app->get('i18n')->translations['mole*'] = [
'class' => PhpMessageSource::className(),
'basePath' => __DIR__ . '/messages',
'sourceLanguage' => 'en-US'
];
}
}
/**
* Добавление агента (обертка)
* @param string $controller
* @param string $name
* @param array $params
* @param int $priority
* @param int $period
* @param date $dateAdd
* @param string $project
* @return boolean
*/
public function add($controller, $name, $params = array(), $priority = 0, $period = 0, $dateAdd = false, $project = false)
{
return MoleTask::add($controller, $name, $params, $priority, $period, $dateAdd, $project);
}
/**
* Выбираем все строки с project
*/
public function getAllTask($project = false)
{
$model = new MoleTask();
return $model->getAllTask($project);
}
}

31
README.md Normal file
View File

@ -0,0 +1,31 @@
Агент для работы с кроном
=========================
Функционал для работы с кроном
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require --prefer-dist dominion/yii2-cron "*"
```
or add
```
"dominion/yii2-cron": "*"
```
to the require section of your `composer.json` file.
Usage
-----
Once the extension is installed, simply use it in your code by :
```php
<?= \dominion\cron\AutoloadExample::widget(); ?>```

21
composer.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "dominion/yii2-cron",
"description": "Функционал для работы с кроном",
"type": "yii2-extension",
"keywords": ["yii2","extension"],
"license": "MIT",
"authors": [
{
"name": "Rybkin Sasha",
"email": "ribkin@dominion.ru"
}
],
"require": {
"yiisoft/yii2": "~2.0.0"
},
"autoload": {
"psr-4": {
"dominion\\cron\\": ""
}
}
}

View File

@ -0,0 +1,99 @@
<?php
/**
* @link https://www.kuvalda.ru/
* @copyright
* @license
*/
namespace dominion\cron\console;
use Yii;
use yii\console\Controller;
use yii\console\ExitCode;
use dominion\cron\models\MoleTask;
use yii\base\InlineAction;
/**
* Запуск агента по расписанию
*
* @author Rybkin Sasha <ribkin@dominion.ru>
* @since 0.1
*/
class MoleController extends Controller
{
/**
* Запуск всех агентов для проекта yii из mole_task
* @return int Exit code
*/
public function actionIndex()
{
$filePatch = Yii::getAlias('@app/runtime/lock.lock');
if (!file_exists($filePatch))
{
$fp = fopen($filePatch, "w");
fwrite($fp, "");
fclose($fp);
}
$file = fopen($filePatch, 'r+');
if (flock($file, LOCK_EX | LOCK_NB))
{
$model = new MoleTask;
$tasks = $model->getAllTask();
foreach ($tasks as $task)
{
try
{
$task->dateStart = date('Y-m-d H:i:s');
$task->save();
$params = explode('/', $task->controller);
$controller = $this->format($params[0]);
$action = 'actionIndex';
if (isset($params[1]))
{
$action = 'action' . $this->format($params[1]);
}
if(stripos($controller, 'Controller') === false)
{
$controller .= 'Controller';
}
$class = 'app\\commands\\' . $controller;
if (class_exists($class))
{
$control = new $class($task->controller, 'product');
if (method_exists($control, $action))
{
$params = $control->{$action}(unserialize($task->params));
if(!empty($params))
{
$task->params = serialize($params);
}
$task->isReady = 1;
}
}
$task->setCompleted();
} catch (\Exception $ex)
{
$task->dateStart = null;
$task->save();
print_r($ex->getMessage());
}
}
}
return ExitCode::OK;
}
protected function format($controller)
{
$params = explode('-', $controller);
foreach ($params as $key => $value)
{
$params[$key] = ucfirst($value);
}
return implode('', $params);
}
}

View File

@ -0,0 +1,135 @@
<?php
namespace dominion\cron\controllers;
use Yii;
use yii\web\Controller;
use dominion\cron\models\search\MoleTaskSearch;
use dominion\cron\models\MoleTask;
use yii\filters\VerbFilter;
use yii\web\NotFoundHttpException;
/**
* Default controller for the `cron` module
*/
class DefaultController extends Controller
{
public function behaviors()
{
if (!Yii::$app->user->getIsGuest() && in_array(Yii::$app->user->id, $this->module->admins))
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
else
{
throw new NotFoundHttpException(Yii::t('mole', 'access denied'));
}
}
/**
* Renders the index view for the module
* @return string
*/
public function actionIndex()
{
$searchModel = new MoleTaskSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single MoleTask model.
* @param integer $id
* @return mixed
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
$model = $this->findModel($id);
return $this->render('view', [
'model' => $model,
]);
}
/**
* Creates a new MoleTask model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate()
{
$model = new MoleTask();
if ($model->load(Yii::$app->request->post()) && $model->save())
{
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing MoleTask model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id
* @return mixed
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save())
{
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('update', [
'model' => $model,
]);
}
/**
* Deletes an existing MoleTask model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param integer $id
* @return mixed
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the MoleTask model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param integer $id
* @return MoleTask the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = MoleTask::findOne($id)) !== null)
{
return $model;
}
throw new NotFoundHttpException(Yii::t('mole', 'The requested page does not exist.'));
}
}

29
messages/ru-RU/mole.php Normal file
View File

@ -0,0 +1,29 @@
<?php
return [
'Task List' => 'Список задач',
'Project' => 'Проект',
'Parent ID' => 'Родительский процесс',
'Date Add' => 'Дата создания',
'Date Start' => 'Дата запуска',
'Date End' => 'Дата завершения',
'Module' => 'Модуль',
'Controller' => 'Контроллер',
'Type' => 'Тип',
'Name' => 'Название',
'Params' => 'Параметры',
'Is Ready' => 'Готов',
'Completed' => 'Завершен',
'Priority' => 'Приоритет',
'Period' => 'Периодичность',
// 'Childs Total Count' => '',
// 'Childs Completed' => '',
'Status' => 'Статус',
'Yes' => 'Да',
'No' => 'Нет',
'Create Task' => 'Создать задачу',
'Save' => 'Сохранить',
'Update'=> 'Изменить',
'Update Task: {nameAttribute}' => 'Изменить задачу: {nameAttribute}',
'Delete' => 'Удалить',
];

151
models/MoleTask.php Normal file
View File

@ -0,0 +1,151 @@
<?php
namespace dominion\cron\models;
use Yii;
/**
* This is the model class for table "mole_task".
*
* @property int $id
* @property int $parentId
* @property string $dateAdd
* @property string $dateStart
* @property string $dateEnd
* @property string $module
* @property string $controller
* @property string $type
* @property string $name
* @property string $params
* @property int $isReady
* @property int $completed
* @property int $priority
* @property int $childsTotalCount
* @property int $childsCompleted
* @property string $status
* @property string $project
* @property int $period
*/
class MoleTask extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'mole_task';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
//[['parentId', 'isReady', 'childsTotalCount', 'childsCompleted', 'status'], 'required'],
[['parentId', 'isReady', 'completed', 'priority', 'childsTotalCount', 'childsCompleted', 'period'], 'integer'],
[['dateAdd', 'dateStart', 'dateEnd'], 'safe'],
[['params'], 'string'],
[['module', 'controller', 'type', 'name', 'status'], 'string', 'max' => 255],
[['project'], 'string', 'max' => 50],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => Yii::t('mole', 'ID'),
'parentId' => Yii::t('mole', 'Parent ID'),
'dateAdd' => Yii::t('mole', 'Date Add'),
'dateStart' => Yii::t('mole', 'Date Start'),
'dateEnd' => Yii::t('mole', 'Date End'),
'module' => Yii::t('mole', 'Module'),
'controller' => Yii::t('mole', 'Controller'),
'type' => Yii::t('mole', 'Type'),
'name' => Yii::t('mole', 'Name'),
'params' => Yii::t('mole', 'Params'),
'isReady' => Yii::t('mole', 'Is Ready'),
'completed' => Yii::t('mole', 'Completed'),
'priority' => Yii::t('mole', 'Priority'),
'childsTotalCount' => Yii::t('mole', 'Childs Total Count'),
'childsCompleted' => Yii::t('mole', 'Childs Completed'),
'status' => Yii::t('mole', 'Status'),
'project' => Yii::t('mole', 'Project'),
'period' => Yii::t('mole', 'Period'),
];
}
/**
* Выбираем все строки с project
*/
public function getAllTask($project = false)
{
return self::find()
->andWhere([
'project' => $project ?: Yii::$app->getModule('cron')->project,
'completed' => 0,
])
->andWhere(['IS', 'dateStart', NULL])
->andWhere(['IS', 'dateEnd', NULL])
->andWhere(['<=', 'dateAdd', date('Y-m-d H:i:s')])
->orderBy('priority DESC')
->all();
}
public function setCompleted()
{
$this->dateEnd = date('Y-m-d H:i:s');
$this->completed = 1;
if ($this->save() && $this->period > 0)
{
$model = new MoleTask;
$model->attributes = $this->attributes;
$model->isReady = 0;
$model->completed = 0;
$model->dateStart = null;
$model->dateEnd = null;
$model->dateAdd = date('Y-m-d H:i:s', (strtotime($this->dateAdd . " +$this->period seconds")));
$model->save();
}
}
/**
* Добавление агента (обертка)
* @param string $controller
* @param string $name
* @param array $params
* @param int $priority
* @param int $period
* @param date $dateAdd
* @param string $project
* @return boolean
*/
public static function add($controller, $name, $params = array(), $priority = 0, $period = 0, $dateAdd = false, $project = false)
{
$module = '';
if(stripos($controller, '.') !== false)
{
$arController = explode('.', $controller);
$module = $arController[0];
$controller = $arController[1];
}
$model = new MoleTask();
$model->controller = $controller;
$model->name = $name;
$model->params = serialize($params);
$model->priority = $priority;
$model->period = $period;
$model->dateAdd = $dateAdd ?: date('Y-m-d H:i:s');
$model->project = $project ?: Yii::$app->getModule('cron')->project;
$model->module = $module;
$model->type = ((empty($model->module) ? $model->project : $model->module) . '.' . $model->controller); // для совместимости
$model->isReady = 1;
return $model->save();
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace dominion\cron\models\search;
use \Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use dominion\cron\models\MoleTask;
/**
* MoleTaskSearch represents the model behind the search form of `app\models\Articles`.
*/
class MoleTaskSearch extends MoleTask
{
/**
* @inheritdoc
*/
public function rules()
{
return [
[['id', 'parentId', 'isReady', 'completed', 'priority', 'childsTotalCount', 'childsCompleted', 'period'], 'integer'],
[['dateAdd', 'dateStart', 'dateEnd'], 'safe'],
[['params'], 'string'],
[['module', 'controller', 'type', 'name', 'status'], 'string', 'max' => 255],
[['project'], 'string', 'max' => 50],
];
}
/**
* @inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = MoleTask::find();
// add conditions that should always apply here
$this->load($params);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => [
'id' => SORT_DESC
]
]
]);
if (!$this->validate())
{
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
'parentId' => $this->parentId,
'isReady' => $this->isReady,
'completed' => $this->completed,
'priority' => $this->priority,
'childsTotalCount' => $this->childsTotalCount,
'childsCompleted' => $this->childsCompleted,
]);
$query->andFilterWhere(['like', 'params', $this->params])
->andFilterWhere(['like', 'controller', $this->controller])
->andFilterWhere(['like', 'module', $this->module])
->andFilterWhere(['like', 'type', $this->type])
->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'status', $this->status])
->andFilterWhere(['like', 'project', $this->project]);
return $dataProvider;
}
}

36
views/default/_form.php Normal file
View File

@ -0,0 +1,36 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model app\models\MailType */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="mole-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'project'); ?>
<?= $form->field($model, 'name'); ?>
<?= $form->field($model, 'dateAdd'); ?>
<?= $form->field($model, 'dateStart'); ?>
<?= $form->field($model, 'dateEnd'); ?>
<?= $form->field($model, 'module'); ?>
<?= $form->field($model, 'controller'); ?>
<?= $form->field($model, 'params'); ?>
<?= $form->field($model, 'period'); ?>
<div class="form-group">
<?= Html::submitButton(Yii::t('mole', 'Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

30
views/default/create.php Normal file
View File

@ -0,0 +1,30 @@
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model app\models\MailType */
$this->title = Yii::t('mole', 'Create Task');
$this->params['breadcrumbs'][] = ['label' => Yii::t('mole', 'Task List'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-12">
<div class="tabs-container">
<div class="tab-content">
<div class="tab-pane active">
<div class="panel-body">
<?=
$this->render('_form', [
'model' => $model,
])
?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

93
views/default/index.php Normal file
View File

@ -0,0 +1,93 @@
<?php
/*
* This file is part of the Dektrium project.
*
* (c) Dektrium project <http://github.com/dektrium>
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/
use yii\grid\GridView;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\web\View;
use yii\widgets\Pjax;
/**
* @var \yii\web\View $this
* @var \yii\data\ActiveDataProvider $dataProvider
* @var \dektrium\user\models\UserSearch $searchModel
*/
$this->title = Yii::t('mole', 'Task List');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-12">
<div class="tabs-container">
<div class="tab-content">
<div class="tab-pane active">
<div class="panel-body">
<p>
<?= Html::a(Yii::t('mole', 'Create Task'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?=
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'layout' => "{items}\n{pager}",
'columns' => [
'id',
'project',
'name',
// 'parentId',
'dateAdd:datetime',
'dateStart:datetime',
'dateEnd:datetime',
//'module',
'controller',
// 'type',
//'params',
[
'attribute' => 'isReady',
'value' => function($model)
{
return $model->isReady ? Yii::t('mole', 'Yes') : Yii::t('mole', 'No');
},
// 'filter' => [0 => Yii::t('mole', 'No'), 1 => Yii::t('mole', 'Yes')]
],
[
'attribute' => 'completed',
'value' => function($model)
{
return $model->completed ? Yii::t('mole', 'Yes') : Yii::t('mole', 'No');
},
// 'filter' => [0 => Yii::t('mole', 'No'), 1 => Yii::t('mole', 'Yes')]
],
'priority',
[
'attribute' => 'period',
'value' => function($model)
{
return $model->period > 0 ? $model->period : Yii::t('mole', 'No');
},
],
//'childsTotalCount',
//'childsCompleted',
// 'status',
[
'class' => 'yii\grid\ActionColumn',
],
],
]);
?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

35
views/default/update.php Normal file
View File

@ -0,0 +1,35 @@
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model app\models\MailType */
$this->title = Yii::t('mole', 'Update Task: {nameAttribute}', [
'nameAttribute' => $model->name,
]);
$this->params['breadcrumbs'][] = ['label' => Yii::t('mole', 'Task List'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = Yii::t('mole', 'Update');
?>
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-12">
<div class="tabs-container">
<div class="tab-content">
<div class="tab-pane active">
<div class="panel-body">
<?=
$this->render('_form', [
'model' => $model,
])
?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

92
views/default/view.php Normal file
View File

@ -0,0 +1,92 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
use yii\grid\GridView;
use app\components\Helper;
/* @var $this yii\web\View */
/* @var $model app\models\MailType */
$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => Yii::t('mole', 'Task List'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-12">
<div class="tabs-container">
<div class="tab-content">
<div class="tab-pane active">
<div class="panel-body">
<p>
<?= Html::a(Yii::t('mole', 'Update'), ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
<?=
Html::a(Yii::t('mole', 'Delete'), ['delete', 'id' => $model->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => Yii::t('mole', 'Are you sure you want to delete this item?'),
'method' => 'post',
],
])
?>
</p>
<?=
DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'project',
'name',
'parentId',
'dateAdd:datetime',
'dateStart:datetime',
'dateEnd:datetime',
'module',
'controller',
'type',
[
'attribute' => 'params',
'value' => function($model)
{
return print_r(json_decode($model->params, true), true);
},
],
[
'attribute' => 'isReady',
'value' => function($model)
{
return $model->isReady ? Yii::t('mole', 'Yes') : Yii::t('mole', 'No');
},
],
[
'attribute' => 'completed',
'value' => function($model)
{
return $model->completed ? Yii::t('mole', 'Yes') : Yii::t('mole', 'No');
},
],
'priority',
'status',
[
'attribute' => 'period',
'value' => function($model)
{
return $model->period > 0 ? $model->period : Yii::t('mole', 'No');
},
],
],
])
?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>