1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-17 15:55:31 +08:00

专栏阶段性提交2

This commit is contained in:
koogua 2021-04-09 17:35:31 +08:00
parent d98b936969
commit c5ef9a2b2d
58 changed files with 917 additions and 204 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
/vendor
/config/config.php
/config/xs.course.ini
/config/xs.article.ini
/config/xs.group.ini
/config/xs.user.ini
/config/alipay/*.crt

View File

@ -0,0 +1,78 @@
<?php
namespace App\Builders;
use App\Repos\Article as ArticleRepo;
use App\Repos\User as UserRepo;
class ArticleFavoriteList extends Builder
{
public function handleArticles(array $relations)
{
$articles = $this->getArticles($relations);
foreach ($relations as $key => $value) {
$relations[$key]['article'] = $articles[$value['article_id']] ?? new \stdClass();
}
return $relations;
}
public function handleUsers(array $relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
}
return $relations;
}
public function getArticles(array $relations)
{
$ids = kg_array_column($relations, 'article_id');
$articleRepo = new ArticleRepo();
$columns = [
'id', 'title', 'cover',
'view_count', 'like_count', 'comment_count', 'favorite_count',
];
$articles = $articleRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($articles->toArray() as $article) {
$article['cover'] = $baseUrl . $article['cover'];
$result[$article['id']] = $article;
}
return $result;
}
public function getUsers(array $relations)
{
$ids = kg_array_column($relations, 'user_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Caches;
use App\Models\Comment as CommentModel;
class MaxCommentId extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'max_comment_id';
}
public function getContent($id = null)
{
$comment = CommentModel::findFirst(['order' => 'id DESC']);
return $comment->id ?? 0;
}
}

View File

@ -0,0 +1,125 @@
<?php
namespace App\Console\Tasks;
use App\Models\Article as ArticleModel;
use App\Services\Search\ArticleDocument;
use App\Services\Search\ArticleSearcher;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ArticleIndexTask extends Task
{
/**
* 搜索测试
*
* @command: php console.php article_index search {query}
* @param array $params
* @throws \XSException
*/
public function searchAction($params)
{
$query = $params[0] ?? null;
if (!$query) {
exit('please special a query word' . PHP_EOL);
}
$result = $this->searchArticles($query);
var_export($result);
}
/**
* 清空索引
*
* @command: php console.php article_index clean
*/
public function cleanAction()
{
$this->cleanArticleIndex();
}
/**
* 重建索引
*
* @command: php console.php article_index rebuild
*/
public function rebuildAction()
{
$this->rebuildArticleIndex();
}
/**
* 清空索引
*/
protected function cleanArticleIndex()
{
$handler = new ArticleSearcher();
$index = $handler->getXS()->getIndex();
echo '------ start clean article index ------' . PHP_EOL;
$index->clean();
echo '------ end clean article index ------' . PHP_EOL;
}
/**
* 重建索引
*/
protected function rebuildArticleIndex()
{
$articles = $this->findArticles();
if ($articles->count() == 0) return;
$handler = new ArticleSearcher();
$documenter = new ArticleDocument();
$index = $handler->getXS()->getIndex();
echo '------ start rebuild article index ------' . PHP_EOL;
$index->beginRebuild();
foreach ($articles as $article) {
$document = $documenter->setDocument($article);
$index->add($document);
}
$index->endRebuild();
echo '------ end rebuild article index ------' . PHP_EOL;
}
/**
* 搜索文章
*
* @param string $query
* @return array
* @throws \XSException
*/
protected function searchArticles($query)
{
$handler = new ArticleSearcher();
return $handler->search($query);
}
/**
* 查找文章
*
* @return ResultsetInterface|Resultset|ArticleModel[]
*/
protected function findArticles()
{
return ArticleModel::query()
->where('published = 1')
->execute();
}
}

View File

@ -3,6 +3,7 @@
namespace App\Console\Tasks;
use App\Library\Sitemap;
use App\Models\Article as ArticleModel;
use App\Models\Course as CourseModel;
use App\Models\Help as HelpModel;
use App\Models\ImGroup as ImGroupModel;
@ -35,6 +36,7 @@ class SitemapTask extends Task
$this->addIndex();
$this->addCourses();
$this->addArticles();
$this->addTeachers();
$this->addTopics();
$this->addImGroups();
@ -74,6 +76,21 @@ class SitemapTask extends Task
}
}
protected function addArticles()
{
/**
* @var Resultset|ArticleModel[] $articles
*/
$articles = ArticleModel::query()->where('published = 1')->execute();
if ($articles->count() == 0) return;
foreach ($articles as $article) {
$loc = sprintf('%s/article/%s', $this->siteUrl, $article->id);
$this->sitemap->addItem($loc, 0.8);
}
}
protected function addTeachers()
{
/**

View File

@ -0,0 +1,60 @@
<?php
namespace App\Console\Tasks;
use App\Repos\Article as ArticleRepo;
use App\Services\Search\ArticleDocument;
use App\Services\Search\ArticleSearcher;
use App\Services\Sync\ArticleIndex as ArticleIndexSync;
class SyncArticleIndexTask extends Task
{
public function mainAction()
{
$redis = $this->getRedis();
$key = $this->getSyncKey();
$articleIds = $redis->sRandMember($key, 1000);
if (!$articleIds) return;
$articleRepo = new ArticleRepo();
$articles = $articleRepo->findByIds($articleIds);
if ($articles->count() == 0) return;
$document = new ArticleDocument();
$handler = new ArticleSearcher();
$index = $handler->getXS()->getIndex();
$index->openBuffer();
foreach ($articles as $article) {
$doc = $document->setDocument($article);
if ($article->published == 1) {
$index->update($doc);
} else {
$index->del($article->id);
}
}
$index->closeBuffer();
$redis->sRem($key, ...$articleIds);
}
protected function getSyncKey()
{
$sync = new ArticleIndexSync();
return $sync->getSyncKey();
}
}

View File

@ -74,7 +74,6 @@ class Category extends Service
$data['type'] = $validator->checkType($post['type']);
$data['name'] = $validator->checkName($post['name']);
$data['priority'] = $validator->checkPriority($post['priority']);
$data['published'] = $validator->checkPublishStatus($post['published']);
$category = new CategoryModel();

View File

@ -64,7 +64,6 @@ class Help extends Service
$data['title'] = $validator->checkTitle($post['title']);
$data['content'] = $validator->checkContent($post['content']);
$data['priority'] = $validator->checkPriority($post['priority']);
$data['published'] = $validator->checkPublishStatus($post['published']);
$data['category_id'] = $category->id;
$help = new HelpModel();

View File

@ -72,7 +72,6 @@ class Nav extends Service
$data['url'] = $validator->checkUrl($post['url']);
$data['target'] = $validator->checkTarget($post['target']);
$data['position'] = $validator->checkPosition($post['position']);
$data['published'] = $validator->checkPublishStatus($post['published']);
$nav = new NavModel();

View File

@ -43,7 +43,6 @@ class Page extends Service
$data['title'] = $validator->checkTitle($post['title']);
$data['content'] = $validator->checkContent($post['content']);
$data['published'] = $validator->checkPublishStatus($post['published']);
$page = new PageModel();

View File

@ -31,13 +31,6 @@
<input class="layui-input" type="text" name="priority" value="10" lay-verify="number">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发布</label>
<div class="layui-input-block">
<input type="radio" name="published" value="1" title="是" checked="checked">
<input type="radio" name="published" value="0" title="否">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">

View File

@ -51,13 +51,6 @@
<input type="radio" name="target" value="_self" title="原窗口">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发布</label>
<div class="layui-input-block">
<input type="radio" name="published" value="1" title="是" checked="checked">
<input type="radio" name="published" value="0" title="否">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">

View File

@ -29,7 +29,7 @@ class ArticleController extends Controller
$sorts = $service->handleSorts();
$params = $service->getParams();
$this->seo->prependTitle('文章');
$this->seo->prependTitle('专栏');
$this->view->setVar('categories', $categories);
$this->view->setVar('sorts', $sorts);

View File

@ -2,6 +2,7 @@
namespace App\Http\Home\Controllers;
use App\Services\Logic\Search\Article as ArticleSearchService;
use App\Services\Logic\Search\Course as CourseSearchService;
use App\Services\Logic\Search\Group as GroupSearchService;
use App\Services\Logic\Search\User as UserSearchService;
@ -41,11 +42,14 @@ class SearchController extends Controller
/**
* @param string $type
* @return CourseSearchService|GroupSearchService|UserSearchService
* @return ArticleSearchService|CourseSearchService|GroupSearchService|UserSearchService
*/
protected function getSearchService($type)
{
switch ($type) {
case 'article':
$service = new ArticleSearchService();
break;
case 'group':
$service = new GroupSearchService();
break;

View File

@ -5,6 +5,7 @@ namespace App\Http\Home\Controllers;
use App\Repos\WeChatSubscribe as WeChatSubscribeRepo;
use App\Services\Logic\Account\OAuthProvider as OAuthProviderService;
use App\Services\Logic\User\Console\AccountInfo as AccountInfoService;
use App\Services\Logic\User\Console\ArticleList as ArticleListService;
use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService;
use App\Services\Logic\User\Console\ConnectList as ConnectListService;
use App\Services\Logic\User\Console\ConsultList as ConsultListService;
@ -134,6 +135,19 @@ class UserConsoleController extends Controller
$this->view->setVar('pager', $pager);
}
/**
* @Get("/articles", name="home.uc.articles")
*/
public function articlesAction()
{
$service = new ArticleListService();
$pager = $service->handle();
$this->view->pick('user/console/articles');
$this->view->setVar('pager', $pager);
}
/**
* @Get("/favorites", name="home.uc.favorites")
*/

View File

@ -2,8 +2,8 @@
namespace App\Http\Home\Controllers;
use App\Services\Logic\User\ArticleList as UserArticleListService;
use App\Services\Logic\User\CourseList as UserCourseListService;
use App\Services\Logic\User\FavoriteList as UserFavoriteListService;
use App\Services\Logic\User\FriendList as UserFriendListService;
use App\Services\Logic\User\GroupList as UserGroupListService;
use App\Services\Logic\User\UserInfo as UserInfoService;
@ -46,18 +46,18 @@ class UserController extends Controller
}
/**
* @Get("/{id:[0-9]+}/favorites", name="home.user.favorites")
* @Get("/{id:[0-9]+}/articles", name="home.user.articles")
*/
public function favoritesAction($id)
public function articlesAction($id)
{
$service = new UserFavoriteListService();
$service = new UserArticleListService();
$pager = $service->handle($id);
$pager->target = 'tab-favorites';
$pager->target = 'tab-articles';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('user/favorites');
$this->view->pick('user/articles');
$this->view->setVar('pager', $pager);
}

View File

@ -97,6 +97,7 @@
{% set course_title = item.course.title %}
{% set course_url = url({'for':'home.course.show','id':item.course.id}) %}
<div class="course-card">
<span class="model layui-badge layui-bg-green">{{ model_info(item.course.model) }}</span>
<div class="cover">
<a href="{{ course_url }}" title="{{ course_title }}" target="_blank">
<img src="{{ item.course.cover }}!cover_270" alt="{{ course_title }}">

View File

@ -0,0 +1,29 @@
{% if pager.total_pages > 0 %}
<div class="search-course-list">
{% for item in pager.items %}
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
<div class="search-course-card clearfix">
<div class="cover">
<a href="{{ article_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}">
</a>
</div>
<div class="info">
<div class="title layui-elip">
<a href="{{ course_url }}" target="_blank">{{ item.title }}</a>
</div>
<div class="summary">{{ item.summary }}</div>
<div class="meta">
<span>作者:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></span>
<span>浏览:{{ item.view_count }}</span>
<span>点赞:{{ item.like_count }}</span>
<span>评论:{{ item.comment_count }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
{{ partial('search/empty') }}
{% endif %}

View File

@ -5,17 +5,17 @@
{% set teacher_url = url({'for':'home.teacher.show','id':item.teacher.id}) %}
<div class="search-course-card clearfix">
<div class="cover">
<a href="{{ course_url }}">
<a href="{{ course_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}">
</a>
</div>
<div class="info">
<div class="title layui-elip">
<a href="{{ course_url }}">{{ item.title }}</a>
<a href="{{ course_url }}" target="_blank">{{ item.title }}</a>
</div>
<div class="summary">{{ item.summary }}</div>
<div class="meta">
<span>讲师:<a href="{{ teacher_url }}">{{ item.teacher.name }}</a></span>
<span>讲师:<a href="{{ teacher_url }}" target="_blank">{{ item.teacher.name }}</a></span>
<span>难度:{{ level_info(item.level) }}</span>
<span>课时:{{ item.lesson_count }}</span>
<span>学员:{{ item.user_count }}</span>

View File

@ -6,17 +6,17 @@
{% set item.about = item.about ? item.about : '这个家伙真懒,什么也没有留下!' %}
<div class="search-group-card clearfix">
<div class="avatar">
<a href="{{ group_url }}">
<a href="{{ group_url }}" target="_blank">
<img src="{{ item.avatar }}!avatar_160" alt="{{ item.name }}">
</a>
</div>
<div class="info">
<div class="name layui-elip">
<a href="{{ group_url }}">{{ item.name }}</a>
<a href="{{ group_url }}" target="_blank">{{ item.name }}</a>
</div>
<div class="about layui-elip">{{ item.about }}</div>
<div class="meta">
<span>组长:<a href="{{ owner_url }}">{{ item.owner.name }}</a></span>
<span>组长:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></span>
<span>成员:{{ item.user_count }}</span>
</div>
</div>

View File

@ -4,7 +4,7 @@
{{ partial('macros/course') }}
{% set types = {'course':'课程','group':'群组','user':'用户'} %}
{% set types = {'course':'课程','article':'专栏','group':'群组','user':'用户'} %}
{% set type = request.get('type','trim','course') %}
{% set query = request.get('query','striptags','') %}
@ -31,6 +31,10 @@
<div class="layui-tab-item layui-show">
{{ partial('search/course') }}
</div>
{% elseif type == 'article' %}
<div class="layui-tab-item layui-show">
{{ partial('search/article') }}
</div>
{% elseif type == 'group' %}
<div class="layui-tab-item layui-show">
{{ partial('search/group') }}

View File

@ -7,13 +7,13 @@
{% set item.about = item.about ? item.about : '这个家伙真懒,什么也没有留下!' %}
<div class="search-group-card clearfix">
<div class="avatar">
<a href="{{ user_url }}">
<a href="{{ user_url }}" target="_blank">
<img src="{{ item.avatar }}!avatar_160" alt="{{ item.name }}">
</a>
</div>
<div class="info">
<div class="name layui-elip">
<a href="{{ user_url }}">{{ item.name }}</a>
<a href="{{ user_url }}" target="_blank">{{ item.name }}</a>
</div>
<div class="about layui-elip">{{ item.about }}</div>
<div class="meta">

View File

@ -0,0 +1,32 @@
{{ partial('macros/article') }}
{% if pager.total_pages > 0 %}
<div class="article-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
<div class="layui-col-md3">
<div class="course-card">
<span class="type layui-badge layui-bg-green">{{ source_type(item.source_type) }}</span>
<div class="cover">
<a href="{{ article_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}" title="{{ item.title }}">
</a>
</div>
<div class="info">
<div class="title layui-elip">
<a href="{{ article_url }}" title="{{ item.title }}" target="_blank">{{ item.title }}</a>
</div>
<div class="meta">
<span class="view">{{ item.view_count }} 浏览</span>
<span class="like">{{ item.like_count }} 点赞</span>
<span class="comment">{{ item.comment_count }} 评论</span>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -0,0 +1,64 @@
{% extends 'templates/main.volt' %}
{% block content %}
{{ partial('macros/article') }}
<div class="layout-main clearfix">
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
<div class="my-content">
<div class="wrap">
<div class="my-nav">
<span class="title">我的文章</span>
</div>
{% if pager.total_pages > 0 %}
<table class="layui-table">
<colgroup>
<col>
<col>
<col>
<col>
<col>
</colgroup>
<thead>
<tr>
<th>文章</th>
<th>浏览</th>
<th>点赞</th>
<th>评论</th>
<th>收藏</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
<tr>
<td>
<p>标题:<a href="{{ article_url }}" target="_blank">{{ item.title }}</a></p>
<p class="meta">
来源:<span class="layui-badge layui-bg-gray">{{ source_type(item.source_type) }}</span>
分类:<span class="layui-badge layui-bg-gray">{{ item.category.name }}</span>
时间:{{ item.create_time|time_ago }}
</p>
</td>
<td>{{ item.view_count }}</td>
<td>{{ item.like_count }}</td>
<td>{{ item.comment_count }}</td>
<td>{{ item.favorite_count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/user.console.js') }}
{% endblock %}

View File

@ -4,48 +4,25 @@
{{ partial('macros/course') }}
{% set types = {'course':'课程','article':'文章'} %}
{% set type = request.get('type','trim','course') %}
<div class="layout-main clearfix">
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
<div class="my-content">
<div class="wrap">
<div class="my-nav">
<span class="title">我的收藏</span>
{% for key,value in types %}
{% set class = (type == key) ? 'layui-btn layui-btn-xs' : 'none' %}
{% set url = url({'for':'home.uc.favorites'},{'type':key}) %}
<a class="{{ class }}" href="{{ url }}">{{ value }}</a>
{% endfor %}
</div>
{% if pager.total_pages > 0 %}
<table class="layui-table" lay-size="lg">
<colgroup>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>课程</th>
<th>学员</th>
<th>评分</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set course_url = url({'for':'home.course.show','id':item.id}) %}
{% set favorite_url = url({'for':'home.course.favorite','id':item.id}) %}
<tr>
<td>
<a href="{{ course_url }}" target="_blank">{{ item.title }}</a>
<span class="layui-badge layui-bg-gray">{{ model_info(item.model) }}</span>
</td>
<td>{{ item.user_count }}</td>
<td>{{ "%0.1f"|format(item.rating) }}</td>
<td class="center">
<button class="layui-btn layui-btn-sm kg-delete" data-tips="确定要取消收藏吗?" data-url="{{ favorite_url }}">取消</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% if type == 'course' %}
{{ partial('user/console/favorites_course') }}
{% elseif type == 'article' %}
{{ partial('user/console/favorites_article') }}
{% endif %}
</div>
</div>

View File

@ -0,0 +1,36 @@
{% if pager.total_pages > 0 %}
<table class="layui-table" lay-size="lg">
<colgroup>
<col>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>文章</th>
<th>浏览</th>
<th>点赞</th>
<th>评论</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
{% set favorite_url = url({'for':'home.article.favorite','id':item.id}) %}
<tr>
<td><a href="{{ article_url }}" target="_blank">{{ item.title }}</a></td>
<td>{{ item.view_count }}</td>
<td>{{ item.like_count }}</td>
<td>{{ item.comment_count }}</td>
<td class="center">
<button class="layui-btn layui-btn-sm kg-delete" data-tips="确定要取消收藏吗?" data-url="{{ favorite_url }}">取消</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endif %}

View File

@ -0,0 +1,36 @@
{% if pager.total_pages > 0 %}
<table class="layui-table" lay-size="lg">
<colgroup>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>课程</th>
<th>学员</th>
<th>评分</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set course_url = url({'for':'home.course.show','id':item.id}) %}
{% set favorite_url = url({'for':'home.course.favorite','id':item.id}) %}
<tr>
<td>
<a href="{{ course_url }}" target="_blank">{{ item.title }}</a>
<span class="layui-badge layui-bg-gray">{{ model_info(item.model) }}</span>
</td>
<td>{{ item.user_count }}</td>
<td>{{ "%0.1f"|format(item.rating) }}</td>
<td class="center">
<button class="layui-btn layui-btn-sm kg-delete" data-tips="确定要取消收藏吗?" data-url="{{ favorite_url }}">取消</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endif %}

View File

@ -18,10 +18,11 @@
</div>
<div class="layui-card">
<div class="layui-card-header">课程中心</div>
<div class="layui-card-header">内容中心</div>
<div class="layui-card-body">
<ul class="my-menu">
<li><a href="{{ url({'for':'home.uc.courses'}) }}">我的课程</a></li>
<li><a href="{{ url({'for':'home.uc.articles'}) }}">我的文章</a></li>
<li><a href="{{ url({'for':'home.uc.favorites'}) }}">我的收藏</a></li>
<li><a href="{{ url({'for':'home.uc.reviews'}) }}">我的评价</a></li>
<li><a href="{{ url({'for':'home.uc.consults'}) }}">我的咨询</a></li>

View File

@ -1,7 +1,7 @@
{{ partial('macros/course') }}
{% if pager.total_pages > 0 %}
<div class="course-list learning-course-list clearfix">
<div class="course-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
<div class="layui-col-md3">

View File

@ -1,14 +0,0 @@
{{ partial('macros/course') }}
{% if pager.total_pages > 0 %}
<div class="course-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
<div class="layui-col-md3">
{{ course_card(item) }}
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -38,12 +38,12 @@
</div>
{% set show_tab_courses = user.course_count > 0 %}
{% set show_tab_favorites = user.favorite_count > 0 %}
{% set show_tab_articles = user.article_count > 0 %}
{% set show_tab_friends = user.friend_count > 0 %}
{% set show_tab_groups = user.group_count > 0 %}
{% set courses_url = url({'for':'home.user.courses','id':user.id}) %}
{% set favorites_url = url({'for':'home.user.favorites','id':user.id}) %}
{% set articles_url = url({'for':'home.user.articles','id':user.id}) %}
{% set friends_url = url({'for':'home.user.friends','id':user.id}) %}
{% set groups_url = url({'for':'home.user.groups','id':user.id}) %}
@ -53,8 +53,8 @@
{% if show_tab_courses %}
<li class="layui-this">课程<span class="tab-count">{{ user.course_count }}</span></li>
{% endif %}
{% if show_tab_favorites %}
<li>收藏<span class="tab-count">{{ user.favorite_count }}</span></li>
{% if show_tab_articles %}
<li>文章<span class="tab-count">{{ user.article_count }}</span></li>
{% endif %}
{% if show_tab_friends %}
<li>好友<span class="tab-count">{{ user.friend_count }}</span></li>
@ -67,8 +67,8 @@
{% if show_tab_courses %}
<div class="layui-tab-item layui-show" id="tab-courses" data-url="{{ courses_url }}"></div>
{% endif %}
{% if show_tab_favorites %}
<div class="layui-tab-item" id="tab-favorites" data-url="{{ favorites_url }}"></div>
{% if show_tab_articles %}
<div class="layui-tab-item" id="tab-articles" data-url="{{ articles_url }}"></div>
{% endif %}
{% if show_tab_friends %}
<div class="layui-tab-item" id="tab-friends" data-url="{{ friends_url }}"></div>

View File

@ -209,6 +209,10 @@ class Article extends Model
$this->cover = self::getCoverPath($this->cover);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->content);
}
if (is_array($this->tags)) {
$this->tags = kg_json_encode($this->tags);
}

View File

@ -69,7 +69,7 @@ class Category extends Model
*
* @var int
*/
public $published = 0;
public $published = 1;
/**
* 删除标识

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Caches\MaxCommentId as MaxCommentIdCache;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
class Comment extends Model
@ -19,105 +20,105 @@ class Comment extends Model
*
* @var integer
*/
public $id;
public $id = 0;
/**
* 内容
*
* @var string
*/
public $content;
public $content = '';
/**
* 父级编号
*
* @var integer
*/
public $parent_id;
public $parent_id = 0;
/**
* 作者编号
*
* @var integer
*/
public $owner_id;
public $owner_id = 0;
/**
* 目标用户
*
* @var integer
*/
public $to_user_id;
public $to_user_id = 0;
/**
* 条目编号
*
* @var integer
*/
public $item_id;
public $item_id = 0;
/**
* 条目类型
*
* @var integer
*/
public $item_type;
public $item_type = 0;
/**
* 终端类型
*
* @var integer
*/
public $client_type;
public $client_type = 0;
/**
* 终端IP
*
* @var integer
*/
public $client_ip;
public $client_ip = '';
/**
* 发布标识
*
* @var integer
*/
public $published;
public $published = 1;
/**
* 删除标识
*
* @var integer
*/
public $deleted;
public $deleted = 0;
/**
* 回复数
*
* @var integer
*/
public $reply_count;
public $reply_count = 0;
/**
* 点赞数
*
* @var integer
*/
public $like_count;
public $like_count = 0;
/**
* 创建时间
*
* @var integer
*/
public $create_time;
public $create_time = 0;
/**
* 更新时间
*
* @var integer
*/
public $update_time;
public $update_time = 0;
public function getSource(): string
{
@ -146,6 +147,13 @@ class Comment extends Model
$this->update_time = time();
}
public function afterCreate()
{
$cache = new MaxCommentIdCache();
$cache->rebuild();
}
public static function itemTypes()
{
return [

View File

@ -338,6 +338,10 @@ class Course extends Model
$this->cover = self::getCoverPath($this->cover);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->details);
}
if (is_array($this->attrs)) {
$this->attrs = kg_json_encode($this->attrs);
}

View File

@ -48,7 +48,7 @@ class Help extends Model
*
* @var int
*/
public $published = 0;
public $published = 1;
/**
* 删除标识

View File

@ -71,7 +71,7 @@ class ImGroup extends Model
*
* @var int
*/
public $published = 0;
public $published = 1;
/**
* 删除状态

View File

@ -87,7 +87,7 @@ class Nav extends Model
*
* @var int
*/
public $published = 0;
public $published = 1;
/**
* 删除标识

View File

@ -34,7 +34,7 @@ class Page extends Model
*
* @var int
*/
public $published = 0;
public $published = 1;
/**
* 删除标识

View File

@ -82,7 +82,7 @@ class Review extends Model
*
* @var int
*/
public $published = 0;
public $published = 1;
/**
* 删除标识

View File

@ -63,6 +63,10 @@ class Article extends Repository
$builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]);
}
if ($sort == 'featured') {
$builder->andWhere('featured = 1');
}
switch ($sort) {
case 'like':
$orderBy = 'like_count DESC';
@ -70,6 +74,9 @@ class Article extends Repository
case 'popular':
$orderBy = 'view_count DESC';
break;
case 'comment':
$orderBy = 'comment_count DESC';
break;
default:
$orderBy = 'id DESC';
break;

View File

@ -2,12 +2,46 @@
namespace App\Repos;
use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\ArticleFavorite as ArticleFavoriteModel;
use Phalcon\Mvc\Model;
class ArticleFavorite extends Repository
{
public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15)
{
$builder = $this->modelsManager->createBuilder();
$builder->from(ArticleFavoriteModel::class);
$builder->where('1 = 1');
if (!empty($where['article_id'])) {
$builder->andWhere('article_id = :article_id:', ['article_id' => $where['article_id']]);
}
if (!empty($where['user_id'])) {
$builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
}
switch ($sort) {
default:
$orderBy = 'id DESC';
break;
}
$builder->orderBy($orderBy);
$pager = new PagerQueryBuilder([
'builder' => $builder,
'page' => $page,
'limit' => $limit,
]);
return $pager->paginate();
}
/**
* @param int $articleId
* @param int $userId

View File

@ -4,6 +4,7 @@ namespace App\Repos;
use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\Article as ArticleModel;
use App\Models\ArticleFavorite as ArticleFavoriteModel;
use App\Models\CourseFavorite as CourseFavoriteModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\ImUser as ImUserModel;
@ -169,8 +170,8 @@ class User extends Repository
public function countArticles($userId)
{
return (int)ArticleModel::count([
'conditions' => 'user_id = :user_id: AND published = 1',
'bind' => ['user_id' => $userId],
'conditions' => 'owner_id = :owner_id: AND published = 1',
'bind' => ['owner_id' => $userId],
]);
}
@ -182,4 +183,12 @@ class User extends Repository
]);
}
public function countArticleFavorites($userId)
{
return (int)ArticleFavoriteModel::count([
'conditions' => 'user_id = :user_id:',
'bind' => ['user_id' => $userId],
]);
}
}

View File

@ -54,7 +54,6 @@ class ArticleInfo extends LogicService
'like_count' => $article->like_count,
'comment_count' => $article->comment_count,
'favorite_count' => $article->favorite_count,
'allow_comment' => $article->allow_comment,
'create_time' => $article->create_time,
'update_time' => $article->update_time,
];
@ -67,6 +66,8 @@ class ArticleInfo extends LogicService
'favorited' => 0,
];
$me['allow_comment'] = $article->allow_comment ? 1 : 0;
if ($user->id > 0) {
$likeRepo = new ArticleLikeRepo();

View File

@ -32,7 +32,7 @@ class ArticleList extends LogicService
return $this->handleArticles($pager);
}
protected function handleArticles($pager)
public function handleArticles($pager)
{
if ($pager->total_items == 0) {
return $pager;
@ -69,6 +69,8 @@ class ArticleList extends LogicService
'title' => $article['title'],
'cover' => $article['cover'],
'summary' => $article['summary'],
'source_type' => $article['source_type'],
'source_url' => $article['source_url'],
'tags' => $article['tags'],
'category' => $category,
'owner' => $owner,

View File

@ -0,0 +1,84 @@
<?php
namespace App\Services\Logic\Search;
use App\Library\Paginator\Adapter\XunSearch as XunSearchPaginator;
use App\Library\Paginator\Query as PagerQuery;
use App\Services\Search\ArticleSearcher as ArticleSearcherService;
class Article extends Handler
{
public function search()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$searcher = new ArticleSearcherService();
$paginator = new XunSearchPaginator([
'xs' => $searcher->getXS(),
'highlight' => $searcher->getHighlightFields(),
'query' => $params['query'],
'page' => $page,
'limit' => $limit,
]);
$pager = $paginator->getPaginate();
return $this->handleArticles($pager);
}
public function getHotQuery($limit = 10, $type = 'total')
{
$searcher = new ArticleSearcherService();
return $searcher->getHotQuery($limit, $type);
}
public function getRelatedQuery($query, $limit = 10)
{
$searcher = new ArticleSearcherService();
return $searcher->getRelatedQuery($query, $limit);
}
protected function handleArticles($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$items = [];
$baseUrl = kg_cos_url();
foreach ($pager->items as $item) {
$item['cover'] = $baseUrl . $item['cover'];
$items[] = [
'id' => (int)$item['id'],
'title' => (string)$item['title'],
'cover' => (string)$item['cover'],
'summary' => (string)$item['summary'],
'create_time' => (int)$item['create_time'],
'view_count' => (int)$item['view_count'],
'like_count' => (int)$item['like_count'],
'favorite_count' => (int)$item['favorite_count'],
'comment_count' => (int)$item['comment_count'],
'tags' => json_decode($item['tags'], true),
'owner' => json_decode($item['owner'], true),
'category' => json_decode($item['category'], true),
];
}
$pager->items = $items;
return $pager;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Services\Logic\User;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\Article as ArticleRepo;
use App\Services\Logic\Article\ArticleList as ArticleListService;
use App\Services\Logic\Service as LogicService;
use App\Services\Logic\UserTrait;
class ArticleList extends LogicService
{
use UserTrait;
public function handle($id)
{
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['owner_id'] = $user->id;
$params['published'] = 1;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$articleRepo = new ArticleRepo();
$pager = $articleRepo->paginate($params, $sort, $page, $limit);
return $this->handleArticles($pager);
}
protected function handleArticles($pager)
{
$service = new ArticleListService();
return $service->handleArticles($pager);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Services\Logic\User\Console;
use App\Services\Logic\Service as LogicService;
use App\Services\Logic\User\ArticleList as UserArticleListService;
class ArticleList extends LogicService
{
public function handle()
{
$user = $this->getLoginUser();
$service = new UserArticleListService();
return $service->handle($user->id);
}
}

View File

@ -63,7 +63,6 @@ class ConsultList extends Service
'update_time' => $consult['update_time'],
'course' => $course,
'chapter' => $chapter,
'show' => false,
];
}

View File

@ -2,19 +2,96 @@
namespace App\Services\Logic\User\Console;
use App\Services\Logic\Service;
use App\Services\Logic\User\FavoriteList as UserFavoriteListService;
use App\Builders\ArticleFavoriteList as ArticleFavoriteListBuilder;
use App\Builders\CourseFavoriteList as CourseFavoriteListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\ArticleFavorite as ArticleFavoriteRepo;
use App\Repos\CourseFavorite as CourseFavoriteRepo;
use App\Services\Logic\Service as LogicService;
class FavoriteList extends Service
class FavoriteList extends LogicService
{
public function handle()
{
$user = $this->getLoginUser();
$service = new UserFavoriteListService();
$pagerQuery = new PagerQuery();
return $service->handle($user->id);
$params = $pagerQuery->getParams();
$type = $params['type'] ?? 'course';
$params['user_id'] = $user->id;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
if ($type == 'course') {
$favoriteRepo = new CourseFavoriteRepo();
$pager = $favoriteRepo->paginate($params, $sort, $page, $limit);
return $this->handleCourses($pager);
} elseif ($type == 'article') {
$favoriteRepo = new ArticleFavoriteRepo();
$pager = $favoriteRepo->paginate($params, $sort, $page, $limit);
return $this->handleArticles($pager);
}
}
protected function handleCourses($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new CourseFavoriteListBuilder();
$relations = $pager->items->toArray();
$courses = $builder->getCourses($relations);
$items = [];
foreach ($relations as $relation) {
$course = $courses[$relation['course_id']] ?? new \stdClass();
$items[] = $course;
}
$pager->items = $items;
return $pager;
}
protected function handleArticles($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new ArticleFavoriteListBuilder();
$relations = $pager->items->toArray();
$articles = $builder->getArticles($relations);
$items = [];
foreach ($relations as $relation) {
$article = $articles[$relation['article_id']] ?? new \stdClass();
$items[] = $article;
}
$pager->items = $items;
return $pager;
}
}

View File

@ -1,62 +0,0 @@
<?php
namespace App\Services\Logic\User;
use App\Builders\CourseFavoriteList as CourseFavoriteListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\CourseFavorite as CourseFavoriteRepo;
use App\Services\Logic\Service;
use App\Services\Logic\UserTrait;
class FavoriteList extends Service
{
use UserTrait;
public function handle($id)
{
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['user_id'] = $user->id;
$params['deleted'] = 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$favoriteRepo = new CourseFavoriteRepo();
$pager = $favoriteRepo->paginate($params, $sort, $page, $limit);
return $this->handleCourses($pager);
}
protected function handleCourses($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new CourseFavoriteListBuilder();
$relations = $pager->items->toArray();
$courses = $builder->getCourses($relations);
$items = [];
foreach ($relations as $relation) {
$course = $courses[$relation['course_id']] ?? new \stdClass();
$items[] = $course;
}
$pager->items = $items;
return $pager;
}
}

View File

@ -36,7 +36,7 @@ class UserInfo extends Service
'vip' => $user->vip,
'locked' => $user->locked,
'course_count' => $user->course_count,
'favorite_count' => $user->favorite_count,
'article_count' => $user->article_count,
'friend_count' => $imUser->friend_count,
'group_count' => $imUser->group_count,
'active_time' => $user->active_time,

View File

@ -61,13 +61,18 @@ class ArticleDocument extends Component
$article->cover = ArticleModel::getCoverPath($article->cover);
if (empty($article->summary)) {
$article->summary = kg_parse_summary($article->content);
}
return [
'id' => $article->id,
'title' => $article->title,
'cover' => $article->cover,
'summary' => $article->summary,
'category_id' => $article->category_id,
'owner_id' => $article->teacher_id,
'owner_id' => $article->owner_id,
'create_time' => $article->create_time,
'tags' => $article->tags,
'category' => $category,
'owner' => $owner,

View File

@ -61,6 +61,10 @@ class CourseDocument extends Component
$course->cover = CourseModel::getCoverPath($course->cover);
if (empty($article->summary)) {
$course->summary = kg_parse_summary($course->details);
}
return [
'id' => $course->id,
'title' => $course->title,

View File

@ -26,6 +26,9 @@ type = string
index = self
tokenizer = full
[create_time]
type = string
[tags]
type = string

View File

@ -1,8 +1,8 @@
project.name = article
project.default_charset = UTF-8
server.index = xunsearch:8383
server.search = xunsearch:8384
server.index = 8383
server.search = 8384
[id]
type = id
@ -26,6 +26,9 @@ type = string
index = self
tokenizer = full
[create_time]
type = string
[tags]
type = string

View File

@ -1542,18 +1542,8 @@
font-size: 30px;
}
.learning-course-card {
height: 260px;
}
.learning-course-card .progress {
padding-top: 15px;
padding-bottom: 10px;
}
.learning-course-card .duration {
font-size: 12px;
color: #666;
.user-tab .course-card .info {
padding: 10px 8px;
}
.user-card {

View File

@ -8,9 +8,9 @@ layui.use(['jquery', 'helper'], function () {
helper.ajaxLoadHtml($tabCourses.data('url'), $tabCourses.attr('id'));
}
if ($('#tab-favorites').length > 0) {
var $tabFavorites = $('#tab-favorites');
helper.ajaxLoadHtml($tabFavorites.data('url'), $tabFavorites.attr('id'));
if ($('#tab-articles').length > 0) {
var $tabArticles = $('#tab-articles');
helper.ajaxLoadHtml($tabArticles.data('url'), $tabArticles.attr('id'));
}
if ($('#tab-friends').length > 0) {