mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-07-25 01:11:44 +08:00
更新索引操作改用计划任务批量完成
This commit is contained in:
parent
971cac05d7
commit
8395074dde
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
/vendor
|
||||
/config/config.php
|
||||
/config/xs.course.ini
|
||||
*KgTest*
|
||||
|
@ -5,7 +5,7 @@ namespace App\Console\Tasks;
|
||||
use App\Repos\Category as CategoryRepo;
|
||||
use Phalcon\Cli\Task;
|
||||
|
||||
class CourseCountTask extends Task
|
||||
class CountCourseTask extends Task
|
||||
{
|
||||
|
||||
public function mainAction()
|
@ -24,20 +24,15 @@ class LearningTask extends Task
|
||||
|
||||
$keys = $this->cache->queryKeys('learning:');
|
||||
|
||||
if (empty($keys)) return;
|
||||
if (empty($keys)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$keys = array_slice($keys, 0, 500);
|
||||
|
||||
$prefix = $this->cache->getPrefix();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
/**
|
||||
* 去掉前缀,避免重复加前缀导致找不到缓存
|
||||
*/
|
||||
if ($prefix) {
|
||||
$key = str_replace($prefix, '', $key);
|
||||
}
|
||||
$this->handleLearning($key);
|
||||
$lastKey = $this->cache->getRawKeyName($key);
|
||||
$this->handleLearning($lastKey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +41,7 @@ class LearningTask extends Task
|
||||
$content = $this->cache->get($key);
|
||||
|
||||
if (empty($content->user_id)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($content->client_ip)) {
|
||||
@ -81,13 +76,17 @@ class LearningTask extends Task
|
||||
|
||||
$chapterUser = $chapterUserRepo->findChapterUser($chapterId, $userId);
|
||||
|
||||
if (!$chapterUser) return false;
|
||||
if (!$chapterUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
$chapterRepo = new ChapterRepo();
|
||||
|
||||
$chapter = $chapterRepo->findById($chapterId);
|
||||
|
||||
if (!$chapter) return false;
|
||||
if (!$chapter) {
|
||||
return;
|
||||
}
|
||||
|
||||
$chapter->duration = $chapter->attrs['duration'] ?: 0;
|
||||
|
||||
|
166
app/Console/Tasks/ManageIndexTask.php
Normal file
166
app/Console/Tasks/ManageIndexTask.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Tasks;
|
||||
|
||||
use App\Models\Course as CourseModel;
|
||||
use App\Searchers\Course as CourseSearcher;
|
||||
use Phalcon\Cli\Task;
|
||||
|
||||
class ManageIndexTask extends Task
|
||||
{
|
||||
|
||||
/**
|
||||
* 搜索测试
|
||||
*
|
||||
* @command: php console.php index search {type} {query}
|
||||
* @param array $params
|
||||
* @throws \XSException
|
||||
*/
|
||||
public function searchAction($params)
|
||||
{
|
||||
$type = $params[0] ?? null;
|
||||
$query = $params[1] ?? null;
|
||||
|
||||
if (!in_array($type, $this->getItemTypes())) {
|
||||
exit("Invalid item type" . PHP_EOL);
|
||||
}
|
||||
|
||||
if (!$query) {
|
||||
exit("Please special a query word" . PHP_EOL);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
if ($type == 'course') {
|
||||
$result = $this->searchCourses($query);
|
||||
}
|
||||
|
||||
var_export($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空索引
|
||||
*
|
||||
* @command: php console.php index clean {type}
|
||||
* @param array $params
|
||||
*/
|
||||
public function cleanAction($params)
|
||||
{
|
||||
$type = $params[0] ?? null;
|
||||
|
||||
if (in_array($type, $this->getItemTypes())) {
|
||||
exit("Invalid item type" . PHP_EOL);
|
||||
}
|
||||
|
||||
if ($type == 'course') {
|
||||
$this->cleanCourseIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建索引
|
||||
*
|
||||
* @command: php console.php index rebuild {type}
|
||||
* @param array $params
|
||||
*/
|
||||
public function rebuildAction($params)
|
||||
{
|
||||
$type = $params[0] ?? null;
|
||||
|
||||
if (in_array($type, $this->getItemTypes())) {
|
||||
exit("Invalid item type" . PHP_EOL);
|
||||
}
|
||||
|
||||
if ($type == 'course') {
|
||||
$this->rebuildCourseIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空课程索引
|
||||
*/
|
||||
protected function cleanCourseIndex()
|
||||
{
|
||||
$searcher = new CourseSearcher();
|
||||
|
||||
$index = $searcher->getXS()->getIndex();
|
||||
|
||||
echo "Start clean index" . PHP_EOL;
|
||||
|
||||
$index->clean();
|
||||
|
||||
echo "End clean index" . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建课程索引
|
||||
*/
|
||||
protected function rebuildCourseIndex()
|
||||
{
|
||||
$courses = $this->findCourses();
|
||||
|
||||
if ($courses->count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$searcher = new CourseSearcher();
|
||||
|
||||
$index = $searcher->getXS()->getIndex();
|
||||
|
||||
echo "Start rebuild index" . PHP_EOL;
|
||||
|
||||
$index->beginRebuild();
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$document = $searcher->setDocument($course);
|
||||
$index->add($document);
|
||||
}
|
||||
|
||||
$index->endRebuild();
|
||||
|
||||
echo "End rebuild index" . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索课程
|
||||
*
|
||||
* @param string $query
|
||||
* @return \stdClass $result
|
||||
* @throws \XSException
|
||||
*/
|
||||
protected function searchCourses($query)
|
||||
{
|
||||
$searcher = new CourseSearcher();
|
||||
|
||||
$result = $searcher->search($query);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找课程
|
||||
*
|
||||
* @return \Phalcon\Mvc\Model\ResultsetInterface
|
||||
*/
|
||||
protected function findCourses()
|
||||
{
|
||||
$courses = CourseModel::query()
|
||||
->where('published = 1')
|
||||
->execute();
|
||||
|
||||
return $courses;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取条目类型
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getItemTypes()
|
||||
{
|
||||
$types = ['course'];
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
}
|
66
app/Console/Tasks/SyncIndexTask.php
Normal file
66
app/Console/Tasks/SyncIndexTask.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Tasks;
|
||||
|
||||
use App\Models\Course as CourseModel;
|
||||
use App\Searchers\Course as CourseSearcher;
|
||||
|
||||
class SyncIndexTask extends Task
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \App\Library\Cache\Backend\Redis
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
public function mainAction()
|
||||
{
|
||||
$this->cache = $this->getDI()->get('cache');
|
||||
|
||||
$this->handleCourseIndex();
|
||||
}
|
||||
|
||||
protected function handleCourseIndex()
|
||||
{
|
||||
$keys = $this->cache->queryKeys('sync:index:course:');
|
||||
|
||||
if (empty($keys)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$keys = array_slice($keys, 0, 100);
|
||||
|
||||
$searcher = new CourseSearcher();
|
||||
|
||||
$index = $searcher->getXS()->getIndex();
|
||||
|
||||
$index->openBuffer();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
|
||||
$lastKey = $this->cache->getRawKeyName($key);
|
||||
$content = $this->cache->get($lastKey);
|
||||
|
||||
if (empty($content->id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$course = CourseModel::findFirstById($content->id);
|
||||
|
||||
$document = $searcher->setDocument($course);
|
||||
|
||||
if ($content->type == 'update') {
|
||||
$index->update($document);
|
||||
} elseif ($content->type == 'delete') {
|
||||
$index->del($course->id);
|
||||
} elseif ($content->type == 'restore') {
|
||||
$index->update($document);
|
||||
}
|
||||
|
||||
$this->cache->delete($lastKey);
|
||||
}
|
||||
|
||||
$index->closeBuffer();
|
||||
}
|
||||
|
||||
}
|
@ -66,6 +66,8 @@ class Course extends Service
|
||||
|
||||
$course->create($data);
|
||||
|
||||
$this->eventsManager->fire('course:afterCreate', $this, $course);
|
||||
|
||||
return $course;
|
||||
}
|
||||
|
||||
@ -135,6 +137,8 @@ class Course extends Service
|
||||
|
||||
$course->update($data);
|
||||
|
||||
$this->eventsManager->fire('course:afterUpdate', $this, $course);
|
||||
|
||||
return $course;
|
||||
}
|
||||
|
||||
@ -150,6 +154,8 @@ class Course extends Service
|
||||
|
||||
$course->update();
|
||||
|
||||
$this->eventsManager->fire('course:afterDelete', $this, $course);
|
||||
|
||||
return $course;
|
||||
}
|
||||
|
||||
@ -165,6 +171,8 @@ class Course extends Service
|
||||
|
||||
$course->update();
|
||||
|
||||
$this->eventsManager->fire('course:afterRestore', $this, $course);
|
||||
|
||||
return $course;
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Prefix
|
||||
* Get prefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -234,7 +234,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Redis Connection
|
||||
* Get redis connection
|
||||
*
|
||||
* @return \Redis
|
||||
*/
|
||||
@ -251,14 +251,29 @@ class Redis extends \Phalcon\Cache\Backend\Redis
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Key Name
|
||||
* Get key name
|
||||
*
|
||||
* @param $keyName
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyName($keyName)
|
||||
public function getKeyName($keyName)
|
||||
{
|
||||
return $this->_prefix . $keyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw key name
|
||||
*
|
||||
* @param $keyName
|
||||
* @return string
|
||||
*/
|
||||
public function getRawKeyName($keyName)
|
||||
{
|
||||
if ($this->_prefix) {
|
||||
$keyName = str_replace($this->_prefix, '', $keyName);
|
||||
}
|
||||
|
||||
return $keyName;
|
||||
}
|
||||
|
||||
}
|
||||
|
124
app/Listeners/Course.php
Normal file
124
app/Listeners/Course.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Models\Course as CourseModel;
|
||||
use Phalcon\Events\Event;
|
||||
|
||||
class Course extends Listener
|
||||
{
|
||||
|
||||
protected $logger;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->logger = $this->getLogger();
|
||||
}
|
||||
|
||||
public function afterCreate(Event $event, $source, CourseModel $course)
|
||||
{
|
||||
$this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [
|
||||
'event' => $event->getType(),
|
||||
'source' => get_class($source),
|
||||
'data' => kg_json_encode($course),
|
||||
]);
|
||||
}
|
||||
|
||||
public function afterUpdate(Event $event, $source, CourseModel $course)
|
||||
{
|
||||
if ($course->published == 1) {
|
||||
$this->syncIndexAfterUpdate($course);
|
||||
}
|
||||
|
||||
$this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [
|
||||
'event' => $event->getType(),
|
||||
'source' => get_class($source),
|
||||
'data' => kg_json_encode($course),
|
||||
]);
|
||||
}
|
||||
|
||||
public function afterDelete(Event $event, $source, CourseModel $course)
|
||||
{
|
||||
if ($course->published == 1) {
|
||||
$this->syncIndexAfterDelete($course);
|
||||
}
|
||||
|
||||
$this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [
|
||||
'event' => $event->getType(),
|
||||
'source' => get_class($source),
|
||||
'data' => kg_json_encode($course),
|
||||
]);
|
||||
}
|
||||
|
||||
public function afterRestore(Event $event, $source, CourseModel $course)
|
||||
{
|
||||
if ($course->published == 1) {
|
||||
$this->syncIndexAfterRestore($course);
|
||||
}
|
||||
|
||||
$this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [
|
||||
'event' => $event->getType(),
|
||||
'source' => get_class($source),
|
||||
'data' => kg_json_encode($course),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function syncIndexAfterUpdate(CourseModel $course)
|
||||
{
|
||||
/**
|
||||
* @var \Phalcon\Cache\Backend $cache
|
||||
*/
|
||||
$cache = $this->getDI()->get('cache');
|
||||
|
||||
$key = $this->getSyncIndexKey($course->id);
|
||||
|
||||
$content = [
|
||||
'id' => $course->id,
|
||||
'type' => 'update',
|
||||
];
|
||||
|
||||
$cache->save($key, $content, 86400);
|
||||
}
|
||||
|
||||
protected function syncIndexAfterDelete($course)
|
||||
{
|
||||
/**
|
||||
* @var \Phalcon\Cache\Backend $cache
|
||||
*/
|
||||
$cache = $this->getDI()->get('cache');
|
||||
|
||||
$key = $this->getSyncIndexKey($course->id);
|
||||
|
||||
$content = [
|
||||
'id' => $course->id,
|
||||
'type' => 'delete',
|
||||
];
|
||||
|
||||
$cache->save($key, $content, 86400);
|
||||
}
|
||||
|
||||
protected function syncIndexAfterRestore($course)
|
||||
{
|
||||
/**
|
||||
* @var \Phalcon\Cache\Backend $cache
|
||||
*/
|
||||
$cache = $this->getDI()->get('cache');
|
||||
|
||||
$key = $this->getSyncIndexKey($course->id);
|
||||
|
||||
$content = [
|
||||
'id' => $course->id,
|
||||
'type' => 'restore',
|
||||
];
|
||||
|
||||
$cache->save($key, $content, 86400);
|
||||
}
|
||||
|
||||
protected function getSyncIndexKey($courseId)
|
||||
{
|
||||
$key = "sync:index:course:{$courseId}";
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
}
|
@ -25,6 +25,7 @@ class Cache extends AbstractProvider
|
||||
'port' => $config->redis->port,
|
||||
'auth' => $config->redis->auth,
|
||||
'index' => $config->redis->index,
|
||||
'prefix' => $config->redis->prefix,
|
||||
'persistent' => $config->redis->persistent,
|
||||
]);
|
||||
|
||||
|
@ -20,6 +20,7 @@ class Session extends AbstractProvider
|
||||
'port' => $config->redis->port,
|
||||
'auth' => $config->redis->auth,
|
||||
'index' => $config->session->index,
|
||||
'prefix' => $config->session->prefix,
|
||||
'lifetime' => $config->session->lifetime,
|
||||
'persistent' => $config->redis->persistent,
|
||||
]);
|
||||
|
@ -8,7 +8,10 @@ use Phalcon\Mvc\User\Component as UserComponent;
|
||||
class Course extends UserComponent
|
||||
{
|
||||
|
||||
private $xs;
|
||||
/**
|
||||
* @var \XS
|
||||
*/
|
||||
protected $xs;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -18,61 +21,53 @@ class Course extends UserComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索
|
||||
* 获取XS
|
||||
* @return \XS
|
||||
*/
|
||||
public function getXS()
|
||||
{
|
||||
return $this->xs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索课程
|
||||
*
|
||||
* @param string $query
|
||||
* @param integer $limit
|
||||
* @param integer $offset
|
||||
* @return \stdClass
|
||||
* @throws \XSException
|
||||
* @return array
|
||||
*/
|
||||
public function search($query, $limit = 15, $offset = 0)
|
||||
{
|
||||
$search = $this->xs->search;
|
||||
$search = $this->xs->getSearch();
|
||||
|
||||
$items = $search->setQuery($query)->setLimit($limit, $offset)->search();
|
||||
$docs = $search->setQuery($query)->setLimit($limit, $offset)->search();
|
||||
|
||||
$total = $search->getLastCount();
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
$fields = array_keys($this->xs->getAllFields());
|
||||
|
||||
/**
|
||||
* 添加索引
|
||||
*
|
||||
* @param CourseModel $course
|
||||
*/
|
||||
public function addIndex($course)
|
||||
{
|
||||
$doc = $this->setXSDocument($course);
|
||||
$items = [];
|
||||
|
||||
$this->xs->index->add($doc);
|
||||
}
|
||||
foreach ($docs as $doc) {
|
||||
$item = new \stdClass();
|
||||
foreach ($fields as $field) {
|
||||
if (in_array($field, ['title', 'summary'])) {
|
||||
$item->{$field} = $search->highlight($doc->{$field});
|
||||
} else {
|
||||
$item->{$field} = $doc->{$field};
|
||||
}
|
||||
}
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新索引
|
||||
*
|
||||
* @param CourseModel $course
|
||||
* @throws \XSException
|
||||
*/
|
||||
public function updateIndex($course)
|
||||
{
|
||||
$doc = $this->setXSDocument($course);
|
||||
$result = new \stdClass();
|
||||
|
||||
$this->xs->index->update($doc);
|
||||
}
|
||||
$result->total = $total;
|
||||
$result->items = $items;
|
||||
|
||||
/**
|
||||
* 删除索引
|
||||
*
|
||||
* @param CourseModel $course
|
||||
*/
|
||||
public function deleteIndex($course)
|
||||
{
|
||||
$this->xs->index->del($course->id);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,7 +76,24 @@ class Course extends UserComponent
|
||||
* @param CourseModel $course
|
||||
* @return \XSDocument
|
||||
*/
|
||||
private function setXSDocument($course)
|
||||
public function setDocument(CourseModel $course)
|
||||
{
|
||||
$doc = new \XSDocument();
|
||||
|
||||
$data = $this->formatDocument($course);
|
||||
|
||||
$doc->setFields($data);
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文档
|
||||
*
|
||||
* @param CourseModel $course
|
||||
* @return array
|
||||
*/
|
||||
public function formatDocument(CourseModel $course)
|
||||
{
|
||||
$data = [
|
||||
'id' => $course->id,
|
||||
@ -101,11 +113,7 @@ class Course extends UserComponent
|
||||
'created_at' => $course->created_at,
|
||||
];
|
||||
|
||||
$doc = new \XSDocument();
|
||||
|
||||
$doc->setFields($data);
|
||||
|
||||
return $doc;
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ $config['redis']['port'] = 6379;
|
||||
$config['redis']['auth'] = '1qaz2wsx3edc';
|
||||
|
||||
/**
|
||||
* 数据库编号
|
||||
* redis库编号
|
||||
*/
|
||||
$config['redis']['index'] = 0;
|
||||
|
||||
@ -83,17 +83,27 @@ $config['redis']['index'] = 0;
|
||||
$config['redis']['persistent'] = false;
|
||||
|
||||
/**
|
||||
* 默认有限期(秒)
|
||||
* redis键前缀
|
||||
*/
|
||||
$config['redis']['lifetime'] = 86400;
|
||||
$config['redis']['prefix'] = '';
|
||||
|
||||
/**
|
||||
* redis有限期(秒)
|
||||
*/
|
||||
$config['redis']['lifetime'] = 7 * 86400;
|
||||
|
||||
/**
|
||||
* 会话键前缀
|
||||
*/
|
||||
$config['session']['prefix'] = '';
|
||||
|
||||
/**
|
||||
* 会话有效期(秒)
|
||||
*/
|
||||
$config['session']['lifetime'] = 7200;
|
||||
$config['session']['lifetime'] = 2 * 3600;
|
||||
|
||||
/**
|
||||
* 数据库编号
|
||||
* redis库编号
|
||||
*/
|
||||
$config['session']['index'] = 1;
|
||||
|
@ -3,6 +3,7 @@
|
||||
$events = [
|
||||
|
||||
'db' => \App\Listeners\Profiler::class,
|
||||
'course' => \App\Listeners\Course::class,
|
||||
'payment' => \App\Listeners\Payment::class,
|
||||
|
||||
];
|
||||
|
@ -27,8 +27,8 @@ layui.use(['jquery', 'form', 'element', 'layer', 'dropdown'], function () {
|
||||
url: data.form.action,
|
||||
data: data.field,
|
||||
success: function (res) {
|
||||
var icon = res.code == 0 ? 1 : 2;
|
||||
if (res.msg != '') {
|
||||
var icon = (res.code == 0) ? 1 : 2;
|
||||
layer.msg(res.msg, {icon: icon});
|
||||
}
|
||||
if (res.location) {
|
||||
|
@ -16,18 +16,21 @@ $scheduler->php($script, $bin, ['--task' => 'close_trade', '--action' => 'main']
|
||||
->at('*/15 * * * *');
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'close_order', '--action' => 'main'])
|
||||
->at('* */6 * * *');
|
||||
->at('* */3 * * *');
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'learning', '--action' => 'main'])
|
||||
->at('*/10 * * * *');
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'refund', '--action' => 'main'])
|
||||
->hourly(15);
|
||||
->hourly(3);
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'sync_index', '--action' => 'main'])
|
||||
->hourly(13);
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'clean_log', '--action' => 'main'])
|
||||
->daily(3, 10);
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'course_count', '--action' => 'main'])
|
||||
$scheduler->php($script, $bin, ['--task' => 'count_course', '--action' => 'main'])
|
||||
->daily(3, 20);
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'unlock_user', '--action' => 'main'])
|
||||
|
Loading…
x
Reference in New Issue
Block a user