diff --git a/app/Caches/IndexFeaturedCourseList.php b/app/Caches/IndexFeaturedCourseList.php new file mode 100644 index 00000000..3f84919f --- /dev/null +++ b/app/Caches/IndexFeaturedCourseList.php @@ -0,0 +1,118 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'index_featured_course_list'; + } + + public function getContent($id = null) + { + $categoryLimit = 5; + + $courseLimit = 8; + + $categories = $this->findCategories($categoryLimit); + + if ($categories->count() == 0) { + return []; + } + + $result = []; + + foreach ($categories as $category) { + + $item = []; + + $item['category'] = [ + 'id' => $category->id, + 'name' => $category->name, + ]; + + $item['courses'] = []; + + $courses = $this->findCategoryCourses($category->id, $courseLimit); + + if ($courses->count() == 0) { + continue; + } + + $categoryCourses = []; + + foreach ($courses as $course) { + $categoryCourses[] = [ + 'id' => $course->id, + 'title' => $course->title, + 'cover' => $course->cover, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + 'model' => $course->model, + 'level' => $course->level, + 'user_count' => $course->user_count, + 'lesson_count' => $course->lesson_count, + ]; + } + + $item['courses'] = $categoryCourses; + + $result[] = $item; + } + + return $result; + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|CategoryModel[] + */ + protected function findCategories($limit = 5) + { + return CategoryModel::query() + ->where('type = :type:', ['type' => CategoryModel::TYPE_COURSE]) + ->andWhere('level = 1 AND published = 1') + ->orderBy('priority ASC') + ->limit($limit) + ->execute(); + } + + /** + * @param int $categoryId + * @param int $limit + * @return ResultsetInterface|Resultset|CourseModel[] + */ + protected function findCategoryCourses($categoryId, $limit = 8) + { + $categoryService = new CategoryService(); + + $categoryIds = $categoryService->getChildCategoryIds($categoryId); + + return CourseModel::query() + ->inWhere('category_id', $categoryIds) + ->andWhere('published = 1') + ->andWhere('featured = 1') + ->orderBy('id DESC') + ->limit($limit) + ->execute(); + } + +} diff --git a/app/Caches/IndexSimpleFeaturedCourseList.php b/app/Caches/IndexSimpleFeaturedCourseList.php new file mode 100644 index 00000000..c7201cc7 --- /dev/null +++ b/app/Caches/IndexSimpleFeaturedCourseList.php @@ -0,0 +1,70 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'index_simple_featured_course_list'; + } + + public function getContent($id = null) + { + $limit = 8; + + $courses = $this->findCourses($limit); + + if ($courses->count() == 0) { + return []; + } + + $result = []; + + foreach ($courses as $course) { + $result[] = [ + 'id' => $course->id, + 'title' => $course->title, + 'cover' => $course->cover, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + 'model' => $course->model, + 'level' => $course->level, + 'user_count' => $course->user_count, + 'lesson_count' => $course->lesson_count, + ]; + } + + return $result; + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|CourseModel[] + */ + protected function findCourses($limit = 8) + { + return CourseModel::query() + ->where('published = 1') + ->andWhere('featured = 1') + ->orderBy('id DESC') + ->limit($limit) + ->execute(); + } + +} diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index 10eaf27e..94525b17 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -172,6 +172,10 @@ class Course extends Service } } + if (isset($post['featured'])) { + $data['featured'] = $validator->checkFeatureStatus($post['featured']); + } + if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); if ($post['published'] == 1) { diff --git a/app/Http/Admin/Views/course/list.volt b/app/Http/Admin/Views/course/list.volt index b4ed45a7..4a2a84ce 100644 --- a/app/Http/Admin/Views/course/list.volt +++ b/app/Http/Admin/Views/course/list.volt @@ -59,6 +59,7 @@ + @@ -67,6 +68,7 @@ 课时数 用户数 价格 + 推荐 发布 操作 @@ -101,6 +103,7 @@

市场:{{ '¥%0.2f'|format(item.market_price) }}

会员:{{ '¥%0.2f'|format(item.vip_price) }}

+
@@ -129,4 +132,45 @@ {{ partial('partials/pager') }} +{% endblock %} + +{% block inline_js %} + + + {% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/course/search.volt b/app/Http/Admin/Views/course/search.volt index 9a8d0935..1076d9a4 100644 --- a/app/Http/Admin/Views/course/search.volt +++ b/app/Http/Admin/Views/course/search.volt @@ -54,6 +54,13 @@
+
+ +
+ + +
+
diff --git a/app/Http/Api/Controllers/IndexController.php b/app/Http/Api/Controllers/IndexController.php index 1aba3a64..d250857a 100644 --- a/app/Http/Api/Controllers/IndexController.php +++ b/app/Http/Api/Controllers/IndexController.php @@ -2,6 +2,7 @@ namespace App\Http\Api\Controllers; +use App\Caches\IndexSimpleFeaturedCourseList; use App\Caches\IndexSimpleFreeCourseList; use App\Caches\IndexSimpleNewCourseList; use App\Caches\IndexSimpleVipCourseList; @@ -25,6 +26,18 @@ class IndexController extends Controller return $this->jsonSuccess(['slides' => $slides]); } + /** + * @Get("/courses/featured", name="api.index.featured_courses") + */ + public function featuredCoursesAction() + { + $cache = new IndexSimpleFeaturedCourseList(); + + $courses = $cache->get(); + + return $this->jsonSuccess(['courses' => $courses]); + } + /** * @Get("/courses/new", name="api.index.new_courses") */ diff --git a/app/Http/Home/Controllers/IndexController.php b/app/Http/Home/Controllers/IndexController.php index ea099b71..04375285 100644 --- a/app/Http/Home/Controllers/IndexController.php +++ b/app/Http/Home/Controllers/IndexController.php @@ -47,6 +47,7 @@ class IndexController extends Controller $this->view->pick('index/full'); $this->view->setVar('lives', $service->getLives()); $this->view->setVar('slides', $service->getSlides()); + $this->view->setVar('featured_courses', $service->getFeaturedCourses()); $this->view->setVar('new_courses', $service->getNewCourses()); $this->view->setVar('free_courses', $service->getFreeCourses()); $this->view->setVar('vip_courses', $service->getVipCourses()); @@ -59,6 +60,7 @@ class IndexController extends Controller $this->view->pick('index/simple'); $this->view->setVar('lives', $service->getLives()); $this->view->setVar('slides', $service->getSlides()); + $this->view->setVar('featured_courses', $service->getSimpleFeaturedCourses()); $this->view->setVar('new_courses', $service->getSimpleNewCourses()); $this->view->setVar('free_courses', $service->getSimpleFreeCourses()); $this->view->setVar('vip_courses', $service->getSimpleVipCourses()); diff --git a/app/Http/Home/Services/Index.php b/app/Http/Home/Services/Index.php index fc46e91d..7e42cb72 100644 --- a/app/Http/Home/Services/Index.php +++ b/app/Http/Home/Services/Index.php @@ -2,9 +2,11 @@ namespace App\Http\Home\Services; +use App\Caches\IndexFeaturedCourseList; use App\Caches\IndexFreeCourseList; use App\Caches\IndexLiveList; use App\Caches\IndexNewCourseList; +use App\Caches\IndexSimpleFeaturedCourseList; use App\Caches\IndexSimpleFreeCourseList; use App\Caches\IndexSimpleNewCourseList; use App\Caches\IndexSimpleVipCourseList; @@ -61,6 +63,15 @@ class Index extends Service return $cache->get(); } + public function getFeaturedCourses() + { + $cache = new IndexFeaturedCourseList(); + + $courses = $cache->get(); + + return $this->handleCategoryCourses($courses); + } + public function getNewCourses() { $cache = new IndexNewCourseList(); @@ -95,6 +106,13 @@ class Index extends Service return $cache->get(); } + public function getSimpleFeaturedCourses() + { + $cache = new IndexSimpleFeaturedCourseList(); + + return $cache->get(); + } + public function getSimpleFreeCourses() { $cache = new IndexSimpleFreeCourseList(); diff --git a/app/Http/Home/Views/index/full.volt b/app/Http/Home/Views/index/full.volt index e80b6783..00786429 100644 --- a/app/Http/Home/Views/index/full.volt +++ b/app/Http/Home/Views/index/full.volt @@ -45,6 +45,13 @@
+
+
推荐课程
+
+ {{ category_courses(featured_courses) }} +
+
+
新上课程
diff --git a/app/Http/Home/Views/index/simple.volt b/app/Http/Home/Views/index/simple.volt index 1bf213b4..78857d29 100644 --- a/app/Http/Home/Views/index/simple.volt +++ b/app/Http/Home/Views/index/simple.volt @@ -30,6 +30,13 @@
+
+
推荐课程
+
+ {{ show_courses(featured_courses) }} +
+
+
新上课程
diff --git a/app/Models/Course.php b/app/Models/Course.php index aaa22404..282da552 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -165,6 +165,13 @@ class Course extends Model */ public $attrs; + /** + * 推荐标识 + * + * @var int + */ + public $featured; + /** * 发布标识 * @@ -377,6 +384,7 @@ class Course extends Model 'rating' => '好评', 'latest' => '最新', 'popular' => '最热', + 'featured' => '推荐', 'free' => '免费', ]; } diff --git a/app/Repos/Course.php b/app/Repos/Course.php index 4dc5a34a..5b774458 100644 --- a/app/Repos/Course.php +++ b/app/Repos/Course.php @@ -67,6 +67,10 @@ class Course extends Repository } } + if (isset($where['featured'])) { + $builder->andWhere('featured = :featured:', ['featured' => $where['featured']]); + } + if (isset($where['published'])) { $builder->andWhere('published = :published:', ['published' => $where['published']]); } @@ -77,6 +81,8 @@ class Course extends Repository if ($sort == 'free') { $builder->andWhere('market_price = 0'); + } elseif ($sort == 'featured') { + $builder->andWhere('featured = 1'); } elseif ($sort == 'vip_discount') { $builder->andWhere('vip_price < market_price'); $builder->andWhere('vip_price > 0'); diff --git a/app/Validators/Course.php b/app/Validators/Course.php index 65b157a7..efa5288f 100644 --- a/app/Validators/Course.php +++ b/app/Validators/Course.php @@ -212,6 +212,15 @@ class Course extends Validator return $expiry; } + public function checkFeatureStatus($status) + { + if (!in_array($status, [0, 1])) { + throw new BadRequestException('course.invalid_feature_status'); + } + + return $status; + } + public function checkPublishStatus($status) { if (!in_array($status, [0, 1])) { diff --git a/config/errors.php b/config/errors.php index 4ccb1907..d599a268 100644 --- a/config/errors.php +++ b/config/errors.php @@ -114,6 +114,7 @@ $error['course.invalid_vip_price'] = '无效的会员价格(范围:0-10000 $error['course.invalid_compare_price'] = '无效的比较定价(会员价格高于市场价格)'; $error['course.invalid_study_expiry'] = '无效的学习期限'; $error['course.invalid_refund_expiry'] = '无效的退款期限'; +$error['course.invalid_feature_status'] = '无效的推荐状态'; $error['course.invalid_publish_status'] = '无效的发布状态'; $error['course.pub_chapter_not_found'] = '尚未发现已发布的课时'; $error['course.pub_chapter_not_enough'] = '已发布的课时太少(小于30%)'; diff --git a/db/migrations/20201227081614_schema_202012271615.php b/db/migrations/20201227081614_schema_202012271615.php new file mode 100644 index 00000000..d58b73ad --- /dev/null +++ b/db/migrations/20201227081614_schema_202012271615.php @@ -0,0 +1,21 @@ +table('kg_course') + ->addColumn('featured', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '推荐标识', + 'after' => 'attrs', + ]) + ->save(); + } + +}