diff --git a/app/Transformers/Transformer.php b/app/Builders/Builder.php similarity index 73% rename from app/Transformers/Transformer.php rename to app/Builders/Builder.php index 3aef33b3..a9b7ab93 100644 --- a/app/Transformers/Transformer.php +++ b/app/Builders/Builder.php @@ -1,10 +1,10 @@ $category['id'], + 'name' => $category['name'], + 'priority' => $category['priority'], + 'children' => [], + ]; + } else { + $key = $category['parent_id']; + $list[$key]['children'][] = [ + 'id' => $category['id'], + 'name' => $category['name'], + 'priority' => $category['priority'], + ]; + } + } + + usort($list, function ($a, $b) { + return $a['priority'] > $b['priority']; + }); + + foreach ($list as $key => $value) { + usort($list[$key]['children'], function ($a, $b) { + return $a['priority'] > $b['priority']; + }); + } + + return $list; + } + +} diff --git a/app/Builders/Chapter.php b/app/Builders/Chapter.php new file mode 100644 index 00000000..d55ee8cc --- /dev/null +++ b/app/Builders/Chapter.php @@ -0,0 +1,19 @@ + $chapter['id'], + 'title' => $chapter['title'], + 'summary' => $chapter['summary'], + 'priority' => $chapter['priority'], + 'children' => [], + ]; + } else { + $key = $chapter['parent_id']; + $list[$key]['children'][] = $this->handleChapter($chapter); + } + } + + usort($list, function ($a, $b) { + return $a['priority'] > $b['priority']; + }); + + foreach ($list as $key => $value) { + usort($list[$key]['children'], function ($a, $b) { + return $a['priority'] > $b['priority']; + }); + } + + return $list; + } + + /** + * @param array $chapter + * @return array + */ + protected function handleChapter($chapter) + { + $attrs = json_decode($chapter['attrs'], true); + + if ($attrs['model'] == CourseModel::MODEL_VOD) { + unset($attrs['file_id'], $attrs['file_status']); + } + + $result = [ + 'id' => $chapter['id'], + 'title' => $chapter['title'], + 'summary' => $chapter['summary'], + 'priority' => $chapter['priority'], + 'free' => $chapter['free'], + 'attrs' => $attrs, + ]; + + return $result; + } + +} diff --git a/app/Builders/Course.php b/app/Builders/Course.php new file mode 100644 index 00000000..39285f1a --- /dev/null +++ b/app/Builders/Course.php @@ -0,0 +1,21 @@ +cover = kg_img_url($course->cover); + + return $course; + } + +} diff --git a/app/Builders/CourseChapterUser.php b/app/Builders/CourseChapterUser.php new file mode 100644 index 00000000..9adece6d --- /dev/null +++ b/app/Builders/CourseChapterUser.php @@ -0,0 +1,86 @@ + $value['duration'], + 'finished' => $value['finished'], + ]; + } + } + + foreach ($chapters as $key => $chapter) { + if ($chapter['parent_id'] > 0) { + $me = [ + 'duration' => $status[$chapter['id']]['duration'] ?? 0, + 'finished' => $status[$chapter['id']]['finished'] ?? 0, + ]; + $chapters[$key]['me'] = $me; + } + } + + return $chapters; + } + + /** + * @param array $chapter + * @return array + */ + protected function handleChapter($chapter) + { + + $attrs = json_decode($chapter['attrs'], true); + + $me = $chapter['me'] ?? []; + + $clickable = $chapter['published']; + + if ($attrs['model'] == CourseModel::MODEL_VOD) { + unset($attrs['file_id'], $attrs['file_status']); + } + + /** + * 直播前后半小时缓冲区间可用 + */ + if ($attrs['model'] == CourseModel::MODEL_LIVE) { + $caseA = $attrs['start_time'] - time() < 1800; + $caseB = time() - $attrs['end_time'] < 1800; + if ($caseA && $caseB) { + $clickable = 1; + } + } + + + $result = [ + 'id' => $chapter['id'], + 'title' => $chapter['title'], + 'summary' => $chapter['summary'], + 'free' => $chapter['free'], + 'clickable' => $clickable, + 'attrs' => $attrs, + 'me' => $me, + ]; + + return $result; + } + +} diff --git a/app/Builders/CourseList.php b/app/Builders/CourseList.php new file mode 100644 index 00000000..20dc5010 --- /dev/null +++ b/app/Builders/CourseList.php @@ -0,0 +1,126 @@ + $course['id'], + 'model' => $course['model'], + 'title' => $course['title'], + 'summary' => $course['summary'], + 'cover' => $course['cover'], + 'market_price' => (float)$course['market_price'], + 'vip_price' => (float)$course['vip_price'], + 'expiry' => $course['expiry'], + 'level' => $course['level'], + 'score' => $course['score'], + 'attrs' => $course['attrs'], + 'categories' => $course['categories'], + 'user_count' => $course['user_count'], + 'lesson_count' => $course['lesson_count'], + 'thread_count' => $course['thread_count'], + 'review_count' => $course['review_count'], + 'favorite_count' => $course['favorite_count'], + ]; + } + + return $result; + } + + public function handleCategories($courses) + { + $categories = $this->getCategories($courses); + + foreach ($courses as $key => $course) { + $courses[$key]['categories'] = $categories[$course['id']] ?? []; + } + + return $courses; + } + + public function handleUsers($courses) + { + $users = $this->getUsers($courses); + + foreach ($courses as $key => $course) { + $courses[$key]['user'] = $users[$course['user_id']] ?? []; + } + + return $courses; + } + + protected function getCategories($courses) + { + $categoryListCache = new CategoryListCache(); + + $categoryList = $categoryListCache->get(); + + if (!$categoryList) { + return []; + } + + $mapping = []; + + foreach ($categoryList as $category) { + $mapping[$category['id']] = [ + 'id' => $category['id'], + 'name' => $category['name'], + ]; + } + + $courseIds = kg_array_column($courses, 'id'); + + $courseCategoryRepo = new CourseCategoryRepo(); + + $relations = $courseCategoryRepo->findByCourseIds($courseIds); + + $result = []; + + foreach ($relations as $relation) { + $categoryId = $relation->category_id; + $courseId = $relation->course_id; + $result[$courseId][] = $mapping[$categoryId] ?? []; + } + + return $result; + } + + protected function getUsers($courses) + { + $ids = kg_array_column($courses, 'user_id'); + + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); + + $imgBaseUrl = kg_img_base_url(); + + $result = []; + + foreach ($users->toArray() as $user) { + $user['avatar'] = $imgBaseUrl . $user['avatar']; + $result[$user['id']] = $user; + } + + return $result; + } + +} diff --git a/app/Transformers/CourseUserList.php b/app/Builders/CourseUserList.php similarity index 95% rename from app/Transformers/CourseUserList.php rename to app/Builders/CourseUserList.php index d1edc302..8188ee96 100644 --- a/app/Transformers/CourseUserList.php +++ b/app/Builders/CourseUserList.php @@ -1,11 +1,11 @@ $nav['id'], + 'name' => $nav['name'], + 'priority' => $nav['priority'], + 'children' => [], + ]; + } else { + $key = $nav['parent_id']; + $list[$key]['children'][] = [ + 'id' => $nav['id'], + 'name' => $nav['name'], + 'priority' => $nav['priority'], + 'target' => $nav['target'], + 'url' => $nav['url'], + ]; + } + } + + usort($list, function ($a, $b) { + return $a['priority'] > $b['priority']; + }); + + foreach ($list as $key => $value) { + usort($list[$key]['children'], function ($a, $b) { + return $a['priority'] > $b['priority']; + }); + } + + return $list; + } + +} diff --git a/app/Transformers/OrderList.php b/app/Builders/OrderList.php similarity index 97% rename from app/Transformers/OrderList.php rename to app/Builders/OrderList.php index 8ab4fdd3..c4c9a557 100644 --- a/app/Transformers/OrderList.php +++ b/app/Builders/OrderList.php @@ -1,11 +1,11 @@ findByIds($ids, ['id', 'name', 'avatar'])->toArray(); + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); $result = []; - foreach ($users as $user) { + $imgBaseUrl = kg_img_base_url(); + + foreach ($users->toArray() as $user) { + + $user['avatar'] = $imgBaseUrl . $user['avatar']; + $result[$user['id']] = $user; } diff --git a/app/Transformers/ReviewList.php b/app/Builders/ReviewList.php similarity index 76% rename from app/Transformers/ReviewList.php rename to app/Builders/ReviewList.php index 7c397448..c48e5826 100644 --- a/app/Transformers/ReviewList.php +++ b/app/Builders/ReviewList.php @@ -1,11 +1,11 @@ getCourses($reviews); foreach ($reviews as $key => $review) { - $reviews[$key]['course'] = $courses[$review['course_id']]; + $reviews[$key]['course'] = $courses[$review['course_id']] ?? []; } return $reviews; @@ -24,7 +24,7 @@ class ReviewList extends Transformer $users = $this->getUsers($reviews); foreach ($reviews as $key => $review) { - $reviews[$key]['user'] = $users[$review['user_id']]; + $reviews[$key]['user'] = $users[$review['user_id']] ?? []; } return $reviews; @@ -36,11 +36,11 @@ class ReviewList extends Transformer $courseRepo = new CourseRepo(); - $courses = $courseRepo->findByIds($ids, ['id', 'title'])->toArray(); + $courses = $courseRepo->findByIds($ids, ['id', 'title']); $result = []; - foreach ($courses as $course) { + foreach ($courses->toArray() as $course) { $result[$course['id']] = $course; } @@ -53,11 +53,14 @@ class ReviewList extends Transformer $userRepo = new UserRepo(); - $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); $result = []; - foreach ($users as $user) { + $imgBaseUrl = kg_img_base_url(); + + foreach ($users->toArray() as $user) { + $user['avatar'] = $imgBaseUrl . $user['avatar']; $result[$user['id']] = $user; } diff --git a/app/Builders/SlideList.php b/app/Builders/SlideList.php new file mode 100644 index 00000000..0174c281 --- /dev/null +++ b/app/Builders/SlideList.php @@ -0,0 +1,19 @@ + $slide) { + $slides[$key]['cover'] = $imgBaseUrl . $slide['cover']; + } + + return $slides; + } + +} diff --git a/app/Transformers/TradeList.php b/app/Builders/TradeList.php similarity index 91% rename from app/Transformers/TradeList.php rename to app/Builders/TradeList.php index 20aa6910..909463e2 100644 --- a/app/Transformers/TradeList.php +++ b/app/Builders/TradeList.php @@ -1,10 +1,10 @@ avatar = kg_img_url($user->avatar); + + return $user; + } + +} diff --git a/app/Transformers/UserList.php b/app/Builders/UserList.php similarity index 82% rename from app/Transformers/UserList.php rename to app/Builders/UserList.php index e2648c4d..1bca819f 100644 --- a/app/Transformers/UserList.php +++ b/app/Builders/UserList.php @@ -1,13 +1,24 @@ $user) { + $users[$key]['avatar'] = $imgBaseUrl . $user['avatar']; + } + + return $users; + } + public function handleAdminRoles($users) { $roles = $this->getAdminRoles($users); diff --git a/app/Caches/Cache.php b/app/Caches/Cache.php index ed592a0f..4db6c76c 100644 --- a/app/Caches/Cache.php +++ b/app/Caches/Cache.php @@ -2,13 +2,11 @@ namespace App\Caches; -use Phalcon\Mvc\User\Component as UserComponent; - -abstract class Cache extends UserComponent +abstract class Cache extends \Phalcon\Mvc\User\Component { /** - * @var \Phalcon\Cache\Backend + * @var \Phalcon\Cache\Backend\Redis */ protected $cache; @@ -20,18 +18,26 @@ abstract class Cache extends UserComponent /** * 获取缓存内容 * - * @param mixed $params + * @param mixed $id * @return mixed */ - public function get($params = null) + public function get($id = null) { - $key = $this->getKey($params); - $content = $this->cache->get($key); - $lifetime = $this->getLifetime(); + $key = $this->getKey($id); + + $content = $this->cache->get($key); + + if (!$this->cache->exists($key)) { + + $content = $this->getContent($id); + + /** + * 原始内容为空,设置较短的生存时间,简单防止穿透 + */ + $lifetime = $content ? $this->getLifetime() : 5 * 60; - if (!$content) { - $content = $this->getContent($params); $this->cache->save($key, $content, $lifetime); + $content = $this->cache->get($key); } @@ -41,35 +47,48 @@ abstract class Cache extends UserComponent /** * 删除缓存内容 * - * @param mixed $params + * @param mixed $id */ - public function delete($params = null) + public function delete($id = null) { - $key = $this->getKey($params); + $key = $this->getKey($id); + $this->cache->delete($key); } + /** + * 重建缓存内容 + * + * @param mixed $id + */ + public function rebuild($id = null) + { + $this->delete($id); + + $this->get($id); + } + /** * 获取缓存有效期 * - * @return integer + * @return int */ - abstract protected function getLifetime(); + abstract public function getLifetime(); /** * 获取键值 * - * @param mixed $params + * @param mixed $id * @return string */ - abstract protected function getKey($params = null); + abstract public function getKey($id = null); /** * 获取原始内容 * - * @param mixed $params + * @param mixed $id * @return mixed */ - abstract protected function getContent($params = null); + abstract public function getContent($id = null); } diff --git a/app/Caches/Category.php b/app/Caches/Category.php index f3027f75..a53e17ac 100644 --- a/app/Caches/Category.php +++ b/app/Caches/Category.php @@ -1,57 +1,35 @@ getById($id); - - if (!$result) { - throw new ModelNotFoundException('category.not_found'); - } - - return $result; - } - - public function get($id) - { - $cacheOptions = [ - 'key' => $this->getKey($id), - 'lifetime' => $this->getLifetime(), - ]; - - $result = CategoryModel::query() - ->where('id = :id:', ['id' => $id]) - ->cache($cacheOptions) - ->execute() - ->getFirst(); - - return $result; - } - - public function delete($id) - { - $key = $this->getKey($id); - - $this->modelsCache->delete($key); - } - - public function getKey($id) - { - return "category:{$id}"; - } + protected $lifetime = 365 * 86400; public function getLifetime() { return $this->lifetime; } + public function getKey($id = null) + { + return "category:{$id}"; + } + + public function getContent($id = null) + { + $categoryRepo = new CategoryRepo(); + + $category = $categoryRepo->findById($id); + + if (!$category) { + return new \stdClass(); + } + + return $category; + } + } diff --git a/app/Caches/CategoryList.php b/app/Caches/CategoryList.php new file mode 100644 index 00000000..a44f708d --- /dev/null +++ b/app/Caches/CategoryList.php @@ -0,0 +1,40 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'category_list'; + } + + /** + * @param null $id + * @return array + */ + public function getContent($id = null) + { + $categories = CategoryModel::query() + ->columns(['id', 'parent_id', 'name', 'priority', 'level', 'path']) + ->where('published = 1 AND deleted = 0') + ->execute(); + + if ($categories->count() == 0) { + return []; + } + + return $categories->toArray(); + } + +} diff --git a/app/Caches/CategoryTreeList.php b/app/Caches/CategoryTreeList.php new file mode 100644 index 00000000..b14ed69e --- /dev/null +++ b/app/Caches/CategoryTreeList.php @@ -0,0 +1,51 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'category_tree'; + } + + public function getContent($id = null) + { + $categories = CategoryModel::query() + ->where('published = 1 AND deleted = 0') + ->execute(); + + if ($categories->count() == 0) { + return []; + } + + return $this->handleContent($categories); + } + + /** + * @param \App\Models\Category[] $categories + * @return array + */ + protected function handleContent($categories) + { + $items = $categories->toArray(); + + $builder = new CategoryTreeListBuilder(); + + $content = $builder->handleTreeList($items); + + return $content; + } + +} diff --git a/app/Caches/Chapter.php b/app/Caches/Chapter.php new file mode 100644 index 00000000..4166fc96 --- /dev/null +++ b/app/Caches/Chapter.php @@ -0,0 +1,35 @@ +lifetime; + } + + public function getKey($id = null) + { + return "chapter:{$id}"; + } + + public function getContent($id = null) + { + $chapterRepo = new ChapterRepo(); + + $chapter = $chapterRepo->findById($id); + + if (!$chapter) { + return new \stdClass(); + } + + return $chapter; + } + +} diff --git a/app/Caches/ChapterCounter.php b/app/Caches/ChapterCounter.php new file mode 100644 index 00000000..4a8e1278 --- /dev/null +++ b/app/Caches/ChapterCounter.php @@ -0,0 +1,40 @@ +lifetime; + } + + public function getKey($id = null) + { + return "chapter_counter:{$id}"; + } + + public function getContent($id = null) + { + $chapterRepo = new ChapterRepo(); + + $chapter = $chapterRepo->findById($id); + + if (!$chapter) return []; + + $content = [ + 'lesson_count' => $chapter->lesson_count, + 'user_count' => $chapter->user_count, + 'comment_count' => $chapter->comment_count, + 'like_count' => $chapter->like_count, + ]; + + return $content; + } + +} diff --git a/app/Caches/Config.php b/app/Caches/Config.php index d5c788ac..4b13dd6a 100644 --- a/app/Caches/Config.php +++ b/app/Caches/Config.php @@ -13,19 +13,21 @@ class Config extends Cache * 获取某组配置项 * * @param string $section - * @return \stdClass|null + * @return array */ public function getSectionConfig($section) { $items = $this->get(); - if (!$items) return; + $result = []; - $result = new \stdClass(); + if (!$items) { + return $result; + } foreach ($items as $item) { - if ($item->section == $section) { - $result->{$item->item_key} = $item->item_value; + if ($item['section'] == $section) { + $result[$item['item_key']] = $item['item_value']; } } @@ -43,28 +45,28 @@ class Config extends Cache { $config = $this->getSectionConfig($section); - $result = $config->{$key} ?? null; + $result = $config[$key] ?? null; return $result; } - protected function getLifetime() + public function getLifetime() { return $this->lifetime; } - protected function getKey($params = null) + public function getKey($id = null) { - return 'site_config'; + return 'config'; } - protected function getContent($params = null) + public function getContent($id = null) { $configRepo = new ConfigRepo(); $items = $configRepo->findAll(); - return $items; + return $items->toArray(); } } diff --git a/app/Caches/Counter.php b/app/Caches/Counter.php new file mode 100644 index 00000000..3e915d51 --- /dev/null +++ b/app/Caches/Counter.php @@ -0,0 +1,122 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + } + + /** + * 获取缓存内容 + * + * @param mixed $id + * @return array + */ + public function get($id = null) + { + $key = $this->getKey($id); + + $content = $this->redis->hGetAll($key); + + if (!$this->cache->exists($key)) { + + $content = $this->getContent($id); + + $lifetime = $this->getLifetime(); + + /** + * 原始内容为空,设置较短的生存时间,简单防止穿透 + */ + if (!$content) { + + $lifetime = 5 * 60; + + $content = ['default' => 0]; + } + + $this->redis->hMSet($key, $content); + + $this->redis->expire($key, $lifetime); + } + + return $content; + } + + /** + * 删除缓存内容 + * + * @param mixed $id + */ + public function delete($id = null) + { + $key = $this->getKey($id); + + $this->cache->delete($key); + } + + /** + * 重建缓存内容 + * + * @param mixed $id + */ + public function rebuild($id = null) + { + $this->delete($id); + + $this->get($id); + } + + public function increment($id, $hashKey, $value = 1) + { + $key = $this->getKey($id); + + $this->redis->hIncrBy($key, $hashKey, $value); + } + + public function decrement($id, $hashKey, $value = 1) + { + $key = $this->getKey($id); + + $this->redis->hIncrBy($key, $hashKey, 0 - $value); + } + + /** + * 获取缓存有效期 + * + * @return int + */ + abstract public function getLifetime(); + + /** + * 获取键值 + * + * @param mixed $id + * @return string + */ + abstract public function getKey($id = null); + + /** + * 获取原始内容 + * + * @param mixed $id + * @return mixed + */ + abstract public function getContent($id = null); + +} diff --git a/app/Caches/Course.php b/app/Caches/Course.php index 34eb7853..ee0f7009 100644 --- a/app/Caches/Course.php +++ b/app/Caches/Course.php @@ -1,57 +1,35 @@ getById($id); - - if (!$result) { - throw new ModelNotFoundException('course.not_found'); - } - - return $result; - } - - public function get($id) - { - $cacheOptions = [ - 'key' => $this->getKey($id), - 'lifetime' => $this->getLifetime(), - ]; - - $result = CourseModel::query() - ->where('id = :id:', ['id' => $id]) - ->cache($cacheOptions) - ->execute() - ->getFirst(); - - return $result; - } - - public function delete($id) - { - $key = $this->getKey($id); - - $this->modelsCache->delete($key); - } - - public function getKey($id) - { - return "course:{$id}"; - } + protected $lifetime = 7 * 86400; public function getLifetime() { return $this->lifetime; } + public function getKey($id = null) + { + return "course:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($id); + + if (!$course) { + return new \stdClass(); + } + + return $course; + } + } diff --git a/app/Caches/CourseCategoryList.php b/app/Caches/CourseCategoryList.php new file mode 100644 index 00000000..3b72400f --- /dev/null +++ b/app/Caches/CourseCategoryList.php @@ -0,0 +1,53 @@ +lifetime; + } + + public function getKey($id = null) + { + return "course_category_list:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $categories = $courseRepo->findCategories($id); + + if ($categories->count() == 0) { + return []; + } + + return $this->handleContent($categories); + } + + /** + * @param \App\Models\Category[] $categories + * @return array + */ + public function handleContent($categories) + { + $result = []; + + foreach ($categories as $category) { + $result[] = [ + 'id' => $category->id, + 'name' => $category->name, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/CourseChapterList.php b/app/Caches/CourseChapterList.php new file mode 100644 index 00000000..abdf924c --- /dev/null +++ b/app/Caches/CourseChapterList.php @@ -0,0 +1,51 @@ +lifetime; + } + + public function getKey($id = null) + { + return "course_chapter_list:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $chapters = $courseRepo->findChapters($id); + + if ($chapters->count() == 0) { + return []; + } + + return $this->handleContent($chapters); + } + + /** + * @param \App\Models\Chapter[] $chapters + * @return array + */ + protected function handleContent($chapters) + { + $items = $chapters->toArray(); + + $builder = new ChapterListBuilder(); + + $content = $builder->handleTreeList($items); + + return $content; + } + +} diff --git a/app/Caches/CourseCounter.php b/app/Caches/CourseCounter.php new file mode 100644 index 00000000..301065fe --- /dev/null +++ b/app/Caches/CourseCounter.php @@ -0,0 +1,41 @@ +lifetime; + } + + public function getKey($id = null) + { + return "course_counter:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($id); + + if (!$course) return []; + + $content = [ + 'user_count' => $course->user_count, + 'lesson_count' => $course->lesson_count, + 'comment_count' => $course->comment_count, + 'review_count' => $course->review_count, + 'favorite_count' => $course->favorite_count, + ]; + + return $content; + } + +} diff --git a/app/Caches/CoursePackageList.php b/app/Caches/CoursePackageList.php new file mode 100644 index 00000000..4c7e48a2 --- /dev/null +++ b/app/Caches/CoursePackageList.php @@ -0,0 +1,82 @@ +lifetime; + } + + public function getKey($id = null) + { + return "course_package_list:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $packages = $courseRepo->findPackages($id); + + if ($packages->count() == 0) { + return []; + } + + return $this->handleContent($packages); + } + + /** + * @param \App\Models\Package[] $packages + * @return array + */ + protected function handleContent($packages) + { + $result = []; + + foreach ($packages as $package) { + + $courses = $this->getPackageCourses($package->id); + + $result[] = [ + 'id' => $package->id, + 'title' => $package->title, + 'market_price' => $package->market_price, + 'vip_price' => $package->vip_price, + 'courses' => $courses, + ]; + } + + return $result; + } + + protected function getPackageCourses($packageId) + { + $packageRepo = new PackageRepo(); + + $courses = $packageRepo->findCourses($packageId); + + $result = []; + + foreach ($courses as $course) { + $result[] = [ + 'id' => $course->id, + 'model' => $course->model, + 'title' => $course->title, + 'summary' => $course->summary, + 'cover' => $course->cover, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/CourseRelatedList.php b/app/Caches/CourseRelatedList.php new file mode 100644 index 00000000..54a8c53a --- /dev/null +++ b/app/Caches/CourseRelatedList.php @@ -0,0 +1,59 @@ +lifetime; + } + + public function getKey($id = null) + { + return "course_related_list:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $courses = $courseRepo->findRelatedCourses($id); + + if ($courses->count() == 0) { + return []; + } + + return $this->handleContent($courses); + } + + + /** + * @param \App\Models\Course[] $courses + * @return array + */ + public function handleContent($courses) + { + $result = []; + + foreach ($courses as $course) { + $result[] = [ + 'id' => $course->id, + 'model' => $course->model, + 'title' => $course->title, + 'cover' => $course->cover, + 'summary' => $course->summary, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/CourseTeacherList.php b/app/Caches/CourseTeacherList.php new file mode 100644 index 00000000..a8d34d9d --- /dev/null +++ b/app/Caches/CourseTeacherList.php @@ -0,0 +1,56 @@ +lifetime; + } + + public function getKey($id = null) + { + return "course_teacher_list:{$id}"; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $users = $courseRepo->findTeachers($id); + + if ($users->count() == 0) { + return []; + } + + return $this->handleContent($users); + } + + /** + * @param \App\Models\User[] $users + * @return array + */ + public function handleContent($users) + { + $result = []; + + foreach ($users as $user) { + $result[] = [ + 'id' => $user->id, + 'name' => $user->name, + 'avatar' => $user->avatar, + 'title' => $user->title, + 'about' => $user->about, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/CourseUser.php b/app/Caches/CourseUser.php new file mode 100644 index 00000000..1fedc9b4 --- /dev/null +++ b/app/Caches/CourseUser.php @@ -0,0 +1,43 @@ +lifetime; + } + + /** + * id = {$courseId}_{$userId} + * + * @param string $id + * @return string + */ + public function getKey($id = null) + { + return "course_user:{$id}"; + } + + public function getContent($id = null) + { + list($courseId, $userId) = explode('_', $id); + + $courseUserRepo = new CourseUserRepo(); + + $courseUser = $courseUserRepo->findCourseUser($courseId, $userId); + + if (!$courseUser) { + return new \stdClass(); + } + + return $courseUser; + } + +} diff --git a/app/Caches/HotCourseList.php b/app/Caches/HotCourseList.php new file mode 100644 index 00000000..09e5e374 --- /dev/null +++ b/app/Caches/HotCourseList.php @@ -0,0 +1,59 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'hot_course_list'; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $courses = $courseRepo->findRelatedCourses($id); + + if ($courses->count() == 0) { + return []; + } + + return $this->handleContent($courses); + } + + + /** + * @param \App\Models\Course[] $courses + * @return array + */ + public function handleContent($courses) + { + $result = []; + + foreach ($courses as $course) { + $result[] = [ + 'id' => $course->id, + 'model' => $course->model, + 'title' => $course->title, + 'summary' => $course->summary, + 'cover' => $course->cover, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/HotTeacherList.php b/app/Caches/HotTeacherList.php new file mode 100644 index 00000000..d16c5579 --- /dev/null +++ b/app/Caches/HotTeacherList.php @@ -0,0 +1,30 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'hot_teacher_list'; + } + + public function getContent($id = null) + { + + } + + protected function handleContent($users) + { + + } + +} diff --git a/app/Caches/LatestCourseList.php b/app/Caches/LatestCourseList.php new file mode 100644 index 00000000..3f715e72 --- /dev/null +++ b/app/Caches/LatestCourseList.php @@ -0,0 +1,59 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'latest_course_list'; + } + + public function getContent($id = null) + { + $courseRepo = new CourseRepo(); + + $courses = $courseRepo->findRelatedCourses($id); + + if ($courses->count() == 0) { + return []; + } + + return $this->handleContent($courses); + } + + + /** + * @param \App\Models\Course[] $courses + * @return array + */ + public function handleContent($courses) + { + $result = []; + + foreach ($courses as $course) { + $result[] = [ + 'id' => $course->id, + 'model' => $course->model, + 'title' => $course->title, + 'summary' => $course->summary, + 'cover' => $course->cover, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/MaxChapterId.php b/app/Caches/MaxChapterId.php new file mode 100644 index 00000000..4e2167a8 --- /dev/null +++ b/app/Caches/MaxChapterId.php @@ -0,0 +1,29 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'max_chapter_id'; + } + + public function getContent($id = null) + { + $chapter = ChapterModel::findFirst(['order' => 'id DESC']); + + return $chapter->id; + } + +} diff --git a/app/Caches/MaxCourseId.php b/app/Caches/MaxCourseId.php new file mode 100644 index 00000000..bbcc41eb --- /dev/null +++ b/app/Caches/MaxCourseId.php @@ -0,0 +1,29 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'max_course_id'; + } + + public function getContent($id = null) + { + $course = CourseModel::findFirst(['order' => 'id DESC']); + + return $course->id; + } + +} diff --git a/app/Caches/MaxUserId.php b/app/Caches/MaxUserId.php new file mode 100644 index 00000000..2724c06c --- /dev/null +++ b/app/Caches/MaxUserId.php @@ -0,0 +1,29 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'max_user_id'; + } + + public function getContent($id = null) + { + $course = UserModel::findFirst(['order' => 'id DESC']); + + return $course->id; + } + +} diff --git a/app/Caches/Nav.php b/app/Caches/Nav.php deleted file mode 100644 index 1754b36f..00000000 --- a/app/Caches/Nav.php +++ /dev/null @@ -1,53 +0,0 @@ -get(); - - if (!$items) return; - - $result = new \stdClass(); - - foreach ($items as $item) { - if ($item->position == 'top') { - $result->{$item->item_key} = $item->item_value; - } - } - - return $result; - } - - public function getBottomNav() - { - - } - - protected function getLifetime() - { - return $this->lifetime; - } - - protected function getKey($params = null) - { - return 'nav'; - } - - protected function getContent($params = null) - { - $navRepo = new NavRepo(); - - $items = $navRepo->findAll(); - - return $items; - } - -} diff --git a/app/Caches/NavTreeList.php b/app/Caches/NavTreeList.php new file mode 100644 index 00000000..2aaf88ef --- /dev/null +++ b/app/Caches/NavTreeList.php @@ -0,0 +1,66 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'nav_tree_list'; + } + + public function getContent($id = null) + { + $navs = NavModel::query() + ->where('published = 1 AND deleted = 0') + ->orderBy('position ASC, priority ASC') + ->execute(); + + if ($navs->count() == 0) { + return []; + } + + return $this->handleContent($navs); + } + + /** + * @param \App\Models\Nav[] $navs + * @return array + */ + protected function handleContent($navs) + { + $list = [ + 'top' => [], + 'bottom' => [], + ]; + + foreach ($navs->toArray() as $nav) { + if ($nav['position'] == 'top') { + $list['top'][] = $nav; + } elseif ($nav['position'] == 'bottom') { + $list['bottom'][] = $nav; + } + } + + $builder = new NavListBuilder(); + + $content = [ + 'top' => $builder->handleTreeList($list['top']), + 'bottom' => $builder->handleTreeList($list['bottom']), + ]; + + return $content; + } + +} diff --git a/app/Caches/SlideList.php b/app/Caches/SlideList.php new file mode 100644 index 00000000..91cd6f53 --- /dev/null +++ b/app/Caches/SlideList.php @@ -0,0 +1,59 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'slide_list'; + } + + public function getContent($id = null) + { + $slides = SlideModel::query() + ->columns(['id', 'title', 'cover', 'summary', 'target', 'content']) + ->where('published = 1 AND deleted = 0') + ->orderBy('priority ASC') + ->execute(); + + if ($slides->count() == 0) { + return []; + } + + return $this->handleContent($slides); + } + + /** + * @param \App\Models\Slide[] $slides + * @return array + */ + protected function handleContent($slides) + { + $result = []; + + foreach ($slides as $slide) { + $result[] = [ + 'id' => $slide->id, + 'title' => $slide->title, + 'cover' => $slide->cover, + 'summary' => $slide->summary, + 'target' => $slide->target, + 'content' => $slide->content, + ]; + } + + return $result; + } + +} diff --git a/app/Caches/User.php b/app/Caches/User.php index 86392d44..dd4be9b1 100644 --- a/app/Caches/User.php +++ b/app/Caches/User.php @@ -1,57 +1,35 @@ getById($id); - - if (!$result) { - throw new ModelNotFoundException('user.not_found'); - } - - return $result; - } - - public function get($id) - { - $cacheOptions = [ - 'key' => $this->getKey($id), - 'lifetime' => $this->getLifetime(), - ]; - - $result = UserModel::query() - ->where('id = :id:', ['id' => $id]) - ->cache($cacheOptions) - ->execute() - ->getFirst(); - - return $result; - } - - public function delete($id) - { - $key = $this->getKey($id); - - $this->modelsCache->delete($key); - } - - public function getKey($id) - { - return "user:{$id}"; - } + protected $lifetime = 7 * 86400; public function getLifetime() { return $this->lifetime; } + public function getKey($id = null) + { + return "user:{$id}"; + } + + public function getContent($id = null) + { + $userRepo = new UserRepo(); + + $user = $userRepo->findById($id); + + if (!$user) { + return new \stdClass(); + } + + return $user; + } + } diff --git a/app/Caches/UserCounter.php b/app/Caches/UserCounter.php new file mode 100644 index 00000000..3fc5c30c --- /dev/null +++ b/app/Caches/UserCounter.php @@ -0,0 +1,38 @@ +lifetime; + } + + public function getKey($id = null) + { + return "user_counter:{$id}"; + } + + public function getContent($id = null) + { + $userRepo = new UserRepo(); + + $user = $userRepo->findById($id); + + if (!$user) return []; + + $content = [ + 'notice_count' => $user->notice_count, + 'msg_count' => $user->msg_count, + ]; + + return $content; + } + +} diff --git a/app/Console/Tasks/CleanLogTask.php b/app/Console/Tasks/CleanLogTask.php index 651980bf..5fe91b98 100644 --- a/app/Console/Tasks/CleanLogTask.php +++ b/app/Console/Tasks/CleanLogTask.php @@ -123,7 +123,7 @@ class CleanLogTask extends Task * 清理日志文件 * * @param string $prefix - * @param integer $keepDays 保留天数 + * @param int $keepDays 保留天数 * @return mixed */ protected function cleanLog($prefix, $keepDays) diff --git a/app/Console/Tasks/CloseOrderTask.php b/app/Console/Tasks/CloseOrderTask.php index 711a2b4b..9ebbe234 100644 --- a/app/Console/Tasks/CloseOrderTask.php +++ b/app/Console/Tasks/CloseOrderTask.php @@ -25,7 +25,7 @@ class CloseOrderTask extends Task /** * 查找待关闭订单 * - * @param integer $limit + * @param int $limit * @return \Phalcon\Mvc\Model\ResultsetInterface */ protected function findOrders($limit = 1000) diff --git a/app/Console/Tasks/CloseSlideTask.php b/app/Console/Tasks/CloseSlideTask.php new file mode 100644 index 00000000..c3b16e75 --- /dev/null +++ b/app/Console/Tasks/CloseSlideTask.php @@ -0,0 +1,40 @@ +findSlides(); + + if ($slides->count() == 0) { + return; + } + + foreach ($slides as $slide) { + $slide->published = 0; + $slide->update(); + } + } + + /** + * 查找待关闭轮播 + * + * @return \Phalcon\Mvc\Model\ResultsetInterface + */ + protected function findSlides() + { + $Slides = SlideModel::query() + ->where('published = 1') + ->andWhere('end_time < :end_time:', ['end_time' => time()]) + ->execute(); + + return $Slides; + } + +} diff --git a/app/Console/Tasks/CloseTradeTask.php b/app/Console/Tasks/CloseTradeTask.php index f87cda90..5907ab1f 100644 --- a/app/Console/Tasks/CloseTradeTask.php +++ b/app/Console/Tasks/CloseTradeTask.php @@ -73,7 +73,7 @@ class CloseTradeTask extends Task /** * 查找待关闭交易 * - * @param integer $limit + * @param int $limit * @return \Phalcon\Mvc\Model\ResultsetInterface */ protected function findTrades($limit = 5) diff --git a/app/Console/Tasks/CountCourseTask.php b/app/Console/Tasks/CountCourseTask.php index 6c065d0d..61687c82 100644 --- a/app/Console/Tasks/CountCourseTask.php +++ b/app/Console/Tasks/CountCourseTask.php @@ -10,15 +10,15 @@ class CountCourseTask extends Task public function mainAction() { - $repo = new CategoryRepo(); + $categoryRepo = new CategoryRepo(); $mapping = []; - $subCategories = $repo->findAll(['level' => 2, 'deleted' => 0]); + $subCategories = $categoryRepo->findAll(['level' => 2, 'deleted' => 0]); foreach ($subCategories as $category) { - $courseCount = $repo->countCourses($category->id); + $courseCount = $categoryRepo->countCourses($category->id); $category->course_count = $courseCount; $category->update(); @@ -31,7 +31,7 @@ class CountCourseTask extends Task } } - $topCategories = $repo->findAll(['level' => 1, 'deleted' => 0]); + $topCategories = $categoryRepo->findAll(['level' => 1, 'deleted' => 0]); foreach ($topCategories as $category) { if (isset($mapping[$category->id])) { diff --git a/app/Console/Tasks/ImageSyncTask.php b/app/Console/Tasks/ImageSyncTask.php index 8aac31b0..ba158793 100644 --- a/app/Console/Tasks/ImageSyncTask.php +++ b/app/Console/Tasks/ImageSyncTask.php @@ -13,24 +13,40 @@ class ImageSyncTask extends Task public function mainAction() { $courses = Course::query() - ->where('id = 42') - ->execute(); + ->where('id > 1155') + ->execute(); $storage = new Storage(); foreach ($courses as $course) { + $cover = $course->cover; + if (Text::startsWith($cover, '//')) { $cover = 'http:' . $cover; } - $url = str_replace('-240-135', '', $cover); + + $url = str_replace('-360-202', '', $cover); $fileName = parse_url($url, PHP_URL_PATH); $filePath = tmp_path() . $fileName; $content = file_get_contents($url); - file_put_contents($filePath, $content); + + if ($content === false) { + echo "get course {$course->id} cover failed" . PHP_EOL; + return; + } + + $put = file_put_contents($filePath, $content); + + if ($put === false) { + echo "put course {$course->id} cover failed" . PHP_EOL; + return; + } + $keyName = $this->getKeyName($filePath); $remoteUrl = $storage->putFile($keyName, $filePath); + if ($remoteUrl) { $course->cover = $keyName; $course->update(); diff --git a/app/Console/Tasks/LearningTask.php b/app/Console/Tasks/LearningTask.php index be1918df..59a5d700 100644 --- a/app/Console/Tasks/LearningTask.php +++ b/app/Console/Tasks/LearningTask.php @@ -2,6 +2,7 @@ namespace App\Console\Tasks; +use App\Models\Course as CourseModel; use App\Models\Learning as LearningModel; use App\Repos\Chapter as ChapterRepo; use App\Repos\ChapterUser as ChapterUserRepo; @@ -24,9 +25,7 @@ class LearningTask extends Task $keys = $this->cache->queryKeys('learning:'); - if (empty($keys)) { - return; - } + if (!$keys) return; $keys = array_slice($keys, 0, 500); @@ -40,32 +39,21 @@ class LearningTask extends Task { $content = $this->cache->get($key); - if (empty($content->user_id)) { - return; - } - - if (!empty($content->client_ip)) { - $region = kg_ip2region($content->client_ip); - $content->country = $region->country; - $content->province = $region->province; - $content->city = $region->city; - } + if (!$content) return; $learningRepo = new LearningRepo(); - $learning = $learningRepo->findByRequestId($content->request_id); + $learning = $learningRepo->findByRequestId($content['request_id']); if (!$learning) { $learning = new LearningModel(); - $data = kg_object_array($content); - $learning->create($data); + $learning->create($content); } else { - $learning->duration += $content->duration; + $learning->duration += $content['duration']; $learning->update(); } - $this->updateChapterUser($content->chapter_id, $content->user_id, $content->duration, $content->position); - $this->updateCourseUser($content->course_id, $content->user_id, $content->duration); + $this->updateChapterUser($content['chapter_id'], $content['user_id'], $content['duration'], $content['position']); $this->cache->delete($key); } @@ -76,63 +64,87 @@ class LearningTask extends Task $chapterUser = $chapterUserRepo->findChapterUser($chapterId, $userId); - if (!$chapterUser) { - return; - } + if (!$chapterUser) return; $chapterRepo = new ChapterRepo(); $chapter = $chapterRepo->findById($chapterId); - if (!$chapter) { - return; - } + if (!$chapter) return; - $chapter->duration = $chapter->attrs['duration'] ?: 0; + $chapterModel = $chapter->attrs['model']; $chapterUser->duration += $duration; - $chapterUser->position = floor($position); /** - * 观看时长超过视频时长80%标记完成学习 + * 消费规则 + * 1.点播观看时间大于时长30% + * 2.直播观看时间超过10分钟 + * 3.图文浏览即消费 */ - if ($chapterUser->duration > $chapter->duration * 0.8) { - if ($chapterUser->finished == 0) { - $chapterUser->finished = 1; - $this->updateCourseProgress($chapterUser->course_id, $chapterUser->user_id); - } + if ($chapterModel == CourseModel::MODEL_VOD) { + + $chapterDuration = $chapter->attrs['duration'] ?: 300; + + $progress = floor(100 * $chapterUser->duration / $chapterDuration); + + $chapterUser->position = floor($position); + $chapterUser->progress = $progress < 100 ? $progress : 100; + $chapterUser->consumed = $chapterUser->duration > 0.3 * $chapterDuration ? 1 : 0; + + } elseif ($chapterModel == CourseModel::MODEL_LIVE) { + + $chapterUser->consumed = $chapterUser->duration > 600 ? 1 : 0; + + } elseif ($chapterModel == CourseModel::MODEL_READ) { + + $chapterUser->consumed = 1; } $chapterUser->update(); - } - protected function updateCourseUser($courseId, $userId, $duration) - { - $courseUserRepo = new CourseUserRepo(); - - $courseUser = $courseUserRepo->findCourseUser($courseId, $userId); - - if ($courseUser) { - $courseUser->duration += $duration; - $courseUser->update(); + if ($chapterUser->consumed == 1) { + $this->updateCourseUser($chapterUser->course_id, $chapterUser->user_id); } } - protected function updateCourseProgress($courseId, $userId) + protected function updateCourseUser($courseId, $userId) { - $courseUserRepo = new CourseUserRepo(); - - $courseUser = $courseUserRepo->findCourseUser($courseId, $userId); - $courseRepo = new CourseRepo(); - $course = $courseRepo->findById($courseId); + $courseLessons = $courseRepo->findLessons($courseId); - if ($courseUser) { - $count = $courseUserRepo->countFinishedChapters($courseId, $userId); - $courseUser->progress = intval(100 * $count / $course->lesson_count); - $courseUser->update(); + if ($courseLessons->count() == 0) { + return; } + + $userLearnings = $courseRepo->findConsumedUserLearnings($courseId, $userId); + + if ($userLearnings->count() == 0) { + return; + } + + $duration = 0; + + foreach ($userLearnings as $learning) { + $duration += $learning->duration; + } + + $courseLessonIds = kg_array_column($courseLessons->toArray(), 'id'); + $userLessonIds = kg_array_column($userLearnings->toArray(), 'chapter_id'); + $consumedLessonIds = array_intersect($courseLessonIds, $userLessonIds); + + $totalCount = count($courseLessonIds); + $consumedCount = count($consumedLessonIds); + $progress = intval(100 * $consumedCount / $totalCount); + + $courseUserRepo = new CourseUserRepo(); + + $courseUser = $courseUserRepo->findCourseUser($courseId, $userId); + + $courseUser->progress = $progress; + $courseUser->duration = $duration; + $courseUser->update(); } } diff --git a/app/Console/Tasks/ManageCourseIndexTask.php b/app/Console/Tasks/ManageCourseIndexTask.php new file mode 100644 index 00000000..5584be01 --- /dev/null +++ b/app/Console/Tasks/ManageCourseIndexTask.php @@ -0,0 +1,130 @@ +searchCourses($query); + + var_export($result); + } + + /** + * 清空索引 + * + * @command: php console.php manage_course_index clean + */ + public function cleanAction() + { + $this->cleanCourseIndex(); + } + + /** + * 重建索引 + * + * @command: php console.php manage_course_index rebuild + */ + public function rebuildAction() + { + $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(); + + $documenter = new CourseDocumenter(); + + $index = $searcher->getXS()->getIndex(); + + echo "start rebuild index" . PHP_EOL; + + $index->beginRebuild(); + + foreach ($courses as $course) { + $document = $documenter->setDocument($course); + $index->add($document); + } + + $index->endRebuild(); + + echo "end rebuild index" . PHP_EOL; + } + + /** + * 搜索课程 + * + * @param string $query + * @return array $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; + } + +} diff --git a/app/Console/Tasks/ManageIndexTask.php b/app/Console/Tasks/ManageIndexTask.php deleted file mode 100644 index edaeafcb..00000000 --- a/app/Console/Tasks/ManageIndexTask.php +++ /dev/null @@ -1,166 +0,0 @@ -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; - } - -} diff --git a/app/Console/Tasks/RebuildChapterCacheTask.php b/app/Console/Tasks/RebuildChapterCacheTask.php new file mode 100644 index 00000000..6757e723 --- /dev/null +++ b/app/Console/Tasks/RebuildChapterCacheTask.php @@ -0,0 +1,69 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + + $this->rebuild(); + } + + protected function rebuild() + { + $key = $this->getCacheKey(); + + $chapterIds = $this->redis->sRandMember($key, 500); + + if (!$chapterIds) return; + + $chapterRepo = new ChapterRepo(); + + $chapters = $chapterRepo->findByIds($chapterIds); + + if ($chapters->count() == 0) { + return; + } + + $chapterCache = new ChapterCache(); + $counterCache = new ChapterCounterCache(); + + foreach ($chapters as $chapter) { + $chapter->user_count = $chapterRepo->countUsers($chapter->id); + $chapter->comment_count = $chapterRepo->countComments($chapter->id); + $chapter->like_count = $chapterRepo->countLikes($chapter->id); + $chapter->update(); + + $chapterCache->rebuild($chapter->id); + $counterCache->rebuild($chapter->id); + } + } + + protected function getCacheKey() + { + $syncer = new ChapterCacheSyncer(); + + return $syncer->getCacheKey(); + } + +} diff --git a/app/Console/Tasks/RebuildCourseCacheTask.php b/app/Console/Tasks/RebuildCourseCacheTask.php new file mode 100644 index 00000000..fc9abf66 --- /dev/null +++ b/app/Console/Tasks/RebuildCourseCacheTask.php @@ -0,0 +1,72 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + + $this->rebuild(); + } + + protected function rebuild() + { + $key = $this->getCacheKey(); + + $courseIds = $this->redis->sRandMember($key, 100); + + if (!$courseIds) return; + + $courseRepo = new CourseRepo(); + + $courses = $courseRepo->findByIds($courseIds); + + if ($courses->count() == 0) { + return; + } + + $courseCache = new CourseCache(); + $counterCache = new CourseCounterCache(); + + foreach ($courses as $course) { + $course->user_count = $courseRepo->countUsers($course->id); + $course->comment_count = $courseRepo->countComments($course->id); + $course->review_count = $courseRepo->countReviews($course->id); + $course->favorite_count = $courseRepo->countFavorites($course->id); + $course->update(); + + $courseCache->rebuild($course->id); + $counterCache->rebuild($course->id); + } + + $this->redis->sRem($key, ...$courseIds); + } + + protected function getCacheKey() + { + $syncer = new CourseCacheSyncer(); + + return $syncer->getCacheKey(); + } + +} diff --git a/app/Console/Tasks/RebuildCourseIndexTask.php b/app/Console/Tasks/RebuildCourseIndexTask.php new file mode 100644 index 00000000..a7b885d0 --- /dev/null +++ b/app/Console/Tasks/RebuildCourseIndexTask.php @@ -0,0 +1,77 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + + $this->rebuild(); + } + + protected function rebuild() + { + $key = $this->getCacheKey(); + + $courseIds = $this->redis->sRandMember($key, 100); + + if (!$courseIds) return; + + $courseRepo = new CourseRepo(); + + $courses = $courseRepo->findByIds($courseIds); + + if ($courses->count() == 0) { + return; + } + + $document = new CourseDocumenter(); + + $searcher = new CourseSearcher(); + + $index = $searcher->getXS()->getIndex(); + + $index->openBuffer(); + + foreach ($courses as $course) { + $doc = $document->setDocument($course); + if ($course->published == 1) { + $index->update($doc); + } else { + $index->del($course->id); + } + } + + $index->closeBuffer(); + + $this->redis->sRem($key, ...$courseIds); + } + + protected function getCacheKey() + { + $syncer = new CourseIndexSyncer(); + + return $syncer->getCacheKey(); + } + +} diff --git a/app/Console/Tasks/RefundTask.php b/app/Console/Tasks/RefundTask.php index aa363578..cc274e5b 100644 --- a/app/Console/Tasks/RefundTask.php +++ b/app/Console/Tasks/RefundTask.php @@ -247,12 +247,6 @@ class RefundTask extends Task } - /** - * 查找退款任务 - * - * @param integer $limit - * @return \Phalcon\Mvc\Model\ResultsetInterface - */ protected function findTasks($limit = 5) { $itemType = TaskModel::TYPE_REFUND; diff --git a/app/Console/Tasks/SpiderTask.php b/app/Console/Tasks/SpiderTask.php index ac1f67ee..586fecc4 100644 --- a/app/Console/Tasks/SpiderTask.php +++ b/app/Console/Tasks/SpiderTask.php @@ -4,18 +4,53 @@ namespace App\Console\Tasks; use App\Models\Chapter as ChapterModel; use App\Models\Course as CourseModel; +use App\Models\CourseUser as CourseUserModel; use App\Models\User as UserModel; +use App\Repos\CourseUser as CourseUserRepo; use Phalcon\Cli\Task; use QL\QueryList; class SpiderTask extends Task { - public function testAction() + public function ctAction() { - $subject = '1-1 课程简介(01:40)开始学习'; - preg_match('/(\d{1,}-\d{1,})\s{1,}(.*?)\((.*?)\)/', $subject, $matches); - dd($matches); + $courses = CourseModel::query() + ->where('class_id = 0') + ->execute(); + + foreach ($courses as $course) { + + $url = "http://www.imooc.com/learn/{$course->id}"; + + $ql = QueryList::getInstance()->get($url); + + $userId = $ql->find('img.js-usercard-dialog')->attr('data-userid'); + + if ($userId) { + $user = UserModel::findFirst($userId); + if (!$user || !$user->avatar) { + $this->handleUserInfo2($course->id, $userId); + } + } + + $ql->destruct(); + + echo "finished course " . $course->id . PHP_EOL; + } + } + + public function user2Action() + { + $users = UserModel::query() + ->where('edu_role = 2') + ->andWhere('name = :name:', ['name' => '']) + ->execute(); + + foreach ($users as $user) { + $this->handleUserInfo($user->id); + echo "finished user: {$user->id}" . PHP_EOL; + } } public function courseListAction($params) @@ -64,91 +99,191 @@ class SpiderTask extends Task public function courseAction() { $courses = CourseModel::query() - ->where('1 = 1') - ->andWhere('id > :id:', ['id' => 1128]) + ->where('id = 762') ->orderBy('id asc') ->execute(); - $baseUrl = 'http://www.imooc.com/learn'; - $instance = QueryList::getInstance(); foreach ($courses as $course) { - $url = $baseUrl . '/' . $course->id; + + $url = "http://www.imooc.com/learn/{$course->id}"; + $ql = $instance->get($url); - $result = $this->handleCourseInfo($course, $ql); - if (!$result) { - continue; - } + + //$this->handleCourseInfo($course, $ql); + $this->handleCourseChapters($course, $ql); + + //$ql->destruct(); + echo "finished course " . $course->id . PHP_EOL; - sleep(1); } } public function teacherAction() - { - $courses = CourseModel::query() - ->where('1 = 1') - ->groupBy('user_id') - ->execute(); - - foreach ($courses as $course) { - $this->handleTeacherInfo($course->user_id); - echo "finished teacher: {$course->user_id}" . PHP_EOL; - sleep(1); - } - } - - public function userAction() { $users = UserModel::query() - ->where('1 = 1') - ->andWhere('name = :name:', ['name' => '']) + ->where('edu_role = 2') ->execute(); foreach ($users as $user) { - $this->handleUserInfo($user->id); - echo "finished user: {$user->id}" . PHP_EOL; - sleep(1); + try { + $this->handleTeacherInfo2($user); + echo "finished teacher: {$user->id}" . PHP_EOL; + } catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; + } } } - protected function handleUserInfo($id) + protected function handleTeacherInfo2(UserModel $user) { - $url = 'http://www.imooc.com/u/'. $id; + $url = "http://www.imooc.com/t/{$user->id}"; $ql = QueryList::getInstance()->get($url); $data = []; - $data['id'] = $id; - $data['avatar'] = $ql->find('.user-pic-bg>img')->attr('src'); - $data['name'] = $ql->find('h3.user-name>span')->text(); - $data['about'] = $ql->find('p.user-desc')->text(); - - $user = new UserModel(); - - $user->save($data); - } - - protected function handleTeacherInfo($id) - { - $url = 'http://www.imooc.com/t/'. $id; - - $ql = QueryList::getInstance()->get($url); - - $data = []; - - $data['id'] = $id; $data['avatar'] = $ql->find('img.tea-header')->attr('src'); $data['name'] = $ql->find('p.tea-nickname')->text(); $data['title'] = $ql->find('p.tea-professional')->text(); $data['about'] = $ql->find('p.tea-desc')->text(); - $user = new UserModel(); + $user->update($data); + } - $user->create($data); + public function userAction() + { + $users = UserModel::query() + ->where('edu_role = 1') + ->execute(); + + foreach ($users as $user) { + $this->handleUserInfo($user->id); + echo "finished user: {$user->id}" . PHP_EOL; + } + } + + protected function handleUserInfo($id) + { + $url = 'https://www.imooc.com/u/' . $id; + + $user = UserModel::findFirst($id); + + try { + + $ql = QueryList::getInstance()->get($url); + + $data = []; + + $data['avatar'] = $ql->find('.user-pic-bg>img')->attr('src'); + $data['name'] = $ql->find('h3.user-name>span')->text(); + $data['about'] = $ql->find('p.user-desc')->text(); + + print_r($data); + + $user->update($data); + + $ql->destruct(); + + } catch (\Exception $e) { + + $user->update(['deleted' => 1]); + + echo "user {$id} not found" . PHP_EOL; + } + } + + protected function handleUserInfo2($courseId, $userId) + { + $url = 'https://www.imooc.com/u/' . $userId; + + $user = UserModel::findFirst($userId); + + try { + + $ql = QueryList::getInstance()->get($url); + + $data = []; + + $data['avatar'] = $ql->find('.user-pic-bg>img')->attr('src'); + $data['name'] = $ql->find('h3.user-name>span')->text(); + $data['about'] = $ql->find('p.user-desc')->text(); + $data['edu_role'] = UserModel::EDU_ROLE_TEACHER; + + if ($user) { + $user->update($data); + } else { + $user = new UserModel(); + $user->create($data); + } + + $cuRepo = new CourseUserRepo(); + + $courseUser = $cuRepo->findCourseUser($courseId, $userId); + + if (!$courseUser) { + $courseUser = new CourseUserModel(); + $courseUser->course_id = $courseId; + $courseUser->user_id = $userId; + $courseUser->role_type = CourseUserModel::ROLE_TEACHER; + $courseUser->expire_time = strtotime('+15 years'); + $courseUser->create(); + } + + echo "teacher {$userId} off " . PHP_EOL; + + $ql->destruct(); + + } catch (\Exception $e) { + + $user->update(['deleted' => 1]); + + echo "user {$userId} not found" . PHP_EOL; + } + } + + protected function handleTeacherInfo($courseId, $userId) + { + $url = "http://www.imooc.com/t/{$userId}"; + + $ql = QueryList::getInstance()->get($url); + + $data = []; + + $data['id'] = $userId; + $data['avatar'] = $ql->find('img.tea-header')->attr('src'); + $data['name'] = $ql->find('p.tea-nickname')->text(); + $data['title'] = $ql->find('p.tea-professional')->text(); + $data['about'] = $ql->find('p.tea-desc')->text(); + $data['edu_role'] = UserModel::EDU_ROLE_TEACHER; + + $user = UserModel::findFirst($userId); + + if ($user) { + $user->update($data); + } else { + $user = new UserModel(); + $user->create($data); + } + + $cuRepo = new CourseUserRepo(); + + $courseUser = $cuRepo->findCourseUser($courseId, $userId); + + if (!$courseUser) { + $courseUser = new CourseUserModel(); + $courseUser->course_id = $courseId; + $courseUser->user_id = $userId; + $courseUser->role_type = CourseUserModel::ROLE_TEACHER; + $courseUser->expire_time = strtotime('+15 years'); + $courseUser->create(); + } + + $ql->destruct(); + + echo "teacher ok" . PHP_EOL; } protected function handleCourseInfo(CourseModel $course, QueryList $ql) @@ -160,27 +295,33 @@ class SpiderTask extends Task $data['duration'] = $ql->find('.static-item:eq(1)>.meta-value')->text(); $data['score'] = $ql->find('.score-btn>.meta-value')->text(); - if (empty($data['user_id'])) { - return false; + $data['attrs']['duration'] = $this->getCourseDuration($data['duration']); + + $course->update($data); + + if ($data['user_id']) { + $this->handleTeacherInfo($course->id, $data['user_id']); } - $data['duration'] = $this->getCourseDuration($data['duration']); - - return $course->update($data); + echo "course info ok" . PHP_EOL; } protected function handleCourseChapters(CourseModel $course, QueryList $ql) { + echo "top chapter" . PHP_EOL; + $topChapters = $ql->rules([ - 'title' => ['.chapter>h3', 'text'], - 'sub_chapter_html' => ['.chapter>.video', 'html'], + 'title' => ['.chapter > h3', 'text'], + 'sub_chapter_html' => ['.chapter > .video', 'html'], ])->query()->getData(); + if ($topChapters->count() == 0) { return false; } foreach ($topChapters->all() as $item) { + $data = [ 'course_id' => $course->id, 'title' => $item['title'], @@ -208,16 +349,29 @@ class SpiderTask extends Task } foreach ($chapters->all() as $item) { - preg_match('/(\d{1,}-\d{1,})\s{1,}(.*?)\((.*?)\)/s', $item, $matches); - if (!isset($matches[3]) || empty($matches[3])) { - continue; - } - $data = [ - 'course_id' => $topChapter->course_id, - 'parent_id' => $topChapter->id, - 'title' => $matches[2], - 'duration' => $this->getChapterDuration($matches[3]), - ]; + + /** + * + * preg_match('/(\d{1,}-\d{1,})\s{1,}(.*?)\((.*?)\)/s', $item, $matches); + * + * if (!isset($matches[3]) || empty($matches[3])) { + * continue; + * } + * + * $data = [ + * 'title' => $matches[2], + * 'duration' => $this->getChapterDuration($matches[3]), + * ]; + */ + + $title = str_replace(["开始学习", "\r", "\n", "\t", " "], "", $item); + $title = preg_replace('/\(\d{2}:\d{2}\)/', '', $title); + + $data = []; + $data['course_id'] = $topChapter->course_id; + $data['parent_id'] = $topChapter->id; + $data['title'] = trim($title); + $model = new ChapterModel(); $model->create($data); } @@ -231,6 +385,8 @@ class SpiderTask extends Task if (preg_match('/(.*?)小时(.*?)分/s', $duration, $matches)) { $hours = trim($matches[1]); $minutes = trim($matches[2]); + } elseif (preg_match('/(.*?)小时/s', $duration, $matches)) { + $hours = trim($matches[1]); } elseif (preg_match('/(.*?)分/s', $duration, $matches)) { $minutes = trim($matches[1]); } @@ -254,7 +410,7 @@ class SpiderTask extends Task $mapping = [ '入门' => CourseModel::LEVEL_ENTRY, '初级' => CourseModel::LEVEL_JUNIOR, - '中级' => CourseModel::LEVEL_MIDDLE, + '中级' => CourseModel::LEVEL_MEDIUM, '高级' => CourseModel::LEVEL_SENIOR, ]; diff --git a/app/Console/Tasks/SyncIndexTask.php b/app/Console/Tasks/SyncIndexTask.php deleted file mode 100644 index e6ce5133..00000000 --- a/app/Console/Tasks/SyncIndexTask.php +++ /dev/null @@ -1,66 +0,0 @@ -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(); - } - -} diff --git a/app/Console/Tasks/UnlockUserTask.php b/app/Console/Tasks/UnlockUserTask.php index e1319a0a..47722d55 100644 --- a/app/Console/Tasks/UnlockUserTask.php +++ b/app/Console/Tasks/UnlockUserTask.php @@ -18,7 +18,7 @@ class UnlockUserTask extends Task foreach ($users as $user) { $user->locked = 0; - $user->locked_expiry = 0; + $user->lock_expiry = 0; $user->update(); } } @@ -26,7 +26,7 @@ class UnlockUserTask extends Task /** * 查找待解锁用户 * - * @param integer $limit + * @param int $limit * @return \Phalcon\Mvc\Model\ResultsetInterface */ protected function findUsers($limit = 1000) @@ -35,7 +35,7 @@ class UnlockUserTask extends Task $users = UserModel::query() ->where('locked = 1') - ->andWhere('locked_expiry < :time:', ['time' => $time]) + ->andWhere('lock_expiry < :time:', ['time' => $time]) ->limit($limit) ->execute(); diff --git a/app/Console/Tasks/VodEventTask.php b/app/Console/Tasks/VodEventTask.php index b0514f60..020b3fd1 100644 --- a/app/Console/Tasks/VodEventTask.php +++ b/app/Console/Tasks/VodEventTask.php @@ -15,9 +15,7 @@ class VodEventTask extends Task { $events = $this->pullEvents(); - if (!$events) { - return; - } + if (!$events) return; $handles = []; @@ -49,9 +47,7 @@ class VodEventTask extends Task $chapter = $chapterRepo->findByFileId($fileId); - if (!$chapter) { - return; - } + if (!$chapter) return; $vodService = new VodService(); @@ -62,14 +58,14 @@ class VodEventTask extends Task } /** - * @var \stdClass $attrs + * @var array $attrs */ $attrs = $chapter->attrs; - $attrs->file_status = ChapterModel::FS_TRANSLATING; - $attrs->duration = $duration; + $attrs['file_status'] = ChapterModel::FS_TRANSLATING; + $attrs['duration'] = (int)$duration; $chapter->update(['attrs' => $attrs]); - $this->updateCourseDuration($chapter->course_id); + $this->updateVodAttrs($chapter->course_id); } protected function handleProcedureStateChangedEvent($event) @@ -81,9 +77,7 @@ class VodEventTask extends Task $chapter = $chapterRepo->findByFileId($fileId); - if (!$chapter) { - return; - } + if (!$chapter) return; $failCount = $successCount = 0; @@ -97,26 +91,26 @@ class VodEventTask extends Task } } - $status = ChapterModel::FS_TRANSLATING; + $fileStatus = ChapterModel::FS_TRANSLATING; /** * 当有一个成功标记为成功 */ if ($successCount > 0) { - $status = ChapterModel::FS_TRANSLATED; + $fileStatus = ChapterModel::FS_TRANSLATED; } elseif ($failCount > 0) { - $status = ChapterModel::FS_FAILED; + $fileStatus = ChapterModel::FS_FAILED; } - if ($status == ChapterModel::FS_TRANSLATING) { + if ($fileStatus == ChapterModel::FS_TRANSLATING) { return; } /** - * @var \stdClass $attrs + * @var array $attrs */ $attrs = $chapter->attrs; - $attrs->file_status = $status; + $attrs['file_status'] = $fileStatus; $chapter->update(['attrs' => $attrs]); } @@ -147,11 +141,11 @@ class VodEventTask extends Task return $result; } - protected function updateCourseDuration($courseId) + protected function updateVodAttrs($courseId) { $courseStats = new CourseStatsService(); - $courseStats->updateVodDuration($courseId); + $courseStats->updateVodAttrs($courseId); } } diff --git a/app/Http/Admin/Controllers/AuditController.php b/app/Http/Admin/Controllers/AuditController.php index 3cc07a48..84e7251d 100644 --- a/app/Http/Admin/Controllers/AuditController.php +++ b/app/Http/Admin/Controllers/AuditController.php @@ -31,7 +31,7 @@ class AuditController extends Controller } /** - * @Get("/{id}/show", name="admin.audit.show") + * @Get("/{id:[0-9]+}/show", name="admin.audit.show") */ public function showAction($id) { diff --git a/app/Http/Admin/Controllers/CategoryController.php b/app/Http/Admin/Controllers/CategoryController.php index 8e0482dc..0462a8b7 100644 --- a/app/Http/Admin/Controllers/CategoryController.php +++ b/app/Http/Admin/Controllers/CategoryController.php @@ -17,10 +17,10 @@ class CategoryController extends Controller { $parentId = $this->request->get('parent_id', 'int', 0); - $service = new CategoryService(); + $categoryService = new CategoryService(); - $parent = $service->getParentCategory($parentId); - $categories = $service->getChildCategories($parentId); + $parent = $categoryService->getParentCategory($parentId); + $categories = $categoryService->getChildCategories($parentId); $this->view->setVar('parent', $parent); $this->view->setVar('categories', $categories); @@ -33,9 +33,9 @@ class CategoryController extends Controller { $parentId = $this->request->get('parent_id', 'int', 0); - $service = new CategoryService(); + $categoryService = new CategoryService(); - $topCategories = $service->getTopCategories(); + $topCategories = $categoryService->getTopCategories(); $this->view->setVar('parent_id', $parentId); $this->view->setVar('top_categories', $topCategories); @@ -46,9 +46,9 @@ class CategoryController extends Controller */ public function createAction() { - $service = new CategoryService(); + $categoryService = new CategoryService(); - $category = $service->createCategory(); + $category = $categoryService->createCategory(); $location = $this->url->get( ['for' => 'admin.category.list'], @@ -64,27 +64,27 @@ class CategoryController extends Controller } /** - * @Get("/{id}/edit", name="admin.category.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.category.edit") */ public function editAction($id) { - $service = new CategoryService(); + $categoryService = new CategoryService(); - $category = $service->getCategory($id); + $category = $categoryService->getCategory($id); $this->view->setVar('category', $category); } /** - * @Post("/{id}/update", name="admin.category.update") + * @Post("/{id:[0-9]+}/update", name="admin.category.update") */ public function updateAction($id) { - $service = new CategoryService(); + $categoryService = new CategoryService(); - $category = $service->getCategory($id); + $category = $categoryService->getCategory($id); - $service->updateCategory($id); + $categoryService->updateCategory($id); $location = $this->url->get( ['for' => 'admin.category.list'], @@ -100,13 +100,13 @@ class CategoryController extends Controller } /** - * @Post("/{id}/delete", name="admin.category.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.category.delete") */ public function deleteAction($id) { - $service = new CategoryService(); + $categoryService = new CategoryService(); - $service->deleteCategory($id); + $categoryService->deleteCategory($id); $location = $this->request->getHTTPReferer(); @@ -119,13 +119,13 @@ class CategoryController extends Controller } /** - * @Post("/{id}/restore", name="admin.category.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.category.restore") */ public function restoreAction($id) { - $service = new CategoryService(); + $categoryService = new CategoryService(); - $service->restoreCategory($id); + $categoryService->restoreCategory($id); $location = $this->request->getHTTPReferer(); diff --git a/app/Http/Admin/Controllers/ChapterController.php b/app/Http/Admin/Controllers/ChapterController.php index 1279cb1f..ac8f2870 100644 --- a/app/Http/Admin/Controllers/ChapterController.php +++ b/app/Http/Admin/Controllers/ChapterController.php @@ -15,19 +15,19 @@ class ChapterController extends Controller { /** - * @Get("/{id}/lessons", name="admin.chapter.lessons") + * @Get("/{id:[0-9]+}/lessons", name="admin.chapter.lessons") */ public function lessonsAction($id) { - $chapterService = new ChapterService(); $courseService = new CourseService(); + $chapterService = new ChapterService(); $chapter = $chapterService->getChapter($id); - $course = $courseService->getCourse($chapter->course_id); $lessons = $chapterService->getLessons($chapter->id); + $course = $courseService->getCourse($chapter->course_id); - $this->view->setVar('lessons', $lessons); $this->view->setVar('chapter', $chapter); + $this->view->setVar('lessons', $lessons); $this->view->setVar('course', $course); } @@ -40,14 +40,14 @@ class ChapterController extends Controller $parentId = $this->request->getQuery('parent_id'); $type = $this->request->getQuery('type'); - $chapterService = new ChapterService(); + $courseService = new CourseService(); - $course = $chapterService->getCourse($courseId); - $courseChapters = $chapterService->getCourseChapters($courseId); + $course = $courseService->getCourse($courseId); + $chapters = $courseService->getChapters($courseId); $this->view->setVar('course', $course); $this->view->setVar('parent_id', $parentId); - $this->view->setVar('course_chapters', $courseChapters); + $this->view->setVar('chapters', $chapters); if ($type == 'chapter') { $this->view->pick('chapter/add_chapter'); @@ -61,9 +61,9 @@ class ChapterController extends Controller */ public function createAction() { - $service = new ChapterService(); + $chapterService = new ChapterService(); - $chapter = $service->createChapter(); + $chapter = $chapterService->createChapter(); $location = $this->url->get([ 'for' => 'admin.course.chapters', @@ -79,54 +79,57 @@ class ChapterController extends Controller } /** - * @Get("/{id}/edit", name="admin.chapter.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.chapter.edit") */ public function editAction($id) { $contentService = new ChapterContentService(); $chapterService = new ChapterService(); + $courseService = new CourseService(); $configService = new ConfigService(); $chapter = $chapterService->getChapter($id); - $course = $chapterService->getCourse($chapter->course_id); + $course = $courseService->getCourse($chapter->course_id); $storage = $configService->getSectionConfig('storage'); - switch ($course->model) { - case CourseModel::MODEL_VOD: - $vod = $contentService->getChapterVod($chapter->id); - $translatedFiles = $contentService->getTranslatedFiles($vod->file_id); - $this->view->setVar('vod', $vod); - $this->view->setVar('translated_files', $translatedFiles); - break; - case CourseModel::MODEL_LIVE: - $live = $contentService->getChapterLive($chapter->id); - $this->view->setVar('live', $live); - break; - case CourseModel::MODEL_ARTICLE: - $article = $contentService->getChapterArticle($chapter->id); - $this->view->setVar('article', $article); - break; - } - $this->view->setVar('storage', $storage); $this->view->setVar('chapter', $chapter); $this->view->setVar('course', $course); if ($chapter->parent_id > 0) { + + switch ($course->model) { + case CourseModel::MODEL_VOD: + $vod = $contentService->getChapterVod($chapter->id); + $vodFiles = $contentService->getVodFiles($chapter->id); + $this->view->setVar('vod', $vod); + $this->view->setVar('vod_files', $vodFiles); + break; + case CourseModel::MODEL_LIVE: + $live = $contentService->getChapterLive($chapter->id); + $this->view->setVar('live', $live); + break; + case CourseModel::MODEL_READ: + $read = $contentService->getChapterRead($chapter->id); + $this->view->setVar('read', $read); + break; + } + $this->view->pick('chapter/edit_lesson'); + } else { $this->view->pick('chapter/edit_chapter'); } } /** - * @Post("/{id}/update", name="admin.chapter.update") + * @Post("/{id:[0-9]+}/update", name="admin.chapter.update") */ public function updateAction($id) { - $service = new ChapterService(); + $chapterService = new ChapterService(); - $chapter = $service->updateChapter($id); + $chapter = $chapterService->updateChapter($id); if ($chapter->parent_id > 0) { $location = $this->url->get([ @@ -149,13 +152,13 @@ class ChapterController extends Controller } /** - * @Post("/{id}/delete", name="admin.chapter.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.chapter.delete") */ public function deleteAction($id) { - $service = new ChapterService(); + $chapterService = new ChapterService(); - $service->deleteChapter($id); + $chapterService->deleteChapter($id); $location = $this->request->getHTTPReferer(); @@ -168,13 +171,13 @@ class ChapterController extends Controller } /** - * @Post("/{id}/restore", name="admin.chapter.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.chapter.restore") */ public function restoreAction($id) { - $service = new ChapterService(); + $chapterService = new ChapterService(); - $service->restoreChapter($id); + $chapterService->restoreChapter($id); $location = $this->request->getHTTPReferer(); @@ -187,7 +190,7 @@ class ChapterController extends Controller } /** - * @Post("/{id}/content", name="admin.chapter.content") + * @Post("/{id:[0-9]+}/content", name="admin.chapter.content") */ public function contentAction($id) { diff --git a/app/Http/Admin/Controllers/ConfigController.php b/app/Http/Admin/Controllers/ConfigController.php index 91d338ab..b41d4eb0 100644 --- a/app/Http/Admin/Controllers/ConfigController.php +++ b/app/Http/Admin/Controllers/ConfigController.php @@ -11,27 +11,27 @@ class ConfigController extends Controller { /** - * @Route("/website", name="admin.config.website") + * @Route("/site", name="admin.config.site") */ - public function websiteAction() + public function siteAction() { - $section = 'website'; + $section = 'site'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateSectionConfig($section, $data); + $configService->updateSectionConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $website = $service->getSectionConfig($section); + $site = $configService->getSectionConfig($section); - $this->view->setVar('website', $website); + $this->view->setVar('site', $site); } } @@ -42,19 +42,19 @@ class ConfigController extends Controller { $section = 'secret'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateStorageConfig($section, $data); + $configService->updateStorageConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $secret = $service->getSectionConfig($section); + $secret = $configService->getSectionConfig($section); $this->view->setVar('secret', $secret); } @@ -67,19 +67,19 @@ class ConfigController extends Controller { $section = 'storage'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateStorageConfig($section, $data); + $configService->updateStorageConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $storage = $service->getSectionConfig($section); + $storage = $configService->getSectionConfig($section); $this->view->setVar('storage', $storage); } @@ -92,19 +92,19 @@ class ConfigController extends Controller { $section = 'vod'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateVodConfig($section, $data); + $configService->updateVodConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $vod = $service->getSectionConfig($section); + $vod = $configService->getSectionConfig($section); $this->view->setVar('vod', $vod); } @@ -117,19 +117,19 @@ class ConfigController extends Controller { $section = 'live'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateLiveConfig($section, $data); + $configService->updateLiveConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $live = $service->getSectionConfig($section); + $live = $configService->getSectionConfig($section); $ptt = json_decode($live->pull_trans_template); @@ -143,21 +143,21 @@ class ConfigController extends Controller */ public function paymentAction() { - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $section = $this->request->getPost('section'); $data = $this->request->getPost(); - $service->updateSectionConfig($section, $data); + $configService->updateSectionConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $alipay = $service->getSectionConfig('payment.alipay'); - $wxpay = $service->getSectionConfig('payment.wxpay'); + $alipay = $configService->getSectionConfig('payment.alipay'); + $wxpay = $configService->getSectionConfig('payment.wxpay'); $this->view->setVar('alipay', $alipay); $this->view->setVar('wxpay', $wxpay); @@ -171,19 +171,19 @@ class ConfigController extends Controller { $section = 'smser'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateSmserConfig($section, $data); + $configService->updateSmserConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $smser = $service->getSectionConfig($section); + $smser = $configService->getSectionConfig($section); $template = json_decode($smser->template); @@ -199,19 +199,19 @@ class ConfigController extends Controller { $section = 'mailer'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateSectionConfig($section, $data); + $configService->updateSectionConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $mailer = $service->getSectionConfig($section); + $mailer = $configService->getSectionConfig($section); $this->view->setVar('mailer', $mailer); } @@ -224,13 +224,13 @@ class ConfigController extends Controller { $section = 'captcha'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateSectionConfig($section, $data); + $configService->updateSectionConfig($section, $data); $content = [ 'location' => $this->request->getHTTPReferer(), @@ -241,7 +241,7 @@ class ConfigController extends Controller } else { - $captcha = $service->getSectionConfig($section); + $captcha = $configService->getSectionConfig($section); $this->view->setVar('captcha', $captcha); } @@ -254,19 +254,19 @@ class ConfigController extends Controller { $section = 'vip'; - $service = new ConfigService(); + $configService = new ConfigService(); if ($this->request->isPost()) { $data = $this->request->getPost(); - $service->updateSectionConfig($section, $data); + $configService->updateSectionConfig($section, $data); return $this->ajaxSuccess(['msg' => '更新配置成功']); } else { - $vip = $service->getSectionConfig($section); + $vip = $configService->getSectionConfig($section); $this->view->setVar('vip', $vip); } diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index 72a5e1dc..3e40ab3c 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -16,17 +16,17 @@ class Controller extends \Phalcon\Mvc\Controller public function beforeExecuteRoute(Dispatcher $dispatcher) { - if ($this->notSafeRequest()) { + if ($this->isNotSafeRequest()) { if (!$this->checkHttpReferer() || !$this->checkCsrfToken()) { $dispatcher->forward([ 'controller' => 'public', - 'action' => 'csrf', + 'action' => 'robot', ]); return false; } } - $this->authUser = $this->getDI()->get('auth')->getAuthUser(); + $this->authUser = $this->getAuthUser(); if (!$this->authUser) { $dispatcher->forward([ @@ -43,7 +43,7 @@ class Controller extends \Phalcon\Mvc\Controller /** * 管理员忽略权限检查 */ - if ($this->authUser->admin) { + if ($this->authUser->root == 1) { return true; } @@ -51,26 +51,21 @@ class Controller extends \Phalcon\Mvc\Controller * 特例白名单 */ $whitelist = [ - 'controller' => [ - 'public', 'index', 'storage', 'vod', 'test', - 'xm_course', - ], - 'route' => [ - 'admin.package.guiding', - ], + 'controllers' => ['public', 'index', 'storage', 'vod', 'test', 'xm_course'], + 'routes' => ['admin.package.guiding'], ]; /** * 特定控制器忽略权限检查 */ - if (in_array($controller, $whitelist['controller'])) { + if (in_array($controller, $whitelist['controllers'])) { return true; } /** * 特定路由忽略权限检查 */ - if (in_array($route->getName(), $whitelist['route'])) { + if (in_array($route->getName(), $whitelist['routes'])) { return true; } @@ -110,4 +105,11 @@ class Controller extends \Phalcon\Mvc\Controller } } + protected function getAuthUser() + { + $auth = $this->getDI()->get('auth'); + + return $auth->getAuthInfo(); + } + } diff --git a/app/Http/Admin/Controllers/CourseController.php b/app/Http/Admin/Controllers/CourseController.php index 705512dd..d011beb5 100644 --- a/app/Http/Admin/Controllers/CourseController.php +++ b/app/Http/Admin/Controllers/CourseController.php @@ -65,7 +65,7 @@ class CourseController extends Controller } /** - * @Get("/{id}/edit", name="admin.course.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.course.edit") */ public function editAction($id) { @@ -83,7 +83,7 @@ class CourseController extends Controller } /** - * @Post("/{id}/update", name="admin.course.update") + * @Post("/{id:[0-9]+}/update", name="admin.course.update") */ public function updateAction($id) { @@ -97,7 +97,7 @@ class CourseController extends Controller } /** - * @Post("/{id}/delete", name="admin.course.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.course.delete") */ public function deleteAction($id) { @@ -114,7 +114,7 @@ class CourseController extends Controller } /** - * @Post("/{id}/restore", name="admin.course.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.course.restore") */ public function restoreAction($id) { @@ -131,7 +131,7 @@ class CourseController extends Controller } /** - * @Get("/{id}/chapters", name="admin.course.chapters") + * @Get("/{id:[0-9]+}/chapters", name="admin.course.chapters") */ public function chaptersAction($id) { diff --git a/app/Http/Admin/Controllers/HelpController.php b/app/Http/Admin/Controllers/HelpController.php new file mode 100644 index 00000000..ccbb1e27 --- /dev/null +++ b/app/Http/Admin/Controllers/HelpController.php @@ -0,0 +1,121 @@ +getHelps(); + + $this->view->setVar('helps', $helps); + } + + /** + * @Get("/add", name="admin.help.add") + */ + public function addAction() + { + + } + + /** + * @Post("/create", name="admin.help.create") + */ + public function createAction() + { + $helpService = new HelpService(); + + $helpService->createHelp(); + + $location = $this->url->get(['for' => 'admin.help.list']); + + $content = [ + 'location' => $location, + 'msg' => '创建帮助成功', + ]; + + return $this->ajaxSuccess($content); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="admin.help.edit") + */ + public function editAction($id) + { + $helpService = new HelpService; + + $help = $helpService->getHelp($id); + + $this->view->setVar('help', $help); + } + + /** + * @Post("/{id:[0-9]+}/update", name="admin.help.update") + */ + public function updateAction($id) + { + $helpService = new HelpService(); + + $helpService->updateHelp($id); + + $location = $this->url->get(['for' => 'admin.help.list']); + + $content = [ + 'location' => $location, + 'msg' => '更新帮助成功', + ]; + + return $this->ajaxSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="admin.help.delete") + */ + public function deleteAction($id) + { + $helpService = new HelpService(); + + $helpService->deleteHelp($id); + + $location = $this->request->getHTTPReferer(); + + $content = [ + 'location' => $location, + 'msg' => '删除帮助成功', + ]; + + return $this->ajaxSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/restore", name="admin.help.restore") + */ + public function restoreAction($id) + { + $helpService = new HelpService(); + + $helpService->restoreHelp($id); + + $location = $this->request->getHTTPReferer(); + + $content = [ + 'location' => $location, + 'msg' => '还原帮助成功', + ]; + + return $this->ajaxSuccess($content); + } + +} diff --git a/app/Http/Admin/Controllers/IndexController.php b/app/Http/Admin/Controllers/IndexController.php index 3e642706..37747f85 100644 --- a/app/Http/Admin/Controllers/IndexController.php +++ b/app/Http/Admin/Controllers/IndexController.php @@ -2,7 +2,7 @@ namespace App\Http\Admin\Controllers; -use App\Http\Admin\Services\AuthMenu as AuthMenuService; +use App\Http\Admin\Services\Index as IndexService; use Phalcon\Mvc\View; /** @@ -16,13 +16,12 @@ class IndexController extends Controller */ public function indexAction() { - $authMenu = new AuthMenuService(); + $indexService = new IndexService(); - $topMenus = $authMenu->getTopMenus(); - $leftMenus = $authMenu->getLeftMenus(); + $topMenus = $indexService->getTopMenus(); + $leftMenus = $indexService->getLeftMenus(); $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); - $this->view->setVar('top_menus', $topMenus); $this->view->setVar('left_menus', $leftMenus); } @@ -32,23 +31,6 @@ class IndexController extends Controller */ public function mainAction() { - /* - $service = new \App\Services\Order(); - $course = \App\Models\Course::findFirstById(1152); - $service->createCourseOrder($course); - */ - - /* - $service = new \App\Services\Order(); - $package = \App\Models\Package::findFirstById(5); - $service->createPackageOrder($package); - */ - - $refund = new \App\Services\Refund(); - $order = \App\Models\Order::findFirstById(131); - $amount = $refund->getRefundAmount($order); - - dd($amount); } diff --git a/app/Http/Admin/Controllers/NavController.php b/app/Http/Admin/Controllers/NavController.php index 9fbac3d3..a06922d2 100644 --- a/app/Http/Admin/Controllers/NavController.php +++ b/app/Http/Admin/Controllers/NavController.php @@ -64,7 +64,7 @@ class NavController extends Controller } /** - * @Get("/{id}/edit", name="admin.nav.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.nav.edit") */ public function editAction($id) { @@ -76,7 +76,7 @@ class NavController extends Controller } /** - * @Post("/{id}/update", name="admin.nav.update") + * @Post("/{id:[0-9]+}/update", name="admin.nav.update") */ public function updateAction($id) { @@ -100,7 +100,7 @@ class NavController extends Controller } /** - * @Post("/{id}/delete", name="admin.nav.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.nav.delete") */ public function deleteAction($id) { @@ -119,7 +119,7 @@ class NavController extends Controller } /** - * @Post("/{id}/restore", name="admin.nav.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.nav.restore") */ public function restoreAction($id) { diff --git a/app/Http/Admin/Controllers/OrderController.php b/app/Http/Admin/Controllers/OrderController.php index d5be7bb9..37648e91 100644 --- a/app/Http/Admin/Controllers/OrderController.php +++ b/app/Http/Admin/Controllers/OrderController.php @@ -31,7 +31,7 @@ class OrderController extends Controller } /** - * @Get("/{id}/show", name="admin.order.show") + * @Get("/{id:[0-9]+}/show", name="admin.order.show") */ public function showAction($id) { @@ -40,16 +40,18 @@ class OrderController extends Controller $order = $orderService->getOrder($id); $trades = $orderService->getTrades($order->sn); $refunds = $orderService->getRefunds($order->sn); + $account = $orderService->getAccount($order->user_id); $user = $orderService->getUser($order->user_id); $this->view->setVar('order', $order); $this->view->setVar('trades', $trades); $this->view->setVar('refunds', $refunds); + $this->view->setVar('account', $account); $this->view->setVar('user', $user); } /** - * @Post("/{id}/close", name="admin.order.close") + * @Post("/{id:[0-9]+}/close", name="admin.order.close") */ public function closeAction($id) { diff --git a/app/Http/Admin/Controllers/PackageController.php b/app/Http/Admin/Controllers/PackageController.php index 84041618..864b7814 100644 --- a/app/Http/Admin/Controllers/PackageController.php +++ b/app/Http/Admin/Controllers/PackageController.php @@ -69,7 +69,7 @@ class PackageController extends Controller } /** - * @Get("/{id}/edit", name="admin.package.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.package.edit") */ public function editAction($id) { @@ -83,7 +83,7 @@ class PackageController extends Controller } /** - * @Post("/{id}/update", name="admin.package.update") + * @Post("/{id:[0-9]+}/update", name="admin.package.update") */ public function updateAction($id) { @@ -97,7 +97,7 @@ class PackageController extends Controller } /** - * @Post("/{id}/delete", name="admin.package.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.package.delete") */ public function deleteAction($id) { @@ -114,7 +114,7 @@ class PackageController extends Controller } /** - * @Post("/{id}/restore", name="admin.package.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.package.restore") */ public function restoreAction($id) { diff --git a/app/Http/Admin/Controllers/PageController.php b/app/Http/Admin/Controllers/PageController.php index 047a0dc7..6d4f7bd1 100644 --- a/app/Http/Admin/Controllers/PageController.php +++ b/app/Http/Admin/Controllers/PageController.php @@ -15,9 +15,9 @@ class PageController extends Controller */ public function listAction() { - $service = new PageService(); + $pageService = new PageService(); - $pager = $service->getPages(); + $pager = $pageService->getPages(); $this->view->setVar('pager', $pager); } @@ -35,9 +35,9 @@ class PageController extends Controller */ public function createAction() { - $service = new PageService(); + $pageService = new PageService(); - $service->createPage(); + $pageService->createPage(); $location = $this->url->get(['for' => 'admin.page.list']); @@ -50,25 +50,25 @@ class PageController extends Controller } /** - * @Get("/{id}/edit", name="admin.page.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.page.edit") */ public function editAction($id) { - $service = new PageService; + $pageService = new PageService; - $page = $service->getPage($id); + $page = $pageService->getPage($id); $this->view->setVar('page', $page); } /** - * @Post("/{id}/update", name="admin.page.update") + * @Post("/{id:[0-9]+}/update", name="admin.page.update") */ public function updateAction($id) { - $service = new PageService(); + $pageService = new PageService(); - $service->updatePage($id); + $pageService->updatePage($id); $location = $this->url->get(['for' => 'admin.page.list']); @@ -81,13 +81,13 @@ class PageController extends Controller } /** - * @Post("/{id}/delete", name="admin.page.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.page.delete") */ public function deleteAction($id) { - $service = new PageService(); + $pageService = new PageService(); - $service->deletePage($id); + $pageService->deletePage($id); $location = $this->request->getHTTPReferer(); @@ -100,13 +100,13 @@ class PageController extends Controller } /** - * @Post("/{id}/restore", name="admin.page.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.page.restore") */ public function restoreAction($id) { - $service = new PageService(); + $pageService = new PageService(); - $service->restorePage($id); + $pageService->restorePage($id); $location = $this->request->getHTTPReferer(); diff --git a/app/Http/Admin/Controllers/PublicController.php b/app/Http/Admin/Controllers/PublicController.php index 4f6793ec..dbceb597 100644 --- a/app/Http/Admin/Controllers/PublicController.php +++ b/app/Http/Admin/Controllers/PublicController.php @@ -25,15 +25,13 @@ class PublicController extends \Phalcon\Mvc\Controller } /** - * @Route("/csrf", name="admin.csrf") + * @Route("/robot", name="admin.robot") */ - public function csrfAction() + public function robotAction() { if ($this->request->isAjax()) { - return $this->ajaxError(['msg' => 'CSRF令牌验证失败']); + return $this->ajaxError(['msg' => '疑似机器人请求']); } - - $this->view->pick('public/csrf'); } /** @@ -44,8 +42,6 @@ class PublicController extends \Phalcon\Mvc\Controller if ($this->request->isAjax()) { return $this->ajaxError(['msg' => '无相关操作权限']); } - - $this->view->pick('public/forbidden'); } /** diff --git a/app/Http/Admin/Controllers/RefundController.php b/app/Http/Admin/Controllers/RefundController.php index ab15c55f..1f087576 100644 --- a/app/Http/Admin/Controllers/RefundController.php +++ b/app/Http/Admin/Controllers/RefundController.php @@ -31,7 +31,7 @@ class RefundController extends Controller } /** - * @Get("/{id}/show", name="admin.refund.show") + * @Get("/{id:[0-9]+}/show", name="admin.refund.show") */ public function showAction($id) { @@ -40,16 +40,18 @@ class RefundController extends Controller $refund = $refundService->getRefund($id); $order = $refundService->getOrder($refund->order_sn); $trade = $refundService->getTrade($refund->trade_sn); + $account = $refundService->getAccount($trade->user_id); $user = $refundService->getUser($trade->user_id); $this->view->setVar('refund', $refund); $this->view->setVar('order', $order); $this->view->setVar('trade', $trade); + $this->view->setVar('account', $account); $this->view->setVar('user', $user); } /** - * @Post("/{id}/review", name="admin.refund.review") + * @Post("/{id:[0-9]+}/review", name="admin.refund.review") */ public function reviewAction($id) { diff --git a/app/Http/Admin/Controllers/ReviewController.php b/app/Http/Admin/Controllers/ReviewController.php index aa1a5a79..456ccd29 100644 --- a/app/Http/Admin/Controllers/ReviewController.php +++ b/app/Http/Admin/Controllers/ReviewController.php @@ -23,16 +23,16 @@ class ReviewController extends Controller */ public function listAction() { - $service = new ReviewService(); + $reviewService = new ReviewService(); - $pager = $service->getReviews(); + $pager = $reviewService->getReviews(); $courseId = $this->request->getQuery('course_id', 'int', 0); $course = null; if ($courseId > 0) { - $course = $service->getCourse($courseId); + $course = $reviewService->getCourse($courseId); } $this->view->setVar('pager', $pager); @@ -40,25 +40,25 @@ class ReviewController extends Controller } /** - * @Get("/{id}/edit", name="admin.review.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.review.edit") */ public function editAction($id) { - $service = new ReviewService(); + $reviewService = new ReviewService(); - $review = $service->getReview($id); + $review = $reviewService->getReview($id); $this->view->setVar('review', $review); } /** - * @Post("/{id}/update", name="admin.review.update") + * @Post("/{id:[0-9]+}/update", name="admin.review.update") */ public function updateAction($id) { - $service = new ReviewService(); + $reviewService = new ReviewService(); - $service->updateReview($id); + $reviewService->updateReview($id); $content = [ 'msg' => '更新评价成功', @@ -68,13 +68,13 @@ class ReviewController extends Controller } /** - * @Post("/{id}/delete", name="admin.review.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.review.delete") */ public function deleteAction($id) { - $service = new ReviewService(); + $reviewService = new ReviewService(); - $service->deleteReview($id); + $reviewService->deleteReview($id); $location = $this->request->getHTTPReferer(); @@ -87,13 +87,13 @@ class ReviewController extends Controller } /** - * @Post("/{id}/restore", name="admin.review.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.review.restore") */ public function restoreAction($id) { - $service = new ReviewService(); + $reviewService = new ReviewService(); - $service->restoreReview($id); + $reviewService->restoreReview($id); $location = $this->request->getHTTPReferer(); diff --git a/app/Http/Admin/Controllers/RoleController.php b/app/Http/Admin/Controllers/RoleController.php index 31462d35..9f5845b4 100644 --- a/app/Http/Admin/Controllers/RoleController.php +++ b/app/Http/Admin/Controllers/RoleController.php @@ -2,7 +2,6 @@ namespace App\Http\Admin\Controllers; -use App\Http\Admin\Services\AuthNode; use App\Http\Admin\Services\Role as RoleService; /** @@ -54,26 +53,21 @@ class RoleController extends Controller } /** - * @Get("/{id}/edit", name="admin.role.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.role.edit") */ public function editAction($id) { $roleService = new RoleService(); $role = $roleService->getRole($id); - - //dd($role->routes); - - $adminNode = new AuthNode(); - - $nodes = $adminNode->getAllNodes(); + $authNodes = $roleService->getAuthNodes(); $this->view->setVar('role', $role); - $this->view->setVar('nodes', kg_array_object($nodes)); + $this->view->setVar('auth_nodes', $authNodes); } /** - * @Post("/{id}/update", name="admin.role.update") + * @Post("/{id:[0-9]+}/update", name="admin.role.update") */ public function updateAction($id) { @@ -92,7 +86,7 @@ class RoleController extends Controller } /** - * @Post("/{id}/delete", name="admin.role.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.role.delete") */ public function deleteAction($id) { @@ -111,7 +105,7 @@ class RoleController extends Controller } /** - * @Post("/{id}/restore", name="admin.role.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.role.restore") */ public function restoreAction($id) { diff --git a/app/Http/Admin/Controllers/SessionController.php b/app/Http/Admin/Controllers/SessionController.php index efbe80e0..d1e8ec9f 100644 --- a/app/Http/Admin/Controllers/SessionController.php +++ b/app/Http/Admin/Controllers/SessionController.php @@ -26,7 +26,7 @@ class SessionController extends \Phalcon\Mvc\Controller if (!$this->checkHttpReferer() || !$this->checkCsrfToken()) { $this->dispatcher->forward([ 'controller' => 'public', - 'action' => 'forbidden', + 'action' => 'robot', ]); return false; } diff --git a/app/Http/Admin/Controllers/SlideController.php b/app/Http/Admin/Controllers/SlideController.php index 754d2db3..342c12ad 100644 --- a/app/Http/Admin/Controllers/SlideController.php +++ b/app/Http/Admin/Controllers/SlideController.php @@ -15,9 +15,9 @@ class SlideController extends Controller */ public function listAction() { - $service = new SlideService(); + $slideService = new SlideService(); - $pager = $service->getSlides(); + $pager = $slideService->getSlides(); $this->view->setVar('pager', $pager); } @@ -35,9 +35,9 @@ class SlideController extends Controller */ public function createAction() { - $service = new SlideService(); + $slideService = new SlideService(); - $slide = $service->createSlide(); + $slide = $slideService->createSlide(); $location = $this->url->get([ 'for' => 'admin.slide.edit', @@ -53,25 +53,25 @@ class SlideController extends Controller } /** - * @Get("/{id}/edit", name="admin.slide.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.slide.edit") */ public function editAction($id) { - $service = new SlideService(); + $slideService = new SlideService(); - $slide = $service->getSlide($id); + $slide = $slideService->getSlide($id); $this->view->setVar('slide', $slide); } /** - * @Post("/{id}/update", name="admin.slide.update") + * @Post("/{id:[0-9]+}/update", name="admin.slide.update") */ public function updateAction($id) { - $service = new SlideService(); + $slideService = new SlideService(); - $service->updateSlide($id); + $slideService->updateSlide($id); $location = $this->url->get(['for' => 'admin.slide.list']); @@ -84,13 +84,13 @@ class SlideController extends Controller } /** - * @Post("/{id}/delete", name="admin.slide.delete") + * @Post("/{id:[0-9]+}/delete", name="admin.slide.delete") */ public function deleteAction($id) { - $service = new SlideService(); + $slideService = new SlideService(); - $service->deleteSlide($id); + $slideService->deleteSlide($id); $location = $this->request->getHTTPReferer(); @@ -103,13 +103,13 @@ class SlideController extends Controller } /** - * @Post("/{id}/restore", name="admin.slide.restore") + * @Post("/{id:[0-9]+}/restore", name="admin.slide.restore") */ public function restoreAction($id) { - $service = new SlideService(); + $slideService = new SlideService(); - $service->restoreSlide($id); + $slideService->restoreSlide($id); $location = $this->request->getHTTPReferer(); diff --git a/app/Http/Admin/Controllers/StudentController.php b/app/Http/Admin/Controllers/StudentController.php index 6ceb4e22..2e96caba 100644 --- a/app/Http/Admin/Controllers/StudentController.php +++ b/app/Http/Admin/Controllers/StudentController.php @@ -25,9 +25,9 @@ class StudentController extends Controller { $courseId = $this->request->getQuery('course_id', 'int', ''); - $service = new CourseStudentService(); + $courseStudentService = new CourseStudentService(); - $pager = $service->getCourseStudents(); + $pager = $courseStudentService->getCourseStudents(); $this->view->setVar('pager', $pager); $this->view->setVar('course_id', $courseId); @@ -48,9 +48,9 @@ class StudentController extends Controller */ public function createAction() { - $service = new CourseStudentService(); + $courseStudentService = new CourseStudentService(); - $student = $service->createCourseStudent(); + $student = $courseStudentService->createCourseStudent(); $location = $this->url->get( ['for' => 'admin.student.list'], @@ -73,11 +73,11 @@ class StudentController extends Controller $courseId = $this->request->getQuery('course_id', 'int'); $userId = $this->request->getQuery('user_id', 'int'); - $service = new CourseStudentService(); + $courseStudentService = new CourseStudentService(); - $courseStudent = $service->getCourseStudent($courseId, $userId); - $course = $service->getCourse($courseId); - $student = $service->getStudent($userId); + $courseStudent = $courseStudentService->getCourseStudent($courseId, $userId); + $course = $courseStudentService->getCourse($courseId); + $student = $courseStudentService->getStudent($userId); $this->view->setVar('course_student', $courseStudent); $this->view->setVar('course', $course); @@ -89,9 +89,9 @@ class StudentController extends Controller */ public function updateAction() { - $service = new CourseStudentService(); + $courseStudentService = new CourseStudentService(); - $student = $service->updateCourseStudent(); + $student = $courseStudentService->updateCourseStudent(); $location = $this->url->get( ['for' => 'admin.student.list'], @@ -111,9 +111,9 @@ class StudentController extends Controller */ public function learningAction() { - $service = new CourseStudentService(); + $courseStudentService = new CourseStudentService(); - $pager = $service->getCourseLearnings(); + $pager = $courseStudentService->getCourseLearnings(); $this->view->setVar('pager', $pager); } diff --git a/app/Http/Admin/Controllers/TopicController.php b/app/Http/Admin/Controllers/TopicController.php new file mode 100644 index 00000000..1c94ef80 --- /dev/null +++ b/app/Http/Admin/Controllers/TopicController.php @@ -0,0 +1,117 @@ +getTopics(); + + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/add", name="admin.topic.add") + */ + public function addAction() + { + + } + + /** + * @Post("/create", name="admin.topic.create") + */ + public function createAction() + { + $topicService = new TopicService(); + + $topic = $topicService->createTopic(); + + $location = $this->url->get([ + 'for' => 'admin.topic.edit', + 'id' => $topic->id, + ]); + + $content = [ + 'location' => $location, + 'msg' => '创建话题成功', + ]; + + return $this->ajaxSuccess($content); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="admin.topic.edit") + */ + public function editAction($id) + { + $topicService = new TopicService(); + + $topic = $topicService->getTopic($id); + $xmCourses = $topicService->getXmCourses($id); + + $this->view->setVar('topic', $topic); + $this->view->setVar('xm_courses', $xmCourses); + } + + /** + * @Post("/{id:[0-9]+}/update", name="admin.topic.update") + */ + public function updateAction($id) + { + $topicService = new TopicService(); + + $topicService->updateTopic($id); + + $content = ['msg' => '更新话题成功']; + + return $this->ajaxSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="admin.topic.delete") + */ + public function deleteAction($id) + { + $topicService = new TopicService(); + + $topicService->deleteTopic($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '删除话题成功', + ]; + + return $this->ajaxSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/restore", name="admin.topic.restore") + */ + public function restoreAction($id) + { + $topicService = new TopicService(); + + $topicService->restoreTopic($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '还原话题成功', + ]; + + return $this->ajaxSuccess($content); + } + +} diff --git a/app/Http/Admin/Controllers/TradeController.php b/app/Http/Admin/Controllers/TradeController.php index f5869b41..d547df2f 100644 --- a/app/Http/Admin/Controllers/TradeController.php +++ b/app/Http/Admin/Controllers/TradeController.php @@ -31,7 +31,7 @@ class TradeController extends Controller } /** - * @Get("/{id}/show", name="admin.trade.show") + * @Get("/{id:[0-9]+}/show", name="admin.trade.show") */ public function showAction($id) { @@ -40,16 +40,18 @@ class TradeController extends Controller $trade = $tradeService->getTrade($id); $refunds = $tradeService->getRefunds($trade->sn); $order = $tradeService->getOrder($trade->order_sn); + $account = $tradeService->getAccount($trade->user_id); $user = $tradeService->getUser($trade->user_id); $this->view->setVar('refunds', $refunds); $this->view->setVar('trade', $trade); $this->view->setVar('order', $order); + $this->view->setVar('account', $account); $this->view->setVar('user', $user); } /** - * @Post("/{id}/close", name="admin.trade.close") + * @Post("/{id:[0-9]+}/close", name="admin.trade.close") */ public function closeAction($id) { @@ -68,7 +70,7 @@ class TradeController extends Controller } /** - * @Post("/{id}/refund", name="admin.trade.refund") + * @Post("/{id:[0-9]+}/refund", name="admin.trade.refund") */ public function refundAction($id) { diff --git a/app/Http/Admin/Controllers/UserController.php b/app/Http/Admin/Controllers/UserController.php index b60b9bea..8b068691 100644 --- a/app/Http/Admin/Controllers/UserController.php +++ b/app/Http/Admin/Controllers/UserController.php @@ -35,7 +35,7 @@ class UserController extends Controller } /** - * @Get("/{id}/show", name="admin.user.show") + * @Get("/{id:[0-9]+}/show", name="admin.user.show") */ public function showAction($id) { @@ -74,7 +74,7 @@ class UserController extends Controller } /** - * @Get("/{id}/edit", name="admin.user.edit") + * @Get("/{id:[0-9]+}/edit", name="admin.user.edit") */ public function editAction($id) { @@ -88,7 +88,7 @@ class UserController extends Controller } /** - * @Post("/{id}/update", name="admin.user.update") + * @Post("/{id:[0-9]+}/update", name="admin.user.update") */ public function updateAction($id) { diff --git a/app/Http/Admin/Controllers/VodController.php b/app/Http/Admin/Controllers/VodController.php index f84d7745..a85a8f06 100644 --- a/app/Http/Admin/Controllers/VodController.php +++ b/app/Http/Admin/Controllers/VodController.php @@ -3,7 +3,7 @@ namespace App\Http\Admin\Controllers; use App\Models\Learning as LearningModel; -use App\Services\Learning as LearningService; +use App\Services\LearningSyncer as LearningSyncerService; use App\Services\Vod as VodService; use Phalcon\Mvc\View; @@ -58,9 +58,9 @@ class VodController extends Controller $learning->chapter_id = $query['chapter_id']; $learning->position = $query['position']; - $learningService = new LearningService(); + $syncerService = new LearningSyncerService(); - $learningService->save($learning, $query['timeout']); + $syncerService->save($learning, $query['timeout']); return $this->ajaxSuccess(); } diff --git a/app/Http/Admin/Module.php b/app/Http/Admin/Module.php index 9ae9f52e..c7e46068 100644 --- a/app/Http/Admin/Module.php +++ b/app/Http/Admin/Module.php @@ -17,7 +17,7 @@ class Module implements ModuleDefinitionInterface public function registerServices(DiInterface $di) { - $di->set('view', function () { + $di->setShared('view', function () { $view = new View(); $view->setViewsDir(__DIR__ . '/Views'); $view->registerEngines([ diff --git a/app/Http/Admin/Services/AuthMenu.php b/app/Http/Admin/Services/AuthMenu.php index 7f23b0b4..31a13654 100644 --- a/app/Http/Admin/Services/AuthMenu.php +++ b/app/Http/Admin/Services/AuthMenu.php @@ -8,7 +8,7 @@ class AuthMenu extends UserComponent { - protected $nodes = []; + protected $authNodes = []; protected $ownedRoutes = []; protected $owned1stLevelIds = []; protected $owned2ndLevelIds = []; @@ -17,8 +17,8 @@ class AuthMenu extends UserComponent public function __construct() { - $this->authUser = $this->getAuthUser(); - $this->nodes = $this->getAllNodes(); + $this->authUser = $this->getAuthInfo(); + $this->authNodes = $this->getAuthNodes(); $this->setOwnedLevelIds(); } @@ -26,8 +26,8 @@ class AuthMenu extends UserComponent { $menus = []; - foreach ($this->nodes as $node) { - if ($this->authUser->admin || in_array($node['id'], $this->owned1stLevelIds)) { + foreach ($this->authNodes as $node) { + if ($this->authUser->root || in_array($node['id'], $this->owned1stLevelIds)) { $menus[] = [ 'id' => $node['id'], 'label' => $node['label'], @@ -35,17 +35,17 @@ class AuthMenu extends UserComponent } } - return kg_array_object($menus); + return $menus; } public function getLeftMenus() { $menus = []; - foreach ($this->nodes as $key => $level) { + foreach ($this->authNodes as $key => $level) { foreach ($level['child'] as $key2 => $level2) { foreach ($level2['child'] as $key3 => $level3) { - $hasRight = $this->authUser->admin || in_array($level3['id'], $this->owned3rdLevelIds); + $hasRight = $this->authUser->root || in_array($level3['id'], $this->owned3rdLevelIds); if ($level3['type'] == 'menu' && $hasRight) { $menus[$key]['id'] = $level['id']; $menus[$key]['label'] = $level['label']; @@ -61,7 +61,7 @@ class AuthMenu extends UserComponent } } - return kg_array_object($menus); + return $menus; } protected function setOwnedLevelIds() @@ -92,7 +92,7 @@ class AuthMenu extends UserComponent { $mapping = []; - foreach ($this->nodes as $level) { + foreach ($this->authNodes as $level) { foreach ($level['child'] as $level2) { foreach ($level2['child'] as $level3) { if ($level3['type'] == 'menu') { @@ -105,18 +105,18 @@ class AuthMenu extends UserComponent return $mapping; } - protected function getAllNodes() + protected function getAuthNodes() { $authNode = new AuthNode(); - return $authNode->getAllNodes(); + return $authNode->getNodes(); } - protected function getAuthUser() + protected function getAuthInfo() { $auth = $this->getDI()->get('auth'); - return $auth->getAuthUser(); + return $auth->getAuthInfo(); } } diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 800d3b6d..384fe6e2 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -5,7 +5,7 @@ namespace App\Http\Admin\Services; class AuthNode extends Service { - public function getAllNodes() + public function getNodes() { $nodes = []; @@ -123,6 +123,37 @@ class AuthNode extends Service ], ], ], + [ + 'id' => '1-4', + 'label' => '话题管理', + 'type' => 'menu', + 'child' => [ + [ + 'id' => '1-4-1', + 'label' => '话题列表', + 'type' => 'menu', + 'route' => 'admin.topic.list', + ], + [ + 'id' => '1-4-2', + 'label' => '添加话题', + 'type' => 'menu', + 'route' => 'admin.topic.add', + ], + [ + 'id' => '1-4-3', + 'label' => '编辑话题', + 'type' => 'button', + 'route' => 'admin.topic.edit', + ], + [ + 'id' => '1-4-4', + 'label' => '删除话题', + 'type' => 'button', + 'route' => 'admin.topic.delete', + ], + ], + ], ], ]; @@ -290,6 +321,37 @@ class AuthNode extends Service ], ], ], + [ + 'id' => '2-7', + 'label' => '帮助管理', + 'type' => 'menu', + 'child' => [ + [ + 'id' => '2-7-1', + 'label' => '帮助列表', + 'type' => 'menu', + 'route' => 'admin.help.list', + ], + [ + 'id' => '2-7-2', + 'label' => '添加帮助', + 'type' => 'menu', + 'route' => 'admin.help.add', + ], + [ + 'id' => '2-7-3', + 'label' => '编辑帮助', + 'type' => 'button', + 'route' => 'admin.help.edit', + ], + [ + 'id' => '2-7-4', + 'label' => '删除帮助', + 'type' => 'button', + 'route' => 'admin.help.delete', + ], + ], + ], ], ]; @@ -515,7 +577,7 @@ class AuthNode extends Service 'id' => '5-1-1', 'label' => '网站设置', 'type' => 'menu', - 'route' => 'admin.config.website', + 'route' => 'admin.config.site', ], [ 'id' => '5-1-2', diff --git a/app/Http/Admin/Services/AuthUser.php b/app/Http/Admin/Services/AuthUser.php index 7bd1b3b8..5623d0f9 100644 --- a/app/Http/Admin/Services/AuthUser.php +++ b/app/Http/Admin/Services/AuthUser.php @@ -4,9 +4,9 @@ namespace App\Http\Admin\Services; use App\Models\Role as RoleModel; use App\Models\User as UserModel; -use Phalcon\Mvc\User\Component as UserComponent; +use App\Repos\Role as RoleRepo; -class AuthUser extends UserComponent +class AuthUser extends \Phalcon\Mvc\User\Component { /** @@ -17,11 +17,15 @@ class AuthUser extends UserComponent */ public function hasPermission($route) { - $authUser = $this->getAuthUser(); + $authUser = $this->getAuthInfo(); - if ($authUser->admin) return true; + if ($authUser->root) { + return true; + } - if (in_array($route, $authUser->routes)) return true; + if (in_array($route, $authUser->routes)) { + return true; + } return false; } @@ -31,27 +35,23 @@ class AuthUser extends UserComponent * * @param UserModel $user */ - public function setAuthUser(UserModel $user) + public function setAuthInfo(UserModel $user) { - $role = RoleModel::findFirstById($user->admin_role); + $roleRepo = new RoleRepo(); - if ($role->id == RoleModel::ROLE_ADMIN) { - $admin = 1; - $routes = []; - } else { - $admin = 0; - $routes = $role->routes; - } + $role = $roleRepo->findById($user->admin_role); - $authKey = $this->getAuthKey(); + $root = $role->id == RoleModel::ROLE_ROOT ? 1 : 0; $authUser = new \stdClass(); $authUser->id = $user->id; $authUser->name = $user->name; $authUser->avatar = $user->avatar; - $authUser->admin = $admin; - $authUser->routes = $routes; + $authUser->routes = $role->routes; + $authUser->root = $root; + + $authKey = $this->getAuthKey(); $this->session->set($authKey, $authUser); } @@ -59,7 +59,7 @@ class AuthUser extends UserComponent /** * 清除会话 */ - public function removeAuthUser() + public function removeAuthInfo() { $authKey = $this->getAuthKey(); @@ -71,7 +71,7 @@ class AuthUser extends UserComponent * * @return mixed */ - public function getAuthUser() + public function getAuthInfo() { $authKey = $this->getAuthKey(); @@ -85,7 +85,7 @@ class AuthUser extends UserComponent */ public function getAuthKey() { - return 'admin'; + return 'admin_info'; } } diff --git a/app/Http/Admin/Services/Category.php b/app/Http/Admin/Services/Category.php index 814c8561..3d2ee3e6 100644 --- a/app/Http/Admin/Services/Category.php +++ b/app/Http/Admin/Services/Category.php @@ -91,6 +91,8 @@ class Category extends Service $category->update(); + $this->updateCategoryStats($category); + return $category; } @@ -114,10 +116,19 @@ class Category extends Service if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); + if ($category->parent_id == 0) { + if ($category->published == 0 && $post['published'] == 1) { + $this->enableChildCategories($category->id); + } elseif ($category->published == 1 && $post['published'] == 0) { + $this->disableChildCategories($category->id); + } + } } $category->update($data); + $this->updateCategoryStats($category); + return $category; } @@ -125,14 +136,16 @@ class Category extends Service { $category = $this->findOrFail($id); - if ($category->deleted == 1) { - return false; - } + $validator = new CategoryValidator(); + + $validator->checkDeleteAbility($category); $category->deleted = 1; $category->update(); + $this->updateCategoryStats($category); + return $category; } @@ -140,17 +153,60 @@ class Category extends Service { $category = $this->findOrFail($id); - if ($category->deleted == 0) { - return false; - } - $category->deleted = 0; $category->update(); + $this->updateCategoryStats($category); + return $category; } + protected function updateCategoryStats(CategoryModel $category) + { + $categoryRepo = new CategoryRepo(); + + if ($category->parent_id > 0) { + $category = $categoryRepo->findById($category->parent_id); + } + + $childCount = $categoryRepo->countChildCategories($category->id); + $category->child_count = $childCount; + $category->update(); + } + + protected function enableChildCategories($parentId) + { + $categoryRepo = new CategoryRepo(); + + $categories = $categoryRepo->findAll(['parent_id' => $parentId]); + + if ($categories->count() == 0) { + return; + } + + foreach ($categories as $category) { + $category->published = 1; + $category->update(); + } + } + + protected function disableChildCategories($parentId) + { + $categoryRepo = new CategoryRepo(); + + $categories = $categoryRepo->findAll(['parent_id' => $parentId]); + + if ($categories->count() == 0) { + return; + } + + foreach ($categories as $category) { + $category->published = 0; + $category->update(); + } + } + protected function findOrFail($id) { $validator = new CategoryValidator(); diff --git a/app/Http/Admin/Services/Chapter.php b/app/Http/Admin/Services/Chapter.php index 5aee88a0..b96ab6ed 100644 --- a/app/Http/Admin/Services/Chapter.php +++ b/app/Http/Admin/Services/Chapter.php @@ -12,28 +12,6 @@ use App\Validators\Chapter as ChapterValidator; class Chapter extends Service { - public function getCourse($courseId) - { - $courseRepo = new CourseRepo(); - - $result = $courseRepo->findById($courseId); - - return $result; - } - - public function getCourseChapters($courseId) - { - $chapterRepo = new ChapterRepo(); - - $result = $chapterRepo->findAll([ - 'course_id' => $courseId, - 'parent_id' => 0, - 'deleted' => 0, - ]); - - return $result; - } - public function getLessons($parentId) { $deleted = $this->request->getQuery('deleted', 'int', 0); @@ -117,7 +95,7 @@ class Chapter extends Service if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); - if ($post['published'] == 1) { + if ($chapter->published == 0 && $post['published'] == 1) { $validator->checkPublishAbility($chapter); } } @@ -134,17 +112,16 @@ class Chapter extends Service { $chapter = $this->findOrFail($id); - if ($chapter->deleted == 1) { - return false; - } + $validator = new ChapterValidator(); + + $validator->checkDeleteAbility($chapter); $chapter->deleted = 1; $chapter->update(); - if ($chapter->parent_id == 0) { - $this->deleteChildChapters($chapter->id); - } + $this->updateChapterStats($chapter); + $this->updateCourseStats($chapter); return $chapter; } @@ -153,57 +130,17 @@ class Chapter extends Service { $chapter = $this->findOrFail($id); - if ($chapter->deleted == 0) { - return false; - } - $chapter->deleted = 0; $chapter->update(); - if ($chapter->parent_id == 0) { - $this->restoreChildChapters($chapter->id); - } - $this->updateChapterStats($chapter); $this->updateCourseStats($chapter); return $chapter; } - protected function deleteChildChapters($parentId) - { - $chapterRepo = new ChapterRepo(); - - $chapters = $chapterRepo->findAll(['parent_id' => $parentId]); - - if ($chapters->count() == 0) { - return; - } - - foreach ($chapters as $chapter) { - $chapter->deleted = 1; - $chapter->update(); - } - } - - protected function restoreChildChapters($parentId) - { - $chapterRepo = new ChapterRepo(); - - $chapters = $chapterRepo->findAll(['parent_id' => $parentId]); - - if ($chapters->count() == 0) { - return; - } - - foreach ($chapters as $chapter) { - $chapter->deleted = 0; - $chapter->update(); - } - } - - protected function updateChapterStats($chapter) + protected function updateChapterStats(ChapterModel $chapter) { $chapterRepo = new ChapterRepo(); @@ -214,10 +151,9 @@ class Chapter extends Service $lessonCount = $chapterRepo->countLessons($chapter->id); $chapter->lesson_count = $lessonCount; $chapter->update(); - } - protected function updateCourseStats($chapter) + protected function updateCourseStats(ChapterModel $chapter) { $courseRepo = new CourseRepo(); @@ -228,11 +164,11 @@ class Chapter extends Service $courseStats->updateLessonCount($course->id); if ($course->model == CourseModel::MODEL_VOD) { - $courseStats->updateVodDuration($course->id); + $courseStats->updateVodAttrs($course->id); } elseif ($course->model == CourseModel::MODEL_LIVE) { - $courseStats->updateLiveDateRange($course->id); - } elseif ($course->model == CourseModel::MODEL_ARTICLE) { - $courseStats->updateArticleWordCount($course->id); + $courseStats->updateLiveAttrs($course->id); + } elseif ($course->model == CourseModel::MODEL_READ) { + $courseStats->updateReadAttrs($course->id); } } diff --git a/app/Http/Admin/Services/ChapterContent.php b/app/Http/Admin/Services/ChapterContent.php index a91328a8..38c8d109 100644 --- a/app/Http/Admin/Services/ChapterContent.php +++ b/app/Http/Admin/Services/ChapterContent.php @@ -7,10 +7,10 @@ use App\Models\Chapter as ChapterModel; use App\Models\Course as CourseModel; use App\Repos\Chapter as ChapterRepo; use App\Repos\Course as CourseRepo; +use App\Services\ChapterVod as ChapterVodService; use App\Services\CourseStats as CourseStatsService; -use App\Services\Vod as VodService; -use App\Validators\ChapterArticle as ChapterArticleValidator; use App\Validators\ChapterLive as ChapterLiveValidator; +use App\Validators\ChapterRead as ChapterReadValidator; use App\Validators\ChapterVod as ChapterVodValidator; class ChapterContent extends Service @@ -34,48 +34,22 @@ class ChapterContent extends Service return $result; } - public function getChapterArticle($chapterId) + public function getChapterRead($chapterId) { $chapterRepo = new ChapterRepo(); - $result = $chapterRepo->findChapterArticle($chapterId); + $result = $chapterRepo->findChapterRead($chapterId); return $result; } - public function getTranslatedFiles($fileId) + public function getVodFiles($chapterId) { - if (!$fileId) return; + $chapterVodService = new ChapterVodService(); - $vodService = new VodService(); + $vodFiles = $chapterVodService->getVodFiles($chapterId); - $mediaInfo = $vodService->getMediaInfo($fileId); - - if (!$mediaInfo) return; - - $result = []; - - $files = $mediaInfo['MediaInfoSet'][0]['TranscodeInfo']['TranscodeSet']; - - foreach ($files as $file) { - - if ($file['Definition'] == 0) { - continue; - } - - $result[] = [ - 'play_url' => $vodService->getPlayUrl($file['Url']), - 'width' => $file['Width'], - 'height' => $file['Height'], - 'definition' => $file['Definition'], - 'duration' => kg_play_duration($file['Duration']), - 'format' => pathinfo($file['Url'], PATHINFO_EXTENSION), - 'size' => sprintf('%0.2f', $file['Size'] / 1024 / 1024), - 'bit_rate' => intval($file['Bitrate'] / 1024), - ]; - } - - return kg_array_object($result); + return kg_array_object($vodFiles); } public function updateChapterContent($chapterId) @@ -93,13 +67,13 @@ class ChapterContent extends Service case CourseModel::MODEL_LIVE: $this->updateChapterLive($chapter); break; - case CourseModel::MODEL_ARTICLE: - $this->updateChapterArticle($chapter); + case CourseModel::MODEL_READ: + $this->updateChapterRead($chapter); break; } } - protected function updateChapterVod($chapter) + protected function updateChapterVod(ChapterModel $chapter) { $post = $this->request->getPost(); @@ -115,15 +89,28 @@ class ChapterContent extends Service return; } - $vod->update(['file_id' => $fileId]); + $vod->update([ + 'file_id' => $fileId, + 'file_attrs' => '', + ]); + /** + * @var array $attrs + */ $attrs = $chapter->attrs; - $attrs->file_id = $fileId; - $attrs->file_status = ChapterModel::FS_UPLOADED; + + $attrs['duration'] = 0; + $attrs['file_id'] = $fileId; + $attrs['file_status'] = ChapterModel::FS_UPLOADED; + $chapter->update(['attrs' => $attrs]); + + $courseStats = new CourseStatsService(); + + $courseStats->updateVodAttrs($chapter->course_id); } - protected function updateChapterLive($chapter) + protected function updateChapterLive(ChapterModel $chapter) { $post = $this->request->getPost(); @@ -142,37 +129,50 @@ class ChapterContent extends Service $live->update($data); + /** + * @var array $attrs + */ $attrs = $chapter->attrs; - $attrs->start_time = $data['start_time']; - $attrs->end_time = $data['end_time']; + + $attrs['start_time'] = $data['start_time']; + $attrs['end_time'] = $data['end_time']; + $chapter->update(['attrs' => $attrs]); $courseStats = new CourseStatsService(); - $courseStats->updateLiveDateRange($chapter->course_id); + + $courseStats->updateLiveAttrs($chapter->course_id); } - protected function updateChapterArticle($chapter) + protected function updateChapterRead(ChapterModel $chapter) { $post = $this->request->getPost(); $chapterRepo = new ChapterRepo(); - $article = $chapterRepo->findChapterArticle($chapter->id); + $read = $chapterRepo->findChapterRead($chapter->id); - $validator = new ChapterArticleValidator(); + $validator = new ChapterReadValidator(); $data = []; $data['content'] = $validator->checkContent($post['content']); - $article->update($data); + $read->update($data); + /** + * @var array $attrs + */ $attrs = $chapter->attrs; - $attrs->word_count = WordUtil::getWordCount($article->content); + + $attrs['word_count'] = WordUtil::getWordCount($read->content); + $attrs['duration'] = WordUtil::getWordDuration($read->content); + $chapter->update(['attrs' => $attrs]); $courseStats = new CourseStatsService(); - $courseStats->updateArticleWordCount($chapter->course_id); + + $courseStats->updateReadAttrs($chapter->course_id); } } diff --git a/app/Http/Admin/Services/Config.php b/app/Http/Admin/Services/Config.php index c79ccbdf..428c2f3f 100644 --- a/app/Http/Admin/Services/Config.php +++ b/app/Http/Admin/Services/Config.php @@ -2,7 +2,6 @@ namespace App\Http\Admin\Services; -use App\Caches\Config as ConfigCache; use App\Repos\Config as ConfigRepo; class Config extends Service @@ -36,10 +35,6 @@ class Config extends Service $item->update(); } } - - $configCache = new ConfigCache(); - - $configCache->delete(); } public function updateStorageConfig($section, $config) diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index 66b33b61..fca9fdfc 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -2,6 +2,7 @@ namespace App\Http\Admin\Services; +use App\Builders\CourseList as CourseListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Models\Course as CourseModel; use App\Models\CourseCategory as CourseCategoryModel; @@ -14,7 +15,6 @@ use App\Repos\CourseCategory as CourseCategoryRepo; use App\Repos\CourseRelated as CourseRelatedRepo; use App\Repos\CourseUser as CourseUserRepo; use App\Repos\User as UserRepo; -use App\Transformers\CourseList as CourseListTransformer; use App\Validators\Course as CourseValidator; class Course extends Service @@ -26,8 +26,9 @@ class Course extends Service $params = $pagerQuery->getParams(); - if (isset($params['xm_category_ids'])) { - $params['id'] = $this->getCategoryCourseIds($params['xm_category_ids']); + if (!empty($params['xm_category_ids'])) { + $xmCategoryIds = explode(',', $params['xm_category_ids']); + $params['category_id'] = count($xmCategoryIds) > 1 ? $xmCategoryIds : $xmCategoryIds[0]; } $params['deleted'] = $params['deleted'] ?? 0; @@ -66,8 +67,6 @@ class Course extends Service $course->create($data); - $this->eventsManager->fire('course:afterCreate', $this, $course); - return $course; } @@ -137,8 +136,6 @@ class Course extends Service $course->update($data); - $this->eventsManager->fire('course:afterUpdate', $this, $course); - return $course; } @@ -146,16 +143,10 @@ class Course extends Service { $course = $this->findOrFail($id); - if ($course->deleted == 1) { - return false; - } - $course->deleted = 1; $course->update(); - $this->eventsManager->fire('course:afterDelete', $this, $course); - return $course; } @@ -163,16 +154,10 @@ class Course extends Service { $course = $this->findOrFail($id); - if ($course->deleted == 0) { - return false; - } - $course->deleted = 0; $course->update(); - $this->eventsManager->fire('course:afterRestore', $this, $course); - return $course; } @@ -308,7 +293,7 @@ class Course extends Service return $result; } - protected function saveTeachers($course, $teacherIds) + protected function saveTeachers(CourseModel $course, $teacherIds) { $courseRepo = new CourseRepo(); @@ -351,7 +336,7 @@ class Course extends Service } } - protected function saveCategories($course, $categoryIds) + protected function saveCategories(CourseModel $course, $categoryIds) { $courseRepo = new CourseRepo(); @@ -391,7 +376,7 @@ class Course extends Service } } - protected function saveRelatedCourses($course, $courseIds) + protected function saveRelatedCourses(CourseModel $course, $courseIds) { $courseRepo = new CourseRepo(); @@ -452,39 +437,17 @@ class Course extends Service } } - protected function getCategoryCourseIds($categoryIds) - { - if (empty($categoryIds)) return []; - - $courseCategoryRepo = new CourseCategoryRepo(); - - $categoryIds = explode(',', $categoryIds); - - $relations = $courseCategoryRepo->findByCategoryIds($categoryIds); - - $result = []; - - if ($relations->count() > 0) { - foreach ($relations as $relation) { - $result[] = $relation->course_id; - } - } - - return $result; - } - protected function handleCourses($pager) { if ($pager->total_items > 0) { - $transformer = new CourseListTransformer(); + $builder = new CourseListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleCourses($pipeA); - $pipeC = $transformer->handleCategories($pipeB); - $pipeD = $transformer->arrayToObject($pipeC); + $pipeB = $builder->handleCategories($pipeA); + $pipeC = $builder->arrayToObject($pipeB); - $pager->items = $pipeD; + $pager->items = $pipeC; } return $pager; diff --git a/app/Http/Admin/Services/CourseStudent.php b/app/Http/Admin/Services/CourseStudent.php index 84c7160e..498689b0 100644 --- a/app/Http/Admin/Services/CourseStudent.php +++ b/app/Http/Admin/Services/CourseStudent.php @@ -2,14 +2,14 @@ namespace App\Http\Admin\Services; +use App\Builders\CourseUserList as CourseUserListBuilder; +use App\Builders\LearningList as LearningListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Models\CourseUser as CourseUserModel; use App\Repos\Course as CourseRepo; use App\Repos\CourseUser as CourseUserRepo; use App\Repos\Learning as LearningRepo; use App\Repos\User as UserRepo; -use App\Transformers\CourseUserList as CourseUserListTransformer; -use App\Transformers\LearningList as LearningListTransformer; use App\Validators\CourseUser as CourseUserValidator; class CourseStudent extends Service @@ -94,7 +94,7 @@ class CourseStudent extends Service $courseUser->create($data); - $this->updateStudentCount($data['course_id']); + $this->updateUserCount($data['course_id']); return $courseUser; } @@ -122,7 +122,7 @@ class CourseStudent extends Service return $courseStudent; } - protected function updateStudentCount($courseId) + protected function updateUserCount($courseId) { $courseRepo = new CourseRepo(); @@ -130,7 +130,7 @@ class CourseStudent extends Service $updater = new CourseStatsUpdater(); - $updater->updateStudentCount($course); + $updater->updateUserCount($course); } protected function findOrFail($courseId, $userId) @@ -146,12 +146,12 @@ class CourseStudent extends Service { if ($pager->total_items > 0) { - $transformer = new CourseUserListTransformer(); + $builder = new CourseUserListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleCourses($pipeA); - $pipeC = $transformer->handleUsers($pipeB); - $pipeD = $transformer->arrayToObject($pipeC); + $pipeB = $builder->handleCourses($pipeA); + $pipeC = $builder->handleUsers($pipeB); + $pipeD = $builder->arrayToObject($pipeC); $pager->items = $pipeD; } @@ -163,13 +163,13 @@ class CourseStudent extends Service { if ($pager->total_items > 0) { - $transformer = new LearningListTransformer(); + $builder = new LearningListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleCourses($pipeA); - $pipeC = $transformer->handleChapters($pipeB); - $pipeD = $transformer->handleUsers($pipeC); - $pipeE = $transformer->arrayToObject($pipeD); + $pipeB = $builder->handleCourses($pipeA); + $pipeC = $builder->handleChapters($pipeB); + $pipeD = $builder->handleUsers($pipeC); + $pipeE = $builder->arrayToObject($pipeD); $pager->items = $pipeE; } diff --git a/app/Http/Admin/Services/Help.php b/app/Http/Admin/Services/Help.php new file mode 100644 index 00000000..3990fbcf --- /dev/null +++ b/app/Http/Admin/Services/Help.php @@ -0,0 +1,114 @@ +request->getQuery('deleted', 'int', 0); + + $helpRepo = new HelpRepo(); + + $helps = $helpRepo->findAll([ + 'deleted' => $deleted, + ]); + + return $helps; + } + + public function getHelp($id) + { + $help = $this->findOrFail($id); + + return $help; + } + + public function createHelp() + { + $post = $this->request->getPost(); + + $validator = new HelpValidator(); + + $data = []; + + $data['title'] = $validator->checkTitle($post['title']); + $data['content'] = $validator->checkContent($post['content']); + $data['priority'] = $validator->checkPriority($post['priority']); + $data['published'] = $validator->checkPublishStatus($post['published']); + + $help = new HelpModel(); + + $help->create($data); + + return $help; + } + + public function updateHelp($id) + { + $help = $this->findOrFail($id); + + $post = $this->request->getPost(); + + $validator = new HelpValidator(); + + $data = []; + + if (isset($post['title'])) { + $data['title'] = $validator->checkTitle($post['title']); + } + + if (isset($post['content'])) { + $data['content'] = $validator->checkContent($post['content']); + } + + if (isset($post['priority'])) { + $data['priority'] = $validator->checkPriority($post['priority']); + } + + if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + } + + $help->update($data); + + return $help; + } + + public function deleteHelp($id) + { + $help = $this->findOrFail($id); + + $help->deleted = 1; + + $help->update(); + + return $help; + } + + public function restoreHelp($id) + { + $help = $this->findOrFail($id); + + $help->deleted = 0; + + $help->update(); + + return $help; + } + + protected function findOrFail($id) + { + $validator = new HelpValidator(); + + $result = $validator->checkHelp($id); + + return $result; + } + +} diff --git a/app/Http/Admin/Services/Index.php b/app/Http/Admin/Services/Index.php new file mode 100644 index 00000000..c8e18709 --- /dev/null +++ b/app/Http/Admin/Services/Index.php @@ -0,0 +1,26 @@ +getTopMenus(); + + return kg_array_object($topMenus); + } + + public function getLeftMenus() + { + $authMenu = new AuthMenu(); + + $leftMenus = $authMenu->getLeftMenus(); + + return kg_array_object($leftMenus); + } + +} diff --git a/app/Http/Admin/Services/Nav.php b/app/Http/Admin/Services/Nav.php index 5d3c16d8..9e278681 100644 --- a/app/Http/Admin/Services/Nav.php +++ b/app/Http/Admin/Services/Nav.php @@ -2,6 +2,7 @@ namespace App\Http\Admin\Services; +use App\Caches\NavList as NavListCache; use App\Models\Nav as NavModel; use App\Repos\Nav as NavRepo; use App\Validators\Nav as NavValidator; @@ -95,6 +96,8 @@ class Nav extends Service $nav->update(); + $this->updateNavStats($nav); + return $nav; } @@ -130,10 +133,19 @@ class Nav extends Service if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); + if ($nav->parent_id == 0) { + if ($nav->published == 0 && $post['published'] == 1) { + $this->enableChildNavs($nav->id); + } elseif ($nav->published == 1 && $post['published'] == 0) { + $this->disableChildNavs($nav->id); + } + } } $nav->update($data); + $this->updateNavStats($nav); + return $nav; } @@ -141,14 +153,16 @@ class Nav extends Service { $nav = $this->findOrFail($id); - if ($nav->deleted == 1) { - return false; - } + $validator = new NavValidator(); + + $validator->checkDeleteAbility($nav); $nav->deleted = 1; $nav->update(); + $this->updateNavStats($nav); + return $nav; } @@ -156,17 +170,63 @@ class Nav extends Service { $nav = $this->findOrFail($id); - if ($nav->deleted == 0) { - return false; - } - $nav->deleted = 0; $nav->update(); + $this->updateNavStats($nav); + return $nav; } + protected function updateNavStats(NavModel $nav) + { + $navRepo = new NavRepo(); + + if ($nav->parent_id > 0) { + $nav = $navRepo->findById($nav->parent_id); + } + + $childCount = $navRepo->countChildNavs($nav->id); + $nav->child_count = $childCount; + $nav->update(); + + $cache = new NavListCache(); + $cache->rebuild(); + } + + protected function enableChildNavs($parentId) + { + $navRepo = new NavRepo(); + + $navs = $navRepo->findAll(['parent_id' => $parentId]); + + if ($navs->count() == 0) { + return; + } + + foreach ($navs as $nav) { + $nav->published = 1; + $nav->update(); + } + } + + protected function disableChildNavs($parentId) + { + $navRepo = new NavRepo(); + + $navs = $navRepo->findAll(['parent_id' => $parentId]); + + if ($navs->count() == 0) { + return; + } + + foreach ($navs as $nav) { + $nav->published = 0; + $nav->update(); + } + } + protected function findOrFail($id) { $validator = new NavValidator(); diff --git a/app/Http/Admin/Services/Order.php b/app/Http/Admin/Services/Order.php index 78e065eb..693e6b8d 100644 --- a/app/Http/Admin/Services/Order.php +++ b/app/Http/Admin/Services/Order.php @@ -2,11 +2,12 @@ namespace App\Http\Admin\Services; +use App\Builders\OrderList as OrderListBuilder; use App\Library\Paginator\Query as PaginateQuery; use App\Models\Order as OrderModel; +use App\Repos\Account as AccountRepo; use App\Repos\Order as OrderRepo; use App\Repos\User as UserRepo; -use App\Transformers\OrderList as OrderListTransformer; use App\Validators\Order as OrderValidator; class Order extends Service @@ -55,6 +56,15 @@ class Order extends Service return $user; } + public function getAccount($userId) + { + $accountRepo = new AccountRepo(); + + $account = $accountRepo->findById($userId); + + return $account; + } + public function getOrder($id) { $order = $this->findOrFail($id); @@ -87,12 +97,12 @@ class Order extends Service { if ($pager->total_items > 0) { - $transformer = new OrderListTransformer(); + $builder = new OrderListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleItems($pipeA); - $pipeC = $transformer->handleUsers($pipeB); - $pipeD = $transformer->arrayToObject($pipeC); + $pipeB = $builder->handleItems($pipeA); + $pipeC = $builder->handleUsers($pipeB); + $pipeD = $builder->arrayToObject($pipeC); $pager->items = $pipeD; } diff --git a/app/Http/Admin/Services/Package.php b/app/Http/Admin/Services/Package.php index f9a05388..c9abd3a5 100644 --- a/app/Http/Admin/Services/Package.php +++ b/app/Http/Admin/Services/Package.php @@ -102,10 +102,6 @@ class Package extends Service { $package = $this->findOrFail($id); - if ($package->deleted == 1) { - return false; - } - $package->deleted = 1; $package->update(); @@ -117,10 +113,6 @@ class Package extends Service { $package = $this->findOrFail($id); - if ($package->deleted == 0) { - return false; - } - $package->deleted = 0; $package->update(); @@ -183,7 +175,7 @@ class Package extends Service return $list; } - protected function saveCourses($package, $courseIds) + protected function saveCourses(PackageModel $package, $courseIds) { $packageRepo = new PackageRepo(); @@ -223,7 +215,7 @@ class Package extends Service } } - protected function updateCourseCount($package) + protected function updateCourseCount(PackageModel $package) { $packageRepo = new PackageRepo(); diff --git a/app/Http/Admin/Services/Page.php b/app/Http/Admin/Services/Page.php index 023a863c..2fc8deae 100644 --- a/app/Http/Admin/Services/Page.php +++ b/app/Http/Admin/Services/Page.php @@ -86,10 +86,6 @@ class Page extends Service { $page = $this->findOrFail($id); - if ($page->deleted == 1) { - return false; - } - $page->deleted = 1; $page->update(); @@ -101,10 +97,6 @@ class Page extends Service { $page = $this->findOrFail($id); - if ($page->deleted == 0) { - return false; - } - $page->deleted = 0; $page->update(); diff --git a/app/Http/Admin/Services/PaymentTest.php b/app/Http/Admin/Services/PaymentTest.php index 388ef269..958f94f4 100644 --- a/app/Http/Admin/Services/PaymentTest.php +++ b/app/Http/Admin/Services/PaymentTest.php @@ -16,7 +16,7 @@ abstract class PaymentTest extends Service */ public function createTestOrder() { - $authUser = $this->getDI()->get('auth')->getAuthUser(); + $authUser = $this->getDI()->get('auth')->getAuthInfo(); $order = new OrderModel(); diff --git a/app/Http/Admin/Services/Refund.php b/app/Http/Admin/Services/Refund.php index a4af36f5..d5da8657 100644 --- a/app/Http/Admin/Services/Refund.php +++ b/app/Http/Admin/Services/Refund.php @@ -2,13 +2,14 @@ namespace App\Http\Admin\Services; +use App\Builders\RefundList as RefundListBuilder; use App\Library\Paginator\Query as PaginateQuery; use App\Models\Task as TaskModel; +use App\Repos\Account as AccountRepo; use App\Repos\Order as OrderRepo; use App\Repos\Refund as RefundRepo; use App\Repos\Trade as TradeRepo; use App\Repos\User as UserRepo; -use App\Transformers\RefundList as RefundListTransformer; use App\Validators\Refund as RefundValidator; class Refund extends Service @@ -64,6 +65,15 @@ class Refund extends Service return $user; } + public function getAccount($userId) + { + $accountRepo = new AccountRepo(); + + $account = $accountRepo->findById($userId); + + return $account; + } + public function reviewRefund($id) { $refund = $this->findOrFail($id); @@ -107,11 +117,11 @@ class Refund extends Service { if ($pager->total_items > 0) { - $transformer = new RefundListTransformer(); + $builder = new RefundListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleUsers($pipeA); - $pipeC = $transformer->arrayToObject($pipeB); + $pipeB = $builder->handleUsers($pipeA); + $pipeC = $builder->arrayToObject($pipeB); $pager->items = $pipeC; } diff --git a/app/Http/Admin/Services/Review.php b/app/Http/Admin/Services/Review.php index 88594afe..7c5ade21 100644 --- a/app/Http/Admin/Services/Review.php +++ b/app/Http/Admin/Services/Review.php @@ -2,10 +2,10 @@ namespace App\Http\Admin\Services; +use App\Builders\ReviewList as ReviewListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Repos\Course as CourseRepo; use App\Repos\Review as ReviewRepo; -use App\Transformers\ReviewList as ReviewListTransformer; use App\Validators\Review as ReviewValidator; class Review extends Service @@ -77,8 +77,6 @@ class Review extends Service { $review = $this->findOrFail($id); - if ($review->deleted == 1) return false; - $review->deleted = 1; $review->update(); @@ -96,8 +94,6 @@ class Review extends Service { $review = $this->findOrFail($id); - if ($review->deleted == 0) return false; - $review->deleted = 0; $review->update(); @@ -124,12 +120,12 @@ class Review extends Service { if ($pager->total_items > 0) { - $transformer = new ReviewListTransformer(); + $builder = new ReviewListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleCourses($pipeA); - $pipeC = $transformer->handleUsers($pipeB); - $pipeD = $transformer->arrayToObject($pipeC); + $pipeB = $builder->handleCourses($pipeA); + $pipeC = $builder->handleUsers($pipeB); + $pipeD = $builder->arrayToObject($pipeC); $pager->items = $pipeD; } diff --git a/app/Http/Admin/Services/Role.php b/app/Http/Admin/Services/Role.php index ced78b5a..6e25f0a4 100644 --- a/app/Http/Admin/Services/Role.php +++ b/app/Http/Admin/Services/Role.php @@ -9,6 +9,15 @@ use App\Validators\Role as RoleValidator; class Role extends Service { + public function getAuthNodes() + { + $authNode = new AuthNode(); + + $nodes = $authNode->getNodes(); + + return kg_array_object($nodes); + } + public function getRoles() { $deleted = $this->request->getQuery('deleted', 'int', 0); @@ -70,10 +79,6 @@ class Role extends Service { $role = $this->findOrFail($id); - if ($role->deleted == 1) { - return false; - } - if ($role->type == RoleModel::TYPE_SYSTEM) { return false; } @@ -89,10 +94,6 @@ class Role extends Service { $role = $this->findOrFail($id); - if ($role->deleted == 0) { - return false; - } - $role->deleted = 0; $role->update(); diff --git a/app/Http/Admin/Services/Session.php b/app/Http/Admin/Services/Session.php index cb8e5083..1bf402ae 100644 --- a/app/Http/Admin/Services/Session.php +++ b/app/Http/Admin/Services/Session.php @@ -2,13 +2,14 @@ namespace App\Http\Admin\Services; -use App\Validators\User as UserValidator; +use App\Validators\Account as AccountValidator; +use App\Validators\Security as SecurityValidator; class Session extends Service { /** - * @var $auth \App\Http\Admin\Services\AuthUser + * @var \App\Http\Admin\Services\AuthUser */ protected $auth; @@ -21,31 +22,29 @@ class Session extends Service { $post = $this->request->getPost(); - $validator = new UserValidator(); + $accountValidator = new AccountValidator(); - $user = $validator->checkLoginAccount($post['account']); - - $validator->checkLoginPassword($user, $post['password']); - - $validator->checkAdminLogin($user); + $user = $accountValidator->checkAdminLogin($post['account'], $post['password']); $config = new Config(); $captcha = $config->getSectionConfig('captcha'); + $securityValidator = new SecurityValidator(); + /** * 验证码是一次性的,放到最后检查,减少第三方调用 */ if ($captcha->enabled) { - $validator->checkCaptchaCode($post['ticket'], $post['rand']); + $securityValidator->checkCaptchaCode($post['ticket'], $post['rand']); } - $this->auth->setAuthUser($user); + $this->auth->setAuthInfo($user); } public function logout() { - $this->auth->removeAuthUser(); + $this->auth->removeAuthInfo(); } } diff --git a/app/Http/Admin/Services/Slide.php b/app/Http/Admin/Services/Slide.php index 5922209d..df4b0d63 100644 --- a/app/Http/Admin/Services/Slide.php +++ b/app/Http/Admin/Services/Slide.php @@ -126,8 +126,6 @@ class Slide extends Service { $slide = $this->findOrFail($id); - if ($slide->deleted == 1) return false; - $slide->deleted = 1; $slide->update(); @@ -139,8 +137,6 @@ class Slide extends Service { $slide = $this->findOrFail($id); - if ($slide->deleted == 0) return false; - $slide->deleted = 0; $slide->update(); diff --git a/app/Http/Admin/Services/Topic.php b/app/Http/Admin/Services/Topic.php new file mode 100644 index 00000000..b9457050 --- /dev/null +++ b/app/Http/Admin/Services/Topic.php @@ -0,0 +1,195 @@ +getParams(); + + $params['deleted'] = $params['deleted'] ?? 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $pageRepo = new TopicRepo(); + + $pager = $pageRepo->paginate($params, $sort, $page, $limit); + + return $pager; + } + + public function getTopic($id) + { + $topic = $this->findOrFail($id); + + return $topic; + } + + public function createTopic() + { + $post = $this->request->getPost(); + + $validator = new TopicValidator(); + + $data = []; + + $data['title'] = $validator->checkTitle($post['title']); + $data['summary'] = $validator->checkSummary($post['summary']); + + $topic = new TopicModel(); + + $topic->create($data); + + return $topic; + } + + public function updateTopic($id) + { + $topic = $this->findOrFail($id); + + $post = $this->request->getPost(); + + $validator = new TopicValidator(); + + $data = []; + + if (isset($post['name'])) { + $data['name'] = $validator->checkName($post['name']); + } + + if (isset($post['summary'])) { + $data['summary'] = $validator->checkSummary($post['summary']); + } + + if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + } + + if (isset($post['xm_course_ids'])) { + $this->saveCourses($topic, $post['xm_course_ids']); + } + + $topic->update($data); + + $this->updateCourseCount($topic); + + return $topic; + } + + public function deleteTopic($id) + { + $topic = $this->findOrFail($id); + + $topic->deleted = 1; + + $topic->update(); + + return $topic; + } + + public function restoreTopic($id) + { + $topic = $this->findOrFail($id); + + $topic->deleted = 0; + + $topic->update(); + + return $topic; + } + + public function getXmCourses($id) + { + $topicRepo = new TopicRepo(); + + $courses = $topicRepo->findCourses($id); + + $list = []; + + if ($courses->count() > 0) { + foreach ($courses as $course) { + $list[] = [ + 'id' => $course->id, + 'title' => $course->title, + 'selected' => true, + ]; + } + } + + return $list; + } + + protected function saveCourses(TopicModel $topic, $courseIds) + { + $topicRepo = new TopicRepo(); + + $courses = $topicRepo->findCourses($topic->id); + + $originCourseIds = []; + + if ($courses->count() > 0) { + foreach ($courses as $course) { + $originCourseIds[] = $course->id; + } + } + + $newCourseIds = explode(',', $courseIds); + $addedCourseIds = array_diff($newCourseIds, $originCourseIds); + + if ($addedCourseIds) { + foreach ($addedCourseIds as $courseId) { + $courseTopic = new CourseTopicModel(); + $courseTopic->create([ + 'course_id' => $courseId, + 'topic_id' => $topic->id, + ]); + } + } + + $deletedCourseIds = array_diff($originCourseIds, $newCourseIds); + + if ($deletedCourseIds) { + $courseTopicRepo = new CourseTopicRepo(); + foreach ($deletedCourseIds as $courseId) { + $courseTopic = $courseTopicRepo->findCourseTopic($courseId, $topic->id); + if ($courseTopic) { + $courseTopic->delete(); + } + } + } + } + + protected function updateCourseCount(TopicModel $topic) + { + $topicRepo = new TopicRepo(); + + $courseCount = $topicRepo->countCourses($topic->id); + + $topic->course_count = $courseCount; + + $topic->update(); + } + + protected function findOrFail($id) + { + $validator = new TopicValidator(); + + $result = $validator->checkTopic($id); + + return $result; + } + +} diff --git a/app/Http/Admin/Services/Trade.php b/app/Http/Admin/Services/Trade.php index 1b88d152..6f711e63 100644 --- a/app/Http/Admin/Services/Trade.php +++ b/app/Http/Admin/Services/Trade.php @@ -2,13 +2,14 @@ namespace App\Http\Admin\Services; +use App\Builders\TradeList as TradeListBuilder; use App\Library\Paginator\Query as PaginateQuery; use App\Models\Refund as RefundModel; use App\Models\Trade as TradeModel; +use App\Repos\Account as AccountRepo; use App\Repos\Order as OrderRepo; use App\Repos\Trade as TradeRepo; use App\Repos\User as UserRepo; -use App\Transformers\TradeList as TradeListTransformer; use App\Validators\Trade as TradeValidator; class Trade extends Service @@ -66,6 +67,15 @@ class Trade extends Service return $user; } + public function getAccount($userId) + { + $accountRepo = new AccountRepo(); + + $account = $accountRepo->findById($userId); + + return $account; + } + public function closeTrade($id) { $trade = $this->findOrFail($id); @@ -115,11 +125,11 @@ class Trade extends Service { if ($pager->total_items > 0) { - $transformer = new TradeListTransformer(); + $builder = new TradeListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleUsers($pipeA); - $pipeC = $transformer->arrayToObject($pipeB); + $pipeB = $builder->handleUsers($pipeA); + $pipeC = $builder->arrayToObject($pipeB); $pager->items = $pipeC; } diff --git a/app/Http/Admin/Services/User.php b/app/Http/Admin/Services/User.php index d11ca290..3c13bbf7 100644 --- a/app/Http/Admin/Services/User.php +++ b/app/Http/Admin/Services/User.php @@ -2,12 +2,12 @@ namespace App\Http\Admin\Services; +use App\Builders\UserList as UserListBuilder; use App\Library\Paginator\Query as PaginateQuery; use App\Library\Util\Password as PasswordUtil; use App\Models\User as UserModel; use App\Repos\Role as RoleRepo; use App\Repos\User as UserRepo; -use App\Transformers\UserList as UserListTransformer; use App\Validators\User as UserValidator; class User extends Service @@ -109,9 +109,9 @@ class User extends Service $data['locked'] = $validator->checkLockStatus($post['locked']); } - if (isset($post['locked_expiry'])) { - $data['locked_expiry'] = $validator->checkLockExpiry($post['locked_expiry']); - if ($data['locked_expiry'] < time()) { + if (isset($post['lock_expiry'])) { + $data['lock_expiry'] = $validator->checkLockExpiry($post['lock_expiry']); + if ($data['lock_expiry'] < time()) { $data['locked'] = 0; } } @@ -142,10 +142,6 @@ class User extends Service protected function updateAdminUserCount($roleId) { - if (!$roleId) { - return false; - } - $roleRepo = new RoleRepo(); $role = $roleRepo->findById($roleId); @@ -165,12 +161,12 @@ class User extends Service { if ($pager->total_items > 0) { - $transformer = new UserListTransformer(); + $builder = new UserListBuilder(); $pipeA = $pager->items->toArray(); - $pipeB = $transformer->handleAdminRoles($pipeA); - $pipeC = $transformer->handleEduRoles($pipeB); - $pipeD = $transformer->arrayToObject($pipeC); + $pipeB = $builder->handleAdminRoles($pipeA); + $pipeC = $builder->handleEduRoles($pipeB); + $pipeD = $builder->arrayToObject($pipeC); $pager->items = $pipeD; } diff --git a/app/Http/Admin/Views/audit/list.volt b/app/Http/Admin/Views/audit/list.volt index ba2ae912..f4e8277b 100644 --- a/app/Http/Admin/Views/audit/list.volt +++ b/app/Http/Admin/Views/audit/list.volt @@ -37,7 +37,7 @@ {{ item.user_id }} {{ item.user_name }} - {{ item.user_ip }} + {{ item.user_ip }} {{ item.req_route }} {{ item.req_path }} {{ date('Y-m-d H:i:s',item.created_at) }} diff --git a/app/Http/Admin/Views/category/list.volt b/app/Http/Admin/Views/category/list.volt index 7685a3ad..deb1d702 100644 --- a/app/Http/Admin/Views/category/list.volt +++ b/app/Http/Admin/Views/category/list.volt @@ -25,6 +25,7 @@ + @@ -32,6 +33,7 @@ 编号 名称 层级 + 节点数 课程数 排序 发布 @@ -48,6 +50,7 @@ {{ item.name }} {% endif %} {{ item.level }} + {{ item.child_count }} {{ item.course_count }} diff --git a/app/Http/Admin/Views/chapter/add_lesson.volt b/app/Http/Admin/Views/chapter/add_lesson.volt index d2b9e806..c8eb6616 100644 --- a/app/Http/Admin/Views/chapter/add_lesson.volt +++ b/app/Http/Admin/Views/chapter/add_lesson.volt @@ -9,7 +9,7 @@
diff --git a/app/Http/Admin/Views/chapter/edit_lesson.volt b/app/Http/Admin/Views/chapter/edit_lesson.volt index cc6044c4..1c1e1448 100644 --- a/app/Http/Admin/Views/chapter/edit_lesson.volt +++ b/app/Http/Admin/Views/chapter/edit_lesson.volt @@ -3,7 +3,7 @@ 点播信息 {% elseif model == 'live' %} 直播信息 - {% elseif model == 'article' %} + {% elseif model == 'read' %} 文章信息 {% endif %} {%- endmacro %} @@ -28,8 +28,8 @@ {{ partial('chapter/edit_lesson_vod') }} {% elseif course.model == 'live' %} {{ partial('chapter/edit_lesson_live') }} - {% elseif course.model == 'article' %} - {{ partial('chapter/edit_lesson_article') }} + {% elseif course.model == 'read' %} + {{ partial('chapter/edit_lesson_read') }} {% endif %}
diff --git a/app/Http/Admin/Views/chapter/edit_lesson_article.volt b/app/Http/Admin/Views/chapter/edit_lesson_read.volt similarity index 89% rename from app/Http/Admin/Views/chapter/edit_lesson_article.volt rename to app/Http/Admin/Views/chapter/edit_lesson_read.volt index 192a170e..74cf5122 100644 --- a/app/Http/Admin/Views/chapter/edit_lesson_article.volt +++ b/app/Http/Admin/Views/chapter/edit_lesson_read.volt @@ -1,7 +1,7 @@
- +
diff --git a/app/Http/Admin/Views/chapter/edit_lesson_vod.volt b/app/Http/Admin/Views/chapter/edit_lesson_vod.volt index 146c5050..6c871074 100644 --- a/app/Http/Admin/Views/chapter/edit_lesson_vod.volt +++ b/app/Http/Admin/Views/chapter/edit_lesson_vod.volt @@ -1,4 +1,4 @@ -{% if translated_files %} +{% if vod_files %}
视频信息
@@ -11,16 +11,16 @@ 大小 操作 - {% for item in translated_files %} + {% for item in vod_files %} {{ item.format }} - {{ item.duration }} + {{ item.duration|play_duration }} {{ item.width }} x {{ item.height }} - {{ item.bit_rate }}kbps + {{ item.rate }}kbps {{ item.size }}M - 预览 - 复制 + 预览 + 复制 {% endfor %} diff --git a/app/Http/Admin/Views/chapter/lessons.volt b/app/Http/Admin/Views/chapter/lessons.volt index 3f7726d5..1bc31e18 100644 --- a/app/Http/Admin/Views/chapter/lessons.volt +++ b/app/Http/Admin/Views/chapter/lessons.volt @@ -23,8 +23,8 @@ {{ partial('chapter/lessons_vod') }} {% elseif course.model == 'live' %} {{ partial('chapter/lessons_live') }} -{% elseif course.model == 'article' %} - {{ partial('chapter/lessons_article') }} +{% elseif course.model == 'read' %} + {{ partial('chapter/lessons_read') }} {% endif %} \ No newline at end of file + \ No newline at end of file diff --git a/app/Http/Admin/Views/course/add.volt b/app/Http/Admin/Views/course/add.volt index 18283d1e..6510f298 100644 --- a/app/Http/Admin/Views/course/add.volt +++ b/app/Http/Admin/Views/course/add.volt @@ -9,7 +9,7 @@
- +
@@ -44,20 +44,19 @@ var $ = layui.jquery; var form = layui.form; - var modelTips = [ - '通过音视频呈现课程内容,内容可视化,有图像有声音,适合大部分场景', - '通过直播呈现课程内容,交互性强,适合需要交互反馈、情绪表达的场景', - '通过图文呈现课程内容,简单直接,适合撰写文档、书籍、教程的场景' - ]; + var modelTips = { + vod: '通过音视频呈现课程内容,内容可视化,有图像有声音,适合大部分场景', + live: '通过直播呈现课程内容,交互性强,适合需要交互反馈、情绪表达的场景', + read: '通过图文呈现课程内容,简单直接,适合撰写文档、书籍、教程的场景' + }; var modelTipsBlock = $('#model-tips'); form.on('radio(model)', function (data) { - var index = data.value - 1; - modelTipsBlock.html(modelTips[index]); + modelTipsBlock.html(modelTips[data.value]); }); - modelTipsBlock.html(modelTips[0]); + modelTipsBlock.html(modelTips.vod); }); diff --git a/app/Http/Admin/Views/course/list.volt b/app/Http/Admin/Views/course/list.volt index d5c49514..b8e711a0 100644 --- a/app/Http/Admin/Views/course/list.volt +++ b/app/Http/Admin/Views/course/list.volt @@ -5,7 +5,7 @@ 点播 {% elseif value == 'live' %} 直播 - {% elseif value == 'article' %} + {% elseif value == 'read' %} 图文 {% endif %} {%- endmacro %} @@ -34,8 +34,7 @@ - - + @@ -46,7 +45,6 @@ - @@ -65,11 +63,6 @@ {{ item.lesson_count }} - + @@ -49,6 +50,7 @@ + @@ -66,6 +68,7 @@ {% endif %} + diff --git a/app/Http/Admin/Views/order/macro.volt b/app/Http/Admin/Views/order/macro.volt index b45a5dd4..cb08eb8f 100644 --- a/app/Http/Admin/Views/order/macro.volt +++ b/app/Http/Admin/Views/order/macro.volt @@ -1,28 +1,28 @@ {%- macro item_info(order) %} {% if order.item_type == 'course' %} - {% set course = order.item_info.course %} + {% set course = order.item_info['course'] %}
-

课程名称:{{ course.title }}

-

市场价格:¥{{ course.market_price }}

-

有效期限:{{ date('Y-m-d', course.expire_time) }}

+

课程名称:{{ course['title'] }}

+

市场价格:¥{{ course['market_price'] }}

+

有效期限:{{ date('Y-m-d', course['expire_time']) }}

{% elseif order.item_type == 'package' %} - {% set courses = order.item_info.courses %} + {% set courses = order.item_info['courses'] %} {% for course in courses %}
-

课程名称:{{ course.title }}

-

市场价格:¥{{ course.market_price }}

-

有效期限:{{ date('Y-m-d', course.expire_time) }}

+

课程名称:{{ course['title'] }}

+

市场价格:¥{{ course['market_price'] }}

+

有效期限:{{ date('Y-m-d', course['expire_time']) }}

{% endfor %} {% elseif order.item_type == 'reward' %} - {% set course = order.item_info.course %} + {% set course = order.item_info['course'] %}
-

课程名称:{{ course.title }}

-

赞赏金额:¥{{ order.amount }}

+

课程名称:{{ course['title'] }}

+

赞赏金额:¥{{ order['amount'] }}

{% elseif order.item_type == 'vip' %} - {% set vip = order.item_info.vip %} + {% set vip = order.item_info['vip'] %}

商品名称:{{ order.subject }}

商品价格:¥{{ order.amount }}

diff --git a/app/Http/Admin/Views/order/show.volt b/app/Http/Admin/Views/order/show.volt index 6183559f..cf2988ad 100644 --- a/app/Http/Admin/Views/order/show.volt +++ b/app/Http/Admin/Views/order/show.volt @@ -93,17 +93,15 @@
课程 课时数学员数 有效期 价格 发布 - - {{ item.student_count }} - - {{ expiry_info(item.expiry) }}

市场:¥{{ item.market_price }}

diff --git a/app/Http/Admin/Views/help/add.volt b/app/Http/Admin/Views/help/add.volt new file mode 100644 index 00000000..bb8ad9f8 --- /dev/null +++ b/app/Http/Admin/Views/help/add.volt @@ -0,0 +1,46 @@ +
+ +
+ 添加帮助 +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +{{ partial('partials/layedit') }} \ No newline at end of file diff --git a/app/Http/Admin/Views/help/edit.volt b/app/Http/Admin/Views/help/edit.volt new file mode 100644 index 00000000..562d57d3 --- /dev/null +++ b/app/Http/Admin/Views/help/edit.volt @@ -0,0 +1,47 @@ +
+ +
+ 编辑帮助 +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +{{ partial('partials/layedit') }} + diff --git a/app/Http/Admin/Views/help/list.volt b/app/Http/Admin/Views/help/list.volt new file mode 100644 index 00000000..f8b1acc0 --- /dev/null +++ b/app/Http/Admin/Views/help/list.volt @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + {% for item in helps %} + + + + + + + + + {% endfor %} + +
编号标题更新时间排序发布操作
{{ item.id }}{{ item.title }}{{ date('Y-m-d H:i',item.updated_at) }} + +
+ + +
+
+ + \ No newline at end of file diff --git a/app/Http/Admin/Views/index.volt b/app/Http/Admin/Views/index.volt index ca9ace40..83c37f4f 100644 --- a/app/Http/Admin/Views/index.volt +++ b/app/Http/Admin/Views/index.volt @@ -7,7 +7,7 @@ 管理后台 {{ stylesheet_link('lib/layui/css/layui.css') }} - {{ stylesheet_link('lib/layui/css/dropdown.css') }} + {{ stylesheet_link('lib/layui/extends/dropdown.css') }} {{ stylesheet_link('admin/css/style.css') }} {{ javascript_include('lib/layui/layui.js') }} {{ javascript_include('admin/js/common.js') }} diff --git a/app/Http/Admin/Views/nav/list.volt b/app/Http/Admin/Views/nav/list.volt index a802c704..a572df9d 100644 --- a/app/Http/Admin/Views/nav/list.volt +++ b/app/Http/Admin/Views/nav/list.volt @@ -42,6 +42,7 @@
编号 名称 层级节点数 位置 目标 排序{{ item.name }}{{ item.level }}{{ item.child_count }} {{ position_info(item.position) }} {{ target_info(item.target) }}
- + - - + - - - + +
编号用户名昵称 邮箱手机号注册时间手机
{{ user.id }} {{ user.name }}{{ user.email }}{{ user.phone }}{{ date('Y-m-d H:i:s',user.created_at) }}{% if account.phone %}{{ account.phone }}{% else %}N/A{% endif %}{% if account.email %}{{ account.email }}{% else %}N/A{% endif %}
diff --git a/app/Http/Admin/Views/partials/pager.volt b/app/Http/Admin/Views/partials/pager.volt index bf9bfb15..3f8b8892 100644 --- a/app/Http/Admin/Views/partials/pager.volt +++ b/app/Http/Admin/Views/partials/pager.volt @@ -1,7 +1,7 @@ {% if pager.total_pages > 1 %}
首页 - 上页 + 上页 下页 尾页
diff --git a/app/Http/Admin/Views/public/login.volt b/app/Http/Admin/Views/public/login.volt index aa3bcc94..2021b0c0 100644 --- a/app/Http/Admin/Views/public/login.volt +++ b/app/Http/Admin/Views/public/login.volt @@ -7,7 +7,7 @@
- +
diff --git a/app/Http/Admin/Views/refund/show.volt b/app/Http/Admin/Views/refund/show.volt index dfee19eb..78384f48 100644 --- a/app/Http/Admin/Views/refund/show.volt +++ b/app/Http/Admin/Views/refund/show.volt @@ -110,16 +110,14 @@ - + - - + - - - + +
编号用户名昵称 邮箱手机号注册时间手机
{{ user.id }} {{ user.name }}{{ user.email }}{{ user.phone }}{{ date('Y-m-d H:i:s',user.created_at) }}{% if account.phone %}{{ account.phone }}{% else %}N/A{% endif %}{% if account.email %}{{ account.email }}{% else %}N/A{% endif %}
\ No newline at end of file diff --git a/app/Http/Admin/Views/role/add.volt b/app/Http/Admin/Views/role/add.volt index 83bbd61e..a0bc1dfc 100644 --- a/app/Http/Admin/Views/role/add.volt +++ b/app/Http/Admin/Views/role/add.volt @@ -7,14 +7,14 @@
- +
- +
diff --git a/app/Http/Admin/Views/role/edit.volt b/app/Http/Admin/Views/role/edit.volt index 3c83c308..d825dfba 100644 --- a/app/Http/Admin/Views/role/edit.volt +++ b/app/Http/Admin/Views/role/edit.volt @@ -7,21 +7,21 @@
- +
- +
- {% for key,level in nodes %} + {% for key,level in auth_nodes %} {% for key2,level2 in level.child %} diff --git a/app/Http/Admin/Views/student/learning.volt b/app/Http/Admin/Views/student/learning.volt index c49eadab..4f3e6e18 100644 --- a/app/Http/Admin/Views/student/learning.volt +++ b/app/Http/Admin/Views/student/learning.volt @@ -6,6 +6,14 @@ {% endif %} {%- endmacro %} +{%- macro last_active(created_at, updated_at) %} + {% if updated_at > 0 %} + {{ date('Y-m-d H:i:s', updated_at) }} + {% else %} + {{ date('Y-m-d H:i:s', created_at) }} + {% endif %} +{%- endmacro %} +
@@ -18,9 +26,9 @@ - - - + + + @@ -32,8 +40,8 @@ - - + + {% endfor %} diff --git a/app/Http/Admin/Views/student/list.volt b/app/Http/Admin/Views/student/list.volt index 4b8d496a..fc909e2a 100644 --- a/app/Http/Admin/Views/student/list.volt +++ b/app/Http/Admin/Views/student/list.volt @@ -20,15 +20,13 @@ - - - + @@ -48,8 +46,7 @@

时长:{{ item.duration|total_duration }}

- - + + - + - + + @@ -61,9 +62,10 @@ {% for item in pager.items %} - + - + +
课时信息 学习时长客户端类型客户端地址创建时间终端类型终端地址最后活跃
{{ item.duration|play_duration }} {{ client_type(item.client_type) }}{{ item.client_ip }}{{ date('Y-m-d H:i',item.created_at) }}{{ item.client_ip }}{{ last_active(item.created_at, item.updated_at) }}
基本信息 学习情况加入时间过期时间有效期限 锁定 操作
{{ date('Y-m-d',item.created_at) }}{{ date('Y-m-d',item.expire_time) }}{{ date('Y-m-d H:i',item.expire_time) }}
diff --git a/app/Http/Admin/Views/topic/add.volt b/app/Http/Admin/Views/topic/add.volt new file mode 100644 index 00000000..daa1da8b --- /dev/null +++ b/app/Http/Admin/Views/topic/add.volt @@ -0,0 +1,29 @@ +
+ +
+ 添加话题 +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
\ No newline at end of file diff --git a/app/Http/Admin/Views/topic/edit.volt b/app/Http/Admin/Views/topic/edit.volt new file mode 100644 index 00000000..302b380a --- /dev/null +++ b/app/Http/Admin/Views/topic/edit.volt @@ -0,0 +1,45 @@ +
+ +
+ 编辑话题 +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+
+ +
+ +{{ javascript_include('lib/xm-select.js') }} +{{ javascript_include('admin/js/xm-course.js') }} + + \ No newline at end of file diff --git a/app/Http/Admin/Views/topic/list.volt b/app/Http/Admin/Views/topic/list.volt new file mode 100644 index 00000000..68c15502 --- /dev/null +++ b/app/Http/Admin/Views/topic/list.volt @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + {% for item in pager.items %} + + + + + + + + {% endfor %} + +
编号标题课程数发布操作
{{ item.id }}{{ item.title }}{{ item.course_count }} +
+ + +
+
+ +{{ partial('partials/pager') }} + + \ No newline at end of file diff --git a/app/Http/Admin/Views/trade/show.volt b/app/Http/Admin/Views/trade/show.volt index 5f6a934a..a559d4ff 100644 --- a/app/Http/Admin/Views/trade/show.volt +++ b/app/Http/Admin/Views/trade/show.volt @@ -91,17 +91,15 @@ - + - - + - - - + +
编号用户名昵称 邮箱手机号注册时间手机
{{ user.id }} {{ user.name }}{{ user.email }}{{ user.phone }}{{ date('Y-m-d H:i:s',user.created_at) }}{% if account.phone %}{{ account.phone }}{% else %}N/A{% endif %}{% if account.email %}{{ account.email }}{% else %}N/A{% endif %}
diff --git a/app/Http/Admin/Views/user/edit.volt b/app/Http/Admin/Views/user/edit.volt index 8ed64f6b..4e32ba83 100644 --- a/app/Http/Admin/Views/user/edit.volt +++ b/app/Http/Admin/Views/user/edit.volt @@ -53,10 +53,10 @@
-
+
- +
@@ -79,12 +79,12 @@ var laydate = layui.laydate; laydate.render({ - elem: 'input[name=locked_expiry]', + elem: 'input[name=lock_expiry]', type: 'datetime' }); form.on('radio(locked)', function (data) { - var block = $('#locked-expiry-block'); + var block = $('#lock-expiry-block'); if (data.value == 1) { block.show(); } else { diff --git a/app/Http/Admin/Views/user/list.volt b/app/Http/Admin/Views/user/list.volt index 741debaf..8884ca15 100644 --- a/app/Http/Admin/Views/user/list.volt +++ b/app/Http/Admin/Views/user/list.volt @@ -11,7 +11,7 @@ {% if user.locked == 0 %} 正常 {% else %} - 锁定 + 锁定 {% endif %} {%- endmacro %} @@ -24,7 +24,6 @@
@@ -45,14 +44,16 @@
编号用户昵称 角色注册日期最后活跃最后位置 状态 操作
{{ item.id }}{{ item.name }}{{ vip_info(item) }}{{ item.name }}{{ vip_info(item) }} {{ role_info(item) }}{{ date('Y-m-d',item.created_at) }}{{ date('Y-m-d H:i',item.last_active) }}{{ item.last_ip }} {{ status_info(item) }}
@@ -78,4 +80,5 @@
-{{ partial('partials/pager') }} \ No newline at end of file +{{ partial('partials/pager') }} +{{ partial('partials/ip2region') }} \ No newline at end of file diff --git a/app/Http/Admin/Views/user/search.volt b/app/Http/Admin/Views/user/search.volt index a8046b21..dec8cb2f 100644 --- a/app/Http/Admin/Views/user/search.volt +++ b/app/Http/Admin/Views/user/search.volt @@ -14,21 +14,7 @@
- -
-
- -
- -
- -
-
- -
- -
- +
diff --git a/app/Http/Home/Controllers/CategoryController.php b/app/Http/Home/Controllers/CategoryController.php deleted file mode 100644 index 9e560451..00000000 --- a/app/Http/Home/Controllers/CategoryController.php +++ /dev/null @@ -1,49 +0,0 @@ -getCategory($id); - - return $this->response->ajaxSuccess($category); - } - - /** - * @Get("/{id}/childs", name="home.category.childs") - */ - public function childsAction($id) - { - $service = new CategoryService(); - - $childs = $service->getChilds($id); - - return $this->response->ajaxSuccess($childs); - } - - /** - * @Get("/{id}/courses", name="home.category.courses") - */ - public function coursesAction($id) - { - $service = new CategoryService(); - - $courses = $service->getCourses($id); - - return $this->response->ajaxSuccess($courses); - } - -} diff --git a/app/Http/Home/Controllers/ChapterController.php b/app/Http/Home/Controllers/ChapterController.php index b9884477..312dc312 100644 --- a/app/Http/Home/Controllers/ChapterController.php +++ b/app/Http/Home/Controllers/ChapterController.php @@ -11,7 +11,7 @@ class ChapterController extends Controller { /** - * @Get("/{id}", name="home.chapter.show") + * @Get("/{id:[0-9]+}", name="home.chapter.show") */ public function showAction($id) { @@ -23,7 +23,7 @@ class ChapterController extends Controller } /** - * @Get("/{id}/comments", name="home.chapter.comments") + * @Get("/{id:[0-9]+}/comments", name="home.chapter.comments") */ public function commentsAction($id) { @@ -35,7 +35,7 @@ class ChapterController extends Controller } /** - * @Post("/{id}/agree", name="home.chapter.agree") + * @Post("/{id:[0-9]+}/agree", name="home.chapter.agree") */ public function agreeAction($id) { @@ -47,7 +47,7 @@ class ChapterController extends Controller } /** - * @Post("/{id}/oppose", name="home.chapter.oppose") + * @Post("/{id:[0-9]+}/oppose", name="home.chapter.oppose") */ public function opposeAction($id) { @@ -59,7 +59,7 @@ class ChapterController extends Controller } /** - * @Post("/{id}/position", name="home.chapter.position") + * @Post("/{id:[0-9]+}/position", name="home.chapter.position") */ public function positionAction($id) { @@ -71,7 +71,7 @@ class ChapterController extends Controller } /** - * @Post("/{id}/finish", name="home.chapter.finish") + * @Post("/{id:[0-9]+}/finish", name="home.chapter.finish") */ public function finishAction($id) { diff --git a/app/Http/Home/Controllers/CommentController.php b/app/Http/Home/Controllers/CommentController.php deleted file mode 100644 index ab1c6ef6..00000000 --- a/app/Http/Home/Controllers/CommentController.php +++ /dev/null @@ -1,85 +0,0 @@ -create(); - - return $this->response->ajaxSuccess($comment); - } - - /** - * @Get("/{id}", name="home.comment.show") - */ - public function showAction($id) - { - $service = new CommentService(); - - $comment = $service->getComment($id); - - return $this->response->ajaxSuccess($comment); - } - - /** - * @Get("/{id}/replies", name="home.comment.replies") - */ - public function repliesAction($id) - { - $service = new CommentService(); - - $replies = $service->getReplies($id); - - return $this->response->ajaxSuccess($replies); - } - - /** - * @Post("/{id}/delete", name="home.comment.delete") - */ - public function deleteAction($id) - { - $service = new CommentService(); - - $service->delete($id); - - return $this->response->ajaxSuccess(); - } - - /** - * @Post("/{id}/agree", name="home.comment.agree") - */ - public function agreeAction($id) - { - $service = new CommentService(); - - $service->agree($id); - - return $this->response->ajaxSuccess(); - } - - /** - * @Post("/{id}/oppose", name="home.comment.oppose") - */ - public function opposeAction($id) - { - $service = new CommentService(); - - $service->oppose($id); - - return $this->response->ajaxSuccess(); - } - -} diff --git a/app/Http/Home/Controllers/ConsultController.php b/app/Http/Home/Controllers/ConsultController.php deleted file mode 100644 index cde47ca3..00000000 --- a/app/Http/Home/Controllers/ConsultController.php +++ /dev/null @@ -1,101 +0,0 @@ -create(); - - $data = $service->getConsult($consult->id); - - return $this->ajaxSuccess($data); - } - - /** - * @Get("/{id}", name="home.consult.show") - */ - public function showAction($id) - { - $service = new ConsultService(); - - $consult = $service->getConsult($id); - - return $this->ajaxSuccess($consult); - } - - /** - * @Post("/{id}/update", name="home.consult.update") - */ - public function updateAction($id) - { - $service = new ConsultService(); - - $consult = $service->update($id); - - $data = $service->getConsult($consult->id); - - return $this->ajaxSuccess($data); - } - - /** - * @Post("/{id}/delete", name="home.consult.delete") - */ - public function deleteAction($id) - { - $service = new ConsultService(); - - $service->delete($id); - - return $this->ajaxSuccess(); - } - - /** - * @Post("/{id}/agree", name="home.consult.agree") - */ - public function agreeAction($id) - { - $service = new ConsultService(); - - $service->agree($id); - - return $this->ajaxSuccess(); - } - - /** - * @Post("/{id}/oppose", name="home.consult.oppose") - */ - public function opposeAction($id) - { - $service = new ConsultService(); - - $service->oppose($id); - - return $this->ajaxSuccess(); - } - - /** - * @Post("/{id}/reply", name="home.consult.reply") - */ - public function replyAction($id) - { - $service = new ConsultService(); - - $service->reply($id); - - return $this->ajaxSuccess(); - } - -} diff --git a/app/Http/Home/Controllers/Controller.php b/app/Http/Home/Controllers/Controller.php index 3eb2bc7e..26b8c7e9 100644 --- a/app/Http/Home/Controllers/Controller.php +++ b/app/Http/Home/Controllers/Controller.php @@ -4,23 +4,63 @@ namespace App\Http\Home\Controllers; use App\Traits\Ajax as AjaxTrait; use App\Traits\Security as SecurityTrait; +use Phalcon\Mvc\Dispatcher; class Controller extends \Phalcon\Mvc\Controller { + protected $siteConfig; + protected $navList; + protected $authUser; + use AjaxTrait, SecurityTrait; + public function beforeExecuteRoute(Dispatcher $dispatcher) + { + if ($this->isNotSafeRequest()) { + if (!$this->checkHttpReferer() || !$this->checkCsrfToken()) { + $dispatcher->forward([ + 'controller' => 'public', + 'action' => 'robot', + ]); + return false; + } + } + + $this->siteConfig = $this->getSiteConfig(); + $this->navList = $this->getNavList(); + $this->authUser = $this->getAuthUser(); + + return true; + } + public function initialize() { - $controllerName = $this->router->getControllerName(); + $this->view->setVar('auth_user', $this->authUser); + $this->view->setVar('site_config', $this->siteConfig); + $this->view->setVar('top_nav_list', $this->navList['top']); + $this->view->setVar('btm_nav_list', $this->navList['bottom']); + } - if ($controllerName != 'index') { - //$this->request->checkReferer(); - } + protected function getAuthUser() + { + $auth = $this->getDI()->get('auth'); - if ($this->request->isPost()) { - //$this->request->checkToken(); - } + return $auth->getAuthInfo(); + } + + protected function getNavList() + { + $cache = new \App\Caches\NavTreeList(); + + return $cache->get(); + } + + protected function getSiteConfig() + { + $cache = new \App\Caches\Config(); + + return $cache->getSectionConfig('site'); } } diff --git a/app/Http/Home/Controllers/CourseController.php b/app/Http/Home/Controllers/CourseController.php index aa57c286..bd2df268 100644 --- a/app/Http/Home/Controllers/CourseController.php +++ b/app/Http/Home/Controllers/CourseController.php @@ -2,8 +2,11 @@ namespace App\Http\Home\Controllers; -use App\Http\Home\Services\Course as CourseService; -use Phalcon\Mvc\View; +use App\Services\Frontend\Course as CourseService; +use App\Services\Frontend\CourseFavorite as CourseFavoriteService; +use App\Services\Frontend\CourseList as CourseListService; +use App\Services\Frontend\CourseRelated as CourseRelatedService; +use App\Services\Frontend\CourseReview as CourseReviewService; /** * @RoutePrefix("/course") @@ -16,144 +19,67 @@ class CourseController extends Controller */ public function listAction() { - $service = new CourseService(); + $courseListService = new CourseListService(); - $courses = $service->getCourses(); + $pager = $courseListService->getCourses(); - $this->view->courses = $courses; + return $this->ajaxSuccess(['pager' => $pager]); + + $this->view->setVar('pager', $pager); } /** - * @Get("/{id}", name="home.course.show") + * @Get("/{id:[0-9]+}", name="home.course.show") */ public function showAction($id) { - $service = new CourseService(); + $courseService = new CourseService(); - $course = $service->getCourse($id); + $course = $courseService->getCourse($id); - $this->view->course = $course; + return $this->ajaxSuccess(['course' => $course]); + + $this->view->setVar('course', $course); } /** - * @Get("/{id}/chapters", name="home.course.chapters") + * @Get("/{id:[0-9]+}/related", name="home.course.related") */ - public function chaptersAction($id) + public function relatedAction($id) { - $service = new CourseService(); + $relatedService = new CourseRelatedService(); - $chapters = $service->getChapters($id); + $courses = $relatedService->getRelated($id); - $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + return $this->ajaxSuccess(['courses' => $courses]); - $this->view->chapters = $chapters; + $this->view->setVar('course', $course); } /** - * @Get("/{id}/consults", name="home.course.consults") - */ - public function consultsAction($id) - { - $service = new CourseService(); - - $consults = $service->getConsults($id); - - $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); - - $this->view->consults = $consults; - } - - /** - * @Get("/{id}/reviews", name="home.course.reviews") + * @Get("/{id:[0-9]+}/reviews", name="home.course.reviews") */ public function reviewsAction($id) { - $service = new CourseService(); + $reviewService = new CourseReviewService(); - $reviews = $service->getReviews($id); + $pager = $reviewService->getReviews($id); - $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + return $this->ajaxSuccess(['pager' => $pager]); - $this->view->reviews = $reviews; + $this->view->setVar('pager', $pager); } /** - * @Get("/{id}/comments", name="home.course.comments") - */ - public function commentsAction($id) - { - $service = new CourseService(); - - $comments = $service->getComments($id); - - $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); - - $this->view->comments = $comments; - } - - /** - * @Get("/{id}/users", name="home.course.users") - */ - public function usersAction($id) - { - $service = new CourseService(); - - $users = $service->getUsers($id); - - $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); - - $this->view->users = $users; - } - - /** - * @Post("/{id}/favorite", name="home.course.favorite") + * @Post("/{id:[0-9]+}/favorite", name="home.course.favorite") */ public function favoriteAction($id) { - $service = new CourseService(); + $favoriteService = new CourseFavoriteService(); - $service->favorite($id); + $favoriteService->saveFavorite($id); return $this->response->ajaxSuccess(); } - /** - * @Post("/{id}/unfavorite", name="home.course.unfavorite") - */ - public function unfavoriteAction($id) - { - $service = new CourseService(); - - $service->unfavorite($id); - - return $this->response->ajaxSuccess(); - } - - /** - * @Post("/{id}/apply", name="home.course.apply") - */ - public function applyAction($id) - { - $service = new CourseService(); - - $service->apply($id); - - return $this->response->ajaxSuccess(); - } - - /** - * @Get("/{id}/start", name="home.course.start") - */ - public function startAction($id) - { - $service = new CourseService(); - - $chapter = $service->getStartChapter($id); - - $this->response->redirect([ - 'for' => 'home.chapter.show', - 'id' => $chapter->id, - ]); - } - } diff --git a/app/Http/Home/Controllers/ErrorController.php b/app/Http/Home/Controllers/ErrorController.php index 8318b4c9..ff05bbfe 100644 --- a/app/Http/Home/Controllers/ErrorController.php +++ b/app/Http/Home/Controllers/ErrorController.php @@ -19,7 +19,7 @@ class ErrorController extends \Phalcon\Mvc\Controller } /** - * @Get("/400", name="error.400") + * @Get("/400", name="home.error.400") */ public function show400Action() { @@ -27,7 +27,7 @@ class ErrorController extends \Phalcon\Mvc\Controller } /** - * @Get("/401", name="error.401") + * @Get("/401", name="home.error.401") */ public function show401Action() { @@ -35,7 +35,7 @@ class ErrorController extends \Phalcon\Mvc\Controller } /** - * @Get("/403", name="error.403") + * @Get("/403", name="home.error.403") */ public function show403Action() { @@ -43,7 +43,7 @@ class ErrorController extends \Phalcon\Mvc\Controller } /** - * @Get("/404", name="error.404") + * @Get("/404", name="home.error.404") */ public function show404Action() { @@ -55,7 +55,7 @@ class ErrorController extends \Phalcon\Mvc\Controller } /** - * @Get("/500", name="error.500") + * @Get("/500", name="home.error.500") */ public function show500Action() { diff --git a/app/Http/Home/Controllers/PublicController.php b/app/Http/Home/Controllers/PublicController.php index 3af537fb..d3874d0a 100644 --- a/app/Http/Home/Controllers/PublicController.php +++ b/app/Http/Home/Controllers/PublicController.php @@ -5,18 +5,20 @@ namespace App\Http\Home\Controllers; use App\Models\ContentImage as ContentImageModel; use App\Services\Storage as StorageService; -class PublicController extends Controller +class PublicController extends \Phalcon\Mvc\Controller { /** - * @Get("/content/img/{id}", name="home.content.img") + * @Get("/content/img/{id:[0-9]+}", name="home.content.img") */ public function contentImageAction($id) { - $contentImage = ContentImageModel::findFirstById($id); + $contentImage = ContentImageModel::findFirst($id); if (!$contentImage) { + $this->response->setStatusCode(404); + return $this->response; } @@ -27,4 +29,12 @@ class PublicController extends Controller $this->response->redirect($location); } + /** + * @Get("/robot", name="home.robot") + */ + public function robotAction() + { + + } + } diff --git a/app/Http/Home/Controllers/ReviewController.php b/app/Http/Home/Controllers/ReviewController.php index 482cd2f2..8a15c34a 100644 --- a/app/Http/Home/Controllers/ReviewController.php +++ b/app/Http/Home/Controllers/ReviewController.php @@ -25,7 +25,7 @@ class ReviewController extends Controller } /** - * @Get("/{id}", name="home.review.show") + * @Get("/{id:[0-9]+}", name="home.review.show") */ public function showAction($id) { @@ -37,7 +37,7 @@ class ReviewController extends Controller } /** - * @Post("/{id}/update", name="home.review.update") + * @Post("/{id:[0-9]+}/update", name="home.review.update") */ public function updateAction($id) { @@ -51,7 +51,7 @@ class ReviewController extends Controller } /** - * @Post("/{id}/delete", name="home.review.delete") + * @Post("/{id:[0-9]+}/delete", name="home.review.delete") */ public function deleteAction($id) { @@ -63,7 +63,7 @@ class ReviewController extends Controller } /** - * @Post("/{id}/agree", name="home.review.agree") + * @Post("/{id:[0-9]+}/agree", name="home.review.agree") */ public function agreeAction($id) { @@ -75,7 +75,7 @@ class ReviewController extends Controller } /** - * @Post("/{id}/oppose", name="home.review.oppose") + * @Post("/{id:[0-9]+}/oppose", name="home.review.oppose") */ public function opposeAction($id) { @@ -87,7 +87,7 @@ class ReviewController extends Controller } /** - * @Post("/{id}/reply", name="home.review.reply") + * @Post("/{id:[0-9]+}/reply", name="home.review.reply") */ public function replyAction($id) { diff --git a/app/Http/Home/Controllers/UserController.php b/app/Http/Home/Controllers/UserController.php index b2386694..e7cb4da4 100644 --- a/app/Http/Home/Controllers/UserController.php +++ b/app/Http/Home/Controllers/UserController.php @@ -9,7 +9,7 @@ class UserController extends Controller { /** - * @Get("/{id}", name="home.user.show") + * @Get("/{id:[0-9]+}", name="home.user.show") */ public function showAction($id) { @@ -17,7 +17,7 @@ class UserController extends Controller } /** - * @Get("/{id}/courses", name="home.user.courses") + * @Get("/{id:[0-9]+}/courses", name="home.user.courses") */ public function coursesAction($id) { @@ -25,7 +25,7 @@ class UserController extends Controller } /** - * @Get("/{id}/following", name="home.user.following") + * @Get("/{id:[0-9]+}/following", name="home.user.following") */ public function followingAction($id) { @@ -33,7 +33,7 @@ class UserController extends Controller } /** - * @Get("/{id}/followers", name="home.user.followers") + * @Get("/{id:[0-9]+}/followers", name="home.user.followers") */ public function followersAction($id) { @@ -41,7 +41,7 @@ class UserController extends Controller } /** - * @Post("/{id}/follow", name="home.user.follow") + * @Post("/{id:[0-9]+}/follow", name="home.user.follow") */ public function followAction($id) { @@ -49,7 +49,7 @@ class UserController extends Controller } /** - * @Post("/{id}/unfollow", name="home.user.unfollow") + * @Post("/{id:[0-9]+}/unfollow", name="home.user.unfollow") */ public function unfollowAction($id) { @@ -57,7 +57,7 @@ class UserController extends Controller } /** - * @Post("/{id}/message", name="home.user.message") + * @Post("/{id:[0-9]+}/message", name="home.user.message") */ public function messageAction($id) { diff --git a/app/Http/Home/Module.php b/app/Http/Home/Module.php index 9380a582..646fe4ca 100644 --- a/app/Http/Home/Module.php +++ b/app/Http/Home/Module.php @@ -2,6 +2,7 @@ namespace App\Http\Home; +use App\Http\Home\Services\AuthUser; use Phalcon\DiInterface; use Phalcon\Mvc\ModuleDefinitionInterface; use Phalcon\Mvc\View; @@ -24,5 +25,10 @@ class Module implements ModuleDefinitionInterface ]); return $view; }); + + $di->setShared('auth', function () { + $authUser = new AuthUser(); + return $authUser; + }); } } diff --git a/app/Http/Home/Services/AuthUser.php b/app/Http/Home/Services/AuthUser.php new file mode 100644 index 00000000..e6e94165 --- /dev/null +++ b/app/Http/Home/Services/AuthUser.php @@ -0,0 +1,62 @@ +getAuthKey(); + + $authUser = new \stdClass(); + + $authUser->id = $user->id; + $authUser->name = $user->name; + $authUser->avatar = $user->avatar; + $authUser->admin_role = $user->admin_role; + $authUser->edu_role = $user->edu_role; + + $this->session->set($authKey, $authUser); + } + + /** + * 清除会话 + */ + public function removeAuthInfo() + { + $authKey = $this->getAuthKey(); + + $this->session->remove($authKey); + } + + /** + * 读取会话 + * + * @return mixed + */ + public function getAuthInfo() + { + $authKey = $this->getAuthKey(); + + return $this->session->get($authKey); + } + + /** + * 获取会话键值 + * + * @return string + */ + public function getAuthKey() + { + return 'user_info'; + } + +} diff --git a/app/Http/Home/Services/Category.php b/app/Http/Home/Services/Category.php index ba65a4fb..43d16f91 100644 --- a/app/Http/Home/Services/Category.php +++ b/app/Http/Home/Services/Category.php @@ -2,12 +2,12 @@ namespace App\Http\Home\Services; +use App\Builders\CourseList as CourseListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Models\Category as CategoryModel; use App\Models\Course as CourseModel; use App\Repos\Category as CategoryRepo; use App\Repos\Course as CourseRepo; -use App\Transformers\CourseList as CourseListTransformer; use App\Validators\Course as CourseFilter; class Category extends Service @@ -79,7 +79,7 @@ class Category extends Service { if ($pager->total_items > 0) { - $builder = new CourseListTransformer(); + $builder = new CourseListBuilder(); $pipeA = $pager->items->toArray(); diff --git a/app/Http/Home/Services/Chapter.php b/app/Http/Home/Services/Chapter.php index c071f5f8..fdebae6a 100644 --- a/app/Http/Home/Services/Chapter.php +++ b/app/Http/Home/Services/Chapter.php @@ -2,16 +2,16 @@ namespace App\Http\Home\Services; +use App\Builders\CommentList as CommentListBuilder; use App\Exceptions\BadRequest as BadRequestException; use App\Library\Paginator\Query as PagerQuery; use App\Models\Comment as CommentModel; use App\Repos\Chapter as ChapterRepo; +use App\Repos\ChapterLike as ChapterVoteRepo; use App\Repos\ChapterUser as ChapterUserRepo; -use App\Repos\ChapterVote as ChapterVoteRepo; use App\Repos\Comment as CommentRepo; use App\Repos\Course as CourseRepo; use App\Repos\Video as VideoRepo; -use App\Transformers\CommentList as CommentListTransformer; use App\Validators\Chapter as ChapterFilter; class Chapter extends Service @@ -153,7 +153,7 @@ class Chapter extends Service { if ($pager->total_items > 0) { - $builder = new CommentListTransformer(); + $builder = new CommentListBuilder(); $pipeA = $pager->items->toArray(); diff --git a/app/Http/Home/Services/ChapterAdmin.php b/app/Http/Home/Services/ChapterAdmin.php deleted file mode 100644 index fe69b6c1..00000000 --- a/app/Http/Home/Services/ChapterAdmin.php +++ /dev/null @@ -1,180 +0,0 @@ -findOrFail($id); - - return $chapter; - } - - public function getCourse($courseId) - { - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($courseId); - - return $course; - } - - public function getTopChapters($courseId) - { - $courseRepo = new CourseRepo(); - - $result = $courseRepo->findTopChapters($courseId); - - return $result; - } - - public function create() - { - $user = $this->getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new ChapterFilter(); - - $data = []; - - $data['course_id'] = $filter->checkCourseId($post['course_id']); - $data['title'] = $filter->checkTitle($post['title']); - $data['summary'] = $filter->checkSummary($post['summary']); - $data['free'] = $filter->checkFree($post['free']); - - $chapterRepo = new ChapterRepo(); - - if (isset($post['parent_id'])) { - $data['parent_id'] = $filter->checkParentId($post['parent_id']); - $data['priority'] = $chapterRepo->maxSectionPriority($data['parent_id']); - } else { - $data['priority'] = $chapterRepo->maxChapterPriority($data['course_id']); - } - - $data['priority'] += 1; - $data['user_id'] = $user->id; - $data['status'] = ChapterModel::STATUS_DRAFT; - - $chapter = $chapterRepo->create($data); - - if ($chapter->parent_id > 0) { - - $service = new ChapterContent(); - - $service->create($chapter); - - $this->updateChapterCount($chapter); - } - - return $chapter; - } - - public function update($id) - { - $chapter = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new ChapterFilter(); - - $filter->checkOwner($user->id, $chapter->user_id); - - $data = []; - - if (isset($post['title'])) { - $data['title'] = $filter->checkTitle($post['title']); - } - - if (isset($post['summary'])) { - $data['summary'] = $filter->checkSummary($post['summary']); - } - - if (isset($post['free'])) { - $data['free'] = $filter->checkFree($post['free']); - } - - if (isset($post['priority'])) { - $data['priority'] = $filter->checkPriority($post['priority']); - } - - $chapter->update($data); - - return $chapter; - } - - public function delete($id) - { - $chapter = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $filter = new ChapterFilter(); - - $filter->checkOwner($user->id, $chapter->user_id); - - if ($chapter->status == ChapterModel::STATUS_DELETED) { - return; - } - - $chapter->status = ChapterModel::STATUS_DELETED; - - $chapter->update(); - - $this->updateChapterCount($chapter); - } - - public function getContent($id) - { - $service = new ChapterContent(); - - $chapter = $this->findOrFail($id); - - $result = $service->get($chapter); - - return $result; - } - - public function updateContent($id) - { - $service = new ChapterContent(); - - $chapter = $this->findOrFail($id); - - $result = $service->update($chapter); - - return $result; - } - - private function findOrFail($id) - { - $repo = new ChapterRepo(); - - $result = $repo->findOrFail($id); - - return $result; - } - - private function updateChapterCount($chapter) - { - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($chapter->course_id); - - $chapterCount = $courseRepo->countChapters($course->id); - - $course->chapter_count = $chapterCount; - - $course->update(); - } - -} diff --git a/app/Http/Home/Services/ChapterContentAdmin.php b/app/Http/Home/Services/ChapterContentAdmin.php deleted file mode 100644 index 462e9930..00000000 --- a/app/Http/Home/Services/ChapterContentAdmin.php +++ /dev/null @@ -1,234 +0,0 @@ -findById($chapter->course_id); - - $result = null; - - switch ($course->model) { - - case CourseModel::MODEL_VIDEO: - $result = $this->getVideo($chapter); - break; - - case CourseModel::MODEL_LIVE: - $result = $this->getLive($chapter); - break; - - case CourseModel::MODEL_ARTICLE: - $result = $this->getArticle($chapter); - break; - } - - return $result; - } - - public function create($chapter) - { - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($chapter->course_id); - - $result = null; - - switch ($course->model) { - - case CourseModel::MODEL_VIDEO: - $result = $this->createVideo($chapter); - break; - - case CourseModel::MODEL_LIVE: - $result = $this->createLive($chapter); - break; - - case CourseModel::MODEL_ARTICLE: - $result = $this->createArticle($chapter); - break; - } - - return $result; - } - - public function update($chapter) - { - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($chapter->course_id); - - $result = null; - - switch ($course->model) { - - case CourseModel::MODEL_VIDEO: - $result = $this->updateVideo($chapter); - break; - - case CourseModel::MODEL_LIVE: - $result = $this->updateLive($chapter); - break; - - case CourseModel::MODEL_ARTICLE: - $result = $this->updateArticle($chapter); - break; - } - - return $result; - } - - private function getVideo($chapter) - { - $videoRepo = new ChapterVideoRepo(); - - $video = $videoRepo->findOriginChapterVideo($chapter->id); - - return $video; - } - - private function getArticle($chapter) - { - $articleRepo = new ChapterArticleRepo(); - - $article = $articleRepo->findByChapterId($chapter->id); - - return $article; - } - - private function getLive($chapter) - { - $liveRepo = new ChapterLiveRepo(); - - $live = $liveRepo->findByChapterId($chapter->id); - - $startTime = strtotime(date('y-m-d')); - $endTime = $startTime + 3600; - - if ($live->start_time == 0) { - $live->start_time = $startTime; - } - - if ($live->end_time == 0) { - $live->end_time = $endTime; - } - - return $live; - } - - private function createVideo($chapter) - { - $video = new ChapterVideoModel(); - - $video->chapter_id = $chapter->id; - $video->definition = ChapterVideoModel::DFN_ORG; - - $video->create(); - - return $video; - } - - private function createLive($chapter) - { - $live = new ChapterLiveModel(); - - $live->chapter_id = $chapter->id; - - $live->create(); - - return $live; - } - - private function createArticle($chapter) - { - $article = new ChapterArticleModel(); - - $article->chapter_id = $chapter->id; - $article->content_markdown = ''; - $article->content_html = ''; - - $article->create(); - - return $article; - } - - private function updateVideo($chapter) - { - $post = $this->request->getPost(); - - $filter = new ChapterVideoFilter(); - - $data = []; - - $data['play_url'] = $filter->checkPlayUrl($post['play_url']); - - $videoRepo = new ChapterVideoRepo(); - - $video = $videoRepo->findOriginChapterVideo($chapter->id); - - if ($video->play_url != $data['play_url']) { - $video->update($data); - } - - return $video; - } - - private function updateLive($chapter) - { - $post = $this->request->getPost(); - - $filter = new ChapterLiveFilter(); - - $data = []; - - $liveTime = $filter->checkLiveTime($post['start_time'], $post['end_time']); - - $data['start_time'] = $liveTime['start_time']; - $data['end_time'] = $liveTime['end_time']; - - $liveRepo = new ChapterLiveRepo(); - - $live = $liveRepo->findByChapterId($chapter->id); - - $live->update($data); - - return $live; - } - - private function updateArticle($chapter) - { - $post = $this->request->getPost(); - - $filter = new ChapterArticleFilter(); - - $data = []; - - $data['content_html'] = $filter->checkContent($post['content']); - - $articleRepo = new ChapterArticleRepo(); - - $article = $articleRepo->findByChapterId($chapter->id); - - $article->update($data); - - return $article; - } - -} diff --git a/app/Http/Home/Services/Comment.php b/app/Http/Home/Services/Comment.php deleted file mode 100644 index 48e17097..00000000 --- a/app/Http/Home/Services/Comment.php +++ /dev/null @@ -1,198 +0,0 @@ -getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new CommentFilter(); - - $data = []; - - $data['user_id'] = $user->id; - $data['chapter_id'] = $filter->checkChapterId($post['chapter_id']); - $data['content'] = $filter->checkContent($post['content']); - - if (!empty($post['parent_id'])) { - $data['parent_id'] = $filter->checkParentId($post['parent_id']); - } - - if (!empty($post['to_user_id'])) { - $data['to_user_id'] = $filter->checkToUserId($post['to_user_id']); - } - - $chapterRepo = new ChapterRepo(); - - $chapter = $chapterRepo->findById($data['chapter_id']); - - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($chapter->course_id); - - $data['course_id'] = $course->id; - - $commentRepo = new CommentRepo(); - - $comment = $commentRepo->create($data); - - $chapter->comment_count += 1; - - $chapter->update(); - - $course->comment_count += 1; - - $course->update(); - - return $comment; - } - - public function getComment($id) - { - $comment = $this->findOrFail($id); - - return $this->handleComment($comment); - } - - public function getReplies($id) - { - $comment = $this->findOrFail($id); - - $pagerQuery = new PagerQuery(); - - $sort = $pagerQuery->getSort(); - $page = $pagerQuery->getPage(); - $limit = $pagerQuery->getLimit(); - - $where = [ - 'parent_id' => $comment->id, - 'status' => CommentModel::STATUS_NORMAL, - ]; - - $commentRepo = new CommentRepo(); - - $pager = $commentRepo->paginate($where, $sort, $page, $limit); - - $pager = $pager->paginate(); - - return $this->handleReplies($pager); - } - - public function delete($id) - { - $comment = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $filter = new CommentFilter(); - - $filter->checkOwner($user->id, $comment->user_id); - - if ($comment->status == CommentModel::STATUS_DELETED) { - return; - } - - $comment->status = CommentModel::STATUS_DELETED; - - $comment->update(); - } - - public function agree($id) - { - $comment = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $voteRepo = new CommentVoteRepo(); - - $vote = $voteRepo->find($user->id, $comment->id); - - if ($vote) { - throw new BadRequestException('comment.has_voted'); - } - - $voteRepo->agree($user->id, $comment->id); - - $comment->agree_count += 1; - - $comment->update(); - } - - public function oppose($id) - { - $comment = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $voteRepo = new CommentVoteRepo(); - - $vote = $voteRepo->find($user->id, $comment->id); - - if ($vote) { - throw new BadRequestException('comment.has_voted'); - } - - $voteRepo->oppose($user->id, $comment->id); - - $comment->oppose_count += 1; - - $comment->update(); - } - - private function findOrFail($id) - { - $repo = new CommentRepo(); - - $result = $repo->findOrFail($id); - - return $result; - } - - private function handleComment($comment) - { - $result = $comment->toArray(); - - $userRepo = new UserRepo(); - - $user = $userRepo->findShallowUser($comment->user_id); - - $result['user'] = $user->toArray(); - - return (object) $result; - } - - private function handleReplies($pager) - { - if ($pager->total_items > 0) { - - $builder = new CommentListTransformer(); - - $pipeA = $pager->items->toArray(); - - $pipeB = $builder->handleUsers($pipeA); - - $pipeC = $builder->arrayToObject($pipeB); - - $pager->items = $pipeC; - } - - return $pager; - } - -} diff --git a/app/Http/Home/Services/Consult.php b/app/Http/Home/Services/Consult.php deleted file mode 100644 index 73c6adb5..00000000 --- a/app/Http/Home/Services/Consult.php +++ /dev/null @@ -1,174 +0,0 @@ -getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new ConsultFilter(); - - $data = []; - - $data['user_id'] = $user->id; - $data['course_id'] = $filter->checkCourseId($post['course_id']); - $data['content'] = $filter->checkContent($post['content']); - - $consultRepo = new ConsultRepo(); - - $consult = $consultRepo->create($data); - - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($data['course_id']); - - $course->consult_count += 1; - - $course->update(); - - return $consult; - } - - public function getConsult($id) - { - $consult = $this->findOrFail($id); - - return $this->handleConsult($consult); - } - - public function update($id) - { - $consult = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new ConsultFilter(); - - $filter->checkOwner($user->id, $consult->user_id); - - $content = $filter->checkContent($post['content']); - - $consult->content = $content; - - return $consult; - } - - public function delete($id) - { - $consult = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $filter = new ConsultFilter(); - - $filter->checkOwner($user->id, $consult->user_id); - - if ($consult->status == ConsultModel::STATUS_DELETED) { - return; - } - - $consult->status = ConsultModel::STATUS_DELETED; - - $consult->update(); - } - - public function agree($id) - { - $consult = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $voteRepo = new ConsultVoteRepo(); - - $vote = $voteRepo->find($user->id, $consult->id); - - if ($vote) { - throw new BadRequestException('consult.has_voted'); - } - - $voteRepo->agree($user->id, $consult->id); - - $consult->agree_count += 1; - - $consult->update(); - } - - public function oppose($id) - { - $consult = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $voteRepo = new ConsultVoteRepo(); - - $vote = $voteRepo->find($user->id, $consult->id); - - if ($vote) { - throw new BadRequestException('consult.has_voted'); - } - - $voteRepo->oppose($user->id, $consult->id); - - $consult->oppose_count += 1; - - $consult->update(); - } - - public function reply($id) - { - $consult = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new ConsultFilter(); - - $filter->checkOwner($user->id, $consult->user_id); - - $reply = $filter->checkReply($post['reply']); - - $consult->reply = $reply; - $consult->reply_time = time(); - - $consult->update(); - } - - private function findOrFail($id) - { - $repo = new ConsultRepo(); - - $result = $repo->findOrFail($id); - - return $result; - } - - private function handleConsult($consult) - { - $result = $consult->toArray(); - - $userRepo = new UserRepo(); - - $user = $userRepo->findShallowUser($consult->user_id); - - $result['user'] = $user->toArray(); - - return (object) $result; - } - -} diff --git a/app/Http/Home/Services/Course.php b/app/Http/Home/Services/Course.php index 818a108c..f1571fa2 100644 --- a/app/Http/Home/Services/Course.php +++ b/app/Http/Home/Services/Course.php @@ -2,101 +2,74 @@ namespace App\Http\Home\Services; -use App\Exceptions\BadRequest as BadRequestException; +use App\Builders\ChapterList as ChapterTreeBuilder; +use App\Builders\CourseList as CourseListBuilder; +use App\Builders\ReviewList as ReviewListBuilder; use App\Library\Paginator\Query as PagerQuery; -use App\Models\Comment as CommentModel; -use App\Models\Consult as ConsultModel; -use App\Models\Course as CourseModel; +use App\Models\CourseFavorite as FavoriteModel; use App\Models\Review as ReviewModel; -use App\Repos\Consult as ConsultRepo; use App\Repos\Course as CourseRepo; -use App\Repos\CourseFavorite as CourseFavoriteRepo; -use App\Repos\CourseStudent as CourseUserRepo; +use App\Repos\CourseFavorite as FavoriteRepo; +use App\Repos\CourseUser as CourseUserRepo; use App\Repos\Review as ReviewRepo; -use App\Repos\User as UserRepo; -use App\Transformers\ChapterList as ChapterListTransformer; -use App\Transformers\CommentList as CommentListTransformer; -use App\Transformers\ConsultList as ConsultListTransformer; -use App\Transformers\CourseUserList as CourseUserListTransformer; -use App\Transformers\ReviewList as ReviewListTransformer; +use App\Validators\Course as CourseValidator; +use App\Validators\Review as ReviewValidator; class Course extends Service { - public function getCategoryPaths($categoryId) + public function getCourses() { - $categoryRepo = new CategoryRepo(); + $pagerQuery = new PagerQuery(); - $category = $categoryRepo->findById($categoryId); + $params = $pagerQuery->getParams(); - $ids = explode(',', trim($category->path, ',')); - - $categories = $categoryRepo->findByIds($ids, ['id', 'name']); - - $paths = []; - - foreach ($categories as $category) { - $paths[] = $category->name; + if (!empty($params['category_id'])) { + $params['category_id'] = $this->getChildCategoryIds($params['category_id']); } - return implode(' > ', $paths); + $params['published'] = 1; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $courseRepo = new CourseRepo(); + $pager = $courseRepo->paginate($params, $sort, $page, $limit); + + return $this->handleCourses($pager); } public function getCourse($id) { - $course = $this->findOrFail($id); + $course = $this->checkCourseCache($id); $user = $this->getCurrentUser(); - return $this->handleCourse($user, $course); + return $this->handleCourse($course, $user); } public function getChapters($id) { - $course = $this->findOrFail($id); + $course = $this->checkCourseCache($id); $user = $this->getCurrentUser(); $courseRepo = new CourseRepo(); - $topChapters = $courseRepo->findTopChapters($course->id); + $chapters = $courseRepo->findChapters($course->id); - $subChapters = $courseRepo->findSubChapters($course->id, $course->model); - - $myChapters = null; - - if ($user->id > 0) { - $myChapters = $courseRepo->findUserChapters($user->id, $course->id); + if ($chapters->count() == 0) { + return []; } - return $this->handleChapters($topChapters, $subChapters, $myChapters); - } - - public function getConsults($id) - { - $course = $this->findOrFail($id); - - $pagerQuery = new PagerQuery(); - - $sort = $pagerQuery->getSort(); - $page = $pagerQuery->getPage(); - $limit = $pagerQuery->getLimit(); - - $where = [ - 'course_id' => $course->id, - 'status' => ConsultModel::STATUS_NORMAL, - ]; - - $consultRepo = new ConsultRepo(); - - $pager = $consultRepo->paginate($where, $sort, $page, $limit); - - return $this->handleConsults($pager); + return $this->handleChapters($chapters, $course, $user); } public function getReviews($id) { - $course = $this->findOrFail($id); + $course = $this->checkCourseCache($id); $pagerQuery = new PagerQuery(); @@ -106,7 +79,8 @@ class Course extends Service $where = [ 'course_id' => $course->id, - 'status' => ReviewModel::STATUS_NORMAL, + 'published' => 1, + 'deleted' => 0, ]; $reviewRepo = new ReviewRepo(); @@ -116,139 +90,124 @@ class Course extends Service return $this->handleReviews($pager); } - public function getComments($id) + public function reviewCourse($id) { - $course = $this->findOrFail($id); + $post = $this->request->getPost(); - $pagerQuery = new PagerQuery(); + $course = $this->checkCourse($id); - $sort = $pagerQuery->getSort(); - $page = $pagerQuery->getPage(); - $limit = $pagerQuery->getLimit(); + $user = $this->getLoginUser(); - $where = [ - 'course_id' => $course->id, - 'status' => CommentModel::STATUS_NORMAL, - ]; + $validator = new ReviewValidator(); - $commentRepo = new CommentRepo(); + $rating = $validator->checkRating($post['rating']); + $content = $validator->checkContent($post['content']); - $pager = $commentRepo->paginate($where, $sort, $page, $limit); + $reviewRepo = new ReviewRepo(); - return $this->handleComments($pager); - } + $review = $reviewRepo->findReview($course->id, $user->id); - public function getUsers($id) - { - $course = $this->findOrFail($id); + if (!$review) { + $review = new ReviewModel(); + $review->course_id = $course->id; + $review->user_id = $user->id; + $review->rating = $rating; + $review->content = $content; + $review->create(); - $pagerQuery = new PagerQuery(); - - $sort = $pagerQuery->getSort(); - $page = $pagerQuery->getPage(); - $limit = $pagerQuery->getLimit(); - - $where = ['course_id' => $course->id]; - - $courseUserRepo = new CourseUserRepo(); - - $pager = $courseUserRepo->paginate($where, $sort, $page, $limit); - - return $this->handleUsers($pager); - } - - public function favorite($id) - { - $course = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $favoriteRepo = new CourseFavoriteRepo(); - - $favorite = $favoriteRepo->find($user->id, $course->id); - - if ($favorite) { - throw new BadRequestException('course.favorite_existed'); + $course->review_count += 1; + $course->update(); + } else { + $review->rating = $rating; + $review->content = $content; + $review->update(); } - - $favoriteRepo->create($user->id, $course->id); - - $course->favorite_count += 1; - $course->update(); } - public function undoFavorite($id) + public function favoriteCourse($id) { - $course = $this->findOrFail($id); + $course = $this->checkCourse($id); - $user = $this->getLoggedUser(); + $user = $this->getLoginUser(); - $favoriteRepo = new CourseFavoriteRepo(); + $favoriteRepo = new FavoriteRepo(); - $favorite = $favoriteRepo->find($user->id, $course->id); + $favorite = $favoriteRepo->findFavorite($course->id, $user->id); if (!$favorite) { - throw new BadRequestException('course.favorite_not_existed'); - } - $favorite->delete(); + $favorite = new FavoriteModel(); + $favorite->course_id = $course->id; + $favorite->user_id = $user->id; + $favorite->create(); - $course->favorite_count -= 1; - $course->update(); - } + $course->favorite_count += 1; + $course->update(); - public function apply($id) - { - $course = $this->findOrFail($id); + } else { - $user = $this->getLoggedUser(); + if ($favorite->deleted == 0) { + $favorite->deleted = 1; + $course->favorite_count -= 1; + } else { + $favorite->deleted = 0; + $course->favorite_count += 1; + } - if ($course->status != CourseModel::STATUS_APPROVED) { - throw new BadRequestException('course.apply_unpublished_course'); - } - - if ($course->price > 0) { - throw new BadRequestException('course.apply_priced_course'); - } - - $courseUserRepo = new CourseUserRepo(); - - $courseUser = $courseUserRepo->find($user->id, $course->id); - - $expired = $courseUser->expire_time < time(); - - if ($courseUser && !$expired) { - throw new BadRequestException('course.has_applied'); - } - - $expireTime = time() + 86400 * $course->expiry; - - $courseUserRepo->create([ - 'user_id' => $user->id, - 'course_id' => $course->id, - 'expire_time' => $expireTime, - ]); - - if (!$courseUser) { - $course->user_count += 1; + $favorite->update(); $course->update(); } } - private function findOrFail($id) + protected function checkCourse($id) { - $repo = new CourseRepo(); + $validator = new CourseValidator(); - $result = $repo->findOrFail($id); + $course = $validator->checkCourse($id); - return $result; + return $course; } - private function handleConsults($pager) + protected function checkCourseCache($id) + { + $validator = new CourseValidator(); + + $course = $validator->checkCourseCache($id); + + return $course; + } + + protected function getChildCategoryIds($id) + { + $categoryService = new \App\Services\Category(); + + $childIds = $categoryService->getChildIds($id); + + return $childIds; + } + + protected function handleCourses($pager) { if ($pager->total_items > 0) { - $builder = new ConsultListTransformer(); + $builder = new CourseListBuilder(); + + $pipeA = $pager->items->toArray(); + $pipeB = $builder->handleCourses($pipeA); + $pipeC = $builder->handleCategories($pipeB); + $pipeD = $builder->arrayToObject($pipeC); + + $pager->items = $pipeD; + } + + return $pager; + } + + protected function handleReviews($pager) + { + if ($pager->total_items > 0) { + + $builder = new ReviewListBuilder(); $pipeA = $pager->items->toArray(); @@ -262,113 +221,77 @@ class Course extends Service return $pager; } - private function handleReviews($pager) - { - if ($pager->total_items > 0) { - - $builder = new ReviewListTransformer(); - - $pipeA = $pager->items->toArray(); - - $pipeB = $builder->handleUsers($pipeA); - - $pipeC = $builder->arrayToObject($pipeB); - - $pager->items = $pipeC; - } - - return $pager; - } - - private function handleComments($pager) - { - if ($pager->total_items > 0) { - - $builder = new CommentListTransformer(); - - $pipeA = $pager->items->toArray(); - - $pipeB = $builder->handleUsers($pipeA); - - $pipeC = $builder->arrayToObject($pipeB); - - $pager->items = $pipeC; - } - - return $pager; - } - - private function handleUsers($pager) - { - if ($pager->total_items > 0) { - - $builder = new CourseUserListTransformer(); - - $pipeA = $pager->items->toArray(); - - $pipeB = $builder->handleUsers($pipeA); - - $pipeC = $builder->arrayToObject($pipeB); - - $pager->items = $pipeC; - } - - return $pager; - } - - private function handleCourse($user, $course) + /** + * @param \App\Models\Course $course + * @param \App\Models\User $user + * @return object + */ + protected function handleCourse($course, $user) { $result = $course->toArray(); - $userRepo = new UserRepo(); + $me = [ + 'reviewed' => 0, + 'favorited' => 0, + 'bought' => 0, + 'progress' => 0, + ]; - $user = $userRepo->findShallowUser($course->user_id); + if (!empty($user->id)) { - $result['user'] = $user->toArray(); + $favoriteRepo = new FavoriteRepo(); - $result['me']['favorited'] = 0; - $result['me']['bought'] = 0; + $favorite = $favoriteRepo->findFavorite($course->id, $user->id); - if ($user->id > 0) { + if ($favorite && $favorite->deleted == 0) { + $me['favorited'] = 1; + } - $cfRepo = new CourseFavoriteRepo(); + $courseUserRepo = new CourseUserRepo(); - $favorite = $cfRepo->find($user->id, $course->id); + $courseUser = $courseUserRepo->findCourseUser($course->id, $user->id); - $result['me']['favorited'] = $favorite ? 1 : 0; - - $csRepo = new CourseUserRepo(); - - $cs = $csRepo->find($user->id, $course->id); - - if ($cs && $cs->expire_time < time()) { - $result['me']['bought'] = 1; - $result['me']['finish_count'] = $cs->finish_count; + if ($courseUser) { + $me['reviewed'] = $courseUser->reviewed; + $me['progress'] = $courseUser->progress; + if ($courseUser->expire_time < time()) { + $me['bought'] = 1; + } } } - return $result; + $result['me'] = $me; + + return kg_array_object($result); } - private function handleChapters($topChapters, $subChapters, $myChapters) + /** + * @param array $chapters + * @param \App\Models\Course $course + * @param \App\Models\User $user + * @return object + */ + protected function handleChapters($chapters, $course, $user) { - $chapters = array_merge($topChapters->toArray(), $subChapters->toArray()); + $chapterList = $chapters->toArray(); - $studyHistory = []; + $studyList = []; - if ($myChapters) { - $studyHistory = $myChapters->toArray(); + if (!empty($user->id)) { + $courseRepo = new CourseRepo(); + $userChapters = $courseRepo->findUserChapters($course->id, $user->id); + $studyList = $userChapters->toArray(); } - $builder = new ChapterListTransformer(); + $builder = new ChapterTreeBuilder(); - $stepA = $builder->handleProcess($chapters, $studyHistory); + $stepA = $builder->handleProcess($chapterList, $studyList); $stepB = $builder->handleTree($stepA); - $result = $builder->arrayToObject($stepB); + $stepC = $builder->arrayToObject($stepB); - return $result; + return $stepC; } } diff --git a/app/Http/Home/Services/CourseAdmin.php b/app/Http/Home/Services/CourseAdmin.php deleted file mode 100644 index 950cb575..00000000 --- a/app/Http/Home/Services/CourseAdmin.php +++ /dev/null @@ -1,177 +0,0 @@ -findOrFail($id); - - return $course; - } - - public function getTopCategories() - { - $categoryRepo = new CategoryRepo(); - - $categories = $categoryRepo->find([ - 'parent_id' => 0, - 'status' => CategoryModel::STATUS_NORMAL, - ]); - - return $categories; - } - - public function getChapters($id) - { - $course = $this->findOrFail($id); - - $courseRepo = new CourseRepo(); - - $topChapters = $courseRepo->findTopChapters($id); - - switch ($course->model) { - - case CourseModel::MODEL_VIDEO: - $subChapters = $courseRepo->findVideoChapters($id); - break; - - case CourseModel::MODEL_LIVE: - $subChapters = $courseRepo->findLiveChapters($id); - break; - - case CourseModel::MODEL_ARTICLE: - $subChapters = $courseRepo->findArticleChapters($id); - break; - } - - $result = $this->handleChapters($topChapters, $subChapters); - - return $result; - } - - public function create() - { - $user = $this->getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new CourseFilter(); - - $data = []; - - $data['user_id'] = $user->id; - $data['status'] = CourseModel::STATUS_DRAFT; - $data['category_id'] = $filter->checkCategoryId($post['category_id']); - $data['model'] = $filter->checkModel($post['model']); - $data['level'] = $filter->checkLevel($post['level']); - $data['title'] = $filter->checkTitle($post['title']); - $data['cover'] = $filter->checkCover($post['cover']); - $data['summary'] = $filter->checkSummary($post['summary']); - $data['keywords'] = $filter->checkKeywords($post['keywords']); - $data['price'] = $filter->checkPrice($post['price']); - $data['expiry'] = $filter->checkExpiry($post['expiry']); - - $courseRepo = new CourseRepo(); - - $course = $courseRepo->create($data); - - return $course; - } - - public function update($id) - { - $course = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $post = $this->request->getPost(); - - $filter = new CourseFilter(); - - $filter->checkOwner($user->id, $course->user_id); - - $data = []; - - if (isset($post['category_id'])) { - $data['category_id'] = $filter->checkCategoryId($post['category_id']); - } - - if (isset($post['level'])) { - $data['level'] = $filter->checkLevel($post['level']); - } - - if (isset($post['title'])) { - $data['title'] = $filter->checkTitle($post['title']); - } - - if (isset($post['cover'])) { - $data['cover'] = $filter->checkCover($post['cover']); - } - - if (isset($post['summary'])) { - $data['summary'] = $filter->checkSummary($post['summary']); - } - - if (isset($post['keywords'])) { - $data['keywords'] = $filter->checkKeywords($post['keywords']); - } - - if (isset($post['price'])) { - $data['price'] = $filter->checkPrice($post['price']); - } - - $course->update($data); - - return $course; - } - - public function delete($id) - { - $course = $this->findOrFail($id); - - $user = $this->getLoggedUser(); - - $filter = new CourseFilter(); - - $filter->checkOwner($user->id, $course->user_id); - - if ($course->status == CourseModel::STATUS_DELETED) { - return false; - } - - $course->status = CourseModel::STATUS_DELETED; - - $course->update(); - } - - private function findOrFail($id) - { - $repo = new CourseRepo(); - - $result = $repo->findOrFail($id); - - return $result; - } - - private function handleChapters($topChapters, $subChapters) - { - $chapters = array_merge($topChapters->toArray(), $subChapters->toArray()); - - $builder = new ChapterListTransformer(); - - $tree = $builder->handleTree($chapters); - - return $builder->arrayToObject($tree); - } - -} diff --git a/app/Http/Home/Services/Danmaku.php b/app/Http/Home/Services/Danmaku.php deleted file mode 100644 index de620708..00000000 --- a/app/Http/Home/Services/Danmaku.php +++ /dev/null @@ -1,87 +0,0 @@ - $chapterId, - 'published' => 1, - 'deleted' => 0 - ]; - - $pager = $danmakuRepo->paginate($params, 'latest', 1, $limit); - - $result = $this->handleDanmakus($pager); - - return $result; - } - - public function createDanmaku() - { - $body = $this->request->getJsonRawBody(); - - $danmaku = new DanmakuModel(); - - $validator = new DanmakuValidator(); - - $data = []; - - $chapter = $validator->checkChapter($body->id); - - $user = $validator->checkUser($body->author, $body->token); - - $validator->checkPostLimit($user->id); - - $data['text'] = $validator->checkText($body->text); - $data['color'] = $validator->checkColor($body->color); - $data['type'] = $validator->checkType($body->type); - $data['time'] = $validator->checkTime($body->time); - $data['ip'] = $this->request->getClientAddress(); - $data['course_id'] = $chapter->course_id; - $data['chapter_id'] = $chapter->id; - $data['user_id'] = $user->id; - - $danmaku->create($data); - - return $danmaku; - } - - protected function handleDanmakus($pager) - { - if ($pager->total_items == 0) { - return []; - } - - $transformer = new DanmakuListTransformer(); - - $items = $pager->items->toArray(); - - $danmakus = $transformer->handleUsers($items); - - $result = []; - - foreach ($danmakus as $danmaku) { - $result[] = [ - $danmaku['time'], - $danmaku['type'], - $danmaku['color'], - $danmaku['user']['name'], - $danmaku['text'] . ' - by ' . $danmaku['user']['name'], - ]; - } - - return $result; - } - -} diff --git a/app/Http/Home/Services/My.php b/app/Http/Home/Services/My.php index b559a530..8b93a935 100644 --- a/app/Http/Home/Services/My.php +++ b/app/Http/Home/Services/My.php @@ -2,19 +2,19 @@ namespace App\Http\Home\Services; +use App\Builders\ConsultList as ConsultListBuilder; +use App\Builders\CourseUserList as CourseUserListBuilder; +use App\Builders\OrderList as OrderListBuilder; +use App\Builders\ReviewList as ReviewListBuilder; +use App\Builders\UserFavoriteList as FavoriteListBuilder; use App\Library\Paginator\Query as PagerQuery; use App\Models\Consult as ConsultModel; use App\Models\Review as ReviewModel; use App\Repos\Consult as ConsultRepo; -use App\Repos\CourseFavorite as CourseFavoriteRepo; +use App\Repos\CourseFavorite as FavoriteRepo; use App\Repos\CourseStudent as CourseUserRepo; use App\Repos\Order as OrderRepo; use App\Repos\Review as ReviewRepo; -use App\Transformers\ConsultList as ConsultListTransformer; -use App\Transformers\CourseFavoriteList as CourseFavoriteListTransformer; -use App\Transformers\CourseUserList as CourseUserListTransformer; -use App\Transformers\OrderList as OrderListTransformer; -use App\Transformers\ReviewList as ReviewListTransformer; use App\Validators\Order as OrderFilter; class My extends Service @@ -51,7 +51,7 @@ class My extends Service $where = ['user_id' => $user->id]; - $favoriteRepo = new CourseFavoriteRepo(); + $favoriteRepo = new FavoriteRepo(); $pager = $favoriteRepo->paginate($where, $sort, $page, $limit); @@ -134,7 +134,7 @@ class My extends Service { if ($pager->total_items > 0) { - $builder = new CourseUserListTransformer(); + $builder = new CourseUserListBuilder(); $pipeA = $pager->items->toArray(); @@ -152,7 +152,7 @@ class My extends Service { if ($pager->total_items > 0) { - $builder = new CourseFavoriteListTransformer(); + $builder = new FavoriteListBuilder(); $pipeA = $pager->items->toArray(); @@ -170,7 +170,7 @@ class My extends Service { if ($pager->total_items > 0) { - $builder = new ConsultListTransformer(); + $builder = new ConsultListBuilder(); $pipeA = $pager->items->toArray(); @@ -188,7 +188,7 @@ class My extends Service { if ($pager->total_items > 0) { - $builder = new ReviewListTransformer(); + $builder = new ReviewListBuilder(); $pipeA = $pager->items->toArray(); @@ -206,7 +206,7 @@ class My extends Service { if ($pager->total_items > 0) { - $builder = new OrderListTransformer(); + $builder = new OrderListBuilder(); $pipeA = $pager->items->toArray(); diff --git a/app/Http/Home/Services/Service.php b/app/Http/Home/Services/Service.php index 94bf125d..d7191295 100644 --- a/app/Http/Home/Services/Service.php +++ b/app/Http/Home/Services/Service.php @@ -3,66 +3,42 @@ namespace App\Http\Home\Services; use App\Models\User as UserModel; -use App\Repos\User as UserRepo; -use App\Validators\Filter as BaseFilter; -use Phalcon\Mvc\User\Component as UserComponent; +use App\Validators\Validator as AppValidator; -class Service extends UserComponent +class Service extends \Phalcon\Mvc\User\Component { public function getCurrentUser() { - $token = $this->getAuthToken(); + $authUser = $this->getAuthUser(); - return $token ? $this->getUser($token) : $this->getGuest(); - } - - public function getLoggedUser() - { - $token = $this->getAuthToken(); - - $filter = new BaseFilter(); - - $filter->checkAuthToken($token); - - $user = $this->getUser($token); - - $filter->checkAuthUser($user); - - return $user; - } - - private function getAuthToken() - { - $token = null; - - if ($this->cookies->has('token')) { - - $cookie = $this->cookies->get('token'); - - $token = $cookie->getValue(); + if ($authUser) { + $user = UserModel::findFirst($authUser->id); + } else { + $user = new UserModel(); } - return $token; + return $user; } - private function getGuest() + public function getLoginUser() { - $guest = new UserModel(); + $authUser = $this->getAuthUser(); - $guest->id = 0; - $guest->name = 'guest'; + $validator = new AppValidator(); - return $guest; - } + $validator->checkAuthUser($authUser); - private function getUser($token) - { - $userRepo = new UserRepo(); - - $user = $userRepo->findById($token); + $user = UserModel::findFirst($authUser->id); return $user; } + public function getAuthUser() + { + $auth = $this->getDI()->get('auth'); + + return $auth->getAuthInfo(); + } + } diff --git a/app/Library/Cache/Backend/Redis.php b/app/Library/Cache/Backend/Redis.php index 560e2d92..d85ac95c 100644 --- a/app/Library/Cache/Backend/Redis.php +++ b/app/Library/Cache/Backend/Redis.php @@ -16,7 +16,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis * {@inheritdoc} * * @param string $keyName - * @param integer $lifetime + * @param int $lifetime * @return mixed|null */ public function get($keyName, $lifetime = null) @@ -105,7 +105,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis } if ($ttl > 0) { - $redis->setTimeout($lastKey, $ttl); + $redis->expire($lastKey, $ttl); } $isBuffering = $frontend->isBuffering(); @@ -135,7 +135,7 @@ class Redis extends \Phalcon\Cache\Backend\Redis $lastKey = $this->getKeyName($keyName); - return (bool)$redis->delete($lastKey); + return (bool)$redis->del($lastKey); } /** @@ -146,11 +146,21 @@ class Redis extends \Phalcon\Cache\Backend\Redis */ public function queryKeys($prefix = null) { + $result = []; + $redis = $this->getRedis(); $pattern = "{$this->_prefix}" . ($prefix ? $prefix : '') . '*'; - return $redis->keys($pattern); + $redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY); + + $it = null; + + while ($keys = $redis->scan($it, $pattern)) { + $result = array_merge($result, $keys); + } + + return $result; } /** diff --git a/app/Library/Cache/Frontend/Json.php b/app/Library/Cache/Frontend/Json.php new file mode 100644 index 00000000..cc498b8c --- /dev/null +++ b/app/Library/Cache/Frontend/Json.php @@ -0,0 +1,21 @@ +initParams(); $pager->first = $this->buildPageUrl($pager->first); - $pager->before = $this->buildPageUrl($pager->before); + $pager->previous = $this->buildPageUrl($pager->previous); $pager->next = $this->buildPageUrl($pager->next); $pager->last = $this->buildPageUrl($pager->last); diff --git a/app/Library/Security.php b/app/Library/Security.php index 031945c5..4712081f 100644 --- a/app/Library/Security.php +++ b/app/Library/Security.php @@ -57,11 +57,11 @@ class Security $lifetime = $this->options['lifetime']; - $cachedContent = $this->cache->get($key); + $cache = $this->cache->get($key); - if ($cachedContent) { - $this->tokenValue = $cachedContent->hash; - if (time() - $cachedContent->time > $lifetime / 2) { + if ($cache) { + $this->tokenValue = $cache['hash']; + if (time() - $cache['time'] > $lifetime / 2) { $this->cache->save($key, $content, $lifetime); $this->tokenValue = $content['hash']; } @@ -77,9 +77,11 @@ class Security $content = $this->cache->get($key); - if (!$content) return false; + if (!$content) { + return false; + } - return $tokenValue == $content->hash; + return $tokenValue == $content['hash']; } protected function getCacheKey($tokenKey) diff --git a/app/Library/Util/Word.php b/app/Library/Util/Word.php index e1a691d6..73e74afd 100644 --- a/app/Library/Util/Word.php +++ b/app/Library/Util/Word.php @@ -20,6 +20,15 @@ class Word return (int)$count; } + public static function getWordDuration($str) + { + $count = self::getWordCount($str); + + $duration = $count * 0.8; + + return (int)$duration; + } + public static function getChineseWordCount($str) { $str = strip_tags($str); diff --git a/app/Listeners/Chapter.php b/app/Listeners/Chapter.php new file mode 100644 index 00000000..e1678b50 --- /dev/null +++ b/app/Listeners/Chapter.php @@ -0,0 +1,73 @@ +logger = $this->getLogger(); + } + + public function afterCreate(Event $event, $source, ChapterModel $chapter) + { + $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ + 'event' => $event->getType(), + 'source' => get_class($source), + 'data' => kg_json_encode($chapter), + ]); + } + + public function afterUpdate(Event $event, $source, ChapterModel $chapter) + { + if ($chapter->published == 1) { + $this->syncCache($chapter); + } + + $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ + 'event' => $event->getType(), + 'source' => get_class($source), + 'data' => kg_json_encode($chapter), + ]); + } + + public function afterDelete(Event $event, $source, ChapterModel $chapter) + { + if ($chapter->published == 1) { + $this->syncCache($chapter); + } + + $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ + 'event' => $event->getType(), + 'source' => get_class($source), + 'data' => kg_json_encode($chapter), + ]); + } + + public function afterRestore(Event $event, $source, ChapterModel $chapter) + { + if ($chapter->published == 1) { + $this->syncCache($chapter); + } + + $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ + 'event' => $event->getType(), + 'source' => get_class($source), + 'data' => kg_json_encode($chapter), + ]); + } + + protected function syncCache(ChapterModel $chapter) + { + $cacheSyncer = new ChapterCacheSyncer(); + $cacheSyncer->save($chapter->id); + } + +} \ No newline at end of file diff --git a/app/Listeners/Course.php b/app/Listeners/Course.php index 71259c50..b350415f 100644 --- a/app/Listeners/Course.php +++ b/app/Listeners/Course.php @@ -3,6 +3,7 @@ namespace App\Listeners; use App\Models\Course as CourseModel; +use App\Services\CourseIndexSyncer; use Phalcon\Events\Event; class Course extends Listener @@ -27,7 +28,7 @@ class Course extends Listener public function afterUpdate(Event $event, $source, CourseModel $course) { if ($course->published == 1) { - $this->syncIndexAfterUpdate($course); + $this->syncIndex($course); } $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ @@ -40,7 +41,7 @@ class Course extends Listener public function afterDelete(Event $event, $source, CourseModel $course) { if ($course->published == 1) { - $this->syncIndexAfterDelete($course); + $this->syncIndex($course); } $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ @@ -53,7 +54,7 @@ class Course extends Listener public function afterRestore(Event $event, $source, CourseModel $course) { if ($course->published == 1) { - $this->syncIndexAfterRestore($course); + $this->syncIndex($course); } $this->logger->debug('Event: {event}, Source: {source}, Data: {data}', [ @@ -63,62 +64,10 @@ class Course extends Listener ]); } - protected function syncIndexAfterUpdate(CourseModel $course) + protected function syncIndex(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; + $cacheSyncer = new CourseIndexSyncer(); + $cacheSyncer->addItem($course->id); } } \ No newline at end of file diff --git a/app/Listeners/Payment.php b/app/Listeners/Payment.php index 5c1ef003..7dc80d63 100644 --- a/app/Listeners/Payment.php +++ b/app/Listeners/Payment.php @@ -88,7 +88,7 @@ class Payment extends Listener $courseUser->source_type = CourseUserModel::SOURCE_PAID; if ($courseUser->create() === false) { - throw new \RuntimeException('Create Course User Failed'); + throw new \RuntimeException('Create CourseSearcher User Failed'); } } @@ -105,7 +105,7 @@ class Payment extends Listener $courseUser->source_type = CourseUserModel::SOURCE_PAID; if ($courseUser->create() === false) { - throw new \RuntimeException('Create Course User Failed'); + throw new \RuntimeException('Create CourseSearcher User Failed'); } } } diff --git a/app/Models/Account.php b/app/Models/Account.php new file mode 100644 index 00000000..e081a59d --- /dev/null +++ b/app/Models/Account.php @@ -0,0 +1,102 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->create_at = time(); + } + + public function beforeUpdate() + { + $this->update_at = time(); + } + + public function afterCreate() + { + $user = new User(); + $user->id = $this->id; + $user->name = "user_{$this->id}"; + $user->create(); + } + +} diff --git a/app/Models/Audit.php b/app/Models/Audit.php index ff55e041..5712344d 100644 --- a/app/Models/Audit.php +++ b/app/Models/Audit.php @@ -8,28 +8,28 @@ class Audit extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 用户编号 * - * @var integer + * @var int */ public $user_id; /** * 用户名称 * - * @var integer + * @var int */ public $user_name; /** * 用户IP * - * @var integer + * @var int */ public $user_ip; @@ -57,7 +57,7 @@ class Audit extends Model /** * 创建时间 * - * @var integer + * @var int */ public $created_at; @@ -70,7 +70,7 @@ class Audit extends Model { $this->created_at = time(); - if (!empty($this->req_data) && is_array($this->req_data)) { + if (!empty($this->req_data)) { $this->req_data = kg_json_encode($this->req_data); } else { $this->req_data = ''; diff --git a/app/Models/Category.php b/app/Models/Category.php index f2d4210c..335af7bd 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Caches\Category as CategoryCache; +use App\Caches\CategoryList as CategoryListCache; +use App\Caches\CategoryTreeList as CategoryTreeListCache; use Phalcon\Mvc\Model\Behavior\SoftDelete; class Category extends Model @@ -10,14 +13,14 @@ class Category extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 上级编号 * - * @var integer + * @var int */ public $parent_id; @@ -31,14 +34,14 @@ class Category extends Model /** * 优先级 * - * @var integer + * @var int */ public $priority; /** * 层级 * - * @var integer + * @var int */ public $level; @@ -52,35 +55,42 @@ class Category extends Model /** * 发布标识 * - * @var integer + * @var int */ public $published; /** * 删除标识 * - * @var integer + * @var int */ public $deleted; + /** + * 节点数 + * + * @var int + */ + public $child_count; + /** * 课程数 * - * @var integer + * @var int */ public $course_count; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -111,4 +121,26 @@ class Category extends Model $this->updated_at = time(); } + public function afterCreate() + { + $this->rebuildCache(); + } + + public function afterUpdate() + { + $this->rebuildCache(); + } + + public function rebuildCache() + { + $categoryCache = new CategoryCache(); + $categoryCache->rebuild($this->id); + + $categoryListCache = new CategoryListCache(); + $categoryListCache->rebuild(); + + $categoryTreeListCache = new CategoryTreeListCache(); + $categoryTreeListCache->rebuild(); + } + } diff --git a/app/Models/Chapter.php b/app/Models/Chapter.php index e9de899e..dadb7e98 100644 --- a/app/Models/Chapter.php +++ b/app/Models/Chapter.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Caches\MaxChapterId as MaxChapterIdCache; +use App\Services\ChapterCacheSyncer; use Phalcon\Mvc\Model\Behavior\SoftDelete; class Chapter extends Model @@ -22,6 +24,7 @@ class Chapter extends Model * 点播扩展属性 */ protected $_vod_attrs = [ + 'model' => 'vod', 'duration' => 0, 'file_id' => 0, 'file_status' => 'pending', @@ -33,6 +36,7 @@ class Chapter extends Model * 直播扩展属性 */ protected $_live_attrs = [ + 'model' => 'live', 'start_time' => 0, 'end_time' => 0, ]; @@ -42,29 +46,33 @@ class Chapter extends Model * * 图文扩展属性 */ - protected $_article_attrs = ['word_count' => 0]; + protected $_read_attrs = [ + 'model' => 'read', + 'duration' => 0, + 'word_count' => 0, + ]; /** * 主键编号 * - * @var integer + * @var int */ public $id; - /** - * 课程编号 - * - * @var integer - */ - public $course_id; - /** * 父级编号 * - * @var integer + * @var int */ public $parent_id; + /** + * 课程编号 + * + * @var int + */ + public $course_id; + /** * 标题 * @@ -82,31 +90,17 @@ class Chapter extends Model /** * 优先级 * - * @var integer + * @var int */ public $priority; /** * 免费标识 * - * @var integer + * @var int */ public $free; - /** - * 发布标识 - * - * @var integer - */ - public $published; - - /** - * 删除标识 - * - * @var integer - */ - public $deleted; - /** * 扩展属性 * @@ -115,44 +109,58 @@ class Chapter extends Model public $attrs; /** - * 学员数 + * 课时数量 * - * @var integer + * @var int */ - public $student_count; + public $lesson_count; /** - * 讨论数 + * 学员数量 * - * @var integer + * @var int */ - public $thread_count; + public $user_count; /** - * 收藏数 + * 评论数量 * - * @var integer + * @var int */ - public $favorite_count; + public $comment_count; /** - * 点赞数 + * 点赞数量 * - * @var integer + * @var int */ public $like_count; + /** + * 发布标识 + * + * @var int + */ + public $published; + + /** + * 删除标识 + * + * @var int + */ + public $deleted; + /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -179,7 +187,7 @@ class Chapter extends Model if ($this->parent_id > 0) { - $course = Course::findFirstById($this->course_id); + $course = Course::findFirst($this->course_id); $attrs = []; @@ -190,14 +198,15 @@ class Chapter extends Model case Course::MODEL_LIVE: $attrs = $this->_live_attrs; break; - case Course::MODEL_ARTICLE: - $attrs = $this->_article_attrs; + case Course::MODEL_READ: + $attrs = $this->_read_attrs; break; } - $this->attrs = $attrs ? kg_json_encode($attrs) : ''; + if (!empty($attrs)) { + $this->attrs = kg_json_encode($attrs); + } } - } public function beforeUpdate() @@ -212,7 +221,7 @@ class Chapter extends Model public function afterFetch() { if (!empty($this->attrs)) { - $this->attrs = json_decode($this->attrs); + $this->attrs = json_decode($this->attrs, true); } } @@ -220,7 +229,7 @@ class Chapter extends Model { if ($this->parent_id > 0) { - $course = Course::findFirstById($this->course_id); + $course = Course::findFirst($this->course_id); $data = [ 'course_id' => $course->id, @@ -236,12 +245,21 @@ class Chapter extends Model $model = new ChapterLive(); $model->create($data); break; - case Course::MODEL_ARTICLE: - $model = new ChapterArticle(); + case Course::MODEL_READ: + $model = new ChapterRead(); $model->create($data); break; } } + + $maxChapterIdCache = new MaxChapterIdCache(); + $maxChapterIdCache->rebuild(); + } + + public function afterUpdate() + { + $chapterCacheSyncer = new ChapterCacheSyncer(); + $chapterCacheSyncer->addItem($this->id); } } diff --git a/app/Models/ChapterLike.php b/app/Models/ChapterLike.php new file mode 100644 index 00000000..94c648ef --- /dev/null +++ b/app/Models/ChapterLike.php @@ -0,0 +1,79 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/ChapterLive.php b/app/Models/ChapterLive.php index 827b6986..189c9a7f 100644 --- a/app/Models/ChapterLive.php +++ b/app/Models/ChapterLive.php @@ -8,56 +8,56 @@ class ChapterLive extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 章节编号 * - * @var integer + * @var int */ public $chapter_id; /** * 开始时间 * - * @var integer + * @var int */ public $start_time; /** * 结束时间 * - * @var integer + * @var int */ public $end_time; /** * 用户限额 * - * @var integer + * @var int */ public $user_limit; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; diff --git a/app/Models/ChapterArticle.php b/app/Models/ChapterRead.php similarity index 75% rename from app/Models/ChapterArticle.php rename to app/Models/ChapterRead.php index 4acd6617..6f8226e1 100644 --- a/app/Models/ChapterArticle.php +++ b/app/Models/ChapterRead.php @@ -2,33 +2,33 @@ namespace App\Models; -class ChapterArticle extends Model +class ChapterRead extends Model { /** * 格式类型 */ - const FORMAT_HTML = 1; - const FORMAT_MARKDOWN = 2; + const FORMAT_HTML = 'html'; + const FORMAT_MARKDOWN = 'markdown'; /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 章节编号 * - * @var integer + * @var int */ public $chapter_id; @@ -42,27 +42,27 @@ class ChapterArticle extends Model /** * 格式 * - * @var integer + * @var string */ public $format; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; public function getSource() { - return 'chapter_article'; + return 'chapter_read'; } public function beforeCreate() diff --git a/app/Models/ChapterUser.php b/app/Models/ChapterUser.php index 21e54e67..ea4a4429 100644 --- a/app/Models/ChapterUser.php +++ b/app/Models/ChapterUser.php @@ -2,83 +2,85 @@ namespace App\Models; +use Phalcon\Mvc\Model\Behavior\SoftDelete; + class ChapterUser extends Model { /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 章节编号 * - * @var integer + * @var int */ public $chapter_id; /** * 用户编号 - * - * @var integer + * + * @var int */ public $user_id; /** * 学习时长 * - * @var integer + * @var int */ public $duration; /** * 播放位置 * - * @var integer + * @var int */ public $position; /** - * 完成标识 + * 学习进度 * - * @var integer + * @var int */ - public $finished; + public $progress; /** - * 收藏标识 + * 消费标识 * - * @var integer + * @var int */ - public $favorited; + public $consumed; /** - * 喜欢标识 + * 删除标识 * - * @var integer + * @var int */ - public $liked; + public $deleted; /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; /** * 更新时间 - * - * @var integer + * + * @var int */ public $updated_at; @@ -87,6 +89,18 @@ class ChapterUser extends Model return 'chapter_user'; } + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + public function beforeCreate() { $this->created_at = time(); diff --git a/app/Models/ChapterVod.php b/app/Models/ChapterVod.php index 81c95224..1b3e70e0 100644 --- a/app/Models/ChapterVod.php +++ b/app/Models/ChapterVod.php @@ -2,48 +2,57 @@ namespace App\Models; +use App\Services\Vod as VodService; + class ChapterVod extends Model { /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 章节编号 * - * @var integer + * @var int */ public $chapter_id; /** * 文件编号 * - * @var integer + * @var string */ public $file_id; + /** + * 文件转码 + * + * @var string + */ + public $file_transcode; + /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -55,11 +64,44 @@ class ChapterVod extends Model public function beforeCreate() { $this->created_at = time(); + + if (!empty($this->file_transcode)) { + $this->file_transcode = kg_json_encode($this->file_transcode); + } } public function beforeUpdate() { $this->updated_at = time(); + + if (!empty($this->file_transcode)) { + $this->file_transcode = kg_json_encode($this->file_transcode); + } + } + + public function afterFetch() + { + if (!empty($this->file_transcode)) { + $this->file_transcode = json_decode($this->file_transcode, true); + } else { + $this->getFileTranscode($this->file_id); + } + } + + protected function getFileTranscode($fileId) + { + if (!$fileId) return []; + + $vodService = new VodService(); + + $transcode = $vodService->getFileTranscode($fileId); + + if ($transcode && empty($this->file_transcode)) { + $this->file_transcode = $transcode; + $this->update(); + } + + return $transcode; } } diff --git a/app/Models/Comment.php b/app/Models/Comment.php new file mode 100644 index 00000000..03356e02 --- /dev/null +++ b/app/Models/Comment.php @@ -0,0 +1,130 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->create_time = time(); + } + + public function beforeUpdate() + { + $this->update_time = time(); + } + +} diff --git a/app/Models/CommentLike.php b/app/Models/CommentLike.php new file mode 100644 index 00000000..598724c6 --- /dev/null +++ b/app/Models/CommentLike.php @@ -0,0 +1,79 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/Config.php b/app/Models/Config.php index 2e52c4c8..8f8c91bb 100644 --- a/app/Models/Config.php +++ b/app/Models/Config.php @@ -2,33 +2,35 @@ namespace App\Models; +use App\Caches\Config as ConfigCache; + class Config extends Model { /** * 主键 - * - * @var integer + * + * @var int */ public $id; /** * 配置块 - * + * * @var string */ public $section; /** * 配置键 - * + * * @var string */ public $item_key; /** * 配置值 - * + * * @var string */ public $item_value; @@ -38,4 +40,10 @@ class Config extends Model return 'config'; } + public function afterUpdate() + { + $configCache = new ConfigCache(); + $configCache->rebuild(); + } + } diff --git a/app/Models/Consult.php b/app/Models/Consult.php new file mode 100644 index 00000000..1c3f9409 --- /dev/null +++ b/app/Models/Consult.php @@ -0,0 +1,107 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/ConsultLike.php b/app/Models/ConsultLike.php new file mode 100644 index 00000000..282294f0 --- /dev/null +++ b/app/Models/ConsultLike.php @@ -0,0 +1,72 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/ContentImage.php b/app/Models/ContentImage.php index b7cb1373..07f442f5 100644 --- a/app/Models/ContentImage.php +++ b/app/Models/ContentImage.php @@ -7,8 +7,8 @@ class ContentImage extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; @@ -21,8 +21,8 @@ class ContentImage extends Model /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; diff --git a/app/Models/Course.php b/app/Models/Course.php index c3ce2633..2e5b6354 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Caches\MaxCourseId as MaxCourseIdCache; +use App\Services\CourseCacheSyncer; +use App\Services\CourseIndexSyncer; use Phalcon\Mvc\Model\Behavior\SoftDelete; class Course extends Model @@ -12,7 +15,7 @@ class Course extends Model */ const MODEL_VOD = 'vod'; // 点播 const MODEL_LIVE = 'live'; // 直播 - const MODEL_ARTICLE = 'article'; // 图文 + const MODEL_READ = 'read'; // 图文 /** * 级别 @@ -41,28 +44,21 @@ class Course extends Model * * 图文扩展属性 */ - protected $_article_attrs = ['word_count' => 0]; + protected $_read_attrs = ['duration' => 0, 'word_count' => 0]; /** * 主键编号 * - * @var integer + * @var int */ public $id; - /** - * 作者编号 - * - * @var integer - */ - public $user_id; - /** * 类型 * * @var string */ - public $model; + public $class_id; /** * 标题 @@ -116,7 +112,7 @@ class Course extends Model /** * 有效期限(天) * - * @var integer + * @var int */ public $expiry; @@ -127,6 +123,13 @@ class Course extends Model */ public $score; + /** + * 模式类型 + * + * @var string + */ + public $model; + /** * 难度级别 * @@ -134,20 +137,6 @@ class Course extends Model */ public $level; - /** - * 发布标识 - * - * @var integer - */ - public $published; - - /** - * 删除标识 - * - * @var integer - */ - public $deleted; - /** * 扩展属性 * @@ -158,49 +147,63 @@ class Course extends Model /** * 课时数 * - * @var integer + * @var int */ public $lesson_count; /** * 学员数 * - * @var integer + * @var int */ - public $student_count; + public $user_count; /** - * 讨论数 + * 评论数 * - * @var integer + * @var int */ - public $thread_count; + public $comment_count; /** * 评价数 * - * @var integer + * @var int */ public $review_count; /** * 收藏数 * - * @var integer + * @var int */ public $favorite_count; + /** + * 发布标识 + * + * @var int + */ + public $published; + + /** + * 删除标识 + * + * @var int + */ + public $deleted; + /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -234,12 +237,15 @@ class Course extends Model case Course::MODEL_LIVE: $attrs = $this->_live_attrs; break; - case Course::MODEL_ARTICLE: - $attrs = $this->_article_attrs; + case Course::MODEL_READ: + $attrs = $this->_read_attrs; break; } - $this->attrs = $attrs ? kg_json_encode($attrs) : ''; + if (!empty($attrs)) { + $this->attrs = kg_json_encode($attrs); + } + } public function beforeUpdate() @@ -253,17 +259,35 @@ class Course extends Model public function afterFetch() { + $this->market_price = (float)$this->market_price; + $this->vip_price = (float)$this->vip_price; + if (!empty($this->attrs)) { - $this->attrs = json_decode($this->attrs); + $this->attrs = json_decode($this->attrs, true); } } + public function afterCreate() + { + $maxCourseIdCache = new MaxCourseIdCache(); + $maxCourseIdCache->rebuild(); + } + + public function afterUpdate() + { + $courseCacheSyncer = new CourseCacheSyncer(); + $courseCacheSyncer->addItem($this->id); + + $courseIndexSyncer = new CourseIndexSyncer(); + $courseIndexSyncer->addItem($this->id); + } + public static function models() { $list = [ self::MODEL_VOD => '点播', self::MODEL_LIVE => '直播', - self::MODEL_ARTICLE => '图文', + self::MODEL_READ => '图文', ]; return $list; diff --git a/app/Models/CourseCategory.php b/app/Models/CourseCategory.php index 70e37749..ef0b4d0b 100644 --- a/app/Models/CourseCategory.php +++ b/app/Models/CourseCategory.php @@ -7,29 +7,29 @@ class CourseCategory extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; /** * 课程编号 - * - * @var integer + * + * @var int */ public $course_id; /** * 分类编号 * - * @var integer + * @var int */ public $category_id; /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; diff --git a/app/Models/CourseFavorite.php b/app/Models/CourseFavorite.php index df12b934..704717d5 100644 --- a/app/Models/CourseFavorite.php +++ b/app/Models/CourseFavorite.php @@ -2,45 +2,78 @@ namespace App\Models; +use Phalcon\Mvc\Model\Behavior\SoftDelete; + class CourseFavorite extends Model { /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 用户编号 - * - * @var integer + * + * @var int */ public $user_id; + /** + * 删除标识 + * + * @var int + */ + public $deleted; + /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; + /** + * 更新时间 + * + * @var int + */ + public $updated_at; + public function getSource() { return 'course_favorite'; } + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + public function beforeCreate() { $this->created_at = time(); } + public function beforeUpdate() + { + $this->updated_at = time(); + } + } diff --git a/app/Models/CoursePackage.php b/app/Models/CoursePackage.php index c2345291..547065e4 100644 --- a/app/Models/CoursePackage.php +++ b/app/Models/CoursePackage.php @@ -7,29 +7,29 @@ class CoursePackage extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; - /** - * 套餐编号 - * - * @var integer - */ - public $package_id; - /** * 课程编号 - * - * @var integer + * + * @var int */ public $course_id; + /** + * 套餐编号 + * + * @var int + */ + public $package_id; + /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; diff --git a/app/Models/CourseRelated.php b/app/Models/CourseRelated.php index 91dae3f7..66b1c80f 100644 --- a/app/Models/CourseRelated.php +++ b/app/Models/CourseRelated.php @@ -7,29 +7,29 @@ class CourseRelated extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; /** * 课程编号 - * - * @var integer + * + * @var int */ public $course_id; /** * 相关编号 - * - * @var integer + * + * @var int */ public $related_id; /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; diff --git a/app/Models/CourseTopic.php b/app/Models/CourseTopic.php index 68946e1e..38e0b3c2 100644 --- a/app/Models/CourseTopic.php +++ b/app/Models/CourseTopic.php @@ -7,29 +7,29 @@ class CourseTopic extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; /** * 课程编号 - * - * @var integer + * + * @var int */ public $course_id; /** * 话题编号 * - * @var integer + * @var int */ public $topic_id; /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; diff --git a/app/Models/CourseUser.php b/app/Models/CourseUser.php index fe4c3175..867cb35b 100644 --- a/app/Models/CourseUser.php +++ b/app/Models/CourseUser.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Caches\CourseUser as CourseUserCache; +use Phalcon\Mvc\Model\Behavior\SoftDelete; + class CourseUser extends Model { @@ -22,21 +25,21 @@ class CourseUser extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 用户编号 * - * @var integer + * @var int */ public $user_id; @@ -57,56 +60,49 @@ class CourseUser extends Model /** * 过期时间 * - * @var integer + * @var int */ public $expire_time; /** * 学习时长 * - * @var integer + * @var int */ public $duration; /** * 学习进度 * - * @var integer + * @var int */ public $progress; - /** - * 评价标识 - * - * @var integer - */ - public $reviewed; - /** * 锁定标识 * - * @var integer + * @var int */ public $locked; /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -115,6 +111,18 @@ class CourseUser extends Model return 'course_user'; } + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + public function beforeCreate() { $this->created_at = time(); @@ -125,6 +133,25 @@ class CourseUser extends Model $this->updated_at = time(); } + public function afterCreate() + { + $this->rebuildCache(); + } + + public function afterUpdate() + { + $this->rebuildCache(); + } + + public function rebuildCache() + { + $key = "{$this->course_id}_{$this->user_id}"; + + $courseUserCache = new CourseUserCache(); + + $courseUserCache->rebuild($key); + } + public static function roles() { $list = [ diff --git a/app/Models/Help.php b/app/Models/Help.php new file mode 100644 index 00000000..eb1fe996 --- /dev/null +++ b/app/Models/Help.php @@ -0,0 +1,79 @@ +created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/Learning.php b/app/Models/Learning.php index cd350dbc..6815e306 100644 --- a/app/Models/Learning.php +++ b/app/Models/Learning.php @@ -13,8 +13,8 @@ class Learning extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; @@ -25,45 +25,45 @@ class Learning extends Model */ public $request_id; - /** - * 用户编号 - * - * @var integer - */ - public $user_id; - /** * 课程编号 * - * @var integer + * @var int */ public $course_id; /** * 章节编号 * - * @var integer + * @var int */ public $chapter_id; + /** + * 用户编号 + * + * @var int + */ + public $user_id; + /** * 持续时长 * - * @var integer + * @var int */ public $duration; /** * 播放位置 * - * @var integer + * @var int */ public $position; /** * 客户端类型 * - * @var integer + * @var int */ public $client_type; @@ -74,38 +74,17 @@ class Learning extends Model */ public $client_ip; - /** - * 所在国家 - * - * @var string - */ - public $country; - - /** - * 所在省份 - * - * @var string - */ - public $province; - - /** - * 所在城市 - * - * @var string - */ - public $city; - /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; /** * 更新时间 - * - * @var integer + * + * @var int */ public $updated_at; diff --git a/app/Models/Nav.php b/app/Models/Nav.php index 86728c6e..3cf569b6 100644 --- a/app/Models/Nav.php +++ b/app/Models/Nav.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Caches\NavListCache; use Phalcon\Mvc\Model\Behavior\SoftDelete; class Nav extends Model @@ -22,14 +23,14 @@ class Nav extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 上级编号 * - * @var integer + * @var int */ public $parent_id; @@ -43,14 +44,14 @@ class Nav extends Model /** * 优先级 * - * @var integer + * @var int */ public $priority; /** * 层级 * - * @var integer + * @var int */ public $level; @@ -85,28 +86,35 @@ class Nav extends Model /** * 发布标识 * - * @var integer + * @var int */ public $published; /** * 删除标识 * - * @var integer + * @var int */ public $deleted; + /** + * 节点数 + * + * @var int + */ + public $child_count; + /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -137,6 +145,22 @@ class Nav extends Model $this->updated_at = time(); } + public function afterCreate() + { + $this->rebuildCache(); + } + + public function afterUpdate() + { + $this->rebuildCache(); + } + + public function rebuildCache() + { + $navListCache = new NavListCache(); + $navListCache->rebuild(); + } + public static function positions() { $list = [ diff --git a/app/Models/Order.php b/app/Models/Order.php index 9d623db1..142cebbe 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -34,7 +34,7 @@ class Order extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -62,7 +62,7 @@ class Order extends Model /** * 用户编号 * - * @var integer + * @var int */ public $user_id; @@ -111,21 +111,21 @@ class Order extends Model /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -154,7 +154,7 @@ class Order extends Model $this->created_at = time(); - if (is_array($this->item_info) && !empty($this->item_info)) { + if (!empty($this->item_info)) { $this->item_info = kg_json_encode($this->item_info); } } @@ -170,8 +170,10 @@ class Order extends Model public function afterFetch() { + $this->amount = (float)$this->amount; + if (!empty($this->item_info)) { - $this->item_info = json_decode($this->item_info); + $this->item_info = json_decode($this->item_info, true); } } diff --git a/app/Models/Package.php b/app/Models/Package.php index 2215a6b4..b7860ecd 100644 --- a/app/Models/Package.php +++ b/app/Models/Package.php @@ -10,7 +10,7 @@ class Package extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -45,35 +45,35 @@ class Package extends Model /** * 课程数量 * - * @var integer + * @var int */ public $course_count; /** * 发布标识 * - * @var integer + * @var int */ public $published; /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; diff --git a/app/Models/Page.php b/app/Models/Page.php index 9158692c..ff5f9dd5 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -10,7 +10,7 @@ class Page extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -31,21 +31,21 @@ class Page extends Model /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; diff --git a/app/Models/Refund.php b/app/Models/Refund.php index ecd504c8..35b67759 100644 --- a/app/Models/Refund.php +++ b/app/Models/Refund.php @@ -20,7 +20,7 @@ class Refund extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -80,8 +80,6 @@ class Refund extends Model */ public $order_sn; - - /** * 状态类型 * @@ -92,21 +90,21 @@ class Refund extends Model /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -141,6 +139,11 @@ class Refund extends Model $this->updated_at = time(); } + public function afterFetch() + { + $this->amount = (float)$this->amount; + } + public static function statuses() { $list = [ diff --git a/app/Models/Reply.php b/app/Models/Reply.php new file mode 100644 index 00000000..326e261a --- /dev/null +++ b/app/Models/Reply.php @@ -0,0 +1,130 @@ + ['id' => 0, 'name' => ''], + 'content' => '', + 'created_at' => '', + ]; + + /** + * 主键编号 + * + * @var int + */ + public $id; + + /** + * 作者编号 + * + * @var int + */ + public $author_id; + + /** + * 主题编号 + * + * @var int + */ + public $thread_id; + + /** + * 内容 + * + * @var string + */ + public $content; + + /** + * 引用 + * + * @var string + */ + public $quote; + + /** + * 点赞数量 + * + * @var int + */ + public $like_count; + + /** + * 终端IP + * + * @var string + */ + public $client_ip; + + /** + * 终端类型 + * + * @var string + */ + public $client_type; + + /** + * 发布标识 + * + * @var int + */ + public $published; + + /** + * 删除标识 + * + * @var int + */ + public $deleted; + + /** + * 创建时间 + * + * @var int + */ + public $created_at; + + /** + * 更新时间 + * + * @var int + */ + public $updated_at; + + public function getSource() + { + return 'reply'; + } + + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/ReplyLike.php b/app/Models/ReplyLike.php new file mode 100644 index 00000000..c2a66db1 --- /dev/null +++ b/app/Models/ReplyLike.php @@ -0,0 +1,79 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/Review.php b/app/Models/Review.php index cb9653ca..c40851e8 100644 --- a/app/Models/Review.php +++ b/app/Models/Review.php @@ -9,29 +9,29 @@ class Review extends Model /** * 主键编号 - * - * @var integer + * + * @var int */ public $id; - /** - * 用户编号 - * - * @var integer - */ - public $user_id; - /** * 课程编号 - * - * @var integer + * + * @var int */ public $course_id; + /** + * 用户编号 + * + * @var int + */ + public $user_id; + /** * 课程评分 - * - * @var integer + * + * @var int */ public $rating; @@ -45,28 +45,28 @@ class Review extends Model /** * 发布标识 * - * @var integer + * @var int */ public $published; /** * 删除标识 - * - * @var integer + * + * @var int */ public $deleted; /** * 创建时间 - * - * @var integer + * + * @var int */ public $created_at; /** * 更新时间 - * - * @var integer + * + * @var int */ public $updated_at; diff --git a/app/Models/Role.php b/app/Models/Role.php index dc7cb745..f9a5f64f 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -16,7 +16,7 @@ class Role extends Model /** * 内置角色 */ - const ROLE_ADMIN = 1; // 管理人员 + const ROLE_ROOT = 1; // 管理人员 const ROLE_OPERATOR = 2; // 运营人员 const ROLE_EDITOR = 3; // 编辑人员 const ROLE_FINANCE = 4; // 财务人员 @@ -24,7 +24,7 @@ class Role extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -59,28 +59,28 @@ class Role extends Model /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 成员数 * - * @var integer + * @var int */ public $user_count; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -105,7 +105,7 @@ class Role extends Model { $this->created_at = time(); - if (is_array($this->routes) && !empty($this->routes)) { + if (!empty($this->routes)) { $this->routes = kg_json_encode($this->routes); } } @@ -114,7 +114,7 @@ class Role extends Model { $this->updated_at = time(); - if (is_array($this->routes) && !empty($this->routes)) { + if (!empty($this->routes)) { $this->routes = kg_json_encode($this->routes); } } @@ -122,7 +122,7 @@ class Role extends Model public function afterFetch() { if (!empty($this->routes)) { - $this->routes = json_decode($this->routes, true); + $this->routes = json_decode($this->routes); } } @@ -139,7 +139,7 @@ class Role extends Model public static function sysRoles() { $list = [ - self::ROLE_ADMIN => '管理人员', + self::ROLE_ROOT => '管理人员', self::ROLE_OPERATOR => '运营人员', self::ROLE_EDITOR => '编辑人员', self::ROLE_FINANCE => '财务人员', diff --git a/app/Models/Slide.php b/app/Models/Slide.php index ba3149ae..5cfd77a8 100644 --- a/app/Models/Slide.php +++ b/app/Models/Slide.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Caches\SlideList as SlideListCache; use Phalcon\Mvc\Model\Behavior\SoftDelete; class Slide extends Model @@ -17,7 +18,7 @@ class Slide extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -59,49 +60,49 @@ class Slide extends Model /** * 优先级 * - * @var integer + * @var int */ public $priority; /** * 发布标识 * - * @var integer + * @var int */ public $published; /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 开始时间 * - * @var integer + * @var int */ public $start_time; /** * 结束时间 * - * @var integer + * @var int */ public $end_time; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -132,6 +133,22 @@ class Slide extends Model $this->updated_at = time(); } + public function afterCreate() + { + $this->rebuildCache(); + } + + public function afterUpdate() + { + $this->rebuildCache(); + } + + public function rebuildCache() + { + $slideListCache = new SlideListCache(); + $slideListCache->rebuild(); + } + public static function targets() { $list = [ diff --git a/app/Models/Task.php b/app/Models/Task.php index 93794ac6..2e9d5c6f 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -27,21 +27,21 @@ class Task extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; /** * 条目编号 * - * @var integer + * @var int */ public $item_id; /** * 条目类型 * - * @var integer + * @var int */ public $item_type; @@ -55,35 +55,35 @@ class Task extends Model /** * 优先级 * - * @var integer + * @var int */ public $priority; /** * 状态标识 * - * @var integer + * @var int */ public $status; /** * 重试次数 * - * @var integer + * @var int */ public $try_count; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; diff --git a/app/Models/Thread.php b/app/Models/Thread.php new file mode 100644 index 00000000..becd5865 --- /dev/null +++ b/app/Models/Thread.php @@ -0,0 +1,149 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->created_at = time(); + } + + public function beforeUpdate() + { + $this->updated_at = time(); + } + +} diff --git a/app/Models/Topic.php b/app/Models/Topic.php index 87947eea..b9f6a97e 100644 --- a/app/Models/Topic.php +++ b/app/Models/Topic.php @@ -10,7 +10,7 @@ class Topic extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -21,6 +21,13 @@ class Topic extends Model */ public $name; + /** + * 别名 + * + * @var string + */ + public $alias; + /** * 简介 * @@ -28,24 +35,38 @@ class Topic extends Model */ public $summary; + /** + * 课程数量 + * + * @var int + */ + public $course_count; + + /** + * 发布标识 + * + * @var int + */ + public $published; + /** * 删除标识 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; diff --git a/app/Models/Trade.php b/app/Models/Trade.php index 3164053d..ef1b70f1 100644 --- a/app/Models/Trade.php +++ b/app/Models/Trade.php @@ -24,7 +24,7 @@ class Trade extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -59,7 +59,7 @@ class Trade extends Model /** * 订单编号 * - * @var integer + * @var int */ public $order_id; @@ -94,21 +94,21 @@ class Trade extends Model /** * 删除表示 * - * @var integer + * @var int */ public $deleted; /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -143,6 +143,11 @@ class Trade extends Model $this->updated_at = time(); } + public function afterFetch() + { + $this->amount = (float)$this->amount; + } + public static function channels() { $list = [ diff --git a/app/Models/User.php b/app/Models/User.php index fb1a68f3..8db228a4 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Caches\MaxUserId as MaxUserIdCache; +use Phalcon\Mvc\Model\Behavior\SoftDelete; + class User extends Model { @@ -14,7 +17,7 @@ class User extends Model /** * 主键编号 * - * @var integer + * @var int */ public $id; @@ -25,34 +28,6 @@ class User extends Model */ public $name; - /** - * 邮箱 - * - * @var string - */ - public $email; - - /** - * 手机 - * - * @var string - */ - public $phone; - - /** - * 密码 - * - * @var string - */ - public $password; - - /** - * 密盐 - * - * @var string - */ - public $salt; - /** * 头像 * @@ -77,49 +52,56 @@ class User extends Model /** * 教学角色 * - * @var integer + * @var int */ public $edu_role; /** * 后台角色 * - * @var integer + * @var int */ public $admin_role; /** * VIP标识 * - * @var integer + * @var int */ public $vip; - /** - * VIP期限 - * - * @var integer - */ - public $vip_expiry; - /** * 锁定标识 * - * @var integer + * @var int */ public $locked; + /** + * 删除标识 + * + * @var int + */ + public $deleted; + + /** + * VIP期限 + * + * @var int + */ + public $vip_expiry; + /** * 锁定期限 * - * @var integer + * @var int */ - public $locked_expiry; + public $lock_expiry; /** * 最后活跃 * - * @var integer + * @var int */ public $last_active; @@ -130,17 +112,32 @@ class User extends Model */ public $last_ip; + /** + * 通知数量 + * + * @var int + */ + public $notice_count; + + /** + * 私信数量 + * + * @var int + */ + public $msg_count; + + /** * 创建时间 * - * @var integer + * @var int */ public $created_at; /** * 更新时间 * - * @var integer + * @var int */ public $updated_at; @@ -149,6 +146,18 @@ class User extends Model return 'user'; } + public function initialize() + { + parent::initialize(); + + $this->addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + public function beforeCreate() { $this->created_at = time(); @@ -159,6 +168,12 @@ class User extends Model $this->updated_at = time(); } + public function afterCreate() + { + $maxUserIdCache = new MaxUserIdCache(); + $maxUserIdCache->rebuild(); + } + public static function eduRoles() { $list = [ diff --git a/app/Providers/Annotation.php b/app/Providers/Annotation.php index 6192327e..77d7a6a9 100644 --- a/app/Providers/Annotation.php +++ b/app/Providers/Annotation.php @@ -5,7 +5,7 @@ namespace App\Providers; use Phalcon\Annotations\Adapter\Files as FileAnnotations; use Phalcon\Annotations\Adapter\Memory as MemoryAnnotations; -class Annotation extends AbstractProvider +class Annotation extends Provider { protected $serviceName = 'annotations'; diff --git a/app/Providers/Cache.php b/app/Providers/Cache.php index 6eddc179..50ccd4e9 100644 --- a/app/Providers/Cache.php +++ b/app/Providers/Cache.php @@ -3,9 +3,9 @@ namespace App\Providers; use App\Library\Cache\Backend\Redis as RedisBackend; -use Phalcon\Cache\Frontend\Json as JsonFrontend; +use Phalcon\Cache\Frontend\Igbinary as IgbinaryFrontend; -class Cache extends AbstractProvider +class Cache extends Provider { protected $serviceName = 'cache'; @@ -16,7 +16,7 @@ class Cache extends AbstractProvider $config = $this->getShared('config'); - $frontend = new JsonFrontend([ + $frontend = new IgbinaryFrontend([ 'lifetime' => $config->redis->lifetime, ]); diff --git a/app/Providers/CliDispatcher.php b/app/Providers/CliDispatcher.php index e6fbf42b..aa31f8c9 100644 --- a/app/Providers/CliDispatcher.php +++ b/app/Providers/CliDispatcher.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Cli\Dispatcher; -class CliDispatcher extends AbstractProvider +class CliDispatcher extends Provider { protected $serviceName = 'dispatcher'; diff --git a/app/Providers/Config.php b/app/Providers/Config.php index 56328565..6205e56f 100644 --- a/app/Providers/Config.php +++ b/app/Providers/Config.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Config as PhalconConfig; -class Config extends AbstractProvider +class Config extends Provider { protected $serviceName = 'config'; diff --git a/app/Providers/Cookie.php b/app/Providers/Cookie.php index 2fde7903..9609c3ce 100644 --- a/app/Providers/Cookie.php +++ b/app/Providers/Cookie.php @@ -2,7 +2,7 @@ namespace App\Providers; -class Cookie extends AbstractProvider +class Cookie extends Provider { protected $serviceName = 'cookies'; diff --git a/app/Providers/Crypt.php b/app/Providers/Crypt.php index 25b6412b..c07c50f4 100644 --- a/app/Providers/Crypt.php +++ b/app/Providers/Crypt.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Crypt as PhalconCrypt; -class Crypt extends AbstractProvider +class Crypt extends Provider { protected $serviceName = 'crypt'; diff --git a/app/Providers/Database.php b/app/Providers/Database.php index 942aaa4e..d8f38396 100644 --- a/app/Providers/Database.php +++ b/app/Providers/Database.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Db\Adapter\Pdo\Mysql as MySqlAdapter; -class Database extends AbstractProvider +class Database extends Provider { protected $serviceName = 'db'; @@ -22,6 +22,10 @@ class Database extends AbstractProvider 'username' => $config->db->username, 'password' => $config->db->password, 'charset' => $config->db->charset, + 'options' => [ + \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_STRINGIFY_FETCHES => false, + ], ]; $connection = new MySqlAdapter($options); diff --git a/app/Providers/Escaper.php b/app/Providers/Escaper.php index e412c219..5505f214 100644 --- a/app/Providers/Escaper.php +++ b/app/Providers/Escaper.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Escaper as PhalconEscaper; -class Escaper extends AbstractProvider +class Escaper extends Provider { protected $serviceName = 'escaper'; diff --git a/app/Providers/EventsManager.php b/app/Providers/EventsManager.php index a2fd58eb..76cc316f 100644 --- a/app/Providers/EventsManager.php +++ b/app/Providers/EventsManager.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Events\Manager as PhalconEventsManager; -class EventsManager extends AbstractProvider +class EventsManager extends Provider { protected $serviceName = 'eventsManager'; diff --git a/app/Providers/Logger.php b/app/Providers/Logger.php index 5a6ee4c6..05bbbec3 100644 --- a/app/Providers/Logger.php +++ b/app/Providers/Logger.php @@ -4,7 +4,7 @@ namespace App\Providers; use App\Library\Logger as AppLogger; -class Logger extends AbstractProvider +class Logger extends Provider { protected $serviceName = 'logger'; diff --git a/app/Providers/MetaData.php b/app/Providers/MetaData.php index 58f11445..2fd61d85 100644 --- a/app/Providers/MetaData.php +++ b/app/Providers/MetaData.php @@ -5,7 +5,7 @@ namespace App\Providers; use Phalcon\Mvc\Model\MetaData\Files as FileMetaData; use Phalcon\Mvc\Model\MetaData\Memory as MemoryMetaData; -class MetaData extends AbstractProvider +class MetaData extends Provider { protected $serviceName = 'modelsMetadata'; diff --git a/app/Providers/AbstractProvider.php b/app/Providers/Provider.php similarity index 61% rename from app/Providers/AbstractProvider.php rename to app/Providers/Provider.php index f98e451b..d5277c58 100644 --- a/app/Providers/AbstractProvider.php +++ b/app/Providers/Provider.php @@ -6,23 +6,23 @@ use Phalcon\DiInterface; use Phalcon\Mvc\User\Component; /** - * \App\Providers\AbstractProvider + * \App\Providers\Provider * * @package App\Providers */ -abstract class AbstractProvider extends Component implements ProviderInterface +abstract class Provider extends Component implements ProviderInterface { /** - * The Service name. + * Service name * * @var string */ protected $serviceName; /** - * AbstractServiceProvider constructor. + * Service provider constructor * - * @param DiInterface $di The Dependency Injector. + * @param DiInterface $di */ public function __construct(DiInterface $di) { @@ -30,7 +30,7 @@ abstract class AbstractProvider extends Component implements ProviderInterface } /** - * Gets the Service name. + * Get service name * * @return string */ diff --git a/app/Providers/Router.php b/app/Providers/Router.php index 238a7043..dd8bb0a1 100644 --- a/app/Providers/Router.php +++ b/app/Providers/Router.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Mvc\Router\Annotations as AnnotationsRouter; -class Router extends AbstractProvider +class Router extends Provider { protected $serviceName = 'router'; @@ -16,9 +16,7 @@ class Router extends AbstractProvider $router = new AnnotationsRouter(false); $router->removeExtraSlashes(true); - $router->setDefaultNamespace('App\Http\Home\Controllers'); - $router->notFound([ 'module' => 'home', 'controller' => 'error', diff --git a/app/Providers/Security.php b/app/Providers/Security.php index f7dc3b60..79079c28 100644 --- a/app/Providers/Security.php +++ b/app/Providers/Security.php @@ -4,7 +4,7 @@ namespace App\Providers; use App\Library\Security as AppSecurity; -class Security extends AbstractProvider +class Security extends Provider { protected $serviceName = 'security'; diff --git a/app/Providers/Session.php b/app/Providers/Session.php index 954641cc..2180d4d0 100644 --- a/app/Providers/Session.php +++ b/app/Providers/Session.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Session\Adapter\Redis as RedisSession; -class Session extends AbstractProvider +class Session extends Provider { protected $serviceName = 'session'; diff --git a/app/Providers/Url.php b/app/Providers/Url.php index d45ff5d1..6a934dfe 100644 --- a/app/Providers/Url.php +++ b/app/Providers/Url.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Mvc\Url as UrlResolver; -class Url extends AbstractProvider +class Url extends Provider { protected $serviceName = 'url'; diff --git a/app/Providers/Volt.php b/app/Providers/Volt.php index 1a4351d7..c6711204 100644 --- a/app/Providers/Volt.php +++ b/app/Providers/Volt.php @@ -4,7 +4,7 @@ namespace App\Providers; use Phalcon\Mvc\View\Engine\Volt as PhalconVolt; -class Volt extends AbstractProvider +class Volt extends Provider { protected $serviceName = 'volt'; @@ -34,6 +34,14 @@ class Volt extends AbstractProvider return 'kg_img_url(' . $resolvedArgs . ')'; }); + $compiler->addFilter('array_object', function ($resolvedArgs) { + return 'kg_array_object(' . $resolvedArgs . ')'; + }); + + $compiler->addFilter('object_array', function ($resolvedArgs) { + return 'kg_object_array(' . $resolvedArgs . ')'; + }); + $compiler->addFilter('play_duration', function ($resolvedArgs) { return 'kg_play_duration(' . $resolvedArgs . ')'; }); diff --git a/app/Repos/Account.php b/app/Repos/Account.php new file mode 100644 index 00000000..9f1780df --- /dev/null +++ b/app/Repos/Account.php @@ -0,0 +1,59 @@ + 'phone = :phone:', + 'bind' => ['phone' => $phone], + ]); + + return $result; + } + + /** + * @param string $email + * @return AccountModel + */ + public function findByEmail($email) + { + $result = AccountModel::findFirst([ + 'conditions' => 'email = :email:', + 'bind' => ['email' => $email], + ]); + + return $result; + } + + public function findByIds($ids, $columns = '*') + { + $result = AccountModel::query() + ->columns($columns) + ->inWhere('id', $ids) + ->execute(); + + return $result; + } + +} diff --git a/app/Repos/Audit.php b/app/Repos/Audit.php index 138d9ee7..edb4a2c4 100644 --- a/app/Repos/Audit.php +++ b/app/Repos/Audit.php @@ -9,12 +9,12 @@ class Audit extends Repository { /** - * @param integer $id + * @param int $id * @return AuditModel */ public function findById($id) { - $result = AuditModel::findFirstById($id); + $result = AuditModel::findFirst($id); return $result; } @@ -73,7 +73,7 @@ class Audit extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Category.php b/app/Repos/Category.php index a4f6eaa6..5c970d81 100644 --- a/app/Repos/Category.php +++ b/app/Repos/Category.php @@ -10,12 +10,12 @@ class Category extends Repository { /** - * @param integer $id + * @param int $id * @return CategoryModel */ public function findById($id) { - $result = CategoryModel::findFirstById($id); + $result = CategoryModel::findFirst($id); return $result; } @@ -64,6 +64,7 @@ class Category extends Repository $result = CategoryModel::query() ->where('parent_id = 0') ->andWhere('deleted = 0') + ->andWhere('published = 1') ->execute(); return $result; @@ -80,17 +81,28 @@ class Category extends Repository return $result; } + public function countChildCategories($categoryId) + { + $count = CategoryModel::count([ + 'conditions' => 'parent_id = :parent_id: AND deleted = 0 AND published = 1', + 'bind' => ['parent_id' => $categoryId], + ]); + + return (int)$count; + } + public function countCourses($categoryId) { $phql = "SELECT COUNT(*) AS total FROM %s cc JOIN %s c ON cc.course_id = c.id - WHERE cc.category_id = :category_id: AND c.deleted = 0"; + WHERE cc.category_id = :category_id: AND c.published = 1 AND c.deleted = 0"; - $row = $this->modelsManager->executeQuery( - sprintf($phql, CourseCategoryModel::class, CourseModel::class), - ['category_id' => $categoryId] - )->getFirst(); + $phql = sprintf($phql, CourseCategoryModel::class, CourseModel::class); - return (int)$row['total']; + $bind = ['category_id' => $categoryId]; + + $record = $this->modelsManager->executeQuery($phql, $bind)->getFirst(); + + return (int)$record['total']; } } diff --git a/app/Repos/Chapter.php b/app/Repos/Chapter.php index b41c4bf4..867c1263 100644 --- a/app/Repos/Chapter.php +++ b/app/Repos/Chapter.php @@ -3,20 +3,23 @@ namespace App\Repos; use App\Models\Chapter as ChapterModel; -use App\Models\ChapterArticle as ChapterArticleModel; +use App\Models\ChapterLike as ChapterLikeModel; use App\Models\ChapterLive as ChapterLiveModel; +use App\Models\ChapterRead as ChapterReadModel; +use App\Models\ChapterUser as ChapterUserModel; use App\Models\ChapterVod as ChapterVodModel; +use App\Models\Comment as CommentModel; class Chapter extends Repository { /** - * @param integer $id + * @param int $id * @return ChapterModel */ public function findById($id) { - $result = ChapterModel::findFirstById($id); + $result = ChapterModel::findFirst($id); return $result; } @@ -75,34 +78,43 @@ class Chapter extends Repository } /** - * @param integer $chapterId + * @param int $chapterId * @return ChapterVodModel */ public function findChapterVod($chapterId) { - $result = ChapterVodModel::findFirstByChapterId($chapterId); + $result = ChapterVodModel::findFirst([ + 'conditions' => 'chapter_id = :chapter_id:', + 'bind' => ['chapter_id' => $chapterId], + ]); return $result; } /** - * @param integer $chapterId + * @param int $chapterId * @return ChapterLiveModel */ public function findChapterLive($chapterId) { - $result = ChapterLiveModel::findFirstByChapterId($chapterId); + $result = ChapterLiveModel::findFirst([ + 'conditions' => 'chapter_id = :chapter_id:', + 'bind' => ['chapter_id' => $chapterId], + ]); return $result; } /** - * @param integer $chapterId - * @return ChapterArticleModel + * @param int $chapterId + * @return ChapterReadModel */ - public function findChapterArticle($chapterId) + public function findChapterRead($chapterId) { - $result = ChapterArticleModel::findFirstByChapterId($chapterId); + $result = ChapterReadModel::findFirst([ + 'conditions' => 'chapter_id = :chapter_id:', + 'bind' => ['chapter_id' => $chapterId], + ]); return $result; } @@ -136,7 +148,37 @@ class Chapter extends Repository 'bind' => ['chapter_id' => $chapterId], ]); - return (int)$result; + return $result; + } + + public function countUsers($chapterId) + { + $count = ChapterUserModel::count([ + 'conditions' => 'chapter_id = :chapter_id: AND deleted = 0', + 'bind' => ['chapter_id' => $chapterId], + ]); + + return $count; + } + + public function countComments($chapterId) + { + $count = CommentModel::count([ + 'conditions' => 'chapter_id = :chapter_id: AND deleted = 0', + 'bind' => ['chapter_id' => $chapterId], + ]); + + return $count; + } + + public function countLikes($chapterId) + { + $count = ChapterLikeModel::count([ + 'conditions' => 'chapter_id = :chapter_id: AND deleted = 0', + 'bind' => ['chapter_id' => $chapterId], + ]); + + return $count; } } diff --git a/app/Repos/ChapterLike.php b/app/Repos/ChapterLike.php new file mode 100644 index 00000000..56e411d2 --- /dev/null +++ b/app/Repos/ChapterLike.php @@ -0,0 +1,57 @@ +where('chapter_id = :chapter_id:', ['chapter_id' => $chapterId]) + ->andWhere('user_id = :user_id:', ['user_id' => $userId]) + ->execute()->getFirst(); + + return $result; + } + + public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15) + { + $builder = $this->modelsManager->createBuilder(); + + $builder->from(ChapterVoteModel::class); + + $builder->where('1 = 1'); + + if (!empty($where['chapter_id'])) { + $builder->andWhere('chapter_id = :chapter_id:', ['chapter_id' => $where['chapter_id']]); + } + + if (!empty($where['user_id'])) { + $builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); + } + + if (isset($where['deleted'])) { + $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + switch ($sort) { + default: + $orderBy = 'id DESC'; + break; + } + + $builder->orderBy($orderBy); + + $pager = new PagerQueryBuilder([ + 'builder' => $builder, + 'page' => $page, + 'limit' => $limit, + ]); + + return $pager; + } + +} diff --git a/app/Repos/ChapterUser.php b/app/Repos/ChapterUser.php index f2a29025..2314f834 100644 --- a/app/Repos/ChapterUser.php +++ b/app/Repos/ChapterUser.php @@ -9,8 +9,8 @@ class ChapterUser extends Repository { /** - * @param integer $chapterId - * @param integer $userId + * @param int $chapterId + * @param int $userId * @return ChapterUserModel */ public function findChapterUser($chapterId, $userId) @@ -18,8 +18,7 @@ class ChapterUser extends Repository $result = ChapterUserModel::query() ->where('chapter_id = :chapter_id:', ['chapter_id' => $chapterId]) ->andWhere('user_id = :user_id:', ['user_id' => $userId]) - ->execute() - ->getFirst(); + ->execute()->getFirst(); return $result; } @@ -81,7 +80,7 @@ class ChapterUser extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Comment.php b/app/Repos/Comment.php new file mode 100644 index 00000000..b11a8cc9 --- /dev/null +++ b/app/Repos/Comment.php @@ -0,0 +1,103 @@ +columns($columns) + ->inWhere('id', $ids) + ->execute(); + + return $result; + } + + public function paginator($where = [], $sort = 'latest', $page = 1, $limit = 15) + { + $builder = $this->modelsManager->createBuilder(); + + $builder->from(CommentModel::class); + + $builder->where('1 = 1'); + + if (!empty($where['id'])) { + $builder->andWhere('id = :id:', ['id' => $where['id']]); + } + + if (!empty($where['parent_id'])) { + $builder->andWhere('parent_id = :parent_id:', ['parent_id' => $where['parent_id']]); + } + + if (!empty($where['author_id'])) { + $builder->andWhere('author_id = :author_id:', ['author_id' => $where['author_id']]); + } + + if (!empty($where['course_id'])) { + $builder->andWhere('course_id = :course_id:', ['course_id' => $where['course_id']]); + } + + if (!empty($where['chapter_id'])) { + $builder->andWhere('chapter_id = :chapter_id:', ['chapter_id' => $where['chapter_id']]); + } + + if (isset($where['published'])) { + $builder->andWhere('c.published = :published:', ['published' => $where['published']]); + } + + if (isset($where['deleted'])) { + $builder->andWhere('c.deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + switch ($sort) { + default: + $orderBy = 'id DESC'; + break; + } + + $builder->orderBy($orderBy); + + $pager = new PagerQueryBuilder([ + 'builder' => $builder, + 'page' => $page, + 'limit' => $limit, + ]); + + return $pager->paginate(); + } + + public function countReplies($commentId) + { + $count = CommentModel::count([ + 'conditions' => 'parent_id = :parent_id: AND deleted = 0', + 'bind' => ['parent_id' => $commentId], + ]); + + return $count; + } + + public function countLikes($commentId) + { + $count = CommentLikeModel::count([ + 'conditions' => 'comment_id = :comment_id: AND deleted = 0', + 'bind' => ['comment_id' => $commentId], + ]); + + return $count; + } + +} diff --git a/app/Repos/CommentLike.php b/app/Repos/CommentLike.php new file mode 100644 index 00000000..51ac23f6 --- /dev/null +++ b/app/Repos/CommentLike.php @@ -0,0 +1,20 @@ +where('comment_id = :comment_id:', ['comment_id' => $commentId]) + ->amdWhere('user_id = :user_id:', ['user_id' => $userId]) + ->execute()->getFirst(); + + return $result; + } + +} diff --git a/app/Repos/Course.php b/app/Repos/Course.php index e2c18674..9f1aac37 100644 --- a/app/Repos/Course.php +++ b/app/Repos/Course.php @@ -6,10 +6,14 @@ use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder; use App\Models\Category as CategoryModel; use App\Models\Chapter as ChapterModel; use App\Models\ChapterUser as ChapterUserModel; +use App\Models\Comment as CommentModel; use App\Models\Course as CourseModel; use App\Models\CourseCategory as CourseCategoryModel; +use App\Models\CourseFavorite as CourseFavoriteModel; +use App\Models\CoursePackage as CoursePackageModel; use App\Models\CourseRelated as CourseRelatedModel; use App\Models\CourseUser as CourseUserModel; +use App\Models\Package as PackageModel; use App\Models\Review as ReviewModel; use App\Models\User as UserModel; @@ -17,12 +21,12 @@ class Course extends Repository { /** - * @param integer $id + * @param int $id * @return CourseModel */ public function findById($id) { - $result = CourseModel::findFirstById($id); + $result = CourseModel::findFirst($id); return $result; } @@ -66,6 +70,19 @@ class Course extends Repository return $result; } + public function findPackages($courseId) + { + $result = $this->modelsManager->createBuilder() + ->columns('p.*') + ->addFrom(PackageModel::class, 'p') + ->join(CoursePackageModel::class, 'p.id = cp.package_id', 'cp') + ->where('cp.course_id = :course_id:', ['course_id' => $courseId]) + ->andWhere('p.deleted = 0') + ->getQuery()->execute(); + + return $result; + } + public function findRelatedCourses($courseId) { $result = $this->modelsManager->createBuilder() @@ -100,7 +117,7 @@ class Course extends Repository return $result; } - public function findUserLessons($courseId, $userId) + public function findUserLearnings($courseId, $userId) { $result = ChapterUserModel::query() ->where('course_id = :course_id:', ['course_id' => $courseId]) @@ -110,63 +127,79 @@ class Course extends Repository return $result; } + public function findConsumedUserLearnings($courseId, $userId) + { + $result = ChapterUserModel::query() + ->where('course_id = :course_id:', ['course_id' => $courseId]) + ->andWhere('user_id = :user_id:', ['user_id' => $userId]) + ->andWhere('consumed = 1') + ->execute(); + + return $result; + } + public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15) { $builder = $this->modelsManager->createBuilder(); - $builder->from(CourseModel::class); - - $builder->where('1 = 1'); + if (!empty($where['category_id'])) { + $builder->addFrom(CourseModel::class, 'c'); + $builder->join(CourseCategoryModel::class, 'c.id = cc.course_id', 'cc'); + if (is_array($where['category_id'])) { + $builder->inWhere('cc.category_id', $where['category_id']); + } else { + $builder->where('cc.category_id = :category_id:', ['category_id' => $where['category_id']]); + } + } else { + $builder->addFrom(CourseModel::class, 'c'); + $builder->where('1 = 1'); + } if (!empty($where['id'])) { if (is_array($where['id'])) { - $builder->inWhere('id', $where['id']); + $builder->inWhere('c.id', $where['id']); } else { - $builder->andWhere('id = :id:', ['id' => $where['id']]); + $builder->andWhere('c.id = :id:', ['id' => $where['id']]); } } - if (!empty($where['user_id'])) { - $builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); - } - if (!empty($where['title'])) { - $builder->andWhere('title LIKE :title:', ['title' => '%' . $where['title'] . '%']); + $builder->andWhere('c.title LIKE :title:', ['title' => "%{$where['title']}%"]); } if (!empty($where['model'])) { - $builder->andWhere('model = :model:', ['model' => $where['model']]); + $builder->andWhere('c.model = :model:', ['model' => $where['model']]); } if (!empty($where['level'])) { - $builder->andWhere('level = :level:', ['level' => $where['level']]); + $builder->andWhere('c.level = :level:', ['level' => $where['level']]); } if (isset($where['free'])) { if ($where['free'] == 1) { - $builder->andWhere('market_price = 0'); + $builder->andWhere('c.market_price = 0'); } else { - $builder->andWhere('market_price > 0'); + $builder->andWhere('c.market_price > 0'); } } if (isset($where['published'])) { - $builder->andWhere('published = :published:', ['published' => $where['published']]); + $builder->andWhere('c.published = :published:', ['published' => $where['published']]); } if (isset($where['deleted'])) { - $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + $builder->andWhere('c.deleted = :deleted:', ['deleted' => $where['deleted']]); } switch ($sort) { case 'rating': - $orderBy = 'rating DESC'; + $orderBy = 'c.rating DESC'; break; case 'score': - $orderBy = 'score DESC'; + $orderBy = 'c.score DESC'; break; default: - $orderBy = 'id DESC'; + $orderBy = 'c.id DESC'; break; } @@ -178,7 +211,7 @@ class Course extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } public function countLessons($courseId) @@ -188,17 +221,17 @@ class Course extends Repository 'bind' => ['course_id' => $courseId], ]); - return (int)$count; + return $count; } - public function countStudents($courseId) + public function countUsers($courseId) { $count = CourseUserModel::count([ 'conditions' => 'course_id = :course_id: AND deleted = 0', 'bind' => ['course_id' => $courseId], ]); - return (int)$count; + return $count; } public function countReviews($courseId) @@ -208,7 +241,27 @@ class Course extends Repository 'bind' => ['course_id' => $courseId], ]); - return (int)$count; + return $count; + } + + public function countComments($courseId) + { + $count = CommentModel::count([ + 'conditions' => 'course_id = :course_id: AND deleted = 0', + 'bind' => ['course_id' => $courseId], + ]); + + return $count; + } + + public function countFavorites($courseId) + { + $count = CourseFavoriteModel::count([ + 'conditions' => 'course_id = :course_id: AND deleted = 0', + 'bind' => ['course_id' => $courseId], + ]); + + return $count; } } diff --git a/app/Repos/CourseFavorite.php b/app/Repos/CourseFavorite.php index c7aa077f..f7c34dd6 100644 --- a/app/Repos/CourseFavorite.php +++ b/app/Repos/CourseFavorite.php @@ -10,10 +10,9 @@ class CourseFavorite extends Repository public function findCourseFavorite($courseId, $userId) { $result = CourseFavoriteModel::query() - ->where('course_id = :course_id:', ['course_id' => $courseId]) - ->andWhere('user_id = :user_id:', ['user_id' => $userId]) - ->execute() - ->getFirst(); + ->where('user_id = :user_id:', ['user_id' => $userId]) + ->andWhere('course_id = :course_id:', ['course_id' => $courseId]) + ->execute()->getFirst(); return $result; } @@ -26,14 +25,18 @@ class CourseFavorite extends Repository $builder->where('1 = 1'); - if (isset($where['course_id'])) { + if (!empty($where['course_id'])) { $builder->andWhere('course_id = :course_id:', ['course_id' => $where['course_id']]); } - if (isset($where['user_id'])) { + if (!empty($where['user_id'])) { $builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); } + if (isset($where['deleted'])) { + $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + switch ($sort) { default: $orderBy = 'id DESC'; diff --git a/app/Repos/CoursePackage.php b/app/Repos/CoursePackage.php index 419b294f..6d2a5d44 100644 --- a/app/Repos/CoursePackage.php +++ b/app/Repos/CoursePackage.php @@ -12,14 +12,13 @@ class CoursePackage extends Repository $result = CoursePackageModel::query() ->where('course_id = :course_id:', ['course_id' => $courseId]) ->andWhere('package_id = :package_id:', ['package_id' => $packageId]) - ->orderBy('id DESC') ->execute() ->getFirst(); return $result; } - public function findByCategoryIds($packageIds) + public function findByPackageIds($packageIds) { $result = CoursePackageModel::query() ->inWhere('package_id', $packageIds) diff --git a/app/Repos/CourseTopic.php b/app/Repos/CourseTopic.php new file mode 100644 index 00000000..4c7df72e --- /dev/null +++ b/app/Repos/CourseTopic.php @@ -0,0 +1,39 @@ +where('course_id = :course_id:', ['course_id' => $courseId]) + ->andWhere('topic_id = :topic_id:', ['topic_id' => $topicId]) + ->execute() + ->getFirst(); + + return $result; + } + + public function findByTopicIds($topicIds) + { + $result = CourseTopicModel::query() + ->inWhere('topic_id', $topicIds) + ->execute(); + + return $result; + } + + public function findByCourseIds($courseIds) + { + $result = CourseTopicModel::query() + ->inWhere('course_id', $courseIds) + ->execute(); + + return $result; + } + +} diff --git a/app/Repos/CourseUser.php b/app/Repos/CourseUser.php index e421dbce..f56b43bc 100644 --- a/app/Repos/CourseUser.php +++ b/app/Repos/CourseUser.php @@ -3,15 +3,14 @@ namespace App\Repos; use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder; -use App\Models\ChapterUser as ChapterUserModel; use App\Models\CourseUser as CourseUserModel; class CourseUser extends Repository { /** - * @param integer $courseId - * @param integer $userId + * @param int $courseId + * @param int $userId * @return CourseUserModel */ public function findCourseUser($courseId, $userId) @@ -19,15 +18,14 @@ class CourseUser extends Repository $result = CourseUserModel::query() ->where('course_id = :course_id:', ['course_id' => $courseId]) ->andWhere('user_id = :user_id:', ['user_id' => $userId]) - ->execute() - ->getFirst(); + ->execute()->getFirst(); return $result; } /** - * @param integer $courseId - * @param integer $userId + * @param int $courseId + * @param int $userId * @return CourseUserModel */ public function findCourseTeacher($courseId, $userId) @@ -38,15 +36,14 @@ class CourseUser extends Repository ->where('course_id = :course_id:', ['course_id' => $courseId]) ->andWhere('user_id = :user_id:', ['user_id' => $userId]) ->andWhere('role_type = :role_type:', ['role_type' => $roleType]) - ->execute() - ->getFirst(); + ->execute()->getFirst(); return $result; } /** - * @param integer $courseId - * @param integer $userId + * @param int $courseId + * @param int $userId * @return CourseUserModel */ public function findCourseStudent($courseId, $userId) @@ -57,22 +54,11 @@ class CourseUser extends Repository ->where('course_id = :course_id:', ['course_id' => $courseId]) ->andWhere('user_id = :user_id:', ['user_id' => $userId]) ->andWhere('role_type = :role_type:', ['role_type' => $roleType]) - ->execute() - ->getFirst(); + ->execute()->getFirst(); return $result; } - public function countFinishedChapters($courseId, $userId) - { - $count = ChapterUserModel::count([ - 'conditions' => 'course_id = :course_id: AND user_id = :user_id: AND finished = 1', - 'bind' => ['course_id' => $courseId, 'user_id' => $userId] - ]); - - return (int)$count; - } - public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15) { $builder = $this->modelsManager->createBuilder(); @@ -119,7 +105,7 @@ class CourseUser extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Help.php b/app/Repos/Help.php new file mode 100644 index 00000000..58dc6f64 --- /dev/null +++ b/app/Repos/Help.php @@ -0,0 +1,52 @@ +columns($columns) + ->inWhere('id', $ids) + ->execute(); + + return $result; + } + + public function findAll($where = []) + { + $query = HelpModel::query(); + + $query->where('1 = 1'); + + if (isset($where['published'])) { + $query->andWhere('published = :published:', ['published' => $where['published']]); + } + + if (isset($where['deleted'])) { + $query->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + $query->orderBy('priority ASC'); + + $result = $query->execute(); + + return $result; + } + +} diff --git a/app/Repos/Learning.php b/app/Repos/Learning.php index 1aee120b..6e1b51b7 100644 --- a/app/Repos/Learning.php +++ b/app/Repos/Learning.php @@ -14,7 +14,10 @@ class Learning extends Repository */ public function findByRequestId($requestId) { - $result = LearningModel::findFirstByRequestId($requestId); + $result = LearningModel::findFirst([ + 'conditions' => 'request_id = :request_id:', + 'bind' => ['request_id' => $requestId], + ]); return $result; } @@ -53,7 +56,7 @@ class Learning extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Nav.php b/app/Repos/Nav.php index 1c062397..10d7b6ef 100644 --- a/app/Repos/Nav.php +++ b/app/Repos/Nav.php @@ -8,12 +8,12 @@ class Nav extends Repository { /** - * @param integer $id + * @param int $id * @return NavModel */ public function findById($id) { - $result = NavModel::findFirstById($id); + $result = NavModel::findFirst($id); return $result; } @@ -61,4 +61,14 @@ class Nav extends Repository return $result; } + public function countChildNavs($navId) + { + $count = NavModel::count([ + 'conditions' => 'parent_id = :parent_id: AND published = 1 AND deleted = 0', + 'bind' => ['parent_id' => $navId], + ]); + + return (int)$count; + } + } diff --git a/app/Repos/Order.php b/app/Repos/Order.php index 25063732..b62e79e7 100644 --- a/app/Repos/Order.php +++ b/app/Repos/Order.php @@ -11,12 +11,12 @@ class Order extends Repository { /** - * @param integer $id + * @param int $id * @return OrderModel */ public function findById($id) { - $result = OrderModel::findFirstById($id); + $result = OrderModel::findFirst($id); return $result; } @@ -27,7 +27,10 @@ class Order extends Repository */ public function findBySn($sn) { - $result = OrderModel::findFirstBySn($sn); + $result = OrderModel::findFirst([ + 'conditions' => 'sn = :sn:', + 'bind' => ['sn' => $sn], + ]); return $result; } @@ -114,7 +117,7 @@ class Order extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Package.php b/app/Repos/Package.php index 98e95283..ffe94c52 100644 --- a/app/Repos/Package.php +++ b/app/Repos/Package.php @@ -11,12 +11,12 @@ class Package extends Repository { /** - * @param integer $id + * @param int $id * @return PackageModel */ public function findById($id) { - $result = PackageModel::findFirstById($id); + $result = PackageModel::findFirst($id); return $result; } @@ -65,7 +65,7 @@ class Package extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } public function findCourses($packageId) @@ -75,6 +75,7 @@ class Package extends Repository ->addFrom(CourseModel::class, 'c') ->join(CoursePackageModel::class, 'c.id = cp.course_id', 'cp') ->where('cp.package_id = :package_id:', ['package_id' => $packageId]) + ->andWhere('c.deleted = 0') ->getQuery() ->execute(); @@ -88,7 +89,7 @@ class Package extends Repository 'bind' => ['package_id' => $packageId], ]); - return (int)$count; + return $count; } } diff --git a/app/Repos/Page.php b/app/Repos/Page.php index 5bcf00bc..b96f0ef5 100644 --- a/app/Repos/Page.php +++ b/app/Repos/Page.php @@ -9,12 +9,12 @@ class Page extends Repository { /** - * @param integer $id + * @param int $id * @return PageModel */ public function findById($id) { - $result = PageModel::findFirstById($id); + $result = PageModel::findFirst($id); return $result; } @@ -59,7 +59,7 @@ class Page extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Refund.php b/app/Repos/Refund.php index 3ea33d7c..46bec28e 100644 --- a/app/Repos/Refund.php +++ b/app/Repos/Refund.php @@ -9,12 +9,12 @@ class Refund extends Repository { /** - * @param integer $id + * @param int $id * @return RefundModel */ public function findById($id) { - $result = RefundModel::findFirstById($id); + $result = RefundModel::findFirst($id); return $result; } @@ -25,7 +25,10 @@ class Refund extends Repository */ public function findBySn($sn) { - $result = RefundModel::findFirstBySn($sn); + $result = RefundModel::findFirst([ + 'conditions' => 'sn = :sn:', + 'bind' => ['sn' => $sn], + ]); return $result; } @@ -80,7 +83,7 @@ class Refund extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/ReplyLike.php b/app/Repos/ReplyLike.php new file mode 100644 index 00000000..182a425a --- /dev/null +++ b/app/Repos/ReplyLike.php @@ -0,0 +1,57 @@ +where('reply_id = :reply_id:', ['reply_id' => $replyId]) + ->andWhere('user_id = :user_id:', ['user_id' => $userId]) + ->execute()->getFirst(); + + return $result; + } + + public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15) + { + $builder = $this->modelsManager->createBuilder(); + + $builder->from(ReplyVoteModel::class); + + $builder->where('1 = 1'); + + if (!empty($where['reply_id'])) { + $builder->andWhere('reply_id = :reply_id:', ['reply_id' => $where['reply_id']]); + } + + if (!empty($where['user_id'])) { + $builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); + } + + if (isset($where['deleted'])) { + $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + switch ($sort) { + default: + $orderBy = 'id DESC'; + break; + } + + $builder->orderBy($orderBy); + + $pager = new PagerQueryBuilder([ + 'builder' => $builder, + 'page' => $page, + 'limit' => $limit, + ]); + + return $pager; + } + +} diff --git a/app/Repos/Repository.php b/app/Repos/Repository.php index 82c45068..040d3128 100644 --- a/app/Repos/Repository.php +++ b/app/Repos/Repository.php @@ -2,9 +2,7 @@ namespace App\Repos; -use Phalcon\Mvc\User\Component as UserComponent; - -class Repository extends UserComponent +class Repository extends \Phalcon\Mvc\User\Component { - + } diff --git a/app/Repos/Review.php b/app/Repos/Review.php index a147c559..644c3270 100644 --- a/app/Repos/Review.php +++ b/app/Repos/Review.php @@ -9,12 +9,12 @@ class Review extends Repository { /** - * @param integer $id + * @param int $id * @return ReviewModel */ public function findById($id) { - $result = ReviewModel::findFirstById($id); + $result = ReviewModel::findFirst($id); return $result; } @@ -29,13 +29,17 @@ class Review extends Repository return $result; } - public function findByUserCourseId($userId, $courseId) + /** + * @param int $courseId + * @param int $userId + * @return ReviewModel + */ + public function findReview($courseId, $userId) { $result = ReviewModel::query() - ->where('user_id = :user_id:', ['user_id' => $userId]) - ->andWhere('course_id = :course_id:', ['course_id' => $courseId]) - ->execute() - ->getFirst(); + ->where('course_id = :course_id:', ['course_id' => $courseId]) + ->andWhere('user_id = :user_id:', ['user_id' => $userId]) + ->execute()->getFirst(); return $result; } @@ -96,7 +100,7 @@ class Review extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Role.php b/app/Repos/Role.php index 7ba04c76..8d26b4f8 100644 --- a/app/Repos/Role.php +++ b/app/Repos/Role.php @@ -9,12 +9,12 @@ class Role extends Repository { /** - * @param integer $id + * @param int $id * @return RoleModel */ public function findById($id) { - $result = RoleModel::findFirstById($id); + $result = RoleModel::findFirst($id); return $result; } diff --git a/app/Repos/Slide.php b/app/Repos/Slide.php index d686e720..ee16ed39 100644 --- a/app/Repos/Slide.php +++ b/app/Repos/Slide.php @@ -9,12 +9,12 @@ class Slide extends Repository { /** - * @param integer $id + * @param int $id * @return SlideModel */ public function findById($id) { - $result = SlideModel::findFirstById($id); + $result = SlideModel::findFirst($id); return $result; } @@ -75,7 +75,7 @@ class Slide extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/Topic.php b/app/Repos/Topic.php new file mode 100644 index 00000000..a934d7d1 --- /dev/null +++ b/app/Repos/Topic.php @@ -0,0 +1,95 @@ +columns($columns) + ->inWhere('id', $ids) + ->execute(); + + return $result; + } + + public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15) + { + $builder = $this->modelsManager->createBuilder(); + + $builder->from(TopicModel::class); + + $builder->where('1 = 1'); + + if (!empty($where['user_id'])) { + $builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); + } + + if (isset($where['published'])) { + $builder->andWhere('published = :published:', ['published' => $where['published']]); + } + + if (isset($where['deleted'])) { + $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + switch ($sort) { + default: + $orderBy = 'id DESC'; + break; + } + + $builder->orderBy($orderBy); + + $pager = new PagerQueryBuilder([ + 'builder' => $builder, + 'page' => $page, + 'limit' => $limit, + ]); + + return $pager->paginate(); + } + + public function findCourses($topicId) + { + $result = $this->modelsManager->createBuilder() + ->columns('c.*') + ->addFrom(CourseModel::class, 'c') + ->join(CourseTopicModel::class, 'c.id = ct.course_id', 'ct') + ->where('ct.topic_id = :topic_id:', ['topic_id' => $topicId]) + ->andWhere('c.deleted = 0') + ->getQuery() + ->execute(); + + return $result; + } + + public function countCourses($topicId) + { + $count = CourseTopicModel::count([ + 'conditions' => 'topic_id = :topic_id:', + 'bind' => ['topic_id' => $topicId], + ]); + + return $count; + } + +} diff --git a/app/Repos/Trade.php b/app/Repos/Trade.php index 197dfff3..6219eb0d 100644 --- a/app/Repos/Trade.php +++ b/app/Repos/Trade.php @@ -10,12 +10,12 @@ class Trade extends Repository { /** - * @param integer $id + * @param int $id * @return TradeModel */ public function findById($id) { - $result = TradeModel::findFirstById($id); + $result = TradeModel::findFirst($id); return $result; } @@ -26,7 +26,10 @@ class Trade extends Repository */ public function findBySn($sn) { - $result = TradeModel::findFirstBySn($sn); + $result = TradeModel::findFirst([ + 'conditions' => 'sn = :sn:', + 'bind' => ['sn' => $sn], + ]); return $result; } @@ -113,7 +116,7 @@ class Trade extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Repos/User.php b/app/Repos/User.php index 8e2bf5f4..6adff837 100644 --- a/app/Repos/User.php +++ b/app/Repos/User.php @@ -3,41 +3,18 @@ namespace App\Repos; use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder; -use App\Library\Validator\Common as CommonValidator; use App\Models\User as UserModel; class User extends Repository { /** - * @param integer $id + * @param int $id * @return UserModel */ public function findById($id) { - $result = UserModel::findFirstById($id); - - return $result; - } - - /** - * @param string $phone - * @return UserModel - */ - public function findByPhone($phone) - { - $result = UserModel::findFirstByPhone($phone); - - return $result; - } - - /** - * @param string $email - * @return UserModel - */ - public function findByEmail($email) - { - $result = UserModel::findFirstByEmail($email); + $result = UserModel::findFirst($id); return $result; } @@ -48,28 +25,14 @@ class User extends Repository */ public function findByName($name) { - $result = UserModel::findFirstByName($name); + $result = UserModel::findFirst([ + 'conditions' => 'name = :name:', + 'bind' => ['name' => $name], + ]); return $result; } - /** - * @param string $account - * @return UserModel - */ - public function findByAccount($account) - { - if (CommonValidator::email($account)) { - $user = $this->findByEmail($account); - } elseif (CommonValidator::phone($account)) { - $user = $this->findByPhone($account); - } else { - $user = $this->findByName($account); - } - - return $user; - } - public function findByIds($ids, $columns = '*') { $result = UserModel::query() @@ -105,15 +68,7 @@ class User extends Repository } if (!empty($where['name'])) { - $builder->andWhere('name = :name:', ['name' => $where['name']]); - } - - if (!empty($where['email'])) { - $builder->andWhere('email = :email:', ['email' => $where['email']]); - } - - if (!empty($where['phone'])) { - $builder->andWhere('phone = :phone:', ['phone' => $where['phone']]); + $builder->andWhere('name LIKE :name:', ['name' => "%{$where['name']}%"]); } if (!empty($where['edu_role'])) { @@ -132,6 +87,10 @@ class User extends Repository $builder->andWhere('locked = :locked:', ['locked' => $where['locked']]); } + if (isset($where['deleted'])) { + $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + switch ($sort) { default: $orderBy = 'id DESC'; @@ -146,7 +105,7 @@ class User extends Repository 'limit' => $limit, ]); - return $pager->getPaginate(); + return $pager->paginate(); } } diff --git a/app/Searchers/Course.php b/app/Searchers/Course.php deleted file mode 100644 index 19384c59..00000000 --- a/app/Searchers/Course.php +++ /dev/null @@ -1,119 +0,0 @@ -xs = new \XS($fileName); - } - - /** - * 获取XS - * @return \XS - */ - public function getXS() - { - return $this->xs; - } - - /** - * 搜索课程 - * - * @param string $query - * @param integer $limit - * @param integer $offset - * @return \stdClass - * @throws \XSException - */ - public function search($query, $limit = 15, $offset = 0) - { - $search = $this->xs->getSearch(); - - $docs = $search->setQuery($query)->setLimit($limit, $offset)->search(); - - $total = $search->getLastCount(); - - $fields = array_keys($this->xs->getAllFields()); - - $items = []; - - 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; - } - - $result = new \stdClass(); - - $result->total = $total; - $result->items = $items; - - return $result; - } - - /** - * 设置文档 - * - * @param CourseModel $course - * @return \XSDocument - */ - 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, - 'title' => $course->title, - 'cover' => $course->cover, - 'summary' => $course->summary, - 'keywords' => $course->keywords, - 'market_price' => $course->market_price, - 'vip_price' => $course->vip_price, - 'expiry' => $course->expiry, - 'rating' => $course->rating, - 'score' => $course->score, - 'model' => $course->model, - 'level' => $course->level, - 'student_count' => $course->student_count, - 'lesson_count' => $course->lesson_count, - 'created_at' => $course->created_at, - ]; - - return $data; - } - -} diff --git a/app/Searchers/CourseDocumenter.php b/app/Searchers/CourseDocumenter.php new file mode 100644 index 00000000..2d3fdf0a --- /dev/null +++ b/app/Searchers/CourseDocumenter.php @@ -0,0 +1,60 @@ +formatDocument($course); + + $doc->setFields($data); + + return $doc; + } + + /** + * 格式化文档 + * + * @param CourseModel $course + * @return array + */ + public function formatDocument(CourseModel $course) + { + if (is_array($course->attrs) || is_object($course->attrs)) { + $course->attrs = kg_json_encode($course->attrs); + } + + $data = [ + 'id' => $course->id, + 'title' => $course->title, + 'cover' => $course->cover, + 'summary' => $course->summary, + 'keywords' => $course->keywords, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + 'expiry' => $course->expiry, + 'score' => $course->score, + 'model' => $course->model, + 'level' => $course->level, + 'attrs' => $course->attrs, + 'user_count' => $course->user_count, + 'lesson_count' => $course->lesson_count, + 'created_at' => $course->created_at, + ]; + + return $data; + } + +} diff --git a/app/Searchers/CourseSearcher.php b/app/Searchers/CourseSearcher.php new file mode 100644 index 00000000..ea7a82df --- /dev/null +++ b/app/Searchers/CourseSearcher.php @@ -0,0 +1,89 @@ +xs = new \XS($fileName); + } + + /** + * 获取XS + * @return \XS + */ + public function getXS() + { + return $this->xs; + } + + /** + * 搜索课程 + * + * @param string $query + * @param int $limit + * @param int $offset + * @return array + * @throws \XSException + */ + public function search($query, $limit = 15, $offset = 0) + { + $search = $this->xs->getSearch(); + + $docs = $search->setQuery($query)->setLimit($limit, $offset)->search(); + + $total = $search->getLastCount(); + + $fields = array_keys($this->xs->getAllFields()); + + $items = []; + + foreach ($docs as $doc) { + $item = []; + foreach ($fields as $field) { + if (in_array($field, ['title', 'summary'])) { + $item[$field] = $search->highlight($doc->{$field}); + } else { + $item[$field] = $doc->{$field}; + } + } + $items[] = $item; + } + + $result = [ + 'total' => $total, + 'items' => $items, + ]; + + return $result; + } + + /** + * 获取相关搜索 + * + * @param string $query + * @param int $limit + * @return array + * @throws \XSException + */ + public function getRelatedQuery($query, $limit = 10) + { + $search = $this->xs->getSearch(); + + $search->setQuery($query); + + $result = $search->getRelatedQuery($query, $limit); + + return $result; + } + +} diff --git a/app/Services/Alipay.php b/app/Services/Alipay.php index 3bd79634..547c1932 100644 --- a/app/Services/Alipay.php +++ b/app/Services/Alipay.php @@ -23,7 +23,7 @@ class Alipay extends Service * 查询订单(扫码生成订单后可执行) * * @param string $outTradeNo - * @return \Yansongda\Supports\Collection + * @return \Yansongda\Supports\Collection|bool */ public function findOrder($outTradeNo) { @@ -50,7 +50,7 @@ class Alipay extends Service * 撤销订单(未生成订单也可执行) * * @param string $outTradeNo - * @return \Yansongda\Supports\Collection + * @return \Yansongda\Supports\Collection|bool */ public function cancelOrder($outTradeNo) { @@ -77,7 +77,7 @@ class Alipay extends Service * 关闭订单(扫码生成订单后可执行) * * @param string $outTradeNo - * @return \Yansongda\Supports\Collection + * @return \Yansongda\Supports\Collection|bool */ public function closeOrder($outTradeNo) { @@ -111,7 +111,7 @@ class Alipay extends Service * * * @param array $order - * @return bool|mixed + * @return \Yansongda\Supports\Collection|bool */ public function refundOrder($order) { @@ -184,7 +184,7 @@ class Alipay extends Service return false; } - if ($data->app_id != $this->config->app_id) { + if ($data->app_id != $this->config['app_id']) { return false; } @@ -192,7 +192,9 @@ class Alipay extends Service $trade = $tradeRepo->findBySn($data->out_trade_no); - if (!$trade) return false; + if (!$trade) { + return false; + } if ($data->total_amount != $trade->amount) { return false; @@ -217,10 +219,10 @@ class Alipay extends Service public function getGateway() { $config = [ - 'app_id' => $this->config->app_id, - 'ali_public_key' => $this->config->public_key, - 'private_key' => $this->config->private_key, - 'notify_url' => $this->config->notify_url, + 'app_id' => $this->config['app_id'], + 'ali_public_key' => $this->config['public_key'], + 'private_key' => $this->config['private_key'], + 'notify_url' => $this->config['notify_url'], 'log' => [ 'file' => log_path('alipay.log'), 'level' => 'debug', diff --git a/app/Services/Captcha.php b/app/Services/Captcha.php index 3ec838f7..2639cf55 100644 --- a/app/Services/Captcha.php +++ b/app/Services/Captcha.php @@ -34,9 +34,10 @@ class Captcha extends Service */ function verify($ticket, $rand) { - $appId = $this->config->app_id; - $secretKey = $this->config->secret_key; $userIp = $this->request->getClientAddress(); + + $appId = $this->config['app_id']; + $secretKey = $this->config['secret_key']; $captchaType = 9; try { @@ -85,10 +86,10 @@ class Captcha extends Service { $secret = $this->getSectionConfig('secret'); - $secretId = $secret->secret_id; - $secretKey = $secret->secret_key; + $secretId = $secret['secret_id']; + $secretKey = $secret['secret_key']; - $region = $this->config->region ?? 'ap-guangzhou'; + $region = $this->config['region'] ?? 'ap-guangzhou'; $credential = new Credential($secretId, $secretKey); diff --git a/app/Services/Category.php b/app/Services/Category.php index b65f6f19..403d9fad 100644 --- a/app/Services/Category.php +++ b/app/Services/Category.php @@ -2,81 +2,84 @@ namespace App\Services; -use App\Repos\Category as CategoryRepo; -use App\Repos\CourseCategory as CourseCategoryRepo; +use App\Caches\Category as CategoryCache; +use App\Caches\CategoryList as CategoryListCache; class Category extends Service { /** - * 通过多个子级分类查找课程号 + * 获取节点路径 * - * @param mixed $categoryIds + * @param int $id * @return array */ - public function getCourseIdsByMultiCategory($categoryIds) + public function getNodePaths($id) { - $courseCategoryRepo = new CourseCategoryRepo(); + $categoryCache = new CategoryCache(); - if (!is_array($categoryIds)) { - $categoryIds = explode(',', $categoryIds); - } + $category = $categoryCache->get($id); - $relations = $courseCategoryRepo->findByCategoryIds($categoryIds); - - $result = []; - - if ($relations->count() > 0) { - foreach ($relations as $relation) { - $result[] = $relation->course_id; - } - } - - return $result; - } - - /** - * 通过单个分类(顶级|子级)查找课程号 - * - * @param integer $categoryId - * @return array - */ - public function getCourseIdsBySingleCategory($categoryId) - { - $categoryRepo = new CategoryRepo(); - - $category = $categoryRepo->findById($categoryId); - - $childCategoryIds = []; - - if ($category->level == 1) { - $childCategories = $categoryRepo->findChildCategories($categoryId); - if ($childCategories->count() > 0) { - foreach ($childCategories as $category) { - $childCategoryIds[] = $category->id; - } - } - } else { - $childCategoryIds[] = $categoryId; - } - - if (empty($childCategoryIds)) { + if (!$category) { return []; } - $courseCategoryRepo = new CourseCategoryRepo(); + if ($category->level == 1) { + return [ + [ + 'id' => $category->id, + 'name' => $category->name, + ] + ]; + } - $relations = $courseCategoryRepo->findByCategoryIds($childCategoryIds); + $parent = $categoryCache->get($category->parent_id); - $result = []; + return [ + [ + 'id' => $parent->id, + 'name' => $parent->name, + ], + [ + 'id' => $category->id, + 'name' => $category->name, + ] + ]; + } - if ($relations->count() > 0) { - foreach ($relations as $relation) { - $result[] = $relation->course_id; + /** + * 获取子节点ID + * + * @param int $id + * @return array + */ + public function getChildNodeIds($id) + { + $categoryCache = new CategoryCache(); + + $category = $categoryCache->get($id); + + if (!$category) { + return []; + } + + if ($category->level == 2) { + return [$id]; + } + + $categoryListCache = new CategoryListCache(); + + $categories = $categoryListCache->get(); + + $nodeIds = []; + + foreach ($categories as $category) { + if ($category['parent_id'] == $id) { + $nodeIds[] = $category['id']; } } - return $result; + return $nodeIds; } } diff --git a/app/Services/ChapterCacheSyncer.php b/app/Services/ChapterCacheSyncer.php new file mode 100644 index 00000000..2399f990 --- /dev/null +++ b/app/Services/ChapterCacheSyncer.php @@ -0,0 +1,41 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + } + + public function addItem($courseId) + { + $key = $this->getCacheKey(); + + $this->redis->sAdd($key, $courseId); + + $this->redis->expire($key, $this->lifetime); + } + + public function getCacheKey() + { + return 'chapter_cache_sync'; + } + +} diff --git a/app/Services/ChapterVod.php b/app/Services/ChapterVod.php new file mode 100644 index 00000000..6659816c --- /dev/null +++ b/app/Services/ChapterVod.php @@ -0,0 +1,34 @@ +findChapterVod($chapterId); + + if (empty($vod->file_transcode)) { + return []; + } + + /** + * @var array $transcode + */ + $transcode = $vod->file_transcode; + + $vod = new Vod(); + + foreach ($transcode as $key => $file) { + $transcode[$key]['url'] = $vod->getPlayUrl($file['url']); + } + + return $transcode; + } + +} diff --git a/app/Services/CourseCacheSyncer.php b/app/Services/CourseCacheSyncer.php new file mode 100644 index 00000000..4c737601 --- /dev/null +++ b/app/Services/CourseCacheSyncer.php @@ -0,0 +1,41 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + } + + public function addItem($courseId) + { + $key = $this->getCacheKey(); + + $this->redis->sAdd($key, $courseId); + + $this->redis->expire($key, $this->lifetime); + } + + public function getCacheKey() + { + return 'course_cache_sync'; + } + +} diff --git a/app/Services/CourseIndexSyncer.php b/app/Services/CourseIndexSyncer.php new file mode 100644 index 00000000..3e3da47d --- /dev/null +++ b/app/Services/CourseIndexSyncer.php @@ -0,0 +1,41 @@ +cache = $this->getDI()->get('cache'); + + $this->redis = $this->cache->getRedis(); + } + + public function addItem($courseId) + { + $key = $this->getCacheKey(); + + $this->redis->sAdd($key, $courseId); + + $this->redis->expire($key, $this->lifetime); + } + + public function getCacheKey() + { + return 'course_index_sync'; + } + +} diff --git a/app/Services/CourseStats.php b/app/Services/CourseStats.php index 380b991d..c4082273 100644 --- a/app/Services/CourseStats.php +++ b/app/Services/CourseStats.php @@ -20,20 +20,20 @@ class CourseStats extends Service $course->update(); } - public function updateStudentCount($courseId) + public function updateUserCount($courseId) { $courseRepo = new CourseRepo(); $course = $courseRepo->findById($courseId); - $studentCount = $courseRepo->countStudents($courseId); + $userCount = $courseRepo->countUsers($courseId); - $course->student_count = $studentCount; + $course->user_count = $userCount; $course->update(); } - public function updateArticleWordCount($courseId) + public function updateReadAttrs($courseId) { $courseRepo = new CourseRepo(); @@ -47,58 +47,29 @@ class CourseStats extends Service $wordCount = 0; + $duration = 0; + foreach ($lessons as $lesson) { - if (isset($lesson->attrs->word_count)) { - $wordCount += $lesson->attrs->word_count; + if (isset($lesson->attrs['word_count'])) { + $wordCount += $lesson->attrs['word_count']; + } + if (isset($lesson->attrs['duration'])) { + $duration += $lesson->attrs['duration']; } } - if ($wordCount == 0) { - return; - } - /** - * @var \stdClass $attrs + * @var array $attrs */ $attrs = $course->attrs; - $attrs->word_count = $wordCount; + + $attrs['word_count'] = $wordCount; + $attrs['duration'] = $duration; + $course->update(['attrs' => $attrs]); } - public function updateLiveDateRange($courseId) - { - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($courseId); - - $lessons = $courseRepo->findChapters($course->id); - - if ($lessons->count() == 0) { - return; - } - - $scopes = []; - - foreach ($lessons as $lesson) { - if (isset($lesson->attrs->start_time)) { - $scopes[] = $lesson->attrs->start_time; - } - } - - if (!$scopes) { - return; - } - - /** - * @var \stdClass $attrs - */ - $attrs = $course->attrs; - $attrs->start_date = date('Y-m-d', min($scopes)); - $attrs->end_date = date('Y-m-d', max($scopes)); - $course->update(['attrs' => $attrs]); - } - - public function updateVodDuration($courseId) + public function updateLiveAttrs($courseId) { $courseRepo = new CourseRepo(); @@ -110,23 +81,54 @@ class CourseStats extends Service return; } - $duration = 0; + $scopes = []; foreach ($lessons as $lesson) { - if (isset($lesson->attrs->duration)) { - $duration += $lesson->attrs->duration; + if (isset($lesson->attrs['start_time'])) { + $scopes[] = $lesson->attrs['start_time']; } } - if ($duration == 0) { + if (!$scopes) return; + + /** + * @var array $attrs + */ + $attrs = $course->attrs; + + $attrs['start_date'] = date('Y-m-d', min($scopes)); + $attrs['end_date'] = date('Y-m-d', max($scopes)); + + $course->update(['attrs' => $attrs]); + } + + public function updateVodAttrs($courseId) + { + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($courseId); + + $lessons = $courseRepo->findChapters($course->id); + + if ($lessons->count() == 0) { return; } + $duration = 0; + + foreach ($lessons as $lesson) { + if (isset($lesson->attrs['duration'])) { + $duration += $lesson->attrs['duration']; + } + } + /** - * @var \stdClass $attrs + * @var array $attrs */ $attrs = $course->attrs; - $attrs->duration = $duration; + + $attrs['duration'] = $duration; + $course->update(['attrs' => $attrs]); } diff --git a/app/Services/Frontend/Course.php b/app/Services/Frontend/Course.php new file mode 100644 index 00000000..a26af584 --- /dev/null +++ b/app/Services/Frontend/Course.php @@ -0,0 +1,197 @@ +checkCourseCache($id); + + $user = $this->getCurrentUser(); + + $this->owned = $this->ownedCourse($course, $user); + + return $this->handleCourse($course, $user); + } + + /** + * @param \App\Models\Course $course + * @param \App\Models\User $user + * @return array + */ + protected function handleCourse($course, $user) + { + $result = $this->formatCourse($course); + + $result['chapters'] = $this->getChapters($course, $user); + $result['teachers'] = $this->getTeachers($course); + $result['related'] = $this->getRelated($course); + + $me = [ + 'owned' => 0, + 'reviewed' => 0, + 'favorited' => 0, + 'progress' => 0, + ]; + + if ($user->id > 0) { + + $reviewRepo = new ReviewRepo(); + + $review = $reviewRepo->findReview($course->id, $user->id); + + if ($review) { + $me['reviewed'] = 1; + } + + $favoriteRepo = new CourseFavoriteRepo(); + + $favorite = $favoriteRepo->findCourseFavorite($course->id, $user->id); + + if ($favorite && $favorite->deleted == 0) { + $me['favorited'] = 1; + } + + $courseUser = $this->getCourseUser($course->id, $user->id); + + if ($courseUser) { + $me['progress'] = $courseUser->progress; + } + + $me['owned'] = $this->owned; + } + + $result['me'] = $me; + + return $result; + } + + /** + * @param \App\Models\Course $course + * @return array + */ + protected function formatCourse($course) + { + $counterCache = new CourseCounterCache(); + + $counter = $counterCache->get($course->id); + + $result = [ + 'id' => $course->id, + 'model' => $course->model, + 'title' => $course->title, + 'cover' => kg_img_url($course->cover), + 'summary' => $course->summary, + 'details' => $course->details, + 'keywords' => $course->keywords, + 'market_price' => $course->market_price, + 'vip_price' => $course->vip_price, + 'expiry' => $course->expiry, + 'score' => $course->score, + 'level' => $course->level, + 'attrs' => $course->attrs, + 'lesson_count' => $counter['lesson_count'], + 'user_count' => $counter['user_count'], + 'thread_count' => $counter['thread_count'], + 'review_count' => $counter['review_count'], + 'favorite_count' => $counter['favorite_count'], + ]; + + return $result; + } + + /** + * @param \App\Models\Course $course + * @param \App\Models\User $user + * @return array + */ + protected function getChapters($course, $user) + { + $ccListCache = new CourseChapterListCache(); + + $chapters = $ccListCache->get($course->id); + + if (!$chapters) return []; + + $learningMapping = $this->getLearningMapping($course, $user); + + foreach ($chapters as &$chapter) { + foreach ($chapter['children'] as &$lesson) { + $owned = ($this->owned || $lesson['free']) ? 1 : 0; + $progress = $learningMapping[$lesson['id']]['progress'] ?? 0; + $lesson['me'] = [ + 'owned' => $owned, + 'progress' => $progress, + ]; + } + } + + return $chapters; + } + + /** + * @param \App\Models\Course $course + * @return array + */ + protected function getTeachers($course) + { + $ctListCache = new CourseTeacherListCache(); + + $teachers = $ctListCache->get($course->id); + + if (!$teachers) return []; + + $imgBaseUrl = kg_img_base_url(); + + foreach ($teachers as &$teacher) { + $teacher['avatar'] = $imgBaseUrl . $teacher['avatar']; + } + + return $teachers; + } + + /** + * @param \App\Models\Course $course + * @param \App\Models\User $user + * @return array + */ + protected function getLearningMapping($course, $user) + { + if ($user->id == 0) { + return []; + } + + $courseRepo = new CourseRepo(); + + $userLearnings = $courseRepo->findUserLearnings($course->id, $user->id); + + $mapping = []; + + if ($userLearnings->count() > 0) { + foreach ($userLearnings as $learning) { + $mapping[$learning['chapter_id']] = [ + 'progress' => (int)$learning['progress'], + ]; + } + } + + return $mapping; + } + +} diff --git a/app/Services/Frontend/CourseFavorite.php b/app/Services/Frontend/CourseFavorite.php new file mode 100644 index 00000000..42a0b810 --- /dev/null +++ b/app/Services/Frontend/CourseFavorite.php @@ -0,0 +1,48 @@ +checkCourse($id); + + $user = $this->getLoginUser(); + + $favoriteRepo = new CourseFavoriteRepo(); + + $favorite = $favoriteRepo->findCourseFavorite($course->id, $user->id); + + if (!$favorite) { + + $favorite = new FavoriteModel(); + $favorite->course_id = $course->id; + $favorite->user_id = $user->id; + $favorite->create(); + + $course->favorite_count += 1; + $course->update(); + + } else { + + if ($favorite->deleted == 0) { + $favorite->deleted = 1; + $course->favorite_count -= 1; + } else { + $favorite->deleted = 0; + $course->favorite_count += 1; + } + + $favorite->update(); + $course->update(); + } + } + +} diff --git a/app/Services/Frontend/CourseList.php b/app/Services/Frontend/CourseList.php new file mode 100644 index 00000000..a251eadd --- /dev/null +++ b/app/Services/Frontend/CourseList.php @@ -0,0 +1,55 @@ +getParams(); + + if (!empty($params['category_id'])) { + $categoryService = new CategoryService(); + $childNodeIds = $categoryService->getChildNodeIds($params['category_id']); + $params['category_id'] = $childNodeIds; + } + + $params['published'] = 1; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $courseRepo = new CourseRepo(); + + $pager = $courseRepo->paginate($params, $sort, $page, $limit); + + return $this->handleCourses($pager); + } + + protected function handleCourses($pager) + { + if ($pager->total_items > 0) { + + $builder = new CourseListBuilder(); + + $pipeA = $pager->items->toArray(); + $pipeB = $builder->handleCourses($pipeA); + $pipeC = $builder->handleCategories($pipeB); + + $pager->items = $pipeC; + } + + return $pager; + } + +} diff --git a/app/Services/Frontend/CourseRelated.php b/app/Services/Frontend/CourseRelated.php new file mode 100644 index 00000000..1084e1be --- /dev/null +++ b/app/Services/Frontend/CourseRelated.php @@ -0,0 +1,33 @@ +checkCourseCache($id); + + $crListCache = new CourseRelatedListCache(); + + $relatedCourses = $crListCache->get($course->id); + + if (!$relatedCourses) { + return []; + } + + $imgBaseUrl = kg_img_base_url(); + + foreach ($relatedCourses as &$course) { + $course['cover'] = $imgBaseUrl . $course['cover']; + } + + return $relatedCourses; + } + +} diff --git a/app/Services/Frontend/CourseReview.php b/app/Services/Frontend/CourseReview.php new file mode 100644 index 00000000..a6d7bbec --- /dev/null +++ b/app/Services/Frontend/CourseReview.php @@ -0,0 +1,88 @@ +checkCourseCache($id); + + $pagerQuery = new PagerQuery(); + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $params = [ + 'course_id' => $course->id, + 'published' => 1, + 'deleted' => 0, + ]; + + $reviewRepo = new ReviewRepo(); + + $pager = $reviewRepo->paginate($params, $sort, $page, $limit); + + return $this->handleReviews($pager); + } + + public function saveReview($id) + { + $post = $this->request->getPost(); + + $course = $this->checkCourse($id); + + $user = $this->getLoginUser(); + + $validator = new ReviewValidator(); + + $rating = $validator->checkRating($post['rating']); + $content = $validator->checkContent($post['content']); + + $reviewRepo = new ReviewRepo(); + + $review = $reviewRepo->findReview($course->id, $user->id); + + if (!$review) { + $review = new ReviewModel(); + $review->course_id = $course->id; + $review->user_id = $user->id; + $review->rating = $rating; + $review->content = $content; + $review->create(); + + $course->review_count += 1; + $course->update(); + } else { + $review->rating = $rating; + $review->content = $content; + $review->update(); + } + } + + protected function handleReviews($pager) + { + if ($pager->total_items > 0) { + + $builder = new ReviewListBuilder(); + + $pipeA = $pager->items->toArray(); + $pipeB = $builder->handleUsers($pipeA); + + $pager->items = $pipeB; + } + + return $pager; + } + +} diff --git a/app/Services/Frontend/CourseTrait.php b/app/Services/Frontend/CourseTrait.php new file mode 100644 index 00000000..9e9215a4 --- /dev/null +++ b/app/Services/Frontend/CourseTrait.php @@ -0,0 +1,80 @@ +checkCourse($id); + + return $course; + } + + public function checkCourseCache($id) + { + $validator = new CourseValidator(); + + $course = $validator->checkCourseCache($id); + + return $course; + } + + /** + * @param $courseId + * @param $userId + * @return \App\Models\CourseUser|null + */ + public function getCourseUser($courseId, $userId) + { + if (!$courseId || !$userId) { + return null; + } + + $key = "{$courseId}_{$userId}"; + + $courseUserCache = new CourseUserCache(); + + $courseUser = $courseUserCache->get($key); + + return $courseUser; + } + + /** + * @param \App\Models\Course $course + * @param \App\Models\User $user + * @return bool + */ + public function ownedCourse($course, $user) + { + if ($course->market_price == 0) { + return true; + } + + if ($course->vip_price == 0 && $user->vip == 1) { + return true; + } + + if ($user->id == 0) { + return false; + } + + $courseUser = $this->getCourseUser($course->id, $user->id); + + $caseAOk = $courseUser->deleted == 0; + $caseBOk = $courseUser->expire_time < time(); + + if ($courseUser && $caseAOk && $caseBOk) { + return true; + } + + return false; + } + +} diff --git a/app/Services/Frontend/Service.php b/app/Services/Frontend/Service.php new file mode 100644 index 00000000..96747a34 --- /dev/null +++ b/app/Services/Frontend/Service.php @@ -0,0 +1,54 @@ +getAuthUser(); + + if ($authUser) { + $user = UserModel::findFirst($authUser->id); + } else { + $user = $this->getGuestUser(); + } + + return $user; + } + + public function getLoginUser() + { + $authUser = $this->getAuthUser(); + + $validator = new AppValidator(); + + $validator->checkAuthUser($authUser); + + $user = UserModel::findFirst($authUser->id); + + return $user; + } + + public function getAuthUser() + { + $auth = $this->getDI()->get('auth'); + + return $auth->getAuthInfo(); + } + + public function getGuestUser() + { + $user = new UserModel(); + + $user->id = 0; + $user->name = 'guest'; + + return $user; + } + +} diff --git a/app/Services/Learning.php b/app/Services/LearningSyncer.php similarity index 91% rename from app/Services/Learning.php rename to app/Services/LearningSyncer.php index dfdffdbd..4b92075b 100644 --- a/app/Services/Learning.php +++ b/app/Services/LearningSyncer.php @@ -5,7 +5,7 @@ namespace App\Services; use App\Models\Learning as LearningModel; use App\Traits\Client as ClientTrait; -class Learning extends Service +class LearningSyncer extends Service { use ClientTrait; @@ -24,7 +24,7 @@ class Learning extends Service public function save(LearningModel $learning, $timeout = 10) { - // 秒和毫秒判断 + // 兼容秒和毫秒 if ($timeout > 1000) { $timeout = intval($timeout / 1000); } @@ -42,20 +42,15 @@ class Learning extends Service 'chapter_id' => $learning->chapter_id, 'user_id' => $learning->user_id, 'position' => $learning->position, - 'client_ip' => $clientIp, 'client_type' => $clientType, + 'client_ip' => $clientIp, ]; if (!$item) { - $content['duration'] = $timeout; - $this->cache->save($key, $content, $this->lifetime); - } else { - - $content['duration'] = $item->duration + $timeout; - + $content['duration'] = $item['duration'] + $timeout; $this->cache->save($key, $content, $this->lifetime); } } diff --git a/app/Services/Live.php b/app/Services/Live.php index 9c1b2aed..776ad2f9 100644 --- a/app/Services/Live.php +++ b/app/Services/Live.php @@ -20,10 +20,10 @@ class Live extends Service */ function getPushUrl($streamName) { - $authEnabled = $this->config->push_auth_enabled; - $authKey = $this->config->push_auth_key; - $expireTime = $this->config->push_auth_delta + time(); - $domain = $this->config->push_domain; + $authEnabled = $this->config['push_auth_enabled']; + $authKey = $this->config['push_auth_key']; + $expireTime = $this->config['push_auth_delta'] + time(); + $domain = $this->config['push_domain']; $appName = 'live'; $authParams = $this->getAuthParams($streamName, $authKey, $expireTime); @@ -47,16 +47,18 @@ class Live extends Service $extensions = ['flv', 'm3u8']; - if (!in_array($extension, $extensions)) return; + if (!in_array($extension, $extensions)) { + return null; + } $appName = 'live'; - $protocol = $this->config->pull_protocol; - $domain = $this->config->pull_domain; - $authEnabled = $this->config->pull_auth_enabled; - $transEnabled = $this->config->pull_trans_enabled; - $authKey = $this->config->pull_auth_key; - $expireTime = $this->config->pull_auth_delta + time(); + $protocol = $this->config['pull_protocol']; + $domain = $this->config['pull_domain']; + $authEnabled = $this->config['pull_auth_enabled']; + $transEnabled = $this->config['pull_trans_enabled']; + $authKey = $this->config['pull_auth_key']; + $expireTime = $this->config['pull_auth_delta'] + time(); $urls = []; @@ -83,7 +85,7 @@ class Live extends Service * * @param string $streamName * @param string $authKey - * @param integer $expireTime + * @param int $expireTime * @return string */ protected function getAuthParams($streamName, $authKey, $expireTime) diff --git a/app/Services/Mailer.php b/app/Services/Mailer.php index 3f1b8139..60d9ac8c 100644 --- a/app/Services/Mailer.php +++ b/app/Services/Mailer.php @@ -41,21 +41,21 @@ class Mailer extends Service $config = [ 'driver' => 'smtp', - 'host' => $opt->smtp_host, - 'port' => $opt->smtp_port, + 'host' => $opt['smtp_host'], + 'port' => $opt['smtp_port'], 'from' => [ - 'email' => $opt->smtp_from_email, - 'name' => $opt->smtp_from_name, + 'email' => $opt['smtp_from_email'], + 'name' => $opt['smtp_from_name'], ], ]; - if ($opt->smtp_encryption) { - $config['encryption'] = $opt->smtp_encryption; + if ($opt['smtp_encryption']) { + $config['encryption'] = $opt['smtp_encryption']; } - if ($opt->smtp_authentication) { - $config['username'] = $opt->smtp_username; - $config['password'] = $opt->smtp_password; + if ($opt['smtp_authentication']) { + $config['username'] = $opt['smtp_username']; + $config['password'] = $opt['smtp_password']; } $manager = new MailerManager($config); diff --git a/app/Services/Order.php b/app/Services/Order.php index 98c3ecb9..9940a23e 100644 --- a/app/Services/Order.php +++ b/app/Services/Order.php @@ -5,6 +5,7 @@ namespace App\Services; use App\Models\Course as CourseModel; use App\Models\Order as OrderModel; use App\Models\Package as PackageModel; +use App\Models\User as UserModel; use App\Repos\Package as PackageRepo; class Order extends Service @@ -14,12 +15,11 @@ class Order extends Service * 创建课程订单 * * @param CourseModel $course + * @param UserModel $user * @return OrderModel $order */ - public function createCourseOrder(CourseModel $course) + public function createCourseOrder(CourseModel $course, UserModel $user) { - $authUser = $this->getDI()->get('auth')->getAuthUser(); - $expiry = $course->expiry; $expireTime = strtotime("+{$expiry} days"); @@ -35,11 +35,11 @@ class Order extends Service ] ]; - $amount = $authUser->vip ? $course->vip_price : $course->market_price; + $amount = $user->vip ? $course->vip_price : $course->market_price; $order = new OrderModel(); - $order->user_id = $authUser->id; + $order->user_id = $user->id; $order->item_id = $course->id; $order->item_type = OrderModel::TYPE_COURSE; $order->item_info = $itemInfo; @@ -54,12 +54,11 @@ class Order extends Service * 创建套餐订单 * * @param PackageModel $package + * @param UserModel $user * @return OrderModel $order */ - public function createPackageOrder(PackageModel $package) + public function createPackageOrder(PackageModel $package, UserModel $user) { - $authUser = $this->getDI()->get('auth')->getAuthUser(); - $packageRepo = new PackageRepo(); $courses = $packageRepo->findCourses($package->id); @@ -87,11 +86,11 @@ class Order extends Service ]; } - $amount = $authUser->vip ? $package->vip_price : $package->market_price; + $amount = $user->vip ? $package->vip_price : $package->market_price; $order = new OrderModel(); - $order->user_id = $authUser->id; + $order->user_id = $user->id; $order->item_id = $package->id; $order->item_type = OrderModel::TYPE_PACKAGE; $order->item_info = $itemInfo; @@ -106,13 +105,12 @@ class Order extends Service * 创建赞赏订单 * * @param CourseModel $course + * @param UserModel $user * @param float $amount * @return OrderModel $order */ - public function createRewardOrder(CourseModel $course, $amount) + public function createRewardOrder(CourseModel $course, UserModel $user, $amount) { - $authUser = $this->getDI()->get('auth')->getAuthUser(); - $itemInfo = [ 'course' => [ 'id' => $course->id, @@ -123,7 +121,7 @@ class Order extends Service $order = new OrderModel(); - $order->user_id = $authUser->id; + $order->user_id = $user->id; $order->item_id = $course->id; $order->item_type = OrderModel::TYPE_REWARD; $order->item_info = $itemInfo; @@ -137,13 +135,12 @@ class Order extends Service /** * 创建会员服务订单 * + * @param UserModel $user * @param string $duration * @return OrderModel */ - public function createVipOrder($duration) + public function createVipOrder(UserModel $user, $duration) { - $authUser = $this->getDI()->get('auth')->getAuthUser(); - $vipInfo = new VipInfo(); $vipItem = $vipInfo->getItem($duration); @@ -158,7 +155,7 @@ class Order extends Service $order = new OrderModel(); - $order->user_id = $authUser->id; + $order->user_id = $user->id; $order->item_type = OrderModel::TYPE_VIP; $order->item_info = $itemInfo; $order->amount = $vipItem['price']; diff --git a/app/Services/Refund.php b/app/Services/Refund.php index b32ff5eb..246ba053 100644 --- a/app/Services/Refund.php +++ b/app/Services/Refund.php @@ -2,7 +2,6 @@ namespace App\Services; -use App\Models\Course as CourseModel; use App\Models\Order as OrderModel; use App\Repos\Course as CourseRepo; @@ -14,7 +13,7 @@ class Refund extends Service $amount = 0.00; if ($order->status != OrderModel::STATUS_FINISHED) { - //return $amount; + return $amount; } if ($order->item_type == OrderModel::TYPE_COURSE) { @@ -28,12 +27,12 @@ class Refund extends Service protected function getCourseRefundAmount(OrderModel $order) { - $course = $order->item_info->course; + $course = $order->item_info['course']; $courseId = $order->item_id; $userId = $order->user_id; $amount = $order->amount; - $expireTime = $course->expire_time; + $expireTime = $course['expire_time']; $refundAmount = 0.00; @@ -47,14 +46,14 @@ class Refund extends Service protected function getPackageRefundAmount(OrderModel $order) { + $courses = $order->item_info['courses']; $userId = $order->user_id; - $courses = $order->item_info->courses; $amount = $order->amount; $totalMarketPrice = 0.00; foreach ($courses as $course) { - $totalMarketPrice += $course->market_price; + $totalMarketPrice += $course['market_price']; } $totalRefundAmount = 0.00; @@ -63,9 +62,9 @@ class Refund extends Service * 按照占比方式计算退款 */ foreach ($courses as $course) { - if ($course->expire_time > time()) { - $pricePercent = round($course->market_price / $totalMarketPrice, 4); - $refundPercent = $this->getCourseRefundPercent($course->id, $userId); + if ($course['expire_time'] > time()) { + $pricePercent = round($course['market_price'] / $totalMarketPrice, 4); + $refundPercent = $this->getCourseRefundPercent($userId, $course['id']); $refundAmount = round($amount * $pricePercent * $refundPercent, 2); $totalRefundAmount += $refundAmount; } @@ -78,47 +77,25 @@ class Refund extends Service { $courseRepo = new CourseRepo(); - $userLessons = $courseRepo->findUserLessons($courseId, $userId); + $courseLessons = $courseRepo->findLessons($courseId); - if ($userLessons->count() == 0) { + if ($courseLessons->count() == 0) { return 1.00; } - $course = $courseRepo->findById($courseId); - $lessons = $courseRepo->findLessons($courseId); + $userLearnings = $courseRepo->findConsumedUserLearnings($courseId, $userId); - $durationMapping = []; - - foreach ($lessons as $lesson) { - $durationMapping[$lesson->id] = $lesson->attrs->duration ?? null; + if ($userLearnings->count() == 0) { + return 1.00; } - $totalCount = $course->lesson_count; - $finishCount = 0; + $courseLessonIds = kg_array_column($courseLessons->toArray(), 'id'); + $userLessonIds = kg_array_column($userLearnings->toArray(), 'chapter_id'); + $consumedLessonIds = array_intersect($courseLessonIds, $userLessonIds); - /** - * 消费规则 - * 1.点播观看时间大于时长30% - * 2.直播观看时间超过10分钟 - * 3.图文浏览即消费 - */ - foreach ($userLessons as $learning) { - $chapterId = $learning->chapter_id; - $duration = $durationMapping[$chapterId] ?? null; - if ($course->model == CourseModel::MODEL_VOD) { - if ($duration && $learning->duration > 0.3 * $duration) { - $finishCount++; - } - } elseif ($course->model == CourseModel::MODEL_LIVE) { - if ($learning->duration > 600) { - $finishCount++; - } - } elseif ($course->model == CourseModel::MODEL_LIVE) { - $finishCount++; - } - } - - $refundCount = $totalCount - $finishCount; + $totalCount = count($courseLessonIds); + $consumedCount = count($consumedLessonIds); + $refundCount = $totalCount - $consumedCount; $percent = round($refundCount / $totalCount, 4); diff --git a/app/Services/Service.php b/app/Services/Service.php index 0bee4d07..894a4dbd 100644 --- a/app/Services/Service.php +++ b/app/Services/Service.php @@ -27,7 +27,7 @@ class Service extends \Phalcon\Mvc\User\Component * 获取某组配置项 * * @param string $section - * @return \stdClass + * @return array */ public function getSectionConfig($section) { diff --git a/app/Services/Smser.php b/app/Services/Smser.php index ba9a4860..2ac484f8 100644 --- a/app/Services/Smser.php +++ b/app/Services/Smser.php @@ -74,14 +74,14 @@ class Smser extends Service protected function createSingleSender() { - $sender = new SmsSingleSender($this->config->app_id, $this->config->app_key); + $sender = new SmsSingleSender($this->config['app_id'], $this->config['app_key']); return $sender; } protected function createMultiSender() { - $sender = new SmsMultiSender($this->config->app_id, $this->config->app_key); + $sender = new SmsMultiSender($this->config['app_id'], $this->config['app_key']); return $sender; } @@ -95,16 +95,16 @@ class Smser extends Service protected function getTemplateId($code) { - $template = json_decode($this->config->template); + $template = json_decode($this->config['template'], true); - $templateId = $template->{$code}->id ?? null; + $templateId = $template[$code]['id'] ?? null; return $templateId; } protected function getSignature() { - return $this->config->signature; + return $this->config['signature']; } } diff --git a/app/Services/Storage.php b/app/Services/Storage.php index 8945cc5a..6b2d4283 100644 --- a/app/Services/Storage.php +++ b/app/Services/Storage.php @@ -121,7 +121,7 @@ class Storage extends Service */ public function putString($key, $body) { - $bucket = $this->config->bucket_name; + $bucket = $this->config['bucket_name']; try { @@ -151,7 +151,7 @@ class Storage extends Service */ public function putFile($key, $fileName) { - $bucket = $this->config->bucket_name; + $bucket = $this->config['bucket_name']; try { @@ -190,8 +190,8 @@ class Storage extends Service /** * 获取数据万象图片URL * @param string $key - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @return string */ public function getCiImageUrl($key, $width = 0, $height = 0) @@ -208,8 +208,9 @@ class Storage extends Service */ public function getBucketBaseUrl() { - $protocol = $this->config->bucket_protocol; - $domain = $this->config->bucket_domain; + $protocol = $this->config['bucket_protocol']; + $domain = $this->config['bucket_domain']; + $result = $protocol . '://' . $domain; return $result; @@ -222,8 +223,9 @@ class Storage extends Service */ public function getCiBaseUrl() { - $protocol = $this->config->ci_protocol; - $domain = $this->config->ci_domain; + $protocol = $this->config['ci_protocol']; + $domain = $this->config['ci_domain']; + $result = $protocol . '://' . $domain; return $result; @@ -268,11 +270,11 @@ class Storage extends Service $secret = $this->getSectionConfig('secret'); $client = new CosClient([ - 'region' => $this->config->bucket_region, + 'region' => $this->config['bucket_region'], 'schema' => 'https', 'credentials' => [ - 'secretId' => $secret->secret_id, - 'secretKey' => $secret->secret_key, + 'secretId' => $secret['secret_id'], + 'secretKey' => $secret['secret_key'], ]]); return $client; diff --git a/app/Services/Trade.php b/app/Services/Trade.php index 2c4e526b..76da91ba 100644 --- a/app/Services/Trade.php +++ b/app/Services/Trade.php @@ -13,7 +13,7 @@ class Trade extends Service * 创建交易 * * @param string $sn - * @param integer $channel + * @param int $channel * @return TradeModel $trade */ public function createTrade($sn, $channel) diff --git a/app/Services/VipInfo.php b/app/Services/VipInfo.php index 6fd54899..1a633a81 100644 --- a/app/Services/VipInfo.php +++ b/app/Services/VipInfo.php @@ -42,22 +42,22 @@ class VipInfo extends Service [ 'duration' => 'one_month', 'label' => '1个月', - 'price' => $this->config->one_month, + 'price' => $this->config['one_month'], ], [ 'duration' => 'three_month', 'label' => '3个月', - 'price' => $this->config->three_month, + 'price' => $this->config['three_month'], ], [ 'duration' => 'six_month', 'label' => '6个月', - 'price' => $this->config->six_month, + 'price' => $this->config['six_month'], ], [ 'duration' => 'twelve_month', 'label' => '12个月', - 'price' => $this->config->twelve_month, + 'price' => $this->config['twelve_month'], ], ]; diff --git a/app/Services/Vod.php b/app/Services/Vod.php index 76834772..58b6c1a9 100644 --- a/app/Services/Vod.php +++ b/app/Services/Vod.php @@ -55,11 +55,11 @@ class Vod extends Service } catch (TencentCloudSDKException $e) { - $this->logger->error('Describe Audio Track Templates Exception ', kg_json_encode([ - 'code' => $e->getErrorCode(), - 'message' => $e->getMessage(), - 'requestId' => $e->getRequestId(), - ])); + $this->logger->error('Describe Audio Track Templates Exception ' . kg_json_encode([ + 'code' => $e->getErrorCode(), + 'message' => $e->getMessage(), + 'requestId' => $e->getRequestId(), + ])); return false; } @@ -74,8 +74,8 @@ class Vod extends Service { $secret = $this->getSectionConfig('secret'); - $secretId = $secret->secret_id; - $secretKey = $secret->secret_key; + $secretId = $secret['secret_id']; + $secretKey = $secret['secret_key']; $params = [ 'secretId' => $secretId, @@ -92,26 +92,65 @@ class Vod extends Service } /** - * 获取播放地址 + * 获取文件转码 * - * @param string $playUrl - * @return string + * @param string $fileId + * @return array|null */ - public function getPlayUrl($playUrl) + public function getFileTranscode($fileId) { - if ($this->config->key_anti_enabled == 0) { - return $playUrl; + if (!$fileId) return null; + + $mediaInfo = $this->getMediaInfo($fileId); + + if (!$mediaInfo) return null; + + $result = []; + + $files = $mediaInfo['MediaInfoSet'][0]['TranscodeInfo']['TranscodeSet']; + + foreach ($files as $file) { + + if ($file['Definition'] == 0) { + continue; + } + + $result[] = [ + 'url' => $file['Url'], + 'width' => $file['Width'], + 'height' => $file['Height'], + 'definition' => $file['Definition'], + 'duration' => intval($file['Duration']), + 'format' => pathinfo($file['Url'], PATHINFO_EXTENSION), + 'size' => sprintf('%0.2f', $file['Size'] / 1024 / 1024), + 'rate' => intval($file['Bitrate'] / 1024), + ]; } - $key = $this->config->key_anti_key; - $expiry = $this->config->key_anti_expiry ?: 10800; + return $result; + } - $path = parse_url($playUrl, PHP_URL_PATH); + /** + * 获取播放地址 + * + * @param string $url + * @return string + */ + public function getPlayUrl($url) + { + if ($this->config['key_anti_enabled'] == 0) { + return $url; + } + + $key = $this->config['key_anti_key']; + $expiry = $this->config['key_anti_expiry'] ?: 10800; + + $path = parse_url($url, PHP_URL_PATH); $pos = strrpos($path, '/'); $fileName = substr($path, $pos + 1); $dirName = str_replace($fileName, '', $path); - $expiredTime = base_convert(time() + $expiry, 10, 16); // 过期时间(十六进制) + $expiredTime = base_convert(time() + $expiry, 10, 16); $tryTime = 0; // 试看时间,0不限制 $ipLimit = 0; // ip数量限制,0不限制 $random = rand(100000, 999999); // 随机数 @@ -123,7 +162,7 @@ class Vod extends Service */ $myTryTime = $tryTime >= 0 ? $tryTime : 0; $myIpLimit = $ipLimit > 0 ? $ipLimit : ''; - $sign = $key . $dirName . $expiredTime . $myTryTime . $myIpLimit . $random; // 签名串 + $sign = $key . $dirName . $expiredTime . $myTryTime . $myIpLimit . $random; $query = []; @@ -140,7 +179,7 @@ class Vod extends Service $query['us'] = $random; $query['sign'] = md5($sign); - $result = $playUrl . '?' . http_build_query($query); + $result = $url . '?' . http_build_query($query); return $result; } @@ -186,7 +225,7 @@ class Vod extends Service * 确认事件 * * @param array $eventHandles - * @return bool|mixed + * @return array|bool */ public function confirmEvents($eventHandles) { @@ -325,9 +364,9 @@ class Vod extends Service $transCodeTaskSet = []; foreach ($videoTransTemplates as $key => $template) { - if ($originVideoInfo['width'] >= $template['width'] || - $originVideoInfo['bit_rate'] >= 1000 * $template['bit_rate'] - ) { + $caseA = $originVideoInfo['width'] >= $template['width']; + $caseB = $originVideoInfo['bit_rate'] >= 1000 * $template['bit_rate']; + if ($caseA || $caseB) { $item = ['Definition' => $key]; if ($watermarkTemplate) { $item['WatermarkSet'][] = ['Definition' => $watermarkTemplate]; @@ -509,8 +548,8 @@ class Vod extends Service { $result = null; - if ($this->config->watermark_enabled && $this->config->watermark_template > 0) { - $result = (int)$this->config->watermark_template; + if ($this->config['watermark_enabled'] && $this->config['watermark_template'] > 0) { + $result = (int)$this->config['watermark_template']; } return $result; @@ -535,7 +574,7 @@ class Vod extends Service 30 => ['width' => 1280, 'bit_rate' => 1024, 'frame_rate' => 25], ]; - $format = $this->config->video_format; + $format = $this->config['video_format']; $result = $format == 'hls' ? $hls : $mp4; @@ -558,7 +597,7 @@ class Vod extends Service 1010 => ['bit_rate' => 128, 'sample_rate' => 44100], ]; - $result = $this->config->audio_format == 'm4a' ? $m4a : $mp3; + $result = $this->config['audio_format'] == 'm4a' ? $m4a : $mp3; return $result; } @@ -572,10 +611,10 @@ class Vod extends Service { $secret = $this->getSectionConfig('secret'); - $secretId = $secret->secret_id; - $secretKey = $secret->secret_key; + $secretId = $secret['secret_id']; + $secretKey = $secret['secret_key']; - $region = $this->config->storage_type == 'fixed' ? $this->config->storage_region : ''; + $region = $this->config['storage_type'] == 'fixed' ? $this->config['storage_region'] : ''; $credential = new Credential($secretId, $secretKey); diff --git a/app/Services/Wxpay.php b/app/Services/Wxpay.php index 5e4b39f9..f05a41d1 100644 --- a/app/Services/Wxpay.php +++ b/app/Services/Wxpay.php @@ -71,7 +71,7 @@ class Wxpay extends Service return false; } - if ($data->mch_id != $this->config->mch_id) { + if ($data->mch_id != $this->config['mch_id']) { return false; } @@ -106,10 +106,10 @@ class Wxpay extends Service public function getGateway() { $config = [ - 'app_id' => $this->config->app_id, - 'mch_id' => $this->config->mch_id, - 'key' => $this->config->key, - 'notify_url' => $this->config->notify_url, + 'app_id' => $this->config['app_id'], + 'mch_id' => $this->config['mch_id'], + 'key' => $this->config['key'], + 'notify_url' => $this->config['notify_url'], 'log' => [ 'file' => log_path('wxpay.log'), 'level' => 'debug', diff --git a/app/Traits/Auth.php b/app/Traits/Auth.php new file mode 100644 index 00000000..364e3fa7 --- /dev/null +++ b/app/Traits/Auth.php @@ -0,0 +1,44 @@ +getAuthUser(); + + if ($authUser) { + $user = UserModel::findFirst($authUser->id); + } else { + $user = new UserModel(); + } + + return $user; + } + + public function getLoginUser() + { + $authUser = $this->getAuthUser(); + + $validator = new Validator(); + + $validator->checkAuthUser($authUser); + + $user = UserModel::findFirst($authUser->id); + + return $user; + } + + public function getAuthUser() + { + $auth = $this->getDI()->get('auth'); + + return $auth->getAuthInfo(); + } + +} diff --git a/app/Traits/Security.php b/app/Traits/Security.php index 248656ea..0592f275 100644 --- a/app/Traits/Security.php +++ b/app/Traits/Security.php @@ -23,16 +23,15 @@ trait Security return $checkHost; } - public function notSafeRequest() + public function isNotSafeRequest() { $method = $this->request->getMethod(); - $whitelist = ['post', 'put', 'patch', 'delete']; + $list = ['post', 'put', 'patch', 'delete']; - $result = in_array(strtolower($method), $whitelist); + $result = in_array(strtolower($method), $list); return $result; - } } \ No newline at end of file diff --git a/app/Transformers/ChapterList.php b/app/Transformers/ChapterList.php deleted file mode 100644 index 81a5c5cb..00000000 --- a/app/Transformers/ChapterList.php +++ /dev/null @@ -1,51 +0,0 @@ - $chapter) { - $chapters[$key]['finished'] = isset($status[$chapter['id']]) ? $status[$chapter['id']] : 0; - } - - return $chapters; - } - - public function handleTree($chapters) - { - $list = []; - - foreach ($chapters as $chapter) { - if ($chapter['parent_id'] == 0) { - $list[$chapter['id']] = $chapter; - $list[$chapter['id']]['child'] = []; - } else { - $list[$chapter['parent_id']]['child'][] = $chapter; - } - } - - usort($list, function($a, $b) { - return $a['priority'] > $b['priority']; - }); - - foreach ($list as $key => $value) { - usort($list[$key]['child'], function($a, $b) { - return $a['priority'] > $b['priority']; - }); - } - - return $list; - } - -} diff --git a/app/Transformers/ChapterUserList.php b/app/Transformers/ChapterUserList.php deleted file mode 100644 index a87e59ce..00000000 --- a/app/Transformers/ChapterUserList.php +++ /dev/null @@ -1,67 +0,0 @@ -getChapters($relations); - - foreach ($relations as $key => $value) { - $relations[$key]['course'] = $courses[$value['chapter_id']]; - } - - return $relations; - } - - public function handleUsers($relations) - { - $users = $this->getUsers($relations); - - foreach ($relations as $key => $value) { - $relations[$key]['user'] = $users[$value['user_id']]; - } - - return $relations; - } - - protected function getChapters($relations) - { - $ids = kg_array_column($relations, 'chapter_id'); - - $courseRepo = new ChapterRepo(); - - $courses = $courseRepo->findByIds($ids, ['id', 'title'])->toArray(); - - $result = []; - - foreach ($courses as $course) { - $result[$course['id']] = $course; - } - - return $result; - } - - protected function getUsers($relations) - { - $ids = kg_array_column($relations, 'user_id'); - - $userRepo = new UserRepo(); - - $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); - - $result = []; - - foreach ($users as $user) { - $result[$user['id']] = $user; - } - - return $result; - } - -} diff --git a/app/Transformers/CommentList.php b/app/Transformers/CommentList.php deleted file mode 100644 index ba37ed0a..00000000 --- a/app/Transformers/CommentList.php +++ /dev/null @@ -1,100 +0,0 @@ -getCourses($comments); - - foreach ($comments as $key => $comment) { - $comments[$key]['course'] = $courses[$comment['course_id']]; - } - - return $comments; - } - - public function handleChapters($comments) - { - $chapters = $this->getChapters($comments); - - foreach ($comments as $key => $comment) { - $comments[$key]['chapter'] = $chapters[$comment['chapter_id']]; - } - - return $comments; - } - - public function handleUsers($comments) - { - $users = $this->getUsers($comments); - - foreach ($comments as $key => $comment) { - $comments[$key]['user'] = $users[$comment['user_id']]; - $comments[$key]['to_user'] = $comment['to_user_id'] > 0 ? $users[$comment['to_user_id']] : []; - } - - return $comments; - } - - protected function getCourses($comments) - { - $ids = kg_array_column($comments, 'course_id'); - - $courseRepo = new CourseRepo(); - - $courses = $courseRepo->findByIds($ids, ['id', 'title'])->toArray(); - - $result = []; - - foreach ($courses as $course) { - $result[$course['id']] = $course; - } - - return $result; - } - - protected function getChapters($comments) - { - $ids = kg_array_column($comments, 'chapter_id'); - - $chapterRepo = new ChapterRepo(); - - $chapters = $chapterRepo->findByIds($ids, ['id', 'title'])->toArray(); - - $result = []; - - foreach ($chapters as $chapter) { - $result[$chapter['id']] = $chapter; - } - - return $result; - } - - protected function getUsers($comments) - { - $userIds = kg_array_column($comments, 'user_id'); - $toUserIds = kg_array_column($comments, 'to_user_id'); - - $ids = array_merge($userIds, $toUserIds); - - $userRepo = new UserRepo(); - - $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); - - $result = []; - - foreach ($users as $user) { - $result[$user['id']] = $user; - } - - return $result; - } - -} diff --git a/app/Transformers/CourseFavoriteList.php b/app/Transformers/CourseFavoriteList.php deleted file mode 100644 index 3833a7da..00000000 --- a/app/Transformers/CourseFavoriteList.php +++ /dev/null @@ -1,67 +0,0 @@ -getCourses($relations); - - foreach ($relations as $key => $value) { - $relations[$key]['course'] = $courses[$value['course_id']]; - } - - return $relations; - } - - public function handleUsers($relations) - { - $users = $this->getUsers($relations); - - foreach ($relations as $key => $value) { - $relations[$key]['user'] = $users[$value['user_id']]; - } - - return $relations; - } - - protected function getCourses($relations) - { - $ids = kg_array_column($relations, 'course_id'); - - $courseRepo = new CourseRepo(); - - $courses = $courseRepo->findByIds($ids, ['id', 'title'])->toArray(); - - $result = []; - - foreach ($courses as $course) { - $result[$course['id']] = $course; - } - - return $result; - } - - protected function getUsers($relations) - { - $ids = kg_array_column($relations, 'user_id'); - - $userRepo = new UserRepo(); - - $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); - - $result = []; - - foreach ($users as $user) { - $result[$user['id']] = $user; - } - - return $result; - } - -} diff --git a/app/Transformers/CourseList.php b/app/Transformers/CourseList.php deleted file mode 100644 index 801f565a..00000000 --- a/app/Transformers/CourseList.php +++ /dev/null @@ -1,92 +0,0 @@ -getCategories($courses); - - foreach ($courses as $key => $course) { - $courses[$key]['categories'] = $categories[$course['id']] ?? []; - } - - return $courses; - } - - public function handleUsers($courses) - { - $users = $this->getUsers($courses); - - foreach ($courses as $key => $course) { - $courses[$key]['user'] = $users[$course['user_id']]; - } - - return $courses; - } - - public function handleCourses($courses) - { - foreach ($courses as $key => $course) { - unset($courses[$key]['details']); - } - - return $courses; - } - - protected function getCategories($courses) - { - $categoryRepo = new CategoryRepo(); - - $categories = $categoryRepo->findAll(); - - $mapping = []; - - foreach ($categories as $category) { - $mapping[$category->id] = [ - 'id' => $category->id, - 'name' => $category->name, - ]; - } - - $courseIds = kg_array_column($courses, 'id'); - - $courseCategoryRepo = new CourseCategoryRepo(); - - $relations = $courseCategoryRepo->findByCourseIds($courseIds); - - $result = []; - - foreach ($relations as $relation) { - $categoryId = $relation->category_id; - $courseId = $relation->course_id; - $result[$courseId][] = $mapping[$categoryId] ?? []; - } - - return $result; - } - - protected function getUsers($courses) - { - $ids = kg_array_column($courses, 'user_id'); - - $userRepo = new UserRepo(); - - $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar'])->toArray(); - - $result = []; - - foreach ($users as $user) { - $result[$user['id']] = $user; - } - - return $result; - } - -} diff --git a/app/Validators/Account.php b/app/Validators/Account.php new file mode 100644 index 00000000..452c5ff7 --- /dev/null +++ b/app/Validators/Account.php @@ -0,0 +1,126 @@ +filter->sanitize($phone, ['trim', 'string']); + + if (!CommonValidator::phone($value)) { + throw new BadRequestException('account.invalid_phone'); + } + + return $value; + } + + public function checkEmail($email) + { + $value = $this->filter->sanitize($email, ['trim', 'string']); + + if (!CommonValidator::email($value)) { + throw new BadRequestException('account.invalid_email'); + } + + return $value; + } + + public function checkPassword($password) + { + $value = $this->filter->sanitize($password, ['trim', 'string']); + + if (!CommonValidator::password($value)) { + throw new BadRequestException('account.invalid_password'); + } + + return $value; + } + + public function checkIfPhoneTaken($phone) + { + $accountRepo = new AccountRepo(); + + $account = $accountRepo->findByPhone($phone); + + if ($account) { + throw new BadRequestException('account.phone_taken'); + } + } + + public function checkIfEmailTaken($email) + { + $accountRepo = new AccountRepo(); + + $account = $accountRepo->findByEmail($email); + + if ($account) { + throw new BadRequestException('account.email_taken'); + } + } + + public function checkOriginPassword($user, $password) + { + $hash = PasswordUtil::hash($password, $user->salt); + + if ($hash != $user->password) { + throw new BadRequestException('user.origin_password_incorrect'); + } + } + + public function checkConfirmPassword($newPassword, $confirmPassword) + { + if ($newPassword != $confirmPassword) { + throw new BadRequestException('user.confirm_password_incorrect'); + } + } + + public function checkUserLogin($name, $password) + { + $accountRepo = new AccountRepo(); + + $account = null; + + if (CommonValidator::email($name)) { + $account = $accountRepo->findByEmail($name); + } elseif (CommonValidator::phone($name)) { + $account = $accountRepo->findByPhone($name); + } + + if (!$account) { + throw new BadRequestException('account.login_name_incorrect'); + } + + $hash = PasswordUtil::hash($password, $account->salt); + + if ($hash != $account->password) { + throw new BadRequestException('account.login_password_incorrect'); + } + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($account->id); + + return $user; + } + + public function checkAdminLogin($name, $password) + { + $user = $this->checkUserLogin($name, $password); + + if ($user->admin_role == 0) { + throw new ForbiddenException('account.admin_not_authorized'); + } + + return $user; + } + +} diff --git a/app/Validators/Category.php b/app/Validators/Category.php index 484cf041..a6b77aa4 100644 --- a/app/Validators/Category.php +++ b/app/Validators/Category.php @@ -3,16 +3,15 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Repos\Category as CategoryRepo; class Category extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Category - * @throws NotFoundException + * @throws BadRequestException */ public function checkCategory($id) { @@ -21,7 +20,7 @@ class Category extends Validator $category = $categoryRepo->findById($id); if (!$category) { - throw new NotFoundException('category.not_found'); + throw new BadRequestException('category.not_found'); } return $category; @@ -70,13 +69,26 @@ class Category extends Validator public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('category.invalid_publish_status'); } - return $value; + return $status; + } + + public function checkDeleteAbility($category) + { + $categoryRepo = new CategoryRepo(); + + $list = $categoryRepo->findAll([ + 'parent_id' => $category->id, + 'deleted' => 0, + ]); + + + if ($list->count() > 0) { + throw new BadRequestException('category.has_child_node'); + } } } diff --git a/app/Validators/Chapter.php b/app/Validators/Chapter.php index 263b20dd..a3a3512d 100644 --- a/app/Validators/Chapter.php +++ b/app/Validators/Chapter.php @@ -2,20 +2,51 @@ namespace App\Validators; +use App\Caches\Chapter as ChapterCache; +use App\Caches\MaxChapterId as MaxChapterIdCache; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Models\Course as CourseModel; use App\Repos\Chapter as ChapterRepo; use App\Repos\Course as CourseRepo; -use App\Repos\CourseUser as CourseUserRepo; class Chapter extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Chapter - * @throws NotFoundException + * @throws BadRequestException + */ + public function checkChapterCache($id) + { + $id = intval($id); + + $maxChapterIdCache = new MaxChapterIdCache(); + + $maxChapterId = $maxChapterIdCache->get(); + + /** + * 防止缓存穿透 + */ + if ($id < 1 || $id > $maxChapterId) { + throw new BadRequestException('chapter.not_found'); + } + + $chapterCache = new ChapterCache(); + + $chapter = $chapterCache->get($id); + + if (!$chapter) { + throw new BadRequestException('chapter.not_found'); + } + + return $chapter; + } + + /** + * @param int $id + * @return \App\Models\Chapter + * @throws BadRequestException */ public function checkChapter($id) { @@ -24,7 +55,7 @@ class Chapter extends Validator $chapter = $chapterRepo->findById($id); if (!$chapter) { - throw new NotFoundException('chapter.not_found'); + throw new BadRequestException('chapter.not_found'); } return $chapter; @@ -40,7 +71,7 @@ class Chapter extends Validator throw new BadRequestException('chapter.invalid_course_id'); } - return $course->id; + return (int)$course->id; } public function checkParentId($parentId) @@ -53,7 +84,7 @@ class Chapter extends Validator throw new BadRequestException('chapter.invalid_parent_id'); } - return $chapter->id; + return (int)$chapter->id; } public function checkTitle($title) @@ -93,24 +124,20 @@ class Chapter extends Validator public function checkFreeStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - if (!in_array($status, [0, 1])) { throw new BadRequestException('chapter.invalid_free_status'); } - return $value; + return (int)$status; } public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('course.invalid_publish_status'); } - return $value; + return (int)$status; } public function checkPublishAbility($chapter) @@ -119,60 +146,34 @@ class Chapter extends Validator $course = $courseRepo->findById($chapter->course_id); + $attrs = $chapter->attrs; + if ($course->model == CourseModel::MODEL_VOD) { - if ($chapter->attrs['upload'] == 0) { - throw new BadRequestException('chapter.vod_not_uploaded'); - } - if ($chapter->attrs['translate'] != 'finished') { - throw new BadRequestException('chapter.vod_not_translated'); + if ($attrs['duration'] == 0) { + throw new BadRequestException('chapter.vod_not_ready'); } } elseif ($course->model == CourseModel::MODEL_LIVE) { - if ($chapter->attrs['start_time'] == 0) { + if ($attrs['start_time'] == 0) { throw new BadRequestException('chapter.live_time_empty'); } - } elseif ($course->model == CourseModel::MODEL_ARTICLE) { - if ($chapter->attrs['word_count'] == 0) { - throw new BadRequestException('chapter.article_content_empty'); + } elseif ($course->model == CourseModel::MODEL_READ) { + if ($attrs['word_count'] == 0) { + throw new BadRequestException('chapter.read_not_ready'); } } } - public function checkViewPrivilege($user, $chapter, $course) + public function checkDeleteAbility($chapter) { - if ($chapter->parent_id == 0) { - return false; - } + $chapterRepo = new ChapterRepo(); - if ($course->deleted == 1) { - return false; - } + $list = $chapterRepo->findAll([ + 'parent_id' => $chapter->id, + 'deleted' => 0, + ]); - if ($chapter->published == 0) { - return false; - } - - if ($chapter->free == 1) { - return true; - } - - if ($course->price == 0) { - return true; - } - - if ($user->id == 0) { - return false; - } - - $courseUserRepo = new CourseUserRepo(); - - $courseUser = $courseUserRepo->findCourseUser($user->id, $course->id); - - if (!$courseUser) { - return false; - } - - if ($courseUser->expire_at < time()) { - return false; + if ($list->count() > 0) { + throw new BadRequestException('chapter.has_child_node'); } } diff --git a/app/Validators/ChapterAudio.php b/app/Validators/ChapterAudio.php deleted file mode 100644 index 02e0c158..00000000 --- a/app/Validators/ChapterAudio.php +++ /dev/null @@ -1,22 +0,0 @@ -filter->sanitize($fileId, ['trim', 'string']); - - if (!CommonValidator::intNumber($value)) { - throw new BadRequestException('chapter_audio.invalid_file_id'); - } - - return $value; - } - -} diff --git a/app/Validators/ChapterArticle.php b/app/Validators/ChapterRead.php similarity index 64% rename from app/Validators/ChapterArticle.php rename to app/Validators/ChapterRead.php index 108be9ce..6fd58a45 100644 --- a/app/Validators/ChapterArticle.php +++ b/app/Validators/ChapterRead.php @@ -4,7 +4,7 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -class ChapterArticle extends Validator +class ChapterRead extends Validator { public function checkContent($content) @@ -14,11 +14,11 @@ class ChapterArticle extends Validator $length = kg_strlen($value); if ($length < 10) { - throw new BadRequestException('chapter_article.content_too_short'); + throw new BadRequestException('chapter_read.content_too_short'); } if ($length > 65535) { - throw new BadRequestException('chapter_article.content_too_long'); + throw new BadRequestException('chapter_read.content_too_long'); } return $value; diff --git a/app/Validators/Course.php b/app/Validators/Course.php index b3e20b8a..c7d1c8ea 100644 --- a/app/Validators/Course.php +++ b/app/Validators/Course.php @@ -2,8 +2,9 @@ namespace App\Validators; +use App\Caches\Course as CourseCache; +use App\Caches\MaxCourseId as MaxCourseIdCache; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Library\Validator\Common as CommonValidator; use App\Models\Course as CourseModel; use App\Repos\Course as CourseRepo; @@ -12,9 +13,40 @@ class Course extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Course - * @throws NotFoundException + * @throws BadRequestException + */ + public function checkCourseCache($id) + { + $id = intval($id); + + $maxCourseIdCache = new MaxCourseIdCache(); + + $maxCourseId = $maxCourseIdCache->get(); + + /** + * 防止缓存穿透 + */ + if ($id < 1 || $id > $maxCourseId) { + throw new BadRequestException('course.not_found'); + } + + $courseCache = new CourseCache(); + + $course = $courseCache->get($id); + + if (!$course) { + throw new BadRequestException('course.not_found'); + } + + return $course; + } + + /** + * @param int $id + * @return \App\Models\Course + * @throws BadRequestException */ public function checkCourse($id) { @@ -23,7 +55,7 @@ class Course extends Validator $course = $courseRepo->findById($id); if (!$course) { - throw new NotFoundException('course.not_found'); + throw new BadRequestException('course.not_found'); } return $course; @@ -31,15 +63,13 @@ class Course extends Validator public function checkModel($model) { - $value = $this->filter->sanitize($model, ['trim', 'string']); + $list = CourseModel::models(); - $scopes = CourseModel::models(); - - if (!isset($scopes[$value])) { + if (!isset($list[$model])) { throw new BadRequestException('course.invalid_model'); } - return $value; + return $model; } public function checkCover($cover) @@ -88,9 +118,20 @@ class Course extends Validator public function checkKeywords($keywords) { - $value = $this->filter->sanitize($keywords, ['trim', 'string']); + $keywords = $this->filter->sanitize($keywords, ['trim', 'string']); + $keywords = str_replace(['|', ';', ';', '、', ','], '@', $keywords); + $keywords = explode('@', $keywords); - return $value; + $list = []; + + foreach ($keywords as $keyword) { + $keyword = trim($keyword); + if (kg_strlen($keyword) > 1) { + $list[] = $keyword; + } + } + + return implode(',', $list); } public function checkMarketPrice($price) @@ -101,7 +142,7 @@ class Course extends Validator throw new BadRequestException('course.invalid_market_price'); } - return $value; + return (float)$value; } public function checkVipPrice($price) @@ -112,7 +153,7 @@ class Course extends Validator throw new BadRequestException('course.invalid_vip_price'); } - return $value; + return (float)$value; } public function checkExpiry($expiry) @@ -123,31 +164,27 @@ class Course extends Validator throw new BadRequestException('course.invalid_expiry'); } - return $value; + return (int)$value; } public function checkLevel($level) { - $value = $this->filter->sanitize($level, ['trim', 'string']); + $list = CourseModel::levels(); - $scopes = CourseModel::levels(); - - if (!isset($scopes[$value])) { + if (!isset($list[$level])) { throw new BadRequestException('course.invalid_level'); } - return $value; + return $level; } public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('course.invalid_publish_status'); } - return $value; + return (int)$status; } public function checkPublishAbility($course) @@ -158,7 +195,7 @@ class Course extends Validator $totalCount = $chapters->count(); - if ($totalCount < 1) { + if ($totalCount == 0) { throw new BadRequestException('course.pub_chapter_not_found'); } diff --git a/app/Validators/CourseUser.php b/app/Validators/CourseUser.php index e7901faf..307576f8 100644 --- a/app/Validators/CourseUser.php +++ b/app/Validators/CourseUser.php @@ -12,8 +12,8 @@ class CourseUser extends Validator { /** - * @param integer $courseId - * @param integer $userId + * @param int $courseId + * @param int $userId * @return \App\Models\CourseUser * @throws BadRequestException */ diff --git a/app/Validators/Favorite.php b/app/Validators/Favorite.php new file mode 100644 index 00000000..2c6920ad --- /dev/null +++ b/app/Validators/Favorite.php @@ -0,0 +1,73 @@ +findFavorite($courseId, $userId); + + if (!$favorite) { + throw new BadRequestException('favorite.not_found'); + } + + return $favorite; + } + + public function checkCourseId($courseId) + { + $value = $this->filter->sanitize($courseId, ['trim', 'int']); + + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($value); + + if (!$course) { + throw new BadRequestException('favorite.course_not_found'); + } + + return $course->id; + } + + public function checkUserId($userId) + { + $value = $this->filter->sanitize($userId, ['trim', 'int']); + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($value); + + if (!$user) { + throw new BadRequestException('favorite.user_not_found'); + } + + return $user->id; + } + + public function checkIfFavorited($courseId, $userId) + { + $favoriteRepo = new FavoriteRepo(); + + $favorite = $favoriteRepo->findFavorite($courseId, $userId); + + if ($favorite) { + throw new BadRequestException('favorite.has_favorited'); + } + } + +} diff --git a/app/Validators/Help.php b/app/Validators/Help.php new file mode 100644 index 00000000..2171dfe1 --- /dev/null +++ b/app/Validators/Help.php @@ -0,0 +1,83 @@ +findById($id); + + if (!$help) { + throw new BadRequestException('help.not_found'); + } + + return $help; + } + + public function checkTitle($title) + { + $value = $this->filter->sanitize($title, ['trim', 'string']); + + $length = kg_strlen($value); + + if ($length < 2) { + throw new BadRequestException('help.title_too_short'); + } + + if ($length > 50) { + throw new BadRequestException('help.title_too_long'); + } + + return $value; + } + + public function checkContent($content) + { + $value = $this->filter->sanitize($content, ['trim']); + + $length = kg_strlen($value); + + if ($length < 10) { + throw new BadRequestException('help.content_too_short'); + } + + if ($length > 65535) { + throw new BadRequestException('help.content_too_long'); + } + + return $value; + } + + public function checkPriority($priority) + { + $value = $this->filter->sanitize($priority, ['trim', 'int']); + + if ($value < 1 || $value > 255) { + throw new BadRequestException('help.invalid_priority'); + } + + return $value; + } + + public function checkPublishStatus($status) + { + if (!in_array($status, [0, 1])) { + throw new BadRequestException('help.invalid_publish_status'); + } + + return $status; + } + +} diff --git a/app/Validators/Nav.php b/app/Validators/Nav.php index 2c579a90..a48db984 100644 --- a/app/Validators/Nav.php +++ b/app/Validators/Nav.php @@ -3,7 +3,6 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Library\Validator\Common as CommonValidator; use App\Models\Nav as NavModel; use App\Repos\Nav as NavRepo; @@ -13,9 +12,9 @@ class Nav extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Nav - * @throws NotFoundException + * @throws BadRequestException */ public function checkNav($id) { @@ -24,7 +23,7 @@ class Nav extends Validator $nav = $navRepo->findById($id); if (!$nav) { - throw new NotFoundException('nav.not_found'); + throw new BadRequestException('nav.not_found'); } return $nav; @@ -87,39 +86,47 @@ class Nav extends Validator public function checkTarget($target) { - $value = $this->filter->sanitize($target, ['trim']); + $list = NavModel::targets(); - $scopes = NavModel::targets(); - - if (!isset($scopes[$value])) { + if (!isset($list[$target])) { throw new BadRequestException('nav.invalid_target'); } - return $value; + return $target; } public function checkPosition($position) { - $value = $this->filter->sanitize($position, ['trim']); + $list = NavModel::positions(); - $scopes = NavModel::positions(); - - if (!isset($scopes[$value])) { + if (!isset($list[$position])) { throw new BadRequestException('nav.invalid_position'); } - return $value; + return $position; } public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('nav.invalid_publish_status'); } - return $value; + return $status; + } + + public function checkDeleteAbility($nav) + { + $navRepo = new NavRepo(); + + $list = $navRepo->findAll([ + 'parent_id' => $nav->id, + 'deleted' => 0, + ]); + + if ($list->count() > 0) { + throw new BadRequestException('nav.has_child_node'); + } } } diff --git a/app/Validators/Order.php b/app/Validators/Order.php index 55ca0aca..4148a13e 100644 --- a/app/Validators/Order.php +++ b/app/Validators/Order.php @@ -3,19 +3,17 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound; -use App\Exceptions\NotFound as NotFoundException; use App\Models\Order as OrderModel; -use App\Repos\CourseStudent as CourseUserRepo; +use App\Repos\CourseUser as CourseUserRepo; use App\Repos\Order as OrderRepo; class Order extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Order - * @throws NotFoundException + * @throws BadRequestException */ public function checkOrder($id) { @@ -24,7 +22,7 @@ class Order extends Validator $order = $orderRepo->findById($id); if (!$order) { - throw new NotFoundException('order.not_found'); + throw new BadRequestException('order.not_found'); } return $order; diff --git a/app/Validators/Package.php b/app/Validators/Package.php index 84e37c0e..0c8d62aa 100644 --- a/app/Validators/Package.php +++ b/app/Validators/Package.php @@ -3,16 +3,15 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Repos\Package as PackageRepo; class Package extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Package - * @throws NotFoundException + * @throws BadRequestException */ public function checkPackage($id) { @@ -21,7 +20,7 @@ class Package extends Validator $package = $packageRepo->findById($id); if (!$package) { - throw new NotFoundException('package.not_found'); + throw new BadRequestException('package.not_found'); } return $package; @@ -75,13 +74,11 @@ class Package extends Validator public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('package.invalid_publish_status'); } - return $value; + return $status; } } diff --git a/app/Validators/Page.php b/app/Validators/Page.php index cee177ca..20b9abec 100644 --- a/app/Validators/Page.php +++ b/app/Validators/Page.php @@ -3,16 +3,15 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Repos\Page as PageRepo; class Page extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Page - * @throws NotFoundException + * @throws BadRequestException */ public function checkPage($id) { @@ -21,7 +20,7 @@ class Page extends Validator $page = $pageRepo->findById($id); if (!$page) { - throw new NotFoundException('page.not_found'); + throw new BadRequestException('page.not_found'); } return $page; @@ -63,13 +62,11 @@ class Page extends Validator public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('page.invalid_publish_status'); } - return $value; + return $status; } } diff --git a/app/Validators/Refund.php b/app/Validators/Refund.php index 97288793..e64da8c0 100644 --- a/app/Validators/Refund.php +++ b/app/Validators/Refund.php @@ -3,7 +3,6 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Models\Refund as RefundModel; use App\Repos\Refund as RefundRepo; @@ -11,9 +10,9 @@ class Refund extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Refund - * @throws NotFoundException + * @throws BadRequestException */ public function checkRefund($id) { @@ -22,7 +21,7 @@ class Refund extends Validator $trade = $tradeRepo->findById($id); if (!$trade) { - throw new NotFoundException('refund.not_found'); + throw new BadRequestException('refund.not_found'); } return $trade; diff --git a/app/Validators/Review.php b/app/Validators/Review.php index 8da6ceae..f7f385fa 100644 --- a/app/Validators/Review.php +++ b/app/Validators/Review.php @@ -3,7 +3,6 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Repos\Course as CourseRepo; use App\Repos\Review as ReviewRepo; @@ -11,9 +10,9 @@ class Review extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Review - * @throws NotFoundException + * @throws BadRequestException */ public function checkReview($id) { @@ -22,7 +21,7 @@ class Review extends Validator $review = $reviewRepo->findById($id); if (!$review) { - throw new NotFoundException('review.not_found'); + throw new BadRequestException('review.not_found'); } return $review; @@ -60,24 +59,20 @@ class Review extends Validator public function checkRating($rating) { - $value = $this->filter->sanitize($rating, ['trim', 'int']); - - if (!in_array($value, [1, 2, 3, 4, 5])) { + if (!in_array($rating, [1, 2, 3, 4, 5])) { throw new BadRequestException('review.invalid_rating'); } - return $value; + return $rating; } public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('review.invalid_publish_status'); } - return $value; + return $status; } } diff --git a/app/Validators/Role.php b/app/Validators/Role.php index f7d31261..76014be2 100644 --- a/app/Validators/Role.php +++ b/app/Validators/Role.php @@ -3,16 +3,15 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Repos\Role as RoleRepo; class Role extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Role - * @throws NotFoundException + * @throws BadRequestException */ public function checkRole($id) { @@ -21,7 +20,7 @@ class Role extends Validator $role = $roleRepo->findById($id); if (!$role) { - throw new NotFoundException('role.not_found'); + throw new BadRequestException('role.not_found'); } return $role; diff --git a/app/Validators/Security.php b/app/Validators/Security.php new file mode 100644 index 00000000..4bfee6f3 --- /dev/null +++ b/app/Validators/Security.php @@ -0,0 +1,30 @@ +verify($ticket, $rand); + + if (!$result) { + throw new BadRequestException('security.invalid_captcha_code'); + } + } + +} diff --git a/app/Validators/Slide.php b/app/Validators/Slide.php index 1649cc10..84b568ff 100644 --- a/app/Validators/Slide.php +++ b/app/Validators/Slide.php @@ -3,7 +3,6 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Library\Validator\Common as CommonValidator; use App\Models\Course as CourseModel; use App\Models\Page as PageModel; @@ -14,9 +13,9 @@ class Slide extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Slide - * @throws NotFoundException + * @throws BadRequestException */ public function checkSlide($id) { @@ -25,7 +24,7 @@ class Slide extends Validator $slide = $slideRepo->findById($id); if (!$slide) { - throw new NotFoundException('slide.not_found'); + throw new BadRequestException('slide.not_found'); } return $slide; @@ -70,9 +69,9 @@ class Slide extends Validator public function checkTarget($target) { - $targets = array_keys(SlideModel::targets()); + $list = SlideModel::targets(); - if (!in_array($target, $targets)) { + if (!isset($list[$target])) { throw new BadRequestException('slide.invalid_target'); } @@ -117,18 +116,16 @@ class Slide extends Validator public function checkPublishStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('slide.invalid_publish_status'); } - return $value; + return $status; } public function checkCourse($courseId) { - $course = CourseModel::findFirstById($courseId); + $course = CourseModel::findFirst($courseId); if (!$course || $course->deleted == 1) { throw new BadRequestException('slide.course_not_found'); @@ -143,7 +140,7 @@ class Slide extends Validator public function checkPage($pageId) { - $page = PageModel::findFirstById($pageId); + $page = PageModel::findFirst($pageId); if (!$page || $page->deleted == 1) { throw new BadRequestException('slide.page_not_found'); diff --git a/app/Validators/Topic.php b/app/Validators/Topic.php new file mode 100644 index 00000000..de591090 --- /dev/null +++ b/app/Validators/Topic.php @@ -0,0 +1,62 @@ +findById($id); + + if (!$topic) { + throw new BadRequestException('topic.not_found'); + } + + return $topic; + } + + public function checkTitle($title) + { + $value = $this->filter->sanitize($title, ['trim', 'string']); + + $length = kg_strlen($value); + + if ($length < 2) { + throw new BadRequestException('topic.title_too_short'); + } + + if ($length > 50) { + throw new BadRequestException('topic.title_too_long'); + } + + return $value; + } + + public function checkSummary($summary) + { + $value = $this->filter->sanitize($summary, ['trim', 'string']); + + return $value; + } + + public function checkPublishStatus($status) + { + if (!in_array($status, [0, 1])) { + throw new BadRequestException('topic.invalid_publish_status'); + } + + return $status; + } + +} diff --git a/app/Validators/Trade.php b/app/Validators/Trade.php index 16806377..e7e497b6 100644 --- a/app/Validators/Trade.php +++ b/app/Validators/Trade.php @@ -3,7 +3,6 @@ namespace App\Validators; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\NotFound as NotFoundException; use App\Models\Refund as RefundModel; use App\Models\Trade as TradeModel; use App\Repos\Trade as TradeRepo; @@ -12,9 +11,9 @@ class Trade extends Validator { /** - * @param integer $id + * @param int $id * @return \App\Models\Trade - * @throws NotFoundException + * @throws BadRequestException */ public function checkTrade($id) { @@ -23,7 +22,7 @@ class Trade extends Validator $trade = $tradeRepo->findById($id); if (!$trade) { - throw new NotFoundException('trade.not_found'); + throw new BadRequestException('trade.not_found'); } return $trade; diff --git a/app/Validators/User.php b/app/Validators/User.php index cd492f36..eb815936 100644 --- a/app/Validators/User.php +++ b/app/Validators/User.php @@ -2,23 +2,52 @@ namespace App\Validators; +use App\Caches\MaxUserId as MaxUserIdCache; +use App\Caches\User as UserCache; use App\Exceptions\BadRequest as BadRequestException; -use App\Exceptions\Forbidden as ForbiddenException; -use App\Exceptions\NotFound as NotFoundException; -use App\Library\Util\Password as PasswordUtil; -use App\Library\Util\Verification as VerificationUtil; use App\Library\Validator\Common as CommonValidator; use App\Models\User as UserModel; use App\Repos\Role as RoleRepo; use App\Repos\User as UserRepo; -use App\Services\Captcha as CaptchaService; class User extends Validator { + /** - * @param integer $id + * @param int $id * @return \App\Models\User - * @throws NotFoundException + * @throws BadRequestException + */ + public function checkUserCache($id) + { + $id = intval($id); + + $maxUserIdCache = new MaxUserIdCache(); + + $maxUserId = $maxUserIdCache->get(); + + /** + * 防止缓存穿透 + */ + if ($id < 1 || $id > $maxUserId) { + throw new BadRequestException('user.not_found'); + } + + $userCache = new UserCache(); + + $user = $userCache->get($id); + + if (!$user) { + throw new BadRequestException('user.not_found'); + } + + return $user; + } + + /** + * @param int $id + * @return \App\Models\User + * @throws BadRequestException */ public function checkUser($id) { @@ -27,45 +56,12 @@ class User extends Validator $user = $userRepo->findById($id); if (!$user) { - throw new NotFoundException('user.not_found'); + throw new BadRequestException('user.not_found'); } return $user; } - public function checkPhone($phone) - { - $value = $this->filter->sanitize($phone, ['trim', 'string']); - - if (!CommonValidator::phone($value)) { - throw new BadRequestException('account.invalid_phone'); - } - - return $value; - } - - public function checkEmail($email) - { - $value = $this->filter->sanitize($email, ['trim', 'string']); - - if (!CommonValidator::email($value)) { - throw new BadRequestException('account.invalid_email'); - } - - return $value; - } - - public function checkPassword($password) - { - $value = $this->filter->sanitize($password, ['trim', 'string']); - - if (!CommonValidator::password($value)) { - throw new BadRequestException('account.invalid_password'); - } - - return $value; - } - public function checkName($name) { $value = $this->filter->sanitize($name, ['trim', 'string']); @@ -141,46 +137,22 @@ class User extends Validator public function checkLockStatus($status) { - $value = $this->filter->sanitize($status, ['trim', 'int']); - - if (!in_array($value, [0, 1])) { + if (!in_array($status, [0, 1])) { throw new BadRequestException('user.invalid_lock_status'); } - return $value; + return $status; } public function checkLockExpiry($expiry) { if (!CommonValidator::date($expiry, 'Y-m-d H:i:s')) { - throw new BadRequestException('user.invalid_locked_expiry'); + throw new BadRequestException('user.invalid_lock_expiry'); } return strtotime($expiry); } - public function checkIfPhoneTaken($phone) - { - $accountRepo = new AccountRepo(); - - $account = $accountRepo->findByPhone($phone); - - if ($account) { - throw new BadRequestException('account.phone_taken'); - } - } - - public function checkIfEmailTaken($email) - { - $accountRepo = new AccountRepo(); - - $account = $accountRepo->findByEmail($email); - - if ($account) { - throw new BadRequestException('account.email_taken'); - } - } - public function checkIfNameTaken($name) { $userRepo = new UserRepo(); @@ -192,79 +164,11 @@ class User extends Validator } } - public function checkVerifyCode($key, $code) - { - if (!VerificationUtil::checkCode($key, $code)) { - throw new BadRequestException('user.invalid_verify_code'); - } - } - - public function checkCaptchaCode($ticket, $rand) - { - $captchaService = new CaptchaService(); - - $result = $captchaService->verify($ticket, $rand); - - if (!$result) { - throw new BadRequestException('user.invalid_captcha_code'); - } - } - - public function checkOriginPassword($user, $password) - { - $hash = PasswordUtil::hash($password, $user->salt); - - if ($hash != $user->password) { - throw new BadRequestException('user.origin_password_incorrect'); - } - } - - public function checkConfirmPassword($newPassword, $confirmPassword) - { - if ($newPassword != $confirmPassword) { - throw new BadRequestException('user.confirm_password_incorrect'); - } - } - - public function checkAdminLogin($user) - { - if ($user->admin_role == 0) { - throw new ForbiddenException('user.admin_not_authorized'); - } - } - - public function checkLoginAccount($account) - { - $userRepo = new UserRepo(); - - $user = $userRepo->findByAccount($account); - - if (!$user) { - throw new BadRequestException('user.login_account_incorrect'); - } - - return $user; - } - - public function checkLoginPassword($user, $password) - { - $hash = PasswordUtil::hash($password, $user->salt); - - if ($hash != $user->password) { - throw new BadRequestException('user.login_password_incorrect'); - } - - if ($user->locked == 1) { - throw new ForbiddenException('user.login_locked'); - } - - } - public function checkIfCanEditUser($user) { $auth = $this->getDI()->get('auth'); - $authUser = $auth->getAuthUser(); + $authUser = $auth->getAuthInfo(); if ($authUser->id) { } diff --git a/app/Validators/Validator.php b/app/Validators/Validator.php index 4cdc58e0..f26bd45e 100644 --- a/app/Validators/Validator.php +++ b/app/Validators/Validator.php @@ -4,9 +4,8 @@ namespace App\Validators; use App\Exceptions\Forbidden as ForbiddenException; use App\Exceptions\Unauthorized as UnauthorizedException; -use Phalcon\Mvc\User\Component as UserComponent; -class Validator extends UserComponent +class Validator extends \Phalcon\Mvc\User\Component { public function checkAuthToken($token) @@ -27,10 +26,10 @@ class Validator extends UserComponent return $user; } - public function checkOwner($itemUserId, $sessionUserId) + public function checkOwnerPriv($userId, $ownerId) { - if ($itemUserId != $sessionUserId) { - throw new ForbiddenException('access_denied'); + if ($userId != $ownerId) { + throw new ForbiddenException('sys.access_denied'); } } diff --git a/bootstrap/HttpErrorHandler.php b/bootstrap/HttpErrorHandler.php index 8d27705e..c5d77cb9 100644 --- a/bootstrap/HttpErrorHandler.php +++ b/bootstrap/HttpErrorHandler.php @@ -12,6 +12,9 @@ use Phalcon\Text; class HttpErrorHandler extends UserComponent { + /** + * @var \Phalcon\Logger\Adapter + */ protected $logger; public function __construct() @@ -23,19 +26,22 @@ class HttpErrorHandler extends UserComponent set_exception_handler([$this, 'handleException']); } - public function handleError($no, $str, $file, $line) + public function handleError($severity, $message, $file, $line) { - $content = compact('no', 'str', 'file', 'line'); - - $error = json_encode($content); - - $this->logger->log($error); + throw new \ErrorException($message, 0, $severity, $file, $line); } + /** + * @param \Exception $e + */ public function handleException($e) { $this->setStatusCode($e); + if ($this->response->getStatusCode() == 500) { + $this->report($e); + } + if ($this->router->getModuleName() == 'api') { $this->apiError($e); } else if ($this->isAjax()) { @@ -45,6 +51,9 @@ class HttpErrorHandler extends UserComponent } } + /** + * @param \Exception $e + */ protected function setStatusCode($e) { if ($e instanceof BadRequestException) { @@ -57,10 +66,12 @@ class HttpErrorHandler extends UserComponent $this->response->setStatusCode(404); } else { $this->response->setStatusCode(500); - $this->report($e); } } + /** + * @param \Exception $e + */ protected function report($e) { $content = sprintf('%s(%d): %s', $e->getFile(), $e->getLine(), $e->getMessage()); @@ -68,22 +79,33 @@ class HttpErrorHandler extends UserComponent $this->logger->error($content); } + /** + * @param \Exception $e + */ protected function apiError($e) { $content = $this->translate($e->getMessage()); $this->response->setJsonContent($content); + $this->response->send(); } + /** + * @param \Exception $e + */ protected function ajaxError($e) { $content = $this->translate($e->getMessage()); $this->response->setJsonContent($content); + $this->response->send(); } + /** + * @param \Exception $e + */ protected function pageError($e) { $content = $this->translate($e->getMessage()); @@ -91,7 +113,7 @@ class HttpErrorHandler extends UserComponent $this->flash->error($content); $this->response->redirect([ - 'for' => 'error.' . $this->response->getStatusCode() + 'for' => 'home.error.' . $this->response->getStatusCode() ])->send(); } diff --git a/composer.json b/composer.json index 7621bc52..2252585d 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,7 @@ { "require": { + "ext-redis": "*", + "ext-json": "*", "phalcon/incubator": "^3.4", "guzzlehttp/guzzle": "^6.3", "swiftmailer/swiftmailer": "^6.0", @@ -14,7 +16,9 @@ "hightman/xunsearch": "^1.4.14" }, "require-dev": { - "odan/phinx-migrations-generator": "^4.6" + "odan/phinx-migrations-generator": "^4.6", + "phalcon/ide-stubs": "^3.4.3", + "jaeger/querylist": "^4.1" }, "repositories": { "packagist": { diff --git a/composer.lock b/composer.lock index c1ad8d04..f7d7afd0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9e0fcf410383f7ba3d69a36d7ff00331", + "content-hash": "4f03ea4d2e7124d05e660b87608e46ed", "packages": [ { "name": "doctrine/lexer", @@ -607,6 +607,7 @@ "cron", "schedule" ], + "abandoned": "dragonmantank/cron-expression", "time": "2017-01-23T04:29:33+00:00" }, { @@ -1870,6 +1871,190 @@ ], "packages-dev": [ { + "name": "cache/adapter-common", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-cache/adapter-common.git", + "reference": "6320bb5f5574cb88438059b59f8708da6b6f1d32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-cache/adapter-common/zipball/6320bb5f5574cb88438059b59f8708da6b6f1d32", + "reference": "6320bb5f5574cb88438059b59f8708da6b6f1d32", + "shasum": "" + }, + "require": { + "cache/tag-interop": "^1.0", + "php": "^5.6 || ^7.0", + "psr/cache": "^1.0", + "psr/log": "^1.0", + "psr/simple-cache": "^1.0" + }, + "require-dev": { + "cache/integration-tests": "^0.16", + "phpunit/phpunit": "^5.7.21" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Cache\\Adapter\\Common\\": "" + } + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Scherer", + "email": "aequasi@gmail.com", + "homepage": "https://github.com/aequasi" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "Common classes for PSR-6 adapters", + "homepage": "http://www.php-cache.com/en/latest/", + "keywords": [ + "cache", + "psr-6", + "tag" + ], + "time": "2018-07-08T13:04:33+00:00" + }, + { + "name": "cache/filesystem-adapter", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-cache/filesystem-adapter.git", + "reference": "d50680b6dabbe39f9831f5fc9efa61c09d936017" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-cache/filesystem-adapter/zipball/d50680b6dabbe39f9831f5fc9efa61c09d936017", + "reference": "d50680b6dabbe39f9831f5fc9efa61c09d936017", + "shasum": "" + }, + "require": { + "cache/adapter-common": "^1.0", + "league/flysystem": "^1.0", + "php": "^5.6 || ^7.0", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0" + }, + "provide": { + "psr/cache-implementation": "^1.0" + }, + "require-dev": { + "cache/integration-tests": "^0.16", + "phpunit/phpunit": "^5.7.21" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Cache\\Adapter\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Scherer", + "email": "aequasi@gmail.com", + "homepage": "https://github.com/aequasi" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A PSR-6 cache implementation using filesystem. This implementation supports tags", + "homepage": "http://www.php-cache.com/en/latest/", + "keywords": [ + "cache", + "filesystem", + "psr-6", + "tag" + ], + "time": "2017-07-16T21:09:25+00:00" + }, + { + "name": "cache/tag-interop", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-cache/tag-interop.git", + "reference": "c7496dd81530f538af27b4f2713cde97bc292832" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-cache/tag-interop/zipball/c7496dd81530f538af27b4f2713cde97bc292832", + "reference": "c7496dd81530f538af27b4f2713cde97bc292832", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "psr/cache": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Cache\\TagInterop\\": "" + } + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com", + "homepage": "https://github.com/nicolas-grekas" + } + ], + "description": "Framework interoperable interfaces for tags", + "homepage": "http://www.php-cache.com/en/latest/", + "keywords": [ + "cache", + "psr", + "psr6", + "tag" + ], + "time": "2017-03-13T09:14:27+00:00" + }, + { "name": "cakephp/cache", "version": "3.8.7", "source": { @@ -2206,6 +2391,219 @@ "time": "2019-11-21T14:18:54+00:00" }, { + "name": "jaeger/g-http", + "version": "V1.6.0", + "source": { + "type": "git", + "url": "https://github.com/jae-jae/GHttp.git", + "reference": "eb34d266a07c687aef45087370ef47d48321bd2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jae-jae/GHttp/zipball/eb34d266a07c687aef45087370ef47d48321bd2e", + "reference": "eb34d266a07c687aef45087370ef47d48321bd2e", + "shasum": "" + }, + "require": { + "cache/filesystem-adapter": "^1.0", + "guzzlehttp/guzzle": "^6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jaeger\\": "src" + } + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaeger", + "email": "JaegerCode@gmail.com" + } + ], + "description": "Simple Http client base on GuzzleHttp", + "time": "2018-12-12T04:21:15+00:00" + }, + { + "name": "jaeger/phpquery-single", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/jae-jae/phpQuery-single.git", + "reference": "2d607a2bcfd8bcf5c42e83d6c66fedaf397c7c3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jae-jae/phpQuery-single/zipball/2d607a2bcfd8bcf5c42e83d6c66fedaf397c7c3f", + "reference": "2d607a2bcfd8bcf5c42e83d6c66fedaf397c7c3f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "phpQuery.php" + ] + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobiasz Cudnik", + "email": "tobiasz.cudnik@gmail.com", + "homepage": "https://github.com/TobiaszCudnik", + "role": "Developer" + }, + { + "name": "Jaeger", + "role": "Packager" + } + ], + "description": "phpQuery单文件版本,是Querylist的依赖(http://querylist.cc/),phpQuery项目主页:http://code.google.com/p/phpquery/", + "homepage": "http://code.google.com/p/phpquery/", + "time": "2019-02-22T07:27:45+00:00" + }, + { + "name": "jaeger/querylist", + "version": "V4.1.1", + "source": { + "type": "git", + "url": "https://github.com/jae-jae/QueryList.git", + "reference": "46f564bc8b1a22b5dca7cd690b4af76e919b39f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jae-jae/QueryList/zipball/46f564bc8b1a22b5dca7cd690b4af76e919b39f7", + "reference": "46f564bc8b1a22b5dca7cd690b4af76e919b39f7", + "shasum": "" + }, + "require": { + "jaeger/g-http": "^1.1", + "jaeger/phpquery-single": "^1", + "php": ">=7.0", + "tightenco/collect": "^5" + }, + "require-dev": { + "phpunit/phpunit": "^7.5", + "symfony/var-dumper": "^3.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "QL\\": "src" + } + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaeger", + "email": "JaegerCode@gmail.com" + } + ], + "description": "Simple, elegant, extensible PHP Web Scraper (crawler/spider),Use the css3 dom selector,Based on phpQuery! 简洁、优雅、可扩展的PHP采集工具(爬虫),基于phpQuery。", + "homepage": "http://querylist.cc", + "keywords": [ + "QueryList", + "phpQuery", + "spider" + ], + "time": "2019-02-22T07:33:54+00:00" + }, + { + "name": "league/flysystem", + "version": "1.0.63", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "8132daec326565036bc8e8d1876f77ec183a7bd6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8132daec326565036bc8e8d1876f77ec183a7bd6", + "reference": "8132daec326565036bc8e8d1876f77ec183a7bd6", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.10" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2020-01-04T16:30:31+00:00" + }, + { "name": "odan/phinx-migrations-generator", "version": "4.6.0", "source": { @@ -2257,6 +2655,54 @@ "time": "2019-11-21T15:15:19+00:00" }, { + "name": "phalcon/ide-stubs", + "version": "v3.4.3", + "source": { + "type": "git", + "url": "https://github.com/phalcon/ide-stubs.git", + "reference": "65144f2b0fad32b182ccb062b1efc1b4edea5d44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phalcon/ide-stubs/zipball/65144f2b0fad32b182ccb062b1efc1b4edea5d44", + "reference": "65144f2b0fad32b182ccb062b1efc1b4edea5d44", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Phalcon Team", + "email": "team@phalconphp.com", + "homepage": "https://phalconphp.com/en/team" + }, + { + "name": "Contributors", + "homepage": "https://github.com/phalcon/ide-stubs/graphs/contributors" + } + ], + "description": "The most complete Phalcon Framework IDE stubs library which enables autocompletion in modern IDEs.", + "homepage": "https://phalconphp.com", + "keywords": [ + "Devtools", + "Eclipse", + "autocomplete", + "ide", + "netbeans", + "phalcon", + "phpstorm", + "stub", + "stubs" + ], + "time": "2018-12-09T14:11:06+00:00" + }, + { "name": "psr/container", "version": "1.0.0", "source": { @@ -2846,6 +3292,82 @@ "time": "2019-11-18T17:27:11+00:00" }, { + "name": "symfony/var-dumper", + "version": "v4.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "be330f919bdb395d1e0c3f2bfb8948512d6bdd99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/be330f919bdb395d1e0c3f2bfb8948512d6bdd99", + "reference": "be330f919bdb395d1e0c3f2bfb8948512d6bdd99", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/console": "<3.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^4.4|^5.0", + "twig/twig": "^1.34|^2.4|^3.0" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2019-12-18T13:41:29+00:00" + }, + { "name": "symfony/yaml", "version": "v5.0.1", "source": { @@ -2903,6 +3425,56 @@ "description": "Symfony Yaml Component", "homepage": "https://symfony.com", "time": "2019-11-18T17:27:11+00:00" + }, + { + "name": "tightenco/collect", + "version": "v5.8.35", + "source": { + "type": "git", + "url": "https://github.com/tightenco/collect.git", + "reference": "c93a7039e6207ad533a09109838fe80933fcc72c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tightenco/collect/zipball/c93a7039e6207ad533a09109838fe80933fcc72c", + "reference": "c93a7039e6207ad533a09109838fe80933fcc72c", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/var-dumper": ">=3.4 <5" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "nesbot/carbon": "^1.26.3", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Collect/Support/helpers.php", + "src/Collect/Support/alias.php" + ], + "psr-4": { + "Tightenco\\Collect\\": "src/Collect" + } + }, + "notification-url": "https://packagist.jp/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylorotwell@gmail.com" + } + ], + "description": "Collect - Illuminate Collections as a separate package.", + "keywords": [ + "collection", + "laravel" + ], + "time": "2019-09-17T18:57:01+00:00" } ], "aliases": [], @@ -2912,6 +3484,9 @@ }, "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "ext-redis": "*", + "ext-json": "*" + }, "platform-dev": [] } diff --git a/config/config.php.default b/config/config.php.default index 5ab4a195..5ac74c3a 100644 --- a/config/config.php.default +++ b/config/config.php.default @@ -55,7 +55,7 @@ $config['db']['password'] = '1qaz2wsx3edc'; /** * 数据库编码 */ -$config['db']['charset'] = 'utf8'; +$config['db']['charset'] = 'utf8mb4'; /** * redis主机名 diff --git a/config/errors.php b/config/errors.php index ee7df884..26f4fa39 100644 --- a/config/errors.php +++ b/config/errors.php @@ -13,30 +13,39 @@ $error['sys.access_denied'] = '访问被拒绝'; $error['sys.session_expired'] = '会话已过期'; $error['sys.unknown_error'] = '未知错误'; +/** + * 安全相关 + */ +$error['security.invalid_captcha_code'] = '无效的验证码'; +$error['security.invalid_verify_code'] = '无效的验证码'; + /** * 帐号相关 */ +$error['account.login_locked'] = '账户已被锁定,无法登录'; +$error['account.login_name_incorrect'] = '登录账户不正确'; +$error['account.login_password_incorrect'] = '登录密码不正确'; +$error['account.invalid_email'] = '无效的电子邮箱'; +$error['account.invalid_phone'] = '无效的手机号'; +$error['account.invalid_password'] = '无效的密码(字母或数字6-16位)'; +$error['account.email_taken'] = '邮箱被占用'; +$error['account.phone_taken'] = '手机号被占用'; +$error['account.origin_password_incorrect'] = '原密码不正确'; +$error['account.confirm_password_incorrect'] = '确认密码不正确'; +$error['account.admin_not_authorized'] = '账户没有登录后台的授权'; + +/** + * 用户相关 + */ $error['user.not_found'] = '用户不存在'; -$error['user.login_locked'] = '账户已被锁定,无法登录'; -$error['user.login_account_incorrect'] = '登录账户不正确'; -$error['user.login_password_incorrect'] = '登录密码不正确'; +$error['user.name_taken'] = '用户名被占用'; $error['user.title_too_long'] = '头衔过长(超过30个字符)'; $error['user.about_too_long'] = '简介过长(超过255个字符)'; -$error['user.invalid_email'] = '无效的电子邮箱'; -$error['user.invalid_phone'] = '无效的手机号'; -$error['user.invalid_password'] = '无效的密码(字母或数字6-16位)'; $error['user.invalid_edu_role'] = '无效的教学角色'; $error['user.invalid_admin_role'] = '无效的后台角色'; $error['user.invalid_lock_status'] = '无效的锁定状态'; $error['user.invalid_lock_expiry'] = '无效的锁定期限'; -$error['user.invalid_captcha_code'] = '无效的验证码'; -$error['user.invalid_verify_code'] = '无效的验证码'; -$error['user.email_taken'] = '邮箱被占用'; -$error['user.phone_taken'] = '手机号被占用'; -$error['user.name_taken'] = '用户名被占用'; -$error['user.origin_password_incorrect'] = '原密码不正确'; -$error['user.confirm_password_incorrect'] = '确认密码不正确'; -$error['user.admin_not_authorized'] = '账户没有登录后台的授权'; + /** * 分类相关 @@ -47,6 +56,21 @@ $error['category.invalid_publish_status'] = '无效的发布状态'; $error['category.invalid_priority'] = '无效的排序值(范围:1-255)'; $error['category.name_too_short'] = '名称太短(少于2个字符)'; $error['category.name_too_long'] = '名称太长(多于30个字符)'; +$error['category.has_child_node'] = '存在子节点,不允许相关操作'; + +/** + * 导航相关 + */ +$error['nav.not_found'] = '分类不存在'; +$error['nav.parent_not_found'] = '父级分类不存在'; +$error['nav.invalid_publish_status'] = '无效的发布状态'; +$error['nav.invalid_url'] = '无效的访问地址'; +$error['nav.invalid_position'] = '无效的位置类型'; +$error['nav.invalid_target'] = '无效的目标类型'; +$error['nav.invalid_priority'] = '无效的排序值(范围:1-255)'; +$error['nav.name_too_short'] = '名称太短(少于2个字符)'; +$error['nav.name_too_long'] = '名称太长(多于30个字符)'; +$error['nav.has_child_node'] = '存在子节点,不允许相关操作'; /** * 课程相关 @@ -63,7 +87,7 @@ $error['course.invalid_vip_price'] = '无效的会员价格'; $error['course.invalid_expiry'] = '无效的期限'; $error['course.invalid_publish_status'] = '无效的发布状态'; $error['course.pub_chapter_not_found'] = '尚未发现已发布的课时'; -$error['course.pub_chapter_too_few'] = '已发布的课时太少(未过三分之一)'; +$error['course.pub_chapter_not_enough'] = '已发布的课时太少(未过三分之一)'; $error['course.has_not_favorited'] = '尚未收藏该课程'; $error['course.has_favorited'] = '已经收藏过该课程了'; @@ -73,10 +97,18 @@ $error['course.has_reviewed'] = '已经评价过该课程了'; $error['course.apply_offline_course'] = '申请未发布的课程'; $error['course.apply_charge_course'] = '申请非免费的课程'; +/** + * 话题相关 + */ +$error['topic.not_found'] = '话题不存在'; +$error['topic.title_too_short'] = '标题太短(少于2个字符)'; +$error['topic.title_too_long'] = '标题太长(多于30个字符)'; +$error['topic.invalid_publish_status'] = '无效的发布状态'; + /** * 套餐相关 */ -$error['package.not_found'] = '课程不存在'; +$error['package.not_found'] = '套餐不存在'; $error['package.title_too_short'] = '标题太短(少于5个字符)'; $error['package.title_too_long'] = '标题太长(多于30个字符)'; $error['package.invalid_market_price'] = '无效的市场价格'; @@ -101,13 +133,14 @@ $error['chapter.invalid_publish_status'] = '无效的发布状态'; $error['chapter.invalid_free_status'] = '无效的免费状态'; $error['chapter.invalid_course_id'] = '无效的课程编号'; $error['chapter.invalid_parent_id'] = '无效的父级编号'; +$error['chapter.invalid_priority'] = '无效的排序值(范围:1-255)'; $error['chapter.title_too_short'] = '标题太短(少于2个字符)'; $error['chapter.title_too_long'] = '标题太长(多于30个字符)'; -$error['chapter.vod_not_uploaded'] = '点播资源文件尚未上传'; -$error['chapter.vod_not_translated'] = '点播资源转码尚未完成'; +$error['chapter.vod_not_ready'] = '点播资源尚未就绪'; $error['chapter.live_not_started'] = '直播尚未开始'; $error['chapter.live_time_empty'] = '直播时间尚未设置'; -$error['chapter.article_content_empty'] = '文章内容尚未设置'; +$error['chapter.read_not_ready'] = '文章内容尚未就绪'; +$error['chapter.has_child_node'] = '存在子节点,不允许相关操作'; /** * 点播相关 @@ -129,9 +162,9 @@ $error['chapter_live.time_too_long'] = '直播时间太长(超过3小时)'; /** * 图文相关 */ -$error['chapter_article.not_found'] = '文章不存在'; -$error['chapter_article.content_too_short'] = '文章内容太短(少于10个字符)'; -$error['chapter_article.content_too_long'] = '文章内容太长(多于65535个字符)'; +$error['chapter_read.not_found'] = '文章不存在'; +$error['chapter_read.content_too_short'] = '文章内容太短(少于10个字符)'; +$error['chapter_read.content_too_long'] = '文章内容太长(多于65535个字符)'; /** * 评价相关 @@ -152,6 +185,17 @@ $error['page.content_too_short'] = '内容太短(少于10个字符)'; $error['page.content_too_long'] = '内容太长(多于65535个字符)'; $error['page.invalid_publish_status'] = '无效的发布状态'; +/** + * 帮助相关 + */ +$error['help.not_found'] = '帮助不存在'; +$error['help.title_too_short'] = '标题太短(少于2个字符)'; +$error['help.title_too_long'] = '标题太长(多于30个字符)'; +$error['help.content_too_short'] = '内容太短(少于10个字符)'; +$error['help.content_too_long'] = '内容太长(多于65535个字符)'; +$error['help.invalid_priority'] = '无效的排序数值(范围:1-255)'; +$error['help.invalid_publish_status'] = '无效的发布状态'; + /** * 轮播相关 */ diff --git a/config/events.php b/config/events.php index bd223f78..e3d6e97e 100644 --- a/config/events.php +++ b/config/events.php @@ -3,7 +3,6 @@ $events = [ 'db' => \App\Listeners\Profiler::class, - 'course' => \App\Listeners\Course::class, 'payment' => \App\Listeners\Payment::class, ]; diff --git a/config/xs.course.ini.default b/config/xs.course.ini.default index 3b1620dc..6574b7e2 100644 --- a/config/xs.course.ini.default +++ b/config/xs.course.ini.default @@ -29,9 +29,6 @@ type = string [expiry] type = string -[rating] -type = string - [score] type = numeric @@ -45,8 +42,8 @@ type = string index = self tokenizer = full -[student_count] -type = numeric +[user_count] +type = string [lesson_count] type = string diff --git a/public/static/admin/js/xm-course.js b/public/static/admin/js/xm-course.js index 239d833b..666def64 100644 --- a/public/static/admin/js/xm-course.js +++ b/public/static/admin/js/xm-course.js @@ -1,7 +1,7 @@ /** * 挑选课程组件 - * @param array data 默认数据 - * @param string url 请求地址 + * @param data array 默认数据 + * @param url string 请求地址 */ function xmCourse(data, url) { @@ -41,44 +41,33 @@ function xmCourse(data, url) { {field: 'id', title: '编号', width: 50}, {field: 'title', title: '标题', width: 300}, { - field: 'model', title: '类型', width: 60, templet: function (d) { - if (d.model == 'vod') { + field: 'model', title: '类型', width: 50, templet: function (d) { + if (d.model === 'vod') { return '点播'; - } else if (d.model == 'live') { + } else if (d.model === 'live') { return '直播'; - } else if (d.model == 'article') { + } else if (d.model === 'read') { return '图文'; } } }, { - field: 'lesson_count', title: '课时数', width: 60, templet: function (d) { + field: 'lesson_count', title: '学员数', width: 55, templet: function (d) { + return '' + d.user_count + ''; + } + }, + { + field: 'lesson_count', title: '课时数', width: 55, templet: function (d) { return '' + d.lesson_count + ''; } }, { - field: 'expiry', title: '有效期', width: 70, templet: function (d) { - if (d.expiry == '30') { - return '一个月'; - } else if (d.expiry == '90') { - return '三个月'; - } - else if (d.expiry == '180') { - return '半年'; - } else if (d.expiry == '365') { - return '一年'; - } else if (d.expiry == '1095') { - return '三年'; - } - } - }, - { - field: 'market_price', title: '市场价', width: 70, templet: function (d) { + field: 'market_price', title: '市场价', width: 60, templet: function (d) { return '¥' + d.market_price; } }, { - field: 'vip_price', title: '会员价', width: 70, templet: function (d) { + field: 'vip_price', title: '会员价', width: 60, templet: function (d) { return '¥' + d.vip_price; } }, @@ -104,4 +93,5 @@ function xmCourse(data, url) { }); }); + } diff --git a/public/static/lib/layui/css/layui.css b/public/static/lib/layui/css/layui.css index 0e8508f6..e31c66b6 100644 --- a/public/static/lib/layui/css/layui.css +++ b/public/static/lib/layui/css/layui.css @@ -1,2 +1,2 @@ /** layui-v2.5.5 MIT License By https://www.layui.com */ - .layui-inline,img{display:inline-block;vertical-align:middle}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-body,.layui-edge,.layui-elip{overflow:hidden}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-elip,.layui-form-checkbox span,.layui-form-pane .layui-form-label{text-overflow:ellipsis;white-space:nowrap}.layui-breadcrumb,.layui-tree-btnGroup{visibility:hidden}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}li{list-style:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=250);src:url(../font/iconfont.eot?v=250#iefix) format('embedded-opentype'),url(../font/iconfont.woff2?v=250) format('woff2'),url(../font/iconfont.woff?v=250) format('woff'),url(../font/iconfont.ttf?v=250) format('truetype'),url(../font/iconfont.svg?v=250#layui-icon) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-reply-fill:before{content:"\e611"}.layui-icon-set-fill:before{content:"\e614"}.layui-icon-menu-fill:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-share:before{content:"\e641"}.layui-icon-set-sm:before{content:"\e620"}.layui-icon-engine:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-chart-screen:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-circle-dot:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-release:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-chart:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-layim-theme:before{content:"\e61b"}.layui-icon-table:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-face-cry:before{content:"\e69c"}.layui-icon-face-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-upload-circle:before{content:"\e62f"}.layui-icon-add-circle:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-templeate-1:before{content:"\e630"}.layui-icon-util:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-edit:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-file:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-rate-half:before{content:"\e6c9"}.layui-icon-add-circle-fine:before{content:"\e608"}.layui-icon-prev-circle:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-carousel:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-code-circle:before{content:"\e635"}.layui-icon-water:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-date:before{content:"\e637"}.layui-icon-layim-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-friends:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-ok:before{content:"\e605"}.layui-icon-layer:before{content:"\e638"}.layui-icon-face-smile-fine:before{content:"\e60c"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-group:before{content:"\e613"}.layui-icon-layim-download:before{content:"\e61e"}.layui-icon-picture-fine:before{content:"\e60d"}.layui-icon-link:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-log:before{content:"\e60e"}.layui-icon-rate-solid:before{content:"\e67a"}.layui-icon-fonts-del:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-fonts-clear:before{content:"\e639"}.layui-icon-triangle-r:before{content:"\e623"}.layui-icon-circle:before{content:"\e63f"}.layui-icon-radio:before{content:"\e643"}.layui-icon-align-center:before{content:"\e647"}.layui-icon-align-right:before{content:"\e648"}.layui-icon-align-left:before{content:"\e649"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-fonts-strong:before{content:"\e62b"}.layui-icon-upload:before{content:"\e67c"}.layui-icon-dialogue:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-cellphone-fine:before{content:"\e63b"}.layui-icon-add-1:before{content:"\e654"}.layui-icon-face-smile-b:before{content:"\e650"}.layui-icon-fonts-html:before{content:"\e64b"}.layui-icon-form:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-tabs:before{content:"\e62a"}.layui-icon-fonts-code:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-fonts-u:before{content:"\e646"}.layui-icon-triangle-d:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-picture:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-flag:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-fonts-i:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-upload-drag:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-file-b:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-website:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-female:before{content:"\e661"}.layui-icon-male:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-rate:before{content:"\e67b"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-icon-screen-full:before{content:"\e622"}.layui-icon-screen-restore:before{content:"\e758"}.layui-icon-cols:before{content:"\e610"}.layui-icon-export:before{content:"\e67d"}.layui-icon-print:before{content:"\e66d"}.layui-icon-slider:before{content:"\e714"}.layui-icon-addition:before{content:"\e624"}.layui-icon-subtraction:before{content:"\e67e"}.layui-icon-service:before{content:"\e626"}.layui-icon-transfer:before{content:"\e691"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{position:fixed;top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-appearance:none;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{position:relative;height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{position:relative;padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-auxiliar-moving{position:fixed;left:0;right:0;top:0;bottom:0;width:100%;height:100%;background:0 0;z-index:9999999999}.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-checked{background-color:#5FB878}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2;-webkit-transition:.5s all;transition:.5s all}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:30px;margin-right:10px;padding-right:30px;cursor:pointer;font-size:0;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;border-radius:2px 0 0 2px;background-color:#d2d2d2;color:#fff;overflow:hidden}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;top:0;width:30px;height:28px;border:1px solid #d2d2d2;border-left:none;border-radius:0 2px 2px 0;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{border-color:#c2c2c2;color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;min-width:18px;min-height:18px;border:none!important;margin-right:0;padding-left:28px;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{padding-left:0;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{right:auto;left:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878!important;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;min-width:35px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:relative;top:0;width:25px;margin-left:21px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-checkbox-disbaled,.layui-checkbox-disbaled i{border-color:#e2e2e2!important}.layui-form-onswitch i{left:100%;margin-left:-21px;background-color:#fff}.layui-form-onswitch em{margin-left:5px;margin-right:21px;color:#fff!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-flow-more a *,.layui-laypage input,.layui-table-view select[lay-ignore]{display:inline-block}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh{vertical-align:top}.layui-laypage .layui-laypage-refresh i{font-size:18px;cursor:pointer}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:100%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table-total,.layui-table-total tr,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-col-set,.layui-table-fixed-r,.layui-table-grid-down,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-total,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box{position:relative;overflow:hidden}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table th.layui-unselect .layui-table-cell span{cursor:pointer}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-table td[data-edit=text]{cursor:text}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-view .layui-form-radio{line-height:0;padding:0}.layui-table-view .layui-form-radio>i{margin:0;font-size:20px}.layui-table-init{position:absolute;left:0;top:0;width:100%;height:100%;text-align:center;z-index:110}.layui-table-init .layui-icon{position:absolute;left:50%;top:50%;margin:-15px 0 0 -15px;font-size:30px;color:#c2c2c2}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-header .layui-table{margin-bottom:-1px}.layui-table-tool .layui-inline[lay-event]{position:relative;width:26px;height:26px;padding:5px;line-height:16px;margin-right:10px;text-align:center;color:#333;border:1px solid #ccc;cursor:pointer;-webkit-transition:.5s all;transition:.5s all}.layui-table-tool .layui-inline[lay-event]:hover{border:1px solid #999}.layui-table-tool-temp{padding-right:120px}.layui-table-tool-self{position:absolute;right:17px;top:10px}.layui-table-tool .layui-table-tool-self .layui-inline[lay-event]{margin:0 0 0 10px}.layui-table-tool-panel{position:absolute;top:29px;left:-1px;padding:5px 0;min-width:150px;min-height:40px;border:1px solid #d2d2d2;text-align:left;overflow-y:auto;background-color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.12)}.layui-table-cell,.layui-table-tool-panel li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.layui-table-tool-panel li{padding:0 10px;line-height:30px;-webkit-transition:.5s all;transition:.5s all}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary]{width:100%;padding-left:28px}.layui-table-tool-panel li:hover{background-color:#f2f2f2}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary] i{position:absolute;left:0;top:0}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary] span{padding:0}.layui-table-tool .layui-table-tool-self .layui-table-tool-panel{left:auto;right:-1px}.layui-table-col-set{position:absolute;right:0;top:0;width:20px;height:100%;border-width:0 0 0 1px;background-color:#fff}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:3px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:5px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary]{top:-1px;padding:0}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-radio,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px;margin-bottom:-1px}.layui-table-body .layui-none{line-height:26px;padding:15px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0;z-index:101}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;z-index:890;width:100%;min-height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-tool .layui-btn-container{margin-bottom:-10px}.layui-table-page,.layui-table-total{border-width:1px 0 0;margin-bottom:-1px;overflow:hidden}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;height:41px;font-size:12px;white-space:nowrap}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}.layui-table-view .layui-form-checkbox i{height:26px}.layui-table-grid .layui-table-cell{overflow:visible}.layui-table-grid-down{position:absolute;top:0;right:0;width:26px;height:100%;padding:5px 0;border-width:0 0 0 1px;text-align:center;background-color:#fff;color:#999;cursor:pointer}.layui-table-grid-down .layui-icon{position:absolute;top:50%;left:50%;margin:-8px 0 0 -8px}.layui-table-grid-down:hover{background-color:#fbfbfb}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.12)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#666}.layui-table-tips-c{position:absolute;right:-3px;top:-13px;width:20px;height:20px;padding:3px;cursor:pointer;background-color:#666;border-radius:50%;color:#fff}.layui-table-tips-c:hover{background-color:#777}.layui-table-tips-c:before{position:relative;right:-2px}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-list{margin:10px 0}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-transfer-active,.layui-transfer-box{display:inline-block;vertical-align:middle}.layui-transfer-box,.layui-transfer-header,.layui-transfer-search{border-width:0;border-style:solid;border-color:#e6e6e6}.layui-transfer-box{position:relative;border-width:1px;width:200px;height:360px;border-radius:2px;background-color:#fff}.layui-transfer-box .layui-form-checkbox{width:100%;margin:0!important}.layui-transfer-header{height:38px;line-height:38px;padding:0 10px;border-bottom-width:1px}.layui-transfer-search{position:relative;padding:10px;border-bottom-width:1px}.layui-transfer-search .layui-input{height:32px;padding-left:30px;font-size:12px}.layui-transfer-search .layui-icon-search{position:absolute;left:20px;top:50%;margin-top:-8px;color:#666}.layui-transfer-active{margin:0 15px}.layui-transfer-active .layui-btn{display:block;margin:0;padding:0 15px;background-color:#5FB878;border-color:#5FB878;color:#fff}.layui-transfer-active .layui-btn-disabled{background-color:#FBFBFB;border-color:#e6e6e6;color:#C9C9C9}.layui-transfer-active .layui-btn:first-child{margin-bottom:15px}.layui-transfer-active .layui-btn .layui-icon{margin:0;font-size:14px!important}.layui-transfer-data{padding:5px 0;overflow:auto}.layui-transfer-data li{height:32px;line-height:32px;padding:0 10px}.layui-transfer-data li:hover{background-color:#f2f2f2;transition:.5s all}.layui-transfer-data .layui-none{padding:15px 10px;text-align:center;color:#999}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:999999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-rate,.layui-rate *{display:inline-block;vertical-align:middle}.layui-rate{padding:10px 5px 10px 0;font-size:0}.layui-rate li i.layui-icon{font-size:20px;color:#FFB800;margin-right:5px;transition:all .3s;-webkit-transition:all .3s}.layui-rate li i:hover{cursor:pointer;transform:scale(1.12);-webkit-transform:scale(1.12)}.layui-rate[readonly] li i:hover{cursor:default;transform:scale(1)}.layui-colorpicker{width:26px;height:26px;border:1px solid #e6e6e6;padding:5px;border-radius:2px;line-height:24px;display:inline-block;cursor:pointer;transition:all .3s;-webkit-transition:all .3s}.layui-colorpicker:hover{border-color:#d2d2d2}.layui-colorpicker.layui-colorpicker-lg{width:34px;height:34px;line-height:32px}.layui-colorpicker.layui-colorpicker-sm{width:24px;height:24px;line-height:22px}.layui-colorpicker.layui-colorpicker-xs{width:22px;height:22px;line-height:20px}.layui-colorpicker-trigger-bgcolor{display:block;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);border-radius:2px}.layui-colorpicker-trigger-span{display:block;height:100%;box-sizing:border-box;border:1px solid rgba(0,0,0,.15);border-radius:2px;text-align:center}.layui-colorpicker-trigger-i{display:inline-block;color:#FFF;font-size:12px}.layui-colorpicker-trigger-i.layui-icon-close{color:#999}.layui-colorpicker-main{position:absolute;z-index:66666666;width:280px;padding:7px;background:#FFF;border:1px solid #d2d2d2;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12)}.layui-colorpicker-main-wrapper{height:180px;position:relative}.layui-colorpicker-basis{width:260px;height:100%;position:relative}.layui-colorpicker-basis-white{width:100%;height:100%;position:absolute;top:0;left:0;background:linear-gradient(90deg,#FFF,hsla(0,0%,100%,0))}.layui-colorpicker-basis-black{width:100%;height:100%;position:absolute;top:0;left:0;background:linear-gradient(0deg,#000,transparent)}.layui-colorpicker-basis-cursor{width:10px;height:10px;border:1px solid #FFF;border-radius:50%;position:absolute;top:-3px;right:-3px;cursor:pointer}.layui-colorpicker-side{position:absolute;top:0;right:0;width:12px;height:100%;background:linear-gradient(red,#FF0,#0F0,#0FF,#00F,#F0F,red)}.layui-colorpicker-side-slider{width:100%;height:5px;box-shadow:0 0 1px #888;box-sizing:border-box;background:#FFF;border-radius:1px;border:1px solid #f0f0f0;cursor:pointer;position:absolute;left:0}.layui-colorpicker-main-alpha{display:none;height:12px;margin-top:7px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.layui-colorpicker-alpha-bgcolor{height:100%;position:relative}.layui-colorpicker-alpha-slider{width:5px;height:100%;box-shadow:0 0 1px #888;box-sizing:border-box;background:#FFF;border-radius:1px;border:1px solid #f0f0f0;cursor:pointer;position:absolute;top:0}.layui-colorpicker-main-pre{padding-top:7px;font-size:0}.layui-colorpicker-pre{width:20px;height:20px;border-radius:2px;display:inline-block;margin-left:6px;margin-bottom:7px;cursor:pointer}.layui-colorpicker-pre:nth-child(11n+1){margin-left:0}.layui-colorpicker-pre-isalpha{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.layui-colorpicker-pre.layui-this{box-shadow:0 0 3px 2px rgba(0,0,0,.15)}.layui-colorpicker-pre>div{height:100%;border-radius:2px}.layui-colorpicker-main-input{text-align:right;padding-top:7px}.layui-colorpicker-main-input .layui-btn-container .layui-btn{margin:0 0 0 10px}.layui-colorpicker-main-input div.layui-inline{float:left;margin-right:10px;font-size:14px}.layui-colorpicker-main-input input.layui-input{width:150px;height:30px;color:#666}.layui-slider{height:4px;background:#e2e2e2;border-radius:3px;position:relative;cursor:pointer}.layui-slider-bar{border-radius:3px;position:absolute;height:100%}.layui-slider-step{position:absolute;top:0;width:4px;height:4px;border-radius:50%;background:#FFF;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.layui-slider-wrap{width:36px;height:36px;position:absolute;top:-16px;-webkit-transform:translateX(-50%);transform:translateX(-50%);z-index:10;text-align:center}.layui-slider-wrap-btn{width:12px;height:12px;border-radius:50%;background:#FFF;display:inline-block;vertical-align:middle;cursor:pointer;transition:.3s}.layui-slider-wrap:after{content:"";height:100%;display:inline-block;vertical-align:middle}.layui-slider-wrap-btn.layui-slider-hover,.layui-slider-wrap-btn:hover{transform:scale(1.2)}.layui-slider-wrap-btn.layui-disabled:hover{transform:scale(1)!important}.layui-slider-tips{position:absolute;top:-42px;z-index:66666666;white-space:nowrap;display:none;-webkit-transform:translateX(-50%);transform:translateX(-50%);color:#FFF;background:#000;border-radius:3px;height:25px;line-height:25px;padding:0 10px}.layui-slider-tips:after{content:'';position:absolute;bottom:-12px;left:50%;margin-left:-6px;width:0;height:0;border-width:6px;border-style:solid;border-color:#000 transparent transparent}.layui-slider-input{width:70px;height:32px;border:1px solid #e6e6e6;border-radius:3px;font-size:16px;line-height:32px;position:absolute;right:0;top:-15px}.layui-slider-input-btn{display:none;position:absolute;top:0;right:0;width:20px;height:100%;border-left:1px solid #d2d2d2}.layui-slider-input-btn i{cursor:pointer;position:absolute;right:0;bottom:0;width:20px;height:50%;font-size:12px;line-height:16px;text-align:center;color:#999}.layui-slider-input-btn i:first-child{top:0;border-bottom:1px solid #d2d2d2}.layui-slider-input-txt{height:100%;font-size:14px}.layui-slider-input-txt input{height:100%;border:none}.layui-slider-input-btn i:hover{color:#009688}.layui-slider-vertical{width:4px;margin-left:34px}.layui-slider-vertical .layui-slider-bar{width:4px}.layui-slider-vertical .layui-slider-step{top:auto;left:0;-webkit-transform:translateY(50%);transform:translateY(50%)}.layui-slider-vertical .layui-slider-wrap{top:auto;left:-16px;-webkit-transform:translateY(50%);transform:translateY(50%)}.layui-slider-vertical .layui-slider-tips{top:auto;left:2px}@media \0screen{.layui-slider-wrap-btn{margin-left:-20px}.layui-slider-vertical .layui-slider-wrap-btn{margin-left:0;margin-bottom:-20px}.layui-slider-vertical .layui-slider-tips{margin-left:-8px}.layui-slider>span{margin-left:8px}}.layui-tree{line-height:22px}.layui-tree .layui-form-checkbox{margin:0!important}.layui-tree-set{width:100%;position:relative}.layui-tree-pack{display:none;padding-left:20px;position:relative}.layui-tree-iconClick,.layui-tree-main{display:inline-block;vertical-align:middle}.layui-tree-line .layui-tree-pack{padding-left:27px}.layui-tree-line .layui-tree-set .layui-tree-set:after{content:'';position:absolute;top:14px;left:-9px;width:17px;height:0;border-top:1px dotted #c0c4cc}.layui-tree-entry{position:relative;padding:3px 0;height:20px;white-space:nowrap}.layui-tree-entry:hover{background-color:#eee}.layui-tree-line .layui-tree-entry:hover{background-color:rgba(0,0,0,0)}.layui-tree-line .layui-tree-entry:hover .layui-tree-txt{color:#999;text-decoration:underline;transition:.3s}.layui-tree-main{cursor:pointer;padding-right:10px}.layui-tree-line .layui-tree-set:before{content:'';position:absolute;top:0;left:-9px;width:0;height:100%;border-left:1px dotted #c0c4cc}.layui-tree-line .layui-tree-set.layui-tree-setLineShort:before{height:13px}.layui-tree-line .layui-tree-set.layui-tree-setHide:before{height:0}.layui-tree-iconClick{position:relative;height:20px;line-height:20px;margin:0 10px;color:#c0c4cc}.layui-tree-icon{height:12px;line-height:12px;width:12px;text-align:center;border:1px solid #c0c4cc}.layui-tree-iconClick .layui-icon{font-size:18px}.layui-tree-icon .layui-icon{font-size:12px;color:#666}.layui-tree-iconArrow{padding:0 5px}.layui-tree-iconArrow:after{content:'';position:absolute;left:4px;top:3px;z-index:100;width:0;height:0;border-width:5px;border-style:solid;border-color:transparent transparent transparent #c0c4cc;transition:.5s}.layui-tree-btnGroup,.layui-tree-editInput{position:relative;vertical-align:middle;display:inline-block}.layui-tree-spread>.layui-tree-entry>.layui-tree-iconClick>.layui-tree-iconArrow:after{transform:rotate(90deg) translate(3px,4px)}.layui-tree-txt{display:inline-block;vertical-align:middle;color:#555}.layui-tree-search{margin-bottom:15px;color:#666}.layui-tree-btnGroup .layui-icon{display:inline-block;vertical-align:middle;padding:0 2px;cursor:pointer}.layui-tree-btnGroup .layui-icon:hover{color:#999;transition:.3s}.layui-tree-entry:hover .layui-tree-btnGroup{visibility:visible}.layui-tree-editInput{height:20px;line-height:20px;padding:0 3px;border:none;background-color:rgba(0,0,0,.05)}.layui-tree-emptyText{text-align:center;color:#999}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout} \ No newline at end of file + .layui-inline,img{display:inline-block;vertical-align:middle}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-body,.layui-edge,.layui-elip{overflow:hidden}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-elip,.layui-form-checkbox span,.layui-form-pane .layui-form-label{text-overflow:ellipsis;white-space:nowrap}.layui-breadcrumb,.layui-tree-btnGroup{visibility:hidden}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}li{list-style:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=250);src:url(../font/iconfont.eot?v=250#iefix) format('embedded-opentype'),url(../font/iconfont.woff2?v=250) format('woff2'),url(../font/iconfont.woff?v=250) format('woff'),url(../font/iconfont.ttf?v=250) format('truetype'),url(../font/iconfont.svg?v=250#layui-icon) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-reply-fill:before{content:"\e611"}.layui-icon-set-fill:before{content:"\e614"}.layui-icon-menu-fill:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-share:before{content:"\e641"}.layui-icon-set-sm:before{content:"\e620"}.layui-icon-engine:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-chart-screen:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-circle-dot:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-release:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-chart:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-layim-theme:before{content:"\e61b"}.layui-icon-table:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-face-cry:before{content:"\e69c"}.layui-icon-face-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-upload-circle:before{content:"\e62f"}.layui-icon-add-circle:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-templeate-1:before{content:"\e630"}.layui-icon-util:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-edit:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-file:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-rate-half:before{content:"\e6c9"}.layui-icon-add-circle-fine:before{content:"\e608"}.layui-icon-prev-circle:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-carousel:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-code-circle:before{content:"\e635"}.layui-icon-water:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-date:before{content:"\e637"}.layui-icon-layim-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-friends:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-ok:before{content:"\e605"}.layui-icon-layer:before{content:"\e638"}.layui-icon-face-smile-fine:before{content:"\e60c"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-group:before{content:"\e613"}.layui-icon-layim-download:before{content:"\e61e"}.layui-icon-picture-fine:before{content:"\e60d"}.layui-icon-link:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-log:before{content:"\e60e"}.layui-icon-rate-solid:before{content:"\e67a"}.layui-icon-fonts-del:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-fonts-clear:before{content:"\e639"}.layui-icon-triangle-r:before{content:"\e623"}.layui-icon-circle:before{content:"\e63f"}.layui-icon-radio:before{content:"\e643"}.layui-icon-align-center:before{content:"\e647"}.layui-icon-align-right:before{content:"\e648"}.layui-icon-align-left:before{content:"\e649"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-fonts-strong:before{content:"\e62b"}.layui-icon-upload:before{content:"\e67c"}.layui-icon-dialogue:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-cellphone-fine:before{content:"\e63b"}.layui-icon-add-1:before{content:"\e654"}.layui-icon-face-smile-b:before{content:"\e650"}.layui-icon-fonts-html:before{content:"\e64b"}.layui-icon-form:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-tabs:before{content:"\e62a"}.layui-icon-fonts-code:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-fonts-u:before{content:"\e646"}.layui-icon-triangle-d:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-picture:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-flag:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-fonts-i:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-upload-drag:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-file-b:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-site:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-female:before{content:"\e661"}.layui-icon-male:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-rate:before{content:"\e67b"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-icon-screen-full:before{content:"\e622"}.layui-icon-screen-restore:before{content:"\e758"}.layui-icon-cols:before{content:"\e610"}.layui-icon-export:before{content:"\e67d"}.layui-icon-print:before{content:"\e66d"}.layui-icon-slider:before{content:"\e714"}.layui-icon-addition:before{content:"\e624"}.layui-icon-subtraction:before{content:"\e67e"}.layui-icon-service:before{content:"\e626"}.layui-icon-transfer:before{content:"\e691"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{position:fixed;top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-appearance:none;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{position:relative;height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{position:relative;padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-auxiliar-moving{position:fixed;left:0;right:0;top:0;bottom:0;width:100%;height:100%;background:0 0;z-index:9999999999}.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-checked{background-color:#5FB878}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2;-webkit-transition:.5s all;transition:.5s all}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:30px;margin-right:10px;padding-right:30px;cursor:pointer;font-size:0;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;border-radius:2px 0 0 2px;background-color:#d2d2d2;color:#fff;overflow:hidden}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;top:0;width:30px;height:28px;border:1px solid #d2d2d2;border-left:none;border-radius:0 2px 2px 0;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{border-color:#c2c2c2;color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;min-width:18px;min-height:18px;border:none!important;margin-right:0;padding-left:28px;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{padding-left:0;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{right:auto;left:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878!important;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;min-width:35px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:relative;top:0;width:25px;margin-left:21px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-checkbox-disbaled,.layui-checkbox-disbaled i{border-color:#e2e2e2!important}.layui-form-onswitch i{left:100%;margin-left:-21px;background-color:#fff}.layui-form-onswitch em{margin-left:5px;margin-right:21px;color:#fff!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-flow-more a *,.layui-laypage input,.layui-table-view select[lay-ignore]{display:inline-block}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh{vertical-align:top}.layui-laypage .layui-laypage-refresh i{font-size:18px;cursor:pointer}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:100%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table-total,.layui-table-total tr,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-col-set,.layui-table-fixed-r,.layui-table-grid-down,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-total,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box{position:relative;overflow:hidden}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table th.layui-unselect .layui-table-cell span{cursor:pointer}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-table td[data-edit=text]{cursor:text}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-view .layui-form-radio{line-height:0;padding:0}.layui-table-view .layui-form-radio>i{margin:0;font-size:20px}.layui-table-init{position:absolute;left:0;top:0;width:100%;height:100%;text-align:center;z-index:110}.layui-table-init .layui-icon{position:absolute;left:50%;top:50%;margin:-15px 0 0 -15px;font-size:30px;color:#c2c2c2}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-header .layui-table{margin-bottom:-1px}.layui-table-tool .layui-inline[lay-event]{position:relative;width:26px;height:26px;padding:5px;line-height:16px;margin-right:10px;text-align:center;color:#333;border:1px solid #ccc;cursor:pointer;-webkit-transition:.5s all;transition:.5s all}.layui-table-tool .layui-inline[lay-event]:hover{border:1px solid #999}.layui-table-tool-temp{padding-right:120px}.layui-table-tool-self{position:absolute;right:17px;top:10px}.layui-table-tool .layui-table-tool-self .layui-inline[lay-event]{margin:0 0 0 10px}.layui-table-tool-panel{position:absolute;top:29px;left:-1px;padding:5px 0;min-width:150px;min-height:40px;border:1px solid #d2d2d2;text-align:left;overflow-y:auto;background-color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.12)}.layui-table-cell,.layui-table-tool-panel li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.layui-table-tool-panel li{padding:0 10px;line-height:30px;-webkit-transition:.5s all;transition:.5s all}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary]{width:100%;padding-left:28px}.layui-table-tool-panel li:hover{background-color:#f2f2f2}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary] i{position:absolute;left:0;top:0}.layui-table-tool-panel li .layui-form-checkbox[lay-skin=primary] span{padding:0}.layui-table-tool .layui-table-tool-self .layui-table-tool-panel{left:auto;right:-1px}.layui-table-col-set{position:absolute;right:0;top:0;width:20px;height:100%;border-width:0 0 0 1px;background-color:#fff}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:3px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:5px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary]{top:-1px;padding:0}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-radio,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px;margin-bottom:-1px}.layui-table-body .layui-none{line-height:26px;padding:15px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0;z-index:101}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;z-index:890;width:100%;min-height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-tool .layui-btn-container{margin-bottom:-10px}.layui-table-page,.layui-table-total{border-width:1px 0 0;margin-bottom:-1px;overflow:hidden}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;height:41px;font-size:12px;white-space:nowrap}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}.layui-table-view .layui-form-checkbox i{height:26px}.layui-table-grid .layui-table-cell{overflow:visible}.layui-table-grid-down{position:absolute;top:0;right:0;width:26px;height:100%;padding:5px 0;border-width:0 0 0 1px;text-align:center;background-color:#fff;color:#999;cursor:pointer}.layui-table-grid-down .layui-icon{position:absolute;top:50%;left:50%;margin:-8px 0 0 -8px}.layui-table-grid-down:hover{background-color:#fbfbfb}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.12)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#666}.layui-table-tips-c{position:absolute;right:-3px;top:-13px;width:20px;height:20px;padding:3px;cursor:pointer;background-color:#666;border-radius:50%;color:#fff}.layui-table-tips-c:hover{background-color:#777}.layui-table-tips-c:before{position:relative;right:-2px}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-list{margin:10px 0}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-transfer-active,.layui-transfer-box{display:inline-block;vertical-align:middle}.layui-transfer-box,.layui-transfer-header,.layui-transfer-search{border-width:0;border-style:solid;border-color:#e6e6e6}.layui-transfer-box{position:relative;border-width:1px;width:200px;height:360px;border-radius:2px;background-color:#fff}.layui-transfer-box .layui-form-checkbox{width:100%;margin:0!important}.layui-transfer-header{height:38px;line-height:38px;padding:0 10px;border-bottom-width:1px}.layui-transfer-search{position:relative;padding:10px;border-bottom-width:1px}.layui-transfer-search .layui-input{height:32px;padding-left:30px;font-size:12px}.layui-transfer-search .layui-icon-search{position:absolute;left:20px;top:50%;margin-top:-8px;color:#666}.layui-transfer-active{margin:0 15px}.layui-transfer-active .layui-btn{display:block;margin:0;padding:0 15px;background-color:#5FB878;border-color:#5FB878;color:#fff}.layui-transfer-active .layui-btn-disabled{background-color:#FBFBFB;border-color:#e6e6e6;color:#C9C9C9}.layui-transfer-active .layui-btn:first-child{margin-bottom:15px}.layui-transfer-active .layui-btn .layui-icon{margin:0;font-size:14px!important}.layui-transfer-data{padding:5px 0;overflow:auto}.layui-transfer-data li{height:32px;line-height:32px;padding:0 10px}.layui-transfer-data li:hover{background-color:#f2f2f2;transition:.5s all}.layui-transfer-data .layui-none{padding:15px 10px;text-align:center;color:#999}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:999999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-rate,.layui-rate *{display:inline-block;vertical-align:middle}.layui-rate{padding:10px 5px 10px 0;font-size:0}.layui-rate li i.layui-icon{font-size:20px;color:#FFB800;margin-right:5px;transition:all .3s;-webkit-transition:all .3s}.layui-rate li i:hover{cursor:pointer;transform:scale(1.12);-webkit-transform:scale(1.12)}.layui-rate[readonly] li i:hover{cursor:default;transform:scale(1)}.layui-colorpicker{width:26px;height:26px;border:1px solid #e6e6e6;padding:5px;border-radius:2px;line-height:24px;display:inline-block;cursor:pointer;transition:all .3s;-webkit-transition:all .3s}.layui-colorpicker:hover{border-color:#d2d2d2}.layui-colorpicker.layui-colorpicker-lg{width:34px;height:34px;line-height:32px}.layui-colorpicker.layui-colorpicker-sm{width:24px;height:24px;line-height:22px}.layui-colorpicker.layui-colorpicker-xs{width:22px;height:22px;line-height:20px}.layui-colorpicker-trigger-bgcolor{display:block;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);border-radius:2px}.layui-colorpicker-trigger-span{display:block;height:100%;box-sizing:border-box;border:1px solid rgba(0,0,0,.15);border-radius:2px;text-align:center}.layui-colorpicker-trigger-i{display:inline-block;color:#FFF;font-size:12px}.layui-colorpicker-trigger-i.layui-icon-close{color:#999}.layui-colorpicker-main{position:absolute;z-index:66666666;width:280px;padding:7px;background:#FFF;border:1px solid #d2d2d2;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12)}.layui-colorpicker-main-wrapper{height:180px;position:relative}.layui-colorpicker-basis{width:260px;height:100%;position:relative}.layui-colorpicker-basis-white{width:100%;height:100%;position:absolute;top:0;left:0;background:linear-gradient(90deg,#FFF,hsla(0,0%,100%,0))}.layui-colorpicker-basis-black{width:100%;height:100%;position:absolute;top:0;left:0;background:linear-gradient(0deg,#000,transparent)}.layui-colorpicker-basis-cursor{width:10px;height:10px;border:1px solid #FFF;border-radius:50%;position:absolute;top:-3px;right:-3px;cursor:pointer}.layui-colorpicker-side{position:absolute;top:0;right:0;width:12px;height:100%;background:linear-gradient(red,#FF0,#0F0,#0FF,#00F,#F0F,red)}.layui-colorpicker-side-slider{width:100%;height:5px;box-shadow:0 0 1px #888;box-sizing:border-box;background:#FFF;border-radius:1px;border:1px solid #f0f0f0;cursor:pointer;position:absolute;left:0}.layui-colorpicker-main-alpha{display:none;height:12px;margin-top:7px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.layui-colorpicker-alpha-bgcolor{height:100%;position:relative}.layui-colorpicker-alpha-slider{width:5px;height:100%;box-shadow:0 0 1px #888;box-sizing:border-box;background:#FFF;border-radius:1px;border:1px solid #f0f0f0;cursor:pointer;position:absolute;top:0}.layui-colorpicker-main-pre{padding-top:7px;font-size:0}.layui-colorpicker-pre{width:20px;height:20px;border-radius:2px;display:inline-block;margin-left:6px;margin-bottom:7px;cursor:pointer}.layui-colorpicker-pre:nth-child(11n+1){margin-left:0}.layui-colorpicker-pre-isalpha{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.layui-colorpicker-pre.layui-this{box-shadow:0 0 3px 2px rgba(0,0,0,.15)}.layui-colorpicker-pre>div{height:100%;border-radius:2px}.layui-colorpicker-main-input{text-align:right;padding-top:7px}.layui-colorpicker-main-input .layui-btn-container .layui-btn{margin:0 0 0 10px}.layui-colorpicker-main-input div.layui-inline{float:left;margin-right:10px;font-size:14px}.layui-colorpicker-main-input input.layui-input{width:150px;height:30px;color:#666}.layui-slider{height:4px;background:#e2e2e2;border-radius:3px;position:relative;cursor:pointer}.layui-slider-bar{border-radius:3px;position:absolute;height:100%}.layui-slider-step{position:absolute;top:0;width:4px;height:4px;border-radius:50%;background:#FFF;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.layui-slider-wrap{width:36px;height:36px;position:absolute;top:-16px;-webkit-transform:translateX(-50%);transform:translateX(-50%);z-index:10;text-align:center}.layui-slider-wrap-btn{width:12px;height:12px;border-radius:50%;background:#FFF;display:inline-block;vertical-align:middle;cursor:pointer;transition:.3s}.layui-slider-wrap:after{content:"";height:100%;display:inline-block;vertical-align:middle}.layui-slider-wrap-btn.layui-slider-hover,.layui-slider-wrap-btn:hover{transform:scale(1.2)}.layui-slider-wrap-btn.layui-disabled:hover{transform:scale(1)!important}.layui-slider-tips{position:absolute;top:-42px;z-index:66666666;white-space:nowrap;display:none;-webkit-transform:translateX(-50%);transform:translateX(-50%);color:#FFF;background:#000;border-radius:3px;height:25px;line-height:25px;padding:0 10px}.layui-slider-tips:after{content:'';position:absolute;bottom:-12px;left:50%;margin-left:-6px;width:0;height:0;border-width:6px;border-style:solid;border-color:#000 transparent transparent}.layui-slider-input{width:70px;height:32px;border:1px solid #e6e6e6;border-radius:3px;font-size:16px;line-height:32px;position:absolute;right:0;top:-15px}.layui-slider-input-btn{display:none;position:absolute;top:0;right:0;width:20px;height:100%;border-left:1px solid #d2d2d2}.layui-slider-input-btn i{cursor:pointer;position:absolute;right:0;bottom:0;width:20px;height:50%;font-size:12px;line-height:16px;text-align:center;color:#999}.layui-slider-input-btn i:first-child{top:0;border-bottom:1px solid #d2d2d2}.layui-slider-input-txt{height:100%;font-size:14px}.layui-slider-input-txt input{height:100%;border:none}.layui-slider-input-btn i:hover{color:#009688}.layui-slider-vertical{width:4px;margin-left:34px}.layui-slider-vertical .layui-slider-bar{width:4px}.layui-slider-vertical .layui-slider-step{top:auto;left:0;-webkit-transform:translateY(50%);transform:translateY(50%)}.layui-slider-vertical .layui-slider-wrap{top:auto;left:-16px;-webkit-transform:translateY(50%);transform:translateY(50%)}.layui-slider-vertical .layui-slider-tips{top:auto;left:2px}@media \0screen{.layui-slider-wrap-btn{margin-left:-20px}.layui-slider-vertical .layui-slider-wrap-btn{margin-left:0;margin-bottom:-20px}.layui-slider-vertical .layui-slider-tips{margin-left:-8px}.layui-slider>span{margin-left:8px}}.layui-tree{line-height:22px}.layui-tree .layui-form-checkbox{margin:0!important}.layui-tree-set{width:100%;position:relative}.layui-tree-pack{display:none;padding-left:20px;position:relative}.layui-tree-iconClick,.layui-tree-main{display:inline-block;vertical-align:middle}.layui-tree-line .layui-tree-pack{padding-left:27px}.layui-tree-line .layui-tree-set .layui-tree-set:after{content:'';position:absolute;top:14px;left:-9px;width:17px;height:0;border-top:1px dotted #c0c4cc}.layui-tree-entry{position:relative;padding:3px 0;height:20px;white-space:nowrap}.layui-tree-entry:hover{background-color:#eee}.layui-tree-line .layui-tree-entry:hover{background-color:rgba(0,0,0,0)}.layui-tree-line .layui-tree-entry:hover .layui-tree-txt{color:#999;text-decoration:underline;transition:.3s}.layui-tree-main{cursor:pointer;padding-right:10px}.layui-tree-line .layui-tree-set:before{content:'';position:absolute;top:0;left:-9px;width:0;height:100%;border-left:1px dotted #c0c4cc}.layui-tree-line .layui-tree-set.layui-tree-setLineShort:before{height:13px}.layui-tree-line .layui-tree-set.layui-tree-setHide:before{height:0}.layui-tree-iconClick{position:relative;height:20px;line-height:20px;margin:0 10px;color:#c0c4cc}.layui-tree-icon{height:12px;line-height:12px;width:12px;text-align:center;border:1px solid #c0c4cc}.layui-tree-iconClick .layui-icon{font-size:18px}.layui-tree-icon .layui-icon{font-size:12px;color:#666}.layui-tree-iconArrow{padding:0 5px}.layui-tree-iconArrow:after{content:'';position:absolute;left:4px;top:3px;z-index:100;width:0;height:0;border-width:5px;border-style:solid;border-color:transparent transparent transparent #c0c4cc;transition:.5s}.layui-tree-btnGroup,.layui-tree-editInput{position:relative;vertical-align:middle;display:inline-block}.layui-tree-spread>.layui-tree-entry>.layui-tree-iconClick>.layui-tree-iconArrow:after{transform:rotate(90deg) translate(3px,4px)}.layui-tree-txt{display:inline-block;vertical-align:middle;color:#555}.layui-tree-search{margin-bottom:15px;color:#666}.layui-tree-btnGroup .layui-icon{display:inline-block;vertical-align:middle;padding:0 2px;cursor:pointer}.layui-tree-btnGroup .layui-icon:hover{color:#999;transition:.3s}.layui-tree-entry:hover .layui-tree-btnGroup{visibility:visible}.layui-tree-editInput{height:20px;line-height:20px;padding:0 3px;border:none;background-color:rgba(0,0,0,.05)}.layui-tree-emptyText{text-align:center;color:#999}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout} \ No newline at end of file diff --git a/public/static/lib/layui/css/dropdown.css b/public/static/lib/layui/extends/dropdown.css similarity index 100% rename from public/static/lib/layui/css/dropdown.css rename to public/static/lib/layui/extends/dropdown.css diff --git a/public/static/lib/layui/font/iconfont.svg b/public/static/lib/layui/font/iconfont.svg index f407df75..6d6cff74 100644 --- a/public/static/lib/layui/font/iconfont.svg +++ b/public/static/lib/layui/font/iconfont.svg @@ -391,8 +391,10 @@ Created by iconfont - - + + diff --git a/scheduler.php b/scheduler.php index 3793c6f6..f78d97df 100644 --- a/scheduler.php +++ b/scheduler.php @@ -13,27 +13,36 @@ $scheduler->php($script, $bin, ['--task' => 'vod_event', '--action' => 'main']) ->at('*/5 * * * *'); $scheduler->php($script, $bin, ['--task' => 'close_trade', '--action' => 'main']) - ->at('*/15 * * * *'); - -$scheduler->php($script, $bin, ['--task' => 'close_order', '--action' => 'main']) - ->at('* */3 * * *'); + ->at('*/13 * * * *'); $scheduler->php($script, $bin, ['--task' => 'learning', '--action' => 'main']) - ->at('*/10 * * * *'); + ->at('*/7 * * * *'); -$scheduler->php($script, $bin, ['--task' => 'refund', '--action' => 'main']) +$scheduler->php($script, $bin, ['--task' => 'close_order', '--action' => 'main']) ->hourly(3); -$scheduler->php($script, $bin, ['--task' => 'sync_index', '--action' => 'main']) - ->hourly(13); +$scheduler->php($script, $bin, ['--task' => 'refund', '--action' => 'main']) + ->hourly(7); + +$scheduler->php($script, $bin, ['--task' => 'rebuild_course_index', '--action' => 'main']) + ->hourly(11); + +$scheduler->php($script, $bin, ['--task' => 'rebuild_course_cache', '--action' => 'main']) + ->hourly(17); + +$scheduler->php($script, $bin, ['--task' => 'rebuild_chapter_cache', '--action' => 'main']) + ->hourly(23); $scheduler->php($script, $bin, ['--task' => 'clean_log', '--action' => 'main']) - ->daily(3, 10); - -$scheduler->php($script, $bin, ['--task' => 'count_course', '--action' => 'main']) - ->daily(3, 20); + ->daily(3, 3); $scheduler->php($script, $bin, ['--task' => 'unlock_user', '--action' => 'main']) - ->daily(3, 30); + ->daily(3, 7); + +$scheduler->php($script, $bin, ['--task' => 'close_slide', '--action' => 'main']) + ->daily(3, 11); + +$scheduler->php($script, $bin, ['--task' => 'count_course', '--action' => 'main']) + ->daily(3, 17); $scheduler->run(); \ No newline at end of file diff --git a/storage/tmp/.gitignore b/storage/tmp/.gitignore deleted file mode 100644 index c96a04f0..00000000 --- a/storage/tmp/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file