1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-25 01:11:44 +08:00

更新索引操作改用计划任务批量完成

This commit is contained in:
xiaochong0302 2019-12-23 22:43:04 +08:00
parent 971cac05d7
commit 8395074dde
15 changed files with 475 additions and 72 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
/vendor
/config/config.php
/config/xs.course.ini
*KgTest*

View File

@ -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()

View File

@ -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;

View 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;
}
}

View 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();
}
}

View File

@ -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;
}

View File

@ -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
View 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;
}
}

View File

@ -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,
]);

View File

@ -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,
]);

View File

@ -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;
}
}

View File

@ -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;

View File

@ -3,6 +3,7 @@
$events = [
'db' => \App\Listeners\Profiler::class,
'course' => \App\Listeners\Course::class,
'payment' => \App\Listeners\Payment::class,
];

View File

@ -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) {

View File

@ -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'])