diff --git a/app/Builders/NavTreeList.php b/app/Builders/NavTreeList.php index d7e1ae4f..950752c6 100644 --- a/app/Builders/NavTreeList.php +++ b/app/Builders/NavTreeList.php @@ -23,6 +23,8 @@ class NavTreeList extends Builder $list[] = [ 'id' => $nav->id, 'name' => $nav->name, + 'target' => $nav->target, + 'url' => $nav->url, 'children' => $this->handleChildren($nav), ]; } @@ -44,6 +46,8 @@ class NavTreeList extends Builder $list[] = [ 'id' => $nav->id, 'name' => $nav->name, + 'target' => $nav->target, + 'url' => $nav->url, ]; } @@ -70,7 +74,7 @@ class NavTreeList extends Builder { return NavModel::query() ->where('position = :position:', ['position' => $position]) - ->andWhere('deleted = 0') + ->andWhere('level = 1 AND deleted = 0') ->execute(); } diff --git a/app/Console/Tasks/CourseIndexTask.php b/app/Console/Tasks/CourseIndexTask.php index 678e934c..050e50b5 100644 --- a/app/Console/Tasks/CourseIndexTask.php +++ b/app/Console/Tasks/CourseIndexTask.php @@ -4,7 +4,7 @@ namespace App\Console\Tasks; use App\Models\Course as CourseModel; use App\Services\Search\CourseDocument; -use App\Services\Search\CourseHandler; +use App\Services\Search\CourseSearcher; use Phalcon\Cli\Task; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -57,7 +57,7 @@ class CourseIndexTask extends Task */ protected function cleanCourseIndex() { - $handler = new CourseHandler(); + $handler = new CourseSearcher(); $index = $handler->getXS()->getIndex(); @@ -79,7 +79,7 @@ class CourseIndexTask extends Task return; } - $handler = new CourseHandler(); + $handler = new CourseSearcher(); $documenter = new CourseDocument(); @@ -108,7 +108,7 @@ class CourseIndexTask extends Task */ protected function searchCourses($query) { - $handler = new CourseHandler(); + $handler = new CourseSearcher(); return $handler->search($query); } diff --git a/app/Console/Tasks/SyncCourseIndexTask.php b/app/Console/Tasks/SyncCourseIndexTask.php index e27ace7b..ecb9f9e3 100644 --- a/app/Console/Tasks/SyncCourseIndexTask.php +++ b/app/Console/Tasks/SyncCourseIndexTask.php @@ -5,7 +5,7 @@ namespace App\Console\Tasks; use App\Library\Cache\Backend\Redis as RedisCache; use App\Repos\Course as CourseRepo; use App\Services\Search\CourseDocument; -use App\Services\Search\CourseHandler; +use App\Services\Search\CourseSearcher; use App\Services\Syncer\CourseIndex as CourseIndexSyncer; class SyncCourseIndexTask extends Task @@ -48,7 +48,7 @@ class SyncCourseIndexTask extends Task $document = new CourseDocument(); - $handler = new CourseHandler(); + $handler = new CourseSearcher(); $index = $handler->getXS()->getIndex(); diff --git a/app/Http/Admin/Services/Slide.php b/app/Http/Admin/Services/Slide.php index 5acaa2a3..37c4d252 100644 --- a/app/Http/Admin/Services/Slide.php +++ b/app/Http/Admin/Services/Slide.php @@ -87,6 +87,10 @@ class Slide extends Service $data['cover'] = $validator->checkCover($post['cover']); } + if (isset($post['bg_color'])) { + $data['bg_color'] = $validator->checkBgColor($post['bg_color']); + } + if (isset($post['content'])) { if ($slide->target == SlideModel::TARGET_COURSE) { $course = $validator->checkCourse($post['content']); diff --git a/app/Http/Admin/Views/slide/edit.volt b/app/Http/Admin/Views/slide/edit.volt index 1840a1f4..8d3f913e 100644 --- a/app/Http/Admin/Views/slide/edit.volt +++ b/app/Http/Admin/Views/slide/edit.volt @@ -29,6 +29,16 @@ +
+ +
+ +
+
+
+
+
+
@@ -75,4 +85,19 @@ -{{ partial('partials/cover_uploader') }} \ No newline at end of file +{{ partial('partials/cover_uploader') }} + + \ No newline at end of file diff --git a/app/Http/Web/Controllers/PageController.php b/app/Http/Web/Controllers/PageController.php new file mode 100644 index 00000000..09289115 --- /dev/null +++ b/app/Http/Web/Controllers/PageController.php @@ -0,0 +1,25 @@ +handle($id); + + $this->view->setVar('page', $page); + } + +} diff --git a/app/Http/Web/Controllers/SearchController.php b/app/Http/Web/Controllers/SearchController.php index 67376508..753ba432 100644 --- a/app/Http/Web/Controllers/SearchController.php +++ b/app/Http/Web/Controllers/SearchController.php @@ -2,6 +2,8 @@ namespace App\Http\Web\Controllers; +use App\Services\Frontend\Search\CourseSearch; + /** * @RoutePrefix("/search") */ @@ -9,80 +11,12 @@ class SearchController extends Controller { /** - * @Get("/courses", name="web.search.courses") + * @Get("/", name="web.search.show") */ - public function coursesAction() + public function showAction() { - $query = $this->request->getQuery('q'); - - $indexer = new \App\Library\Indexer\Course(); - - $courses = $indexer->search($query); - - echo "total: {$courses['total']}
"; - echo "
"; - - foreach ($courses['items'] as $course) { - echo "title:{$course->title}
"; - echo "summary:{$course->summary}
"; - echo "tags:{$course->tags}
"; - echo "
"; - } - - exit; - } - - /** - * @Get("/course/update", name="web.search.update_course") - */ - public function updateCourseAction() - { - $indexer = new \App\Library\Indexer\Course(); - - $courseRepo = new \App\Repos\Course(); - - $course = $courseRepo->findById(1); - - $indexer->updateIndex($course); - - echo "update ok"; - - exit; - } - - /** - * @Get("/course/create", name="web.search.create_course") - */ - public function createCourseAction() - { - $indexer = new \App\Library\Indexer\Course(); - - $courseRepo = new \App\Repos\Course(); - - $course = $courseRepo->findById(1); - - $indexer->addIndex($course); - - echo "create ok"; - - exit; - } - - /** - * @Get("/course/delete", name="web.search.delete_course") - */ - public function deleteCourseAction() - { - $indexer = new \App\Library\Indexer\Course(); - - $courseRepo = new \App\Repos\Course(); - - $course = $courseRepo->findById(1); - - $indexer->deleteIndex($course); - - echo "delete ok"; - + $service = new CourseSearch(); + dd($service->handle()); exit; } diff --git a/app/Http/Web/Services/Index.php b/app/Http/Web/Services/Index.php index 94fb2bf0..38c68ada 100644 --- a/app/Http/Web/Services/Index.php +++ b/app/Http/Web/Services/Index.php @@ -7,6 +7,7 @@ use App\Caches\IndexLiveList as IndexLiveListCache; use App\Caches\IndexNewCourseList as IndexNewCourseListCache; use App\Caches\IndexSlideList as IndexSlideListCache; use App\Caches\IndexVipCourseList as IndexVipCourseListCache; +use App\Models\Slide as SlideModel; class Index extends Service { @@ -15,7 +16,36 @@ class Index extends Service { $cache = new IndexSlideListCache(); - return $cache->get(); + /** + * @var array $slides + */ + $slides = $cache->get(); + + if (!$slides) return []; + + foreach ($slides as $key => $slide) { + switch ($slide['target']) { + case SlideModel::TARGET_COURSE: + $slides[$key]['url'] = $this->url->get([ + 'for' => 'web.course.show', + 'id' => $slide['content'], + ]); + break; + case SlideModel::TARGET_PAGE: + $slides[$key]['url'] = $this->url->get([ + 'for' => 'web.page.show', + 'id' => $slide['content'], + ]); + break; + case SlideModel::TARGET_LINK: + $slides[$key]['url'] = $slide['content']; + break; + default: + break; + } + } + + return $slides; } public function getLives() diff --git a/app/Http/Web/Views/index/index.volt b/app/Http/Web/Views/index/index.volt index d44e0272..65038bc6 100644 --- a/app/Http/Web/Views/index/index.volt +++ b/app/Http/Web/Views/index/index.volt @@ -2,18 +2,77 @@ {% block content %} - {%- macro model_info(value) %} - {% if value == 'vod' %} - 点播{{ request.get('id') }} - {% elseif value == 'live' %} - 直播 - {% elseif value == 'read' %} - 图文 - {% endif %} + {%- macro category_courses(courses) %} +
+
    + {% for category in courses %} +
  • {{ category.name }}
  • + {% endfor %} +
+
+ {% for category in courses %} +
+ {% for course in category.courses %} +
+
+
{{ course.title }}
+
+
+ {% endfor %} +
+ {% endfor %} +
+
{%- endmacro %} -
{{ model_info('vod') }}
-

I am body

-

ID:{{ request.get('id') }}

+
+ +
+ +
+
新上课程
+
+ {{ category_courses(new_courses) }} +
+
+ +
+
免费课程
+
+ {{ category_courses(free_courses) }} +
+
+ +
+
会员课程
+
+ {{ category_courses(vip_courses) }} +
+
+ +{% endblock %} + +{% block inline_js %} + + {% endblock %} \ No newline at end of file diff --git a/app/Http/Web/Views/partials/footer.volt b/app/Http/Web/Views/partials/footer.volt index 49325c9a..2550a068 100644 --- a/app/Http/Web/Views/partials/footer.volt +++ b/app/Http/Web/Views/partials/footer.volt @@ -1 +1,14 @@ -

I am footer

+
+ {% for nav in site_navs.bottom %} + {{ nav.name }} + {% endfor %} +
+ \ No newline at end of file diff --git a/app/Http/Web/Views/partials/header.volt b/app/Http/Web/Views/partials/header.volt index b5659ae0..e69863be 100644 --- a/app/Http/Web/Views/partials/header.volt +++ b/app/Http/Web/Views/partials/header.volt @@ -1 +1,54 @@ -

I am header

\ No newline at end of file + + +
+ +
+ + + +
+ {% if auth_user %} + + {% else %} + + {% endif %} +
diff --git a/app/Http/Web/Views/templates/base.volt b/app/Http/Web/Views/templates/base.volt index 319aba68..a6794222 100644 --- a/app/Http/Web/Views/templates/base.volt +++ b/app/Http/Web/Views/templates/base.volt @@ -1,29 +1,42 @@ - + {{ site_seo.getTitle() }} - {{ icon_link("favicon.ico") }} - {{ css_link("lib/layui/css/layui.css") }} - {{ css_link("web/css/common.css") }} + {{ icon_link('favicon.ico') }} + {{ css_link('lib/layui/css/layui.css') }} + {{ css_link('web/css/common.css') }} {% block link_css %}{% endblock %} {% block inline_css %}{% endblock %} + +
{% block content %}{% endblock %}
+ -{{ js_include("lib/layui/layui.js") }} + +{{ js_include('lib/layui/layui.js') }} + + + {% block include_js %}{% endblock %} + {% block inline_js %}{% endblock %} + \ No newline at end of file diff --git a/app/Library/Paginator/Adapter/XunSearch.php b/app/Library/Paginator/Adapter/XunSearch.php new file mode 100644 index 00000000..c97fdd4c --- /dev/null +++ b/app/Library/Paginator/Adapter/XunSearch.php @@ -0,0 +1,155 @@ + + * use App\Library\Paginator\Adapter\XunSearch; + * + * $paginator = new XunSearch( + * [ + * "xs" => $xs, + * "query" => $query, + * "highlight" => $highlight, + * "page" => $page, + * "limit" => $limit, + * ] + * ); + * + */ +class XunSearch extends PaginatorAdapter +{ + + protected $config; + + protected $url; + + protected $params = []; + + public function __construct(array $config) + { + if (!isset($config['xs']) || ($config['xs'] instanceof \XS) == false) { + throw new PaginatorException('Invalid xs parameter'); + } + + if (empty($config['query'])) { + throw new PaginatorException('Invalid query parameter'); + } + + if (empty($config['page']) || $config['page'] != intval($config['page'])) { + throw new PaginatorException('Invalid page parameter'); + } + + if (empty($config['limit']) || $config['limit'] != intval($config['limit'])) { + throw new PaginatorException('Invalid limit parameter'); + } + + if (isset($config['highlight']) && !is_array($config['highlight'])) { + throw new PaginatorException('Invalid highlight parameter'); + } + + $this->config = $config; + $this->_page = $config['page'] ?? 1; + $this->_limitRows = $config['limit'] ?? 15; + } + + public function paginate() + { + /** + * @var \XS $xs + */ + $xs = $this->config['xs']; + + $page = $this->_page; + $limit = $this->_limitRows; + $offset = ($page - 1) * $limit; + + $search = $xs->getSearch(); + + $docs = $search->setQuery($this->config['query']) + ->setLimit($limit, $offset) + ->search(); + + $totalCount = $search->getLastCount(); + + $fields = array_keys($xs->getAllFields()); + + $items = []; + + foreach ($docs as $doc) { + $item = []; + foreach ($fields as $field) { + if (in_array($field, $this->config['highlight'])) { + $item[$field] = $search->highlight($doc->{$field}); + } else { + $item[$field] = $doc->{$field}; + } + } + $items[] = $item; + } + + $totalPages = ceil($totalCount / $limit); + + $pager = new \stdClass(); + + $pager->first = 1; + $pager->previous = $page > 1 ? $page - 1 : 1; + $pager->next = $page < $totalPages ? $page + 1 : $page; + $pager->last = $totalPages; + $pager->total_items = $totalCount; + $pager->items = $items; + + $this->initParams(); + + $pager->first = $this->buildPageUrl($pager->first); + $pager->previous = $this->buildPageUrl($pager->previous); + $pager->next = $this->buildPageUrl($pager->next); + $pager->last = $this->buildPageUrl($pager->last); + + return $pager; + } + + public function getPaginate() + { + return $this->paginate(); + } + + protected function initParams() + { + $request = new HttpRequest(); + + $params = $request->get(); + + if ($params) { + foreach ($params as $key => $value) { + if (strlen($value) == 0) { + unset($params[$key]); + } + } + } + + $this->params = $params; + + if (!empty($this->params['_url'])) { + $this->url = $this->params['_url']; + unset($this->params['_url']); + } else { + $this->url = $request->get('_url'); + } + } + + protected function buildPageUrl($page) + { + $this->params['page'] = $page; + + return $this->url . '?' . http_build_query($this->params); + } + +} diff --git a/app/Models/Slide.php b/app/Models/Slide.php index 9c52a0b7..6353cd79 100644 --- a/app/Models/Slide.php +++ b/app/Models/Slide.php @@ -36,6 +36,13 @@ class Slide extends Model */ public $cover; + /** + * 背景色 + * + * @var string + */ + public $bg_color; + /** * 摘要 * diff --git a/app/Services/Frontend/Page/PageInfo.php b/app/Services/Frontend/Page/PageInfo.php new file mode 100644 index 00000000..e24f004d --- /dev/null +++ b/app/Services/Frontend/Page/PageInfo.php @@ -0,0 +1,32 @@ +checkPageCache($id); + + return $this->handlePage($page); + } + + protected function handlePage(PageModel $page) + { + return [ + 'id' => $page->id, + 'title' => $page->title, + 'content' => $page->content, + 'create_time' => $page->create_time, + 'update_time' => $page->update_time, + ]; + } + +} diff --git a/app/Services/Frontend/PageTrait.php b/app/Services/Frontend/PageTrait.php new file mode 100644 index 00000000..5ea86118 --- /dev/null +++ b/app/Services/Frontend/PageTrait.php @@ -0,0 +1,24 @@ +checkPage($id); + } + + public function checkPageCache($id) + { + $validator = new PageValidator(); + + return $validator->checkPageCache($id); + } + +} diff --git a/app/Services/Frontend/Search/CourseSearch.php b/app/Services/Frontend/Search/CourseSearch.php new file mode 100644 index 00000000..0eb73925 --- /dev/null +++ b/app/Services/Frontend/Search/CourseSearch.php @@ -0,0 +1,37 @@ +getParams(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $courseSearcher = new CourseSearcherService(); + + $paginator = new XunSearchPaginator([ + 'xs' => $courseSearcher->getXS(), + 'highlight' => $courseSearcher->getHighlightFields(), + 'query' => $params['query'], + 'page' => $page, + 'limit' => $limit, + ]); + + $pager = $paginator->getPaginate(); + + dd($pager); + } + +} diff --git a/app/Services/Search/CourseHandler.php b/app/Services/Search/CourseSearcher.php similarity index 89% rename from app/Services/Search/CourseHandler.php rename to app/Services/Search/CourseSearcher.php index f9322e20..f3c5493d 100644 --- a/app/Services/Search/CourseHandler.php +++ b/app/Services/Search/CourseSearcher.php @@ -4,7 +4,7 @@ namespace App\Services\Search; use Phalcon\Mvc\User\Component; -class CourseHandler extends Component +class CourseSearcher extends Component { /** @@ -29,6 +29,16 @@ class CourseHandler extends Component return $this->xs; } + /** + * 获取高亮字段 + * + * @return array + */ + public function getHighlightFields() + { + return ['title', 'summary']; + } + /** * 搜索课程 * diff --git a/app/Validators/Slide.php b/app/Validators/Slide.php index cda6b48b..144b5d9a 100644 --- a/app/Validators/Slide.php +++ b/app/Validators/Slide.php @@ -66,6 +66,17 @@ class Slide extends Validator return $value; } + public function checkBgColor($bgColor) + { + $value = $this->filter->sanitize($bgColor, ['trim', 'string']); + + if (!preg_match('/^#[0-9a-fA-F]{6}$/', $bgColor)) { + throw new BadRequestException('slide.invalid_bg_color'); + } + + return $value; + } + public function checkTarget($target) { $list = SlideModel::targetTypes();