--- /dev/null
+<?php
+
+namespace app\controllers;
+
+use Yii;
+use yii\base\DynamicModel;
+use yii\data\ActiveDataProvider;
+use yii\web\Controller;
+use yii_app\records\TrackEvent;
+use yii_app\services\TrackEventService;
+
+class TrackEventMonitorController extends Controller
+{
+ public function actionIndex() {
+
+ $model = DynamicModel::validateData([
+ 'date_from' => date('Y-m-d H:i:s', strtotime('-1 month', time())),
+ 'date_to' => date('Y-m-d H:i:s'),
+ 'tags' => null,
+ ], [
+ [['date_from', 'date_to', 'tags'], 'safe'],
+ ]);
+
+ $model->load(Yii::$app->request->get());
+
+ $id = TrackEventService::create('track_event_monitor_index_hit', TrackEvent::STATE_CREATED, null, ['response' => 'ok']);
+
+ $tags = array_unique(TrackEvent::find()->select(['tag'])->column());
+
+ $tagsSelected = array_filter($tags, function($el, $ind) use($model) { return in_array('' . $ind, empty($model->tags) ? [] : $model->tags); }, ARRAY_FILTER_USE_BOTH);
+
+ $dataProvider = new ActiveDataProvider(['query' => TrackEvent::find()->select([
+ 'COUNT(*) as total',
+ 'sum(CASE WHEN state=\''. TrackEvent::STATE_CREATED . '\' THEN 1 ELSE 0 END) as created',
+ 'sum(CASE WHEN state=\''. TrackEvent::STATE_REALISED . '\' THEN 1 ELSE 0 END) as realised',
+ 'sum(CASE WHEN state=\''. TrackEvent::STATE_NOT_REALISED . '\' THEN 1 ELSE 0 END) as not_realised',
+ 'user_id'
+ ])->where(['tag' => $tagsSelected])->andWhere(['BETWEEN', 'created_at', $model->date_from, $model->date_to])
+ ->orderBy(['user_id' => SORT_ASC])->groupBy(['user_id'])]);
+
+ TrackEventService::success($id);
+
+ return $this->render('index', compact('model', 'tags', 'dataProvider'));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m241206_134922_create_table_track_event
+ */
+class m241206_134922_create_table_track_event extends Migration
+{
+ const TABLE_NAME = 'erp24.track_event';
+ /**
+ * {@inheritdoc}
+ */
+ public function safeUp()
+ {
+ $this->createTable(self::TABLE_NAME, [
+ 'id' => $this->primaryKey(),
+ 'tag' => $this->string(1000)->notNull()->comment('для фильтра'),
+ 'user_id' => $this->integer()->null()->comment('пользователь, которому направлено событие'),
+ 'state' => $this->tinyInteger()->notNull()->defaultValue(1)->comment('1 - создан, 2 - реализован, 3 - не реализован'),
+ 'details' => $this->text()->null()->comment('Детали события описанные в формате json'),
+ 'created_at' => $this->dateTime()->notNull()->comment('Время создания события'),
+ 'updated_at' => $this->dateTime()->null()->comment('Время обновления события'),
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeDown()
+ {
+ $this->dropTable(self::TABLE_NAME);
+ }
+}
--- /dev/null
+<?php
+
+namespace yii_app\records;
+
+use Yii;
+
+/**
+ * This is the model class for table "track_event".
+ *
+ * @property int $id
+ * @property string $tag для фильтра
+ * @property int|null $user_id пользователь, которому направлено событие
+ * @property int $state 1 - создан, 2 - реализован, 3 - не реализован
+ * @property string|null $details Детали события описанные в формате json
+ * @property string $created_at Время создания события
+ * @property string|null $updated_at Время обновления события
+ */
+class TrackEvent extends \yii\db\ActiveRecord
+{
+ public $total;
+ public $created;
+ public $realised;
+ public $not_realised;
+
+ const STATE_CREATED = 1;
+ const STATE_REALISED = 2;
+ const STATE_NOT_REALISED = 3;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function tableName()
+ {
+ return 'track_event';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rules()
+ {
+ return [
+ [['tag', 'created_at'], 'required'],
+ [['user_id', 'state'], 'default', 'value' => null],
+ [['user_id', 'state'], 'integer'],
+ [['details'], 'string'],
+ [['created_at', 'updated_at'], 'safe'],
+ [['tag'], 'string', 'max' => 1000],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => 'ID',
+ 'tag' => 'Tag',
+ 'user_id' => 'пользователь',
+ 'state' => 'State',
+ 'details' => 'Details',
+ 'created_at' => 'Created At',
+ ];
+ }
+}
--- /dev/null
+<?php
+
+namespace yii_app\services;
+
+use yii\helpers\Json;
+use yii_app\records\TrackEvent;
+
+class TrackEventService
+{
+ public static function create($tag, $state, $userId = null, $details = null) {
+ $event = new TrackEvent;
+ $event->tag = $tag;
+ $event->created_at = date('Y-m-d H:i:s');
+ $event->state = $state;
+ if ($details) {
+ $event->details = Json::encode($details);
+ }
+ if ($userId) {
+ $event->user_id = $userId;
+ }
+ $event->save();
+
+ return $event->id;
+ }
+
+ public static function success($id, $details = null) {
+ $te = TrackEvent::findOne($id);
+ if ($te) {
+ $te->state = TrackEvent::STATE_REALISED;
+ if ($details) {
+ $te->details = Json::encode($details);
+ }
+ $te->updated_at = date('Y-m-d H:i:s');
+ $te->save();
+ }
+ }
+
+ public static function fail($id, $details = null) {
+ $te = TrackEvent::findOne($id);
+ if ($te) {
+ $te->state = TrackEvent::STATE_NOT_REALISED;
+ if ($details) {
+ $te->details = Json::encode($details);
+ }
+ $te->updated_at = date('Y-m-d H:i:s');
+ $te->save();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+use yii\helpers\Html;
+use yii\widgets\ActiveForm;
+use yii\base\DynamicModel;
+use yii\data\ActiveDataProvider;
+
+use kartik\grid\GridView;
+use kartik\select2\Select2;
+use dosamigos\datetimepicker\DateTimePicker;
+
+/* @var $model DynamicModel */
+/* @var $tags array */
+/* @var $dataProvider ActiveDataProvider */
+
+?>
+
+<div class="trackEventMonitorIndex m-5">
+
+ <?= Html::a('Сбросить фильтры', '/track-event-monitor/index', ['class' => 'btn btn-link']) ?>
+
+ <?php $form = ActiveForm::begin([
+ 'action' => '/track-event-monitor/index',
+ 'method' => 'GET'
+ ]) ?>
+
+ <div class="row">
+ <div class="col-6">
+ <?= $form->field($model, 'date_from')->widget(DateTimePicker::class, [
+ 'language' => 'ru',
+ 'template' => '{input}',
+ 'clientOptions' => [
+ 'autoclose' => true,
+ 'format' => 'Y-m-d H:i:s',
+ 'todayBtn' => true
+ ],
+ ])->label(false) ?>
+ </div>
+ <div class="col-6">
+ <?= $form->field($model, 'date_to')->widget(DateTimePicker::class, [
+ 'language' => 'ru',
+ 'template' => '{input}',
+ 'clientOptions' => [
+ 'autoclose' => true,
+ 'format' => 'Y-m-d H:i:s',
+ 'todayBtn' => true
+ ],
+ ])->label(false) ?>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-12">
+ <?= $form->field($model, 'tags')->widget(Select2::class, [
+ 'data' => $tags,
+ 'language' => 'ru',
+ 'options' => ['placeholder' => 'Tags...'],
+ 'pluginOptions' => [
+ 'allowClear' => true,
+ 'multiple' => true,
+ ]
+ ])->label(false) ?>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <?= Html::submitButton('Применить', ['class' => 'btn btn-success']) ?>
+ </div>
+
+ <?php ActiveForm::end() ?>
+
+ <div class="row">
+ <div class="col-12">
+ <?= GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'user_id',
+ [
+ 'attribute' => 'created',
+ 'label' => 'Созданных',
+ 'pageSummary' => true,
+ ],
+ [
+ 'attribute' => 'realised',
+ 'label' => 'Реализованных',
+ 'pageSummary' => true,
+ ],
+ [
+ 'attribute' => 'not_realised',
+ 'label' => 'Не реализованных',
+ 'pageSummary' => true,
+ ],
+ [
+ 'attribute' => 'total',
+ 'label' => 'Всего',
+ 'pageSummary' => true,
+ ],
+ ],
+ 'showPageSummary' => true,
+ 'layout' => '{items}',
+ ]) ?>
+ </div>
+ </div>
+
+</div>