From 73a2e21cd6836605637e175f21898a0a1c8314d1 Mon Sep 17 00:00:00 2001 From: dongye Date: Fri, 30 Apr 2021 10:46:01 +0800 Subject: [PATCH 1/5] =?UTF-8?q?!66=20=E4=BF=AE=E5=A4=8DTag=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=94=99=E8=AF=AF=E7=9A=84BUG=20*=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0tag=E6=8E=92=E5=BA=8F=E5=AD=97=E6=AE=B5=E6=9C=AA?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=97=AE=E9=A2=98=20*=20update=20app/Http/Ad?= =?UTF-8?q?min/Views/tag/add.volt.=20=E5=A2=9E=E5=8A=A0=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E9=80=89=E9=A1=B9=EF=BC=8CFix=20=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E6=B7=BB=E5=8A=A0BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Services/Tag.php | 1 + app/Http/Admin/Views/tag/add.volt | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/app/Http/Admin/Services/Tag.php b/app/Http/Admin/Services/Tag.php index b0a75ef5..61a86c5e 100644 --- a/app/Http/Admin/Services/Tag.php +++ b/app/Http/Admin/Services/Tag.php @@ -42,6 +42,7 @@ class Tag extends Service $tag = new TagModel(); $tag->name = $validator->checkName($post['name']); + $tag->priority = $validator->checkPriority($post['priority']); $tag->published = $validator->checkPublishStatus($post['published']); $tag->create(); diff --git a/app/Http/Admin/Views/tag/add.volt b/app/Http/Admin/Views/tag/add.volt index e3cb3cbf..8dc363f1 100644 --- a/app/Http/Admin/Views/tag/add.volt +++ b/app/Http/Admin/Views/tag/add.volt @@ -18,6 +18,13 @@ +
+ +
+ + +
+
From caa34d2675165196c22d2d2347a020877b26d143 Mon Sep 17 00:00:00 2001 From: koogua Date: Fri, 7 May 2021 19:34:31 +0800 Subject: [PATCH 2/5] =?UTF-8?q?v1.3.4=E9=98=B6=E6=AE=B5=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Builders/AnswerList.php | 70 ++ app/Builders/QuestionList.php | 53 + app/Builders/TagFollowList.php | 75 ++ app/Caches/HotQuestionList.php | 114 +++ app/Caches/MaxAnswerId.php | 29 + app/Caches/MaxQuestionId.php | 29 + app/Caches/Question.php | 31 + ...eRelatedList.php => TaggedArticleList.php} | 20 +- app/Caches/TaggedQuestionList.php | 69 ++ app/Caches/TopAnswererList.php | 103 ++ app/Console/Tasks/ArticleIndexTask.php | 2 +- app/Console/Tasks/QuestionIndexTask.php | 125 +++ app/Console/Tasks/SyncArticleIndexTask.php | 3 +- app/Console/Tasks/SyncArticleScoreTask.php | 44 + app/Console/Tasks/SyncCourseScoreTask.php | 6 +- app/Console/Tasks/SyncQuestionIndexTask.php | 61 ++ app/Console/Tasks/SyncQuestionScoreTask.php | 44 + .../Admin/Controllers/UploadController.php | 21 + app/Http/Admin/Services/AuthNode.php | 12 +- app/Http/Admin/Services/Tag.php | 7 +- app/Http/Admin/Views/tag/add.volt | 13 - app/Http/Admin/Views/tag/edit.volt | 20 +- app/Http/Admin/Views/tag/list.volt | 11 +- .../Home/Controllers/AnswerController.php | 126 +++ .../Home/Controllers/ArticleController.php | 25 +- .../Home/Controllers/ChapterController.php | 13 - .../Home/Controllers/CommentController.php | 24 +- .../Home/Controllers/QuestionController.php | 252 +++++ .../Home/Controllers/SearchController.php | 6 +- app/Http/Home/Controllers/TagController.php | 70 ++ .../Controllers/UserConsoleController.php | 28 + .../Home/Controllers/WidgetController.php | 58 ++ app/Http/Home/Services/Answer.php | 24 + app/Http/Home/Services/Question.php | 61 ++ app/Http/Home/Services/QuestionQuery.php | 64 ++ app/Http/Home/Views/answer/add.volt | 34 + app/Http/Home/Views/answer/edit.volt | 33 + app/Http/Home/Views/article/comment.volt | 2 +- app/Http/Home/Views/article/edit.volt | 24 +- app/Http/Home/Views/article/hot_authors.volt | 2 +- app/Http/Home/Views/article/list.volt | 42 +- app/Http/Home/Views/article/pager.volt | 22 +- app/Http/Home/Views/article/related.volt | 2 +- app/Http/Home/Views/article/show.volt | 6 +- app/Http/Home/Views/chapter/comment.volt | 2 +- app/Http/Home/Views/comment/list.volt | 8 +- app/Http/Home/Views/comment/replies.volt | 4 +- app/Http/Home/Views/comment/reply.volt | 19 - app/Http/Home/Views/course/consults.volt | 6 +- app/Http/Home/Views/course/reviews.volt | 2 +- app/Http/Home/Views/macros/answer.volt | 11 + app/Http/Home/Views/macros/question.volt | 11 + app/Http/Home/Views/partials/header.volt | 2 +- app/Http/Home/Views/question/answers.volt | 62 ++ app/Http/Home/Views/question/edit.volt | 71 ++ .../Home/Views/question/hot_questions.volt | 27 + app/Http/Home/Views/question/list.volt | 51 + app/Http/Home/Views/question/pager.volt | 43 + app/Http/Home/Views/question/related.volt | 20 + app/Http/Home/Views/question/show.volt | 108 +++ app/Http/Home/Views/question/sticky.volt | 27 + .../Home/Views/question/top_answerers.volt | 27 + app/Http/Home/Views/search/article.volt | 16 +- app/Http/Home/Views/search/index.volt | 6 +- app/Http/Home/Views/search/question.volt | 25 + app/Http/Home/Views/tag/list.volt | 38 + app/Http/Home/Views/tag/list_pager.volt | 23 + app/Http/Home/Views/tag/my_pager.volt | 21 + app/Http/Home/Views/user/console/answers.volt | 65 ++ app/Http/Home/Views/user/console/menu.volt | 16 +- .../Home/Views/user/console/questions.volt | 73 ++ app/Http/Home/Views/widget/my_tags.volt | 20 + app/Library/Helper.php | 44 + app/Listeners/Answer.php | 41 + app/Listeners/Question.php | 66 ++ app/Listeners/UserDailyCounter.php | 10 + app/Models/Answer.php | 181 ++++ app/Models/AnswerLike.php | 46 + app/Models/Article.php | 48 +- app/Models/Category.php | 2 + app/Models/Comment.php | 7 + app/Models/Consult.php | 7 + app/Models/Course.php | 2 - app/Models/Nav.php | 2 +- app/Models/PointHistory.php | 1 + app/Models/Question.php | 310 ++++++ app/Models/QuestionFavorite.php | 46 + app/Models/QuestionLike.php | 46 + app/Models/QuestionTag.php | 46 + app/Models/Report.php | 100 ++ app/Models/Review.php | 7 + app/Models/Tag.php | 27 + app/Models/TagFollow.php | 46 + app/Models/User.php | 14 + app/Repos/Answer.php | 104 ++ app/Repos/AnswerLike.php | 37 + app/Repos/Question.php | 202 ++++ app/Repos/QuestionFavorite.php | 58 ++ app/Repos/QuestionLike.php | 24 + app/Repos/QuestionTag.php | 48 + app/Repos/Tag.php | 4 +- app/Repos/TagFollow.php | 59 ++ app/Repos/User.php | 18 + app/Services/CourseStat.php | 86 -- app/Services/Logic/Answer/AnswerAccept.php | 62 ++ app/Services/Logic/Answer/AnswerCreate.php | 97 ++ app/Services/Logic/Answer/AnswerDelete.php | 66 ++ app/Services/Logic/Answer/AnswerInfo.php | 68 ++ app/Services/Logic/Answer/AnswerLike.php | 96 ++ app/Services/Logic/Answer/AnswerUpdate.php | 52 + app/Services/Logic/Answer/CommentList.php | 41 + app/Services/Logic/AnswerTrait.php | 17 + app/Services/Logic/Article/ArticleList.php | 11 +- .../Logic/Article/RelatedArticleList.php | 33 + app/Services/Logic/Article/RelatedList.php | 20 - app/Services/Logic/Course/CourseList.php | 4 + .../Logic/Notice/System/AnswerAccepted.php | 33 + .../Logic/Notice/System/AnswerLiked.php | 33 + .../Logic/Notice/System/QuestionFavorited.php | 28 + .../Logic/Notice/System/QuestionLiked.php | 28 + .../Logic/Point/History/AnswerAccept.php | 69 ++ .../Logic/Point/History/AnswerPost.php | 80 ++ .../Logic/Point/History/QuestionPost.php | 75 ++ app/Services/Logic/Question/AnswerList.php | 108 +++ app/Services/Logic/Question/CommentList.php | 41 + .../Logic/Question/HotQuestionList.php | 20 + .../Logic/Question/QuestionCreate.php | 46 + .../Logic/Question/QuestionDataTrait.php | 100 ++ .../Logic/Question/QuestionDelete.php | 36 + .../Logic/Question/QuestionFavorite.php | 108 +++ app/Services/Logic/Question/QuestionInfo.php | 150 +++ app/Services/Logic/Question/QuestionLike.php | 96 ++ app/Services/Logic/Question/QuestionList.php | 115 +++ .../Logic/Question/QuestionUpdate.php | 58 ++ .../Logic/Question/RelatedQuestionList.php | 33 + .../Logic/Question/TopAnswererList.php | 20 + app/Services/Logic/QuestionTrait.php | 24 + app/Services/Logic/Search/Article.php | 5 +- app/Services/Logic/Search/Question.php | 93 ++ app/Services/Logic/Tag/FollowList.php | 58 ++ app/Services/Logic/Tag/TagFollow.php | 69 ++ app/Services/Logic/Tag/TagList.php | 89 ++ app/Services/Logic/TagTrait.php | 24 + app/Services/Logic/User/ArticleList.php | 2 +- .../Logic/User/Console/AnswerList.php | 77 ++ .../Logic/User/Console/ArticleList.php | 2 +- .../Logic/User/Console/QuestionList.php | 42 + app/Services/MyStorage.php | 26 +- app/Services/Search/ArticleDocument.php | 6 +- app/Services/Search/CourseDocument.php | 4 +- app/Services/Search/GroupDocument.php | 4 +- app/Services/Search/QuestionDocument.php | 123 +++ app/Services/Search/QuestionSearcher.php | 25 + app/Services/Storage.php | 4 +- app/Services/Sync/ArticleScore.php | 33 + app/Services/Sync/QuestionIndex.php | 33 + app/Services/Sync/QuestionScore.php | 33 + app/Services/Utils/ArticleScore.php | 65 ++ app/Services/Utils/CourseScore.php | 94 ++ app/Services/Utils/QuestionScore.php | 65 ++ app/Validators/Answer.php | 100 ++ app/Validators/ArticleQuery.php | 4 +- app/Validators/Consult.php | 4 +- app/Validators/Question.php | 130 +++ app/Validators/QuestionQuery.php | 36 + app/Validators/Review.php | 2 +- app/Validators/Tag.php | 12 + app/Validators/UserLimit.php | 30 +- config/errors.php | 22 +- config/events.php | 4 + config/xs.question.default.ini | 75 ++ config/xs.question.ini | 75 ++ .../20200901121917_insert_role_data.php | 8 +- db/migrations/20210430023157.php | 916 ++++++++++++++++++ public/static/admin/css/common.css | 8 +- public/static/admin/js/avatar.upload.js | 1 - public/static/admin/js/icon.upload.js | 25 + public/static/home/css/common.css | 176 +++- public/static/home/js/answer.js | 106 ++ public/static/home/js/article.edit.js | 25 +- public/static/home/js/article.list.js | 16 +- public/static/home/js/article.show.js | 6 +- public/static/home/js/comment.js | 4 +- public/static/home/js/question.edit.js | 20 + public/static/home/js/question.list.js | 27 + public/static/home/js/question.share.js | 27 + public/static/home/js/question.show.js | 83 ++ public/static/home/js/tag.list.js | 49 + public/static/home/js/vditor.js | 3 +- scheduler.php | 14 +- 190 files changed, 8820 insertions(+), 455 deletions(-) create mode 100644 app/Builders/AnswerList.php create mode 100644 app/Builders/QuestionList.php create mode 100644 app/Builders/TagFollowList.php create mode 100644 app/Caches/HotQuestionList.php create mode 100644 app/Caches/MaxAnswerId.php create mode 100644 app/Caches/MaxQuestionId.php create mode 100644 app/Caches/Question.php rename app/Caches/{ArticleRelatedList.php => TaggedArticleList.php} (75%) create mode 100644 app/Caches/TaggedQuestionList.php create mode 100644 app/Caches/TopAnswererList.php create mode 100644 app/Console/Tasks/QuestionIndexTask.php create mode 100644 app/Console/Tasks/SyncArticleScoreTask.php create mode 100644 app/Console/Tasks/SyncQuestionIndexTask.php create mode 100644 app/Console/Tasks/SyncQuestionScoreTask.php create mode 100644 app/Http/Home/Controllers/AnswerController.php create mode 100644 app/Http/Home/Controllers/QuestionController.php create mode 100644 app/Http/Home/Controllers/TagController.php create mode 100644 app/Http/Home/Controllers/WidgetController.php create mode 100644 app/Http/Home/Services/Answer.php create mode 100644 app/Http/Home/Services/Question.php create mode 100644 app/Http/Home/Services/QuestionQuery.php create mode 100644 app/Http/Home/Views/answer/add.volt create mode 100644 app/Http/Home/Views/answer/edit.volt delete mode 100644 app/Http/Home/Views/comment/reply.volt create mode 100644 app/Http/Home/Views/macros/answer.volt create mode 100644 app/Http/Home/Views/macros/question.volt create mode 100644 app/Http/Home/Views/question/answers.volt create mode 100644 app/Http/Home/Views/question/edit.volt create mode 100644 app/Http/Home/Views/question/hot_questions.volt create mode 100644 app/Http/Home/Views/question/list.volt create mode 100644 app/Http/Home/Views/question/pager.volt create mode 100644 app/Http/Home/Views/question/related.volt create mode 100644 app/Http/Home/Views/question/show.volt create mode 100644 app/Http/Home/Views/question/sticky.volt create mode 100644 app/Http/Home/Views/question/top_answerers.volt create mode 100644 app/Http/Home/Views/search/question.volt create mode 100644 app/Http/Home/Views/tag/list.volt create mode 100644 app/Http/Home/Views/tag/list_pager.volt create mode 100644 app/Http/Home/Views/tag/my_pager.volt create mode 100644 app/Http/Home/Views/user/console/answers.volt create mode 100644 app/Http/Home/Views/user/console/questions.volt create mode 100644 app/Http/Home/Views/widget/my_tags.volt create mode 100644 app/Listeners/Answer.php create mode 100644 app/Listeners/Question.php create mode 100644 app/Models/Answer.php create mode 100644 app/Models/AnswerLike.php create mode 100644 app/Models/Question.php create mode 100644 app/Models/QuestionFavorite.php create mode 100644 app/Models/QuestionLike.php create mode 100644 app/Models/QuestionTag.php create mode 100644 app/Models/Report.php create mode 100644 app/Models/TagFollow.php create mode 100644 app/Repos/Answer.php create mode 100644 app/Repos/AnswerLike.php create mode 100644 app/Repos/Question.php create mode 100644 app/Repos/QuestionFavorite.php create mode 100644 app/Repos/QuestionLike.php create mode 100644 app/Repos/QuestionTag.php create mode 100644 app/Repos/TagFollow.php create mode 100644 app/Services/Logic/Answer/AnswerAccept.php create mode 100644 app/Services/Logic/Answer/AnswerCreate.php create mode 100644 app/Services/Logic/Answer/AnswerDelete.php create mode 100644 app/Services/Logic/Answer/AnswerInfo.php create mode 100644 app/Services/Logic/Answer/AnswerLike.php create mode 100644 app/Services/Logic/Answer/AnswerUpdate.php create mode 100644 app/Services/Logic/Answer/CommentList.php create mode 100644 app/Services/Logic/AnswerTrait.php create mode 100644 app/Services/Logic/Article/RelatedArticleList.php delete mode 100644 app/Services/Logic/Article/RelatedList.php create mode 100644 app/Services/Logic/Notice/System/AnswerAccepted.php create mode 100644 app/Services/Logic/Notice/System/AnswerLiked.php create mode 100644 app/Services/Logic/Notice/System/QuestionFavorited.php create mode 100644 app/Services/Logic/Notice/System/QuestionLiked.php create mode 100644 app/Services/Logic/Point/History/AnswerAccept.php create mode 100644 app/Services/Logic/Point/History/AnswerPost.php create mode 100644 app/Services/Logic/Point/History/QuestionPost.php create mode 100644 app/Services/Logic/Question/AnswerList.php create mode 100644 app/Services/Logic/Question/CommentList.php create mode 100644 app/Services/Logic/Question/HotQuestionList.php create mode 100644 app/Services/Logic/Question/QuestionCreate.php create mode 100644 app/Services/Logic/Question/QuestionDataTrait.php create mode 100644 app/Services/Logic/Question/QuestionDelete.php create mode 100644 app/Services/Logic/Question/QuestionFavorite.php create mode 100644 app/Services/Logic/Question/QuestionInfo.php create mode 100644 app/Services/Logic/Question/QuestionLike.php create mode 100644 app/Services/Logic/Question/QuestionList.php create mode 100644 app/Services/Logic/Question/QuestionUpdate.php create mode 100644 app/Services/Logic/Question/RelatedQuestionList.php create mode 100644 app/Services/Logic/Question/TopAnswererList.php create mode 100644 app/Services/Logic/QuestionTrait.php create mode 100644 app/Services/Logic/Search/Question.php create mode 100644 app/Services/Logic/Tag/FollowList.php create mode 100644 app/Services/Logic/Tag/TagFollow.php create mode 100644 app/Services/Logic/Tag/TagList.php create mode 100644 app/Services/Logic/TagTrait.php create mode 100644 app/Services/Logic/User/Console/AnswerList.php create mode 100644 app/Services/Logic/User/Console/QuestionList.php create mode 100644 app/Services/Search/QuestionDocument.php create mode 100644 app/Services/Search/QuestionSearcher.php create mode 100644 app/Services/Sync/ArticleScore.php create mode 100644 app/Services/Sync/QuestionIndex.php create mode 100644 app/Services/Sync/QuestionScore.php create mode 100644 app/Services/Utils/ArticleScore.php create mode 100644 app/Services/Utils/CourseScore.php create mode 100644 app/Services/Utils/QuestionScore.php create mode 100644 app/Validators/Answer.php create mode 100644 app/Validators/Question.php create mode 100644 app/Validators/QuestionQuery.php create mode 100644 config/xs.question.default.ini create mode 100644 config/xs.question.ini create mode 100644 db/migrations/20210430023157.php create mode 100644 public/static/admin/js/icon.upload.js create mode 100644 public/static/home/js/answer.js create mode 100644 public/static/home/js/question.edit.js create mode 100644 public/static/home/js/question.list.js create mode 100644 public/static/home/js/question.share.js create mode 100644 public/static/home/js/question.show.js create mode 100644 public/static/home/js/tag.list.js diff --git a/app/Builders/AnswerList.php b/app/Builders/AnswerList.php new file mode 100644 index 00000000..4633f7b6 --- /dev/null +++ b/app/Builders/AnswerList.php @@ -0,0 +1,70 @@ +getQuestions($answers); + + foreach ($answers as $key => $answer) { + $answers[$key]['question'] = $questions[$answer['question_id']] ?? new \stdClass(); + } + + return $answers; + } + + public function handleUsers(array $answers) + { + $users = $this->getUsers($answers); + + foreach ($answers as $key => $answer) { + $answers[$key]['owner'] = $users[$answer['owner_id']] ?? new \stdClass(); + } + + return $answers; + } + + public function getQuestions(array $answers) + { + $ids = kg_array_column($answers, 'question_id'); + + $questionRepo = new QuestionRepo(); + + $questions = $questionRepo->findByIds($ids, ['id', 'title']); + + $result = []; + + foreach ($questions->toArray() as $question) { + $result[$question['id']] = $question; + } + + return $result; + } + + public function getUsers(array $answers) + { + $ids = kg_array_column($answers, 'owner_id'); + + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($users->toArray() as $user) { + $user['avatar'] = $baseUrl . $user['avatar']; + $result[$user['id']] = $user; + } + + return $result; + } + +} diff --git a/app/Builders/QuestionList.php b/app/Builders/QuestionList.php new file mode 100644 index 00000000..95299dfb --- /dev/null +++ b/app/Builders/QuestionList.php @@ -0,0 +1,53 @@ + $question) { + $questions[$key]['tags'] = json_decode($question['tags'], true); + } + + return $questions; + } + + public function handleUsers(array $questions) + { + $users = $this->getUsers($questions); + + foreach ($questions as $key => $question) { + $questions[$key]['owner'] = $users[$question['owner_id']] ?? new \stdClass(); + $questions[$key]['last_replier'] = $users[$question['last_replier_id']] ?? new \stdClass(); + } + + return $questions; + } + + public function getUsers($questions) + { + $ownerIds = kg_array_column($questions, 'owner_id'); + $lastReplierIds = kg_array_column($questions, 'last_replier_id'); + $ids = array_merge($ownerIds, $lastReplierIds); + + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($users->toArray() as $user) { + $user['avatar'] = $baseUrl . $user['avatar']; + $result[$user['id']] = $user; + } + + return $result; + } + +} diff --git a/app/Builders/TagFollowList.php b/app/Builders/TagFollowList.php new file mode 100644 index 00000000..c4b83377 --- /dev/null +++ b/app/Builders/TagFollowList.php @@ -0,0 +1,75 @@ +getTags($relations); + + foreach ($relations as $key => $value) { + $relations[$key]['tag'] = $tags[$value['tag_id']] ?? new \stdClass(); + } + + return $relations; + } + + public function handleUsers(array $relations) + { + $users = $this->getUsers($relations); + + foreach ($relations as $key => $value) { + $relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass(); + } + + return $relations; + } + + public function getTags(array $relations) + { + $ids = kg_array_column($relations, 'tag_id'); + + $tagRepo = new TagRepo(); + + $columns = ['id', 'name', 'alias', 'icon', 'follow_count']; + + $tags = $tagRepo->findByIds($ids, $columns); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($tags->toArray() as $tag) { + $tag['icon'] = $baseUrl . $tag['icon']; + $result[$tag['id']] = $tag; + } + + return $result; + } + + public function getUsers(array $relations) + { + $ids = kg_array_column($relations, 'user_id'); + + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($users->toArray() as $user) { + $user['avatar'] = $baseUrl . $user['avatar']; + $result[$user['id']] = $user; + } + + return $result; + } + +} diff --git a/app/Caches/HotQuestionList.php b/app/Caches/HotQuestionList.php new file mode 100644 index 00000000..22d115af --- /dev/null +++ b/app/Caches/HotQuestionList.php @@ -0,0 +1,114 @@ +findWeeklyHotQuestions(); + + if ($questions->count() > 0) { + return $this->handleQuestions($questions); + } + + $questions = $this->findMonthlyHotQuestions(); + + if ($questions->count() > 0) { + return $this->handleQuestions($questions); + } + + $questions = $this->findYearlyHotQuestions(); + + if ($questions->count() > 0) { + return $this->handleQuestions($questions); + } + + return []; + } + + /** + * @param QuestionModel[] $questions + * @return array + */ + protected function handleQuestions($questions) + { + $result = []; + + foreach ($questions as $question) { + $result[] = [ + 'id' => $question->id, + 'title' => $question->title, + ]; + } + + return $result; + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|QuestionModel[] + */ + protected function findWeeklyHotQuestions($limit = 10) + { + $createTime = strtotime('monday this week'); + + return $this->findHotQuestions($createTime, $limit); + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|QuestionModel[] + */ + protected function findMonthlyHotQuestions($limit = 10) + { + $createTime = strtotime(date('Y-m-01')); + + return $this->findHotQuestions($createTime, $limit); + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset|QuestionModel[] + */ + protected function findYearlyHotQuestions($limit = 10) + { + $createTime = strtotime(date('Y-01-01')); + + return $this->findHotQuestions($createTime, $limit); + } + + /** + * @param int $createTime + * @param int $limit + * @return ResultsetInterface|Resultset|QuestionModel[] + */ + protected function findHotQuestions($createTime, $limit = 10) + { + return QuestionModel::query() + ->where('create_time > :create_time:', ['create_time' => $createTime]) + ->orderBy('score DESC') + ->limit($limit) + ->execute(); + } + +} diff --git a/app/Caches/MaxAnswerId.php b/app/Caches/MaxAnswerId.php new file mode 100644 index 00000000..02320473 --- /dev/null +++ b/app/Caches/MaxAnswerId.php @@ -0,0 +1,29 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'max_answer_id'; + } + + public function getContent($id = null) + { + $answer = AnswerModel::findFirst(['order' => 'id DESC']); + + return $answer->id ?? 0; + } + +} diff --git a/app/Caches/MaxQuestionId.php b/app/Caches/MaxQuestionId.php new file mode 100644 index 00000000..71b92f58 --- /dev/null +++ b/app/Caches/MaxQuestionId.php @@ -0,0 +1,29 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'max_question_id'; + } + + public function getContent($id = null) + { + $question = QuestionModel::findFirst(['order' => 'id DESC']); + + return $question->id ?? 0; + } + +} diff --git a/app/Caches/Question.php b/app/Caches/Question.php new file mode 100644 index 00000000..b536fe37 --- /dev/null +++ b/app/Caches/Question.php @@ -0,0 +1,31 @@ +lifetime; + } + + public function getKey($id = null) + { + return "question:{$id}"; + } + + public function getContent($id = null) + { + $questionRepo = new QuestionRepo(); + + $question = $questionRepo->findById($id); + + return $question ?: null; + } + +} diff --git a/app/Caches/ArticleRelatedList.php b/app/Caches/TaggedArticleList.php similarity index 75% rename from app/Caches/ArticleRelatedList.php rename to app/Caches/TaggedArticleList.php index 78c97f4b..722e484d 100644 --- a/app/Caches/ArticleRelatedList.php +++ b/app/Caches/TaggedArticleList.php @@ -5,11 +5,9 @@ namespace App\Caches; use App\Models\Article as ArticleModel; use App\Repos\Article as ArticleRepo; -class ArticleRelatedList extends Cache +class TaggedArticleList extends Cache { - protected $articleId; - protected $limit = 5; protected $lifetime = 1 * 86400; @@ -21,25 +19,15 @@ class ArticleRelatedList extends Cache public function getKey($id = null) { - return "article_related_list:{$id}"; + return "tagged_article_list:{$id}"; } public function getContent($id = null) { - $this->articleId = $id; - $articleRepo = new ArticleRepo(); - $article = $articleRepo->findById($id); - - if (empty($article->tags)) return []; - - $tagIds = kg_array_column($article->tags, 'id'); - - $randKey = array_rand($tagIds); - $where = [ - 'tag_id' => $tagIds[$randKey], + 'tag_id' => $id, 'published' => ArticleModel::PUBLISH_APPROVED, ]; @@ -61,7 +49,7 @@ class ArticleRelatedList extends Cache $count = 0; foreach ($articles as $article) { - if ($article->id != $this->articleId && $count < $this->limit) { + if ($count < $this->limit) { $result[] = [ 'id' => $article->id, 'title' => $article->title, diff --git a/app/Caches/TaggedQuestionList.php b/app/Caches/TaggedQuestionList.php new file mode 100644 index 00000000..e1bb7c2a --- /dev/null +++ b/app/Caches/TaggedQuestionList.php @@ -0,0 +1,69 @@ +lifetime; + } + + public function getKey($id = null) + { + return "tagged_question_list:{$id}"; + } + + public function getContent($id = null) + { + + $questionRepo = new QuestionRepo(); + + $where = [ + 'tag_id' => $id, + 'published' => QuestionModel::PUBLISH_APPROVED, + ]; + + $pager = $questionRepo->paginate($where); + + if ($pager->total_items == 0) return []; + + return $this->handleContent($pager->items); + } + + /** + * @param QuestionModel[] $questions + * @return array + */ + public function handleContent($questions) + { + $result = []; + + $count = 0; + + foreach ($questions as $question) { + if ($count < $this->limit) { + $result[] = [ + 'id' => $question->id, + 'title' => $question->title, + 'view_count' => $question->view_count, + 'like_count' => $question->like_count, + 'answer_count' => $question->answer_count, + 'favorite_count' => $question->favorite_count, + ]; + $count++; + } + } + + return $result; + } + +} diff --git a/app/Caches/TopAnswererList.php b/app/Caches/TopAnswererList.php new file mode 100644 index 00000000..c4f0aaf9 --- /dev/null +++ b/app/Caches/TopAnswererList.php @@ -0,0 +1,103 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'question_top_answerer_list'; + } + + public function getContent($id = null) + { + $rankings = $this->findWeeklyAuthorRankings(); + + if ($rankings->count() > 0) { + $userIds = kg_array_column($rankings->toArray(), 'author_id'); + return $this->handleUsers($userIds); + } + + $randOwners = $this->findRandArticleOwners(); + + if ($randOwners->count() > 0) { + $userIds = kg_array_column($randOwners->toArray(), 'owner_id'); + return $this->handleUsers($userIds); + } + + return []; + } + + protected function handleUsers($userIds) + { + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($userIds); + + $result = []; + + foreach ($users as $user) { + $result[] = [ + 'id' => $user->id, + 'name' => $user->name, + 'avatar' => $user->avatar, + 'title' => $user->title, + 'about' => $user->about, + 'vip' => $user->vip, + ]; + } + + return $result; + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset + */ + protected function findRandArticleOwners($limit = 10) + { + return ArticleModel::query() + ->columns(['owner_id']) + ->orderBy('RAND()') + ->limit($limit) + ->execute(); + } + + /** + * @param int $limit + * @return ResultsetInterface|Resultset + */ + protected function findWeeklyAuthorRankings($limit = 10) + { + $createTime = strtotime('monday this week'); + + $columns = [ + 'author_id' => 'a.owner_id', + 'like_count' => 'count(al.user_id)', + ]; + + return $this->modelsManager->createBuilder() + ->columns($columns) + ->addFrom(ArticleLikeModel::class, 'al') + ->join(ArticleModel::class, 'al.article_id = a.id', 'a') + ->where('al.create_time > :create_time:', ['create_time' => $createTime]) + ->groupBy('author_id') + ->orderBy('like_count DESC') + ->limit($limit)->getQuery()->execute(); + } + +} diff --git a/app/Console/Tasks/ArticleIndexTask.php b/app/Console/Tasks/ArticleIndexTask.php index addf2e4f..5528fbe0 100644 --- a/app/Console/Tasks/ArticleIndexTask.php +++ b/app/Console/Tasks/ArticleIndexTask.php @@ -118,7 +118,7 @@ class ArticleIndexTask extends Task protected function findArticles() { return ArticleModel::query() - ->where('published = 1') + ->where('published = :published:', ['published' => ArticleModel::PUBLISH_APPROVED]) ->execute(); } diff --git a/app/Console/Tasks/QuestionIndexTask.php b/app/Console/Tasks/QuestionIndexTask.php new file mode 100644 index 00000000..b6bfbc6a --- /dev/null +++ b/app/Console/Tasks/QuestionIndexTask.php @@ -0,0 +1,125 @@ +searchQuestions($query); + + var_export($result); + } + + /** + * 清空索引 + * + * @command: php console.php question_index clean + */ + public function cleanAction() + { + $this->cleanQuestionIndex(); + } + + /** + * 重建索引 + * + * @command: php console.php question_index rebuild + */ + public function rebuildAction() + { + $this->rebuildQuestionIndex(); + } + + /** + * 清空索引 + */ + protected function cleanQuestionIndex() + { + $handler = new QuestionSearcher(); + + $index = $handler->getXS()->getIndex(); + + echo '------ start clean question index ------' . PHP_EOL; + + $index->clean(); + + echo '------ end clean question index ------' . PHP_EOL; + } + + /** + * 重建索引 + */ + protected function rebuildQuestionIndex() + { + $questions = $this->findQuestions(); + + if ($questions->count() == 0) return; + + $handler = new QuestionSearcher(); + + $documenter = new QuestionDocument(); + + $index = $handler->getXS()->getIndex(); + + echo '------ start rebuild question index ------' . PHP_EOL; + + $index->beginRebuild(); + + foreach ($questions as $question) { + $document = $documenter->setDocument($question); + $index->add($document); + } + + $index->endRebuild(); + + echo '------ end rebuild question index ------' . PHP_EOL; + } + + /** + * 搜索文章 + * + * @param string $query + * @return array + * @throws \XSException + */ + protected function searchQuestions($query) + { + $handler = new QuestionSearcher(); + + return $handler->search($query); + } + + /** + * 查找文章 + * + * @return ResultsetInterface|Resultset|QuestionModel[] + */ + protected function findQuestions() + { + return QuestionModel::query() + ->where('published = :published:', ['published' => QuestionModel::PUBLISH_APPROVED]) + ->execute(); + } + +} diff --git a/app/Console/Tasks/SyncArticleIndexTask.php b/app/Console/Tasks/SyncArticleIndexTask.php index ace75ba4..dbd0ea51 100644 --- a/app/Console/Tasks/SyncArticleIndexTask.php +++ b/app/Console/Tasks/SyncArticleIndexTask.php @@ -2,6 +2,7 @@ namespace App\Console\Tasks; +use App\Models\Article as ArticleModel; use App\Repos\Article as ArticleRepo; use App\Services\Search\ArticleDocument; use App\Services\Search\ArticleSearcher; @@ -38,7 +39,7 @@ class SyncArticleIndexTask extends Task $doc = $document->setDocument($article); - if ($article->published == 1) { + if ($article->published == ArticleModel::PUBLISH_APPROVED) { $index->update($doc); } else { $index->del($article->id); diff --git a/app/Console/Tasks/SyncArticleScoreTask.php b/app/Console/Tasks/SyncArticleScoreTask.php new file mode 100644 index 00000000..b6ee3376 --- /dev/null +++ b/app/Console/Tasks/SyncArticleScoreTask.php @@ -0,0 +1,44 @@ +getRedis(); + + $key = $this->getSyncKey(); + + $articleIds = $redis->sRandMember($key, 1000); + + if (!$articleIds) return; + + $articleRepo = new ArticleRepo(); + + $articles = $articleRepo->findByIds($articleIds); + + if ($articles->count() == 0) return; + + $service = new ArticleScoreService(); + + foreach ($articles as $article) { + $service->handle($article); + } + + $redis->sRem($key, ...$articleIds); + } + + protected function getSyncKey() + { + $sync = new ArticleScoreSync(); + + return $sync->getSyncKey(); + } + +} diff --git a/app/Console/Tasks/SyncCourseScoreTask.php b/app/Console/Tasks/SyncCourseScoreTask.php index 3a789633..b5cf1901 100644 --- a/app/Console/Tasks/SyncCourseScoreTask.php +++ b/app/Console/Tasks/SyncCourseScoreTask.php @@ -3,8 +3,8 @@ namespace App\Console\Tasks; use App\Repos\Course as CourseRepo; -use App\Services\CourseStat as CourseStatService; use App\Services\Sync\CourseScore as CourseScoreSync; +use App\Services\Utils\CourseScore as CourseScoreService; class SyncCourseScoreTask extends Task { @@ -25,10 +25,10 @@ class SyncCourseScoreTask extends Task if ($courses->count() == 0) return; - $statService = new CourseStatService(); + $service = new CourseScoreService(); foreach ($courses as $course) { - $statService->updateScore($course->id); + $service->handle($course); } $redis->sRem($key, ...$courseIds); diff --git a/app/Console/Tasks/SyncQuestionIndexTask.php b/app/Console/Tasks/SyncQuestionIndexTask.php new file mode 100644 index 00000000..6c99a6a5 --- /dev/null +++ b/app/Console/Tasks/SyncQuestionIndexTask.php @@ -0,0 +1,61 @@ +getRedis(); + + $key = $this->getSyncKey(); + + $questionIds = $redis->sRandMember($key, 1000); + + if (!$questionIds) return; + + $questionRepo = new QuestionRepo(); + + $questions = $questionRepo->findByIds($questionIds); + + if ($questions->count() == 0) return; + + $document = new QuestionDocument(); + + $handler = new QuestionSearcher(); + + $index = $handler->getXS()->getIndex(); + + $index->openBuffer(); + + foreach ($questions as $question) { + + $doc = $document->setDocument($question); + + if ($question->published == QuestionModel::PUBLISH_APPROVED) { + $index->update($doc); + } else { + $index->del($question->id); + } + } + + $index->closeBuffer(); + + $redis->sRem($key, ...$questionIds); + } + + protected function getSyncKey() + { + $sync = new QuestionIndexSync(); + + return $sync->getSyncKey(); + } + +} diff --git a/app/Console/Tasks/SyncQuestionScoreTask.php b/app/Console/Tasks/SyncQuestionScoreTask.php new file mode 100644 index 00000000..44bc0086 --- /dev/null +++ b/app/Console/Tasks/SyncQuestionScoreTask.php @@ -0,0 +1,44 @@ +getRedis(); + + $key = $this->getSyncKey(); + + $questionIds = $redis->sRandMember($key, 1000); + + if (!$questionIds) return; + + $questionRepo = new QuestionRepo(); + + $questions = $questionRepo->findByIds($questionIds); + + if ($questions->count() == 0) return; + + $service = new QuestionScoreService(); + + foreach ($questions as $question) { + $service->handle($question); + } + + $redis->sRem($key, ...$questionIds); + } + + protected function getSyncKey() + { + $sync = new QuestionScoreSync(); + + return $sync->getSyncKey(); + } + +} diff --git a/app/Http/Admin/Controllers/UploadController.php b/app/Http/Admin/Controllers/UploadController.php index 25edb530..22f2e82f 100644 --- a/app/Http/Admin/Controllers/UploadController.php +++ b/app/Http/Admin/Controllers/UploadController.php @@ -53,6 +53,27 @@ class UploadController extends Controller return $this->jsonSuccess(['data' => $data]); } + /** + * @Post("/icon/img", name="admin.upload.icon_img") + */ + public function uploadIconImageAction() + { + $service = new StorageService(); + + $file = $service->uploadIconImage(); + + if (!$file) { + return $this->jsonError(['msg' => '上传文件失败']); + } + + $data = [ + 'src' => $service->getImageUrl($file->path), + 'title' => $file->name, + ]; + + return $this->jsonSuccess(['data' => $data]); + } + /** * @Post("/cover/img", name="admin.upload.cover_img") */ diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 1c7871e3..04e00268 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -542,6 +542,12 @@ class AuthNode extends Service 'type' => 'menu', 'route' => 'admin.slide.list', ], + [ + 'id' => '2-5-5', + 'title' => '搜索轮播', + 'type' => 'menu', + 'route' => 'admin.slide.search', + ], [ 'id' => '2-5-2', 'title' => '添加轮播', @@ -560,12 +566,6 @@ class AuthNode extends Service 'type' => 'button', 'route' => 'admin.slide.delete', ], - [ - 'id' => '2-5-5', - 'title' => '搜索轮播', - 'type' => 'menu', - 'route' => 'admin.slide.search', - ], ], ], [ diff --git a/app/Http/Admin/Services/Tag.php b/app/Http/Admin/Services/Tag.php index 61a86c5e..584a8086 100644 --- a/app/Http/Admin/Services/Tag.php +++ b/app/Http/Admin/Services/Tag.php @@ -19,7 +19,7 @@ class Tag extends Service $params['deleted'] = $params['deleted'] ?? 0; - $sort = 'priority'; + $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); $limit = $pagerQuery->getLimit(); @@ -43,7 +43,6 @@ class Tag extends Service $tag->name = $validator->checkName($post['name']); $tag->priority = $validator->checkPriority($post['priority']); - $tag->published = $validator->checkPublishStatus($post['published']); $tag->create(); @@ -69,6 +68,10 @@ class Tag extends Service } } + if (isset($post['icon'])) { + $data['icon'] = $validator->checkIcon($post['icon']); + } + if (isset($post['priority'])) { $data['priority'] = $validator->checkPriority($post['priority']); } diff --git a/app/Http/Admin/Views/tag/add.volt b/app/Http/Admin/Views/tag/add.volt index 8dc363f1..b2d30288 100644 --- a/app/Http/Admin/Views/tag/add.volt +++ b/app/Http/Admin/Views/tag/add.volt @@ -12,19 +12,6 @@
-
- -
- -
-
-
- -
- - -
-
diff --git a/app/Http/Admin/Views/tag/edit.volt b/app/Http/Admin/Views/tag/edit.volt index 1128c729..e39e3058 100644 --- a/app/Http/Admin/Views/tag/edit.volt +++ b/app/Http/Admin/Views/tag/edit.volt @@ -7,15 +7,19 @@ 编辑标签
- -
- + +
+ + +
+
+
- +
- +
@@ -34,4 +38,10 @@
+{% endblock %} + +{% block include_js %} + + {{ js_include('admin/js/icon.upload.js') }} + {% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/tag/list.volt b/app/Http/Admin/Views/tag/list.volt index 99785aaa..2ca61555 100644 --- a/app/Http/Admin/Views/tag/list.volt +++ b/app/Http/Admin/Views/tag/list.volt @@ -29,15 +29,17 @@ + 编号 + 图标 名称 + 关注数 创建时间 更新时间 - 排序 发布 操作 @@ -49,10 +51,11 @@ {% set delete_url = url({'for':'admin.tag.delete','id':item.id}) %} {{ item.id }} + {{ item.name }} {{ item.name }} - {{ date('Y-m-d H:i',item.create_time) }} - {{ date('Y-m-d H:i',item.update_time) }} - + {{ item.follow_count }} + {{ date('Y-m-d',item.create_time) }} + {{ date('Y-m-d',item.update_time) }} diff --git a/app/Http/Home/Controllers/AnswerController.php b/app/Http/Home/Controllers/AnswerController.php new file mode 100644 index 00000000..628a867a --- /dev/null +++ b/app/Http/Home/Controllers/AnswerController.php @@ -0,0 +1,126 @@ +getAnswer($id); + + $this->view->setVar('answer', $answer); + } + + /** + * @Get("/{id:[0-9]+}", name="home.answer.show") + */ + public function showAction($id) + { + $service = new AnswerInfoService(); + + $answer = $service->handle($id); + + $this->view->setVar('answer', $answer); + } + + /** + * @Post("/create", name="home.answer.create") + */ + public function createAction() + { + $service = new AnswerCreateService(); + + $service->handle(); + + $content = ['msg' => '创建答案成功']; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/update", name="home.answer.update") + */ + public function updateAction($id) + { + $service = new AnswerUpdateService(); + + $service->handle($id); + + $content = ['msg' => '更新答案成功']; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="home.answer.delete") + */ + public function deleteAction($id) + { + $service = new AnswerDeleteService(); + + $service->handle($id); + + $location = $this->url->get(['for' => 'home.uc.answers']); + + $content = [ + 'location' => $location, + 'msg' => '删除答案成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/like", name="home.answer.like") + */ + public function likeAction($id) + { + $service = new AnswerLikeService(); + + $data = $service->handle($id); + + $msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功'; + + return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); + } + + /** + * @Post("/{id:[0-9]+}/accept", name="home.answer.accept") + */ + public function acceptAction($id) + { + $service = new AnswerAcceptService(); + + $data = $service->handle($id); + + $msg = $data['action'] == 'do' ? '采纳成功' : '取消采纳成功'; + + return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); + } + +} diff --git a/app/Http/Home/Controllers/ArticleController.php b/app/Http/Home/Controllers/ArticleController.php index ba081e4b..21021b08 100644 --- a/app/Http/Home/Controllers/ArticleController.php +++ b/app/Http/Home/Controllers/ArticleController.php @@ -8,9 +8,8 @@ use App\Services\Logic\Article\ArticleFavorite as ArticleFavoriteService; use App\Services\Logic\Article\ArticleInfo as ArticleInfoService; use App\Services\Logic\Article\ArticleLike as ArticleLikeService; use App\Services\Logic\Article\ArticleList as ArticleListService; -use App\Services\Logic\Article\CommentList as ArticleCommentListService; use App\Services\Logic\Article\HotAuthorList as HotAuthorListService; -use App\Services\Logic\Article\RelatedList as ArticleRelatedListService; +use App\Services\Logic\Article\RelatedArticleList as RelatedArticleListService; use Phalcon\Mvc\View; /** @@ -78,7 +77,7 @@ class ArticleController extends Controller $article = $service->getArticleModel(); $xmTags = $service->getXmTags(0); - $this->seo->prependTitle('撰写文章'); + $this->seo->prependTitle('写文章'); $this->view->pick('article/edit'); $this->view->setVar('source_types', $sourceTypes); @@ -132,7 +131,7 @@ class ArticleController extends Controller */ public function relatedAction($id) { - $service = new ArticleRelatedListService(); + $service = new RelatedArticleListService(); $articles = $service->handle($id); @@ -140,19 +139,6 @@ class ArticleController extends Controller $this->view->setVar('articles', $articles); } - /** - * @Get("/{id:[0-9]+}/comments", name="home.article.comments") - */ - public function commentsAction($id) - { - $service = new ArticleCommentListService(); - - $comments = $service->handle($id); - - $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); - $this->view->setVar('comments', $comments); - } - /** * @Post("/create", name="home.article.create") */ @@ -200,8 +186,10 @@ class ArticleController extends Controller $service->deleteArticle($id); + $location = $this->url->get(['for' => 'home.uc.articles']); + $content = [ - 'location' => $this->request->getHTTPReferer(), + 'location' => $location, 'msg' => '删除文章成功', ]; @@ -236,5 +224,4 @@ class ArticleController extends Controller return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); } - } diff --git a/app/Http/Home/Controllers/ChapterController.php b/app/Http/Home/Controllers/ChapterController.php index f9230ad6..dcb97734 100644 --- a/app/Http/Home/Controllers/ChapterController.php +++ b/app/Http/Home/Controllers/ChapterController.php @@ -6,7 +6,6 @@ use App\Models\ChapterLive as LiveModel; use App\Models\Course as CourseModel; use App\Services\Logic\Chapter\ChapterInfo as ChapterInfoService; use App\Services\Logic\Chapter\ChapterLike as ChapterLikeService; -use App\Services\Logic\Chapter\DanmuList as ChapterDanmuListService; use App\Services\Logic\Chapter\Learning as ChapterLearningService; use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService; use App\Services\Logic\Course\BasicInfo as CourseInfoService; @@ -30,18 +29,6 @@ class ChapterController extends Controller $this->view->setVar('items', $items); } - /** - * @Get("/{id:[0-9]+}/danmus", name="home.chapter.danmus") - */ - public function danmusAction($id) - { - $service = new ChapterDanmuListService(); - - $items = $service->handle($id); - - return $this->jsonSuccess(['items' => $items]); - } - /** * @Get("/{id:[0-9]+}", name="home.chapter.show") */ diff --git a/app/Http/Home/Controllers/CommentController.php b/app/Http/Home/Controllers/CommentController.php index 4d11ee38..934c10f4 100644 --- a/app/Http/Home/Controllers/CommentController.php +++ b/app/Http/Home/Controllers/CommentController.php @@ -63,26 +63,6 @@ class CommentController extends Controller $this->view->setVar('comment', $comment); } - /** - * @Get("/add", name="home.comment.add") - */ - public function addAction() - { - - } - - /** - * @Get("/{id:[0-9]+}/reply", name="home.comment.reply") - */ - public function replyAction($id) - { - $service = new CommentInfoService(); - - $comment = $service->handle($id); - - $this->view->setVar('comment', $comment); - } - /** * @Post("/create", name="home.comment.create") */ @@ -100,9 +80,9 @@ class CommentController extends Controller } /** - * @Post("/{id:[0-9]+}/reply", name="home.comment.create_reply") + * @Post("/{id:[0-9]+}/reply", name="home.comment.reply") */ - public function createReplyAction($id) + public function replyAction($id) { $service = new CommentReplyService(); diff --git a/app/Http/Home/Controllers/QuestionController.php b/app/Http/Home/Controllers/QuestionController.php new file mode 100644 index 00000000..57c8d207 --- /dev/null +++ b/app/Http/Home/Controllers/QuestionController.php @@ -0,0 +1,252 @@ +handle(); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('question/hot_questions'); + $this->view->setVar('questions', $questions); + } + + /** + * @Get("/top/answerers", name="home.question.top_answerers") + */ + public function topAnswerersAction() + { + $service = new TopAnswererListService(); + + $answerers = $service->handle(); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('question/top_answerers'); + $this->view->setVar('answerers', $answerers); + } + + /** + * @Get("/list", name="home.question.list") + */ + public function listAction() + { + $service = new QuestionQueryService(); + + $sorts = $service->handleSorts(); + $params = $service->getParams(); + + $this->seo->prependTitle('问答'); + + $this->view->setVar('sorts', $sorts); + $this->view->setVar('params', $params); + } + + /** + * @Get("/pager", name="home.question.pager") + */ + public function pagerAction() + { + $service = new QuestionListService(); + + $pager = $service->handle(); + + $pager->target = 'question-list'; + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/add", name="home.question.add") + */ + public function addAction() + { + $service = new QuestionService(); + + $question = $service->getQuestionModel(); + + $xmTags = $service->getXmTags(0); + + $this->seo->prependTitle('提问题'); + + $this->view->pick('question/edit'); + $this->view->setVar('question', $question); + $this->view->setVar('xm_tags', $xmTags); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="home.question.edit") + */ + public function editAction($id) + { + $service = new QuestionService(); + + $question = $service->getQuestion($id); + + $xmTags = $service->getXmTags($id); + + $this->seo->prependTitle('编辑问题'); + + $this->view->setVar('question', $question); + $this->view->setVar('xm_tags', $xmTags); + } + + /** + * @Get("/{id:[0-9]+}", name="home.question.show") + */ + public function showAction($id) + { + $service = new QuestionInfoService(); + + $question = $service->handle($id); + + $this->seo->prependTitle($question['title']); + + $this->view->setVar('question', $question); + } + + /** + * @Get("/{id:[0-9]+}/answers", name="home.question.answers") + */ + public function answersAction($id) + { + $service = new QuestionService(); + + $question = $service->getQuestion($id); + + $service = new AnswerListService(); + + $pager = $service->handle($id); + + $pager->target = 'answer-list'; + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->setVar('question', $question); + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/{id:[0-9]+}/related", name="home.question.related") + */ + public function relatedAction($id) + { + $service = new RelatedQuestionListService(); + + $questions = $service->handle($id); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->setVar('questions', $questions); + } + + /** + * @Post("/create", name="home.question.create") + */ + public function createAction() + { + $service = new QuestionCreateService(); + + $service->handle(); + + $location = $this->url->get(['for' => 'home.uc.questions']); + + $content = [ + 'location' => $location, + 'msg' => '创建问题成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/update", name="home.question.update") + */ + public function updateAction($id) + { + $service = new QuestionUpdateService(); + + $service->handle($id); + + $location = $this->url->get(['for' => 'home.uc.questions']); + + $content = [ + 'location' => $location, + 'msg' => '更新问题成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="home.question.delete") + */ + public function deleteAction($id) + { + $service = new QuestionDeleteService(); + + $service->handle($id); + + $location = $this->url->get(['for' => 'home.uc.questions']); + + $content = [ + 'location' => $location, + 'msg' => '删除问题成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/favorite", name="home.question.favorite") + */ + public function favoriteAction($id) + { + $service = new QuestionFavoriteService(); + + $data = $service->handle($id); + + $msg = $data['action'] == 'do' ? '收藏成功' : '取消收藏成功'; + + return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); + } + + /** + * @Post("/{id:[0-9]+}/like", name="home.question.like") + */ + public function likeAction($id) + { + $service = new QuestionLikeService(); + + $data = $service->handle($id); + + $msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功'; + + return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); + } + +} diff --git a/app/Http/Home/Controllers/SearchController.php b/app/Http/Home/Controllers/SearchController.php index ba3507ef..2e2927af 100644 --- a/app/Http/Home/Controllers/SearchController.php +++ b/app/Http/Home/Controllers/SearchController.php @@ -5,6 +5,7 @@ namespace App\Http\Home\Controllers; use App\Services\Logic\Search\Article as ArticleSearchService; use App\Services\Logic\Search\Course as CourseSearchService; use App\Services\Logic\Search\Group as GroupSearchService; +use App\Services\Logic\Search\Question as QuestionSearchService; use App\Services\Logic\Search\User as UserSearchService; /** @@ -42,7 +43,7 @@ class SearchController extends Controller /** * @param string $type - * @return ArticleSearchService|CourseSearchService|GroupSearchService|UserSearchService + * @return ArticleSearchService|QuestionSearchService|CourseSearchService|GroupSearchService|UserSearchService */ protected function getSearchService($type) { @@ -50,6 +51,9 @@ class SearchController extends Controller case 'article': $service = new ArticleSearchService(); break; + case 'question': + $service = new QuestionSearchService(); + break; case 'group': $service = new GroupSearchService(); break; diff --git a/app/Http/Home/Controllers/TagController.php b/app/Http/Home/Controllers/TagController.php new file mode 100644 index 00000000..262b8614 --- /dev/null +++ b/app/Http/Home/Controllers/TagController.php @@ -0,0 +1,70 @@ +seo->prependTitle('标签'); + } + + /** + * @Get("/list/pager", name="home.tag.list_pager") + */ + public function listPagerAction() + { + $service = new TagListService(); + + $pager = $service->handle(); + + $pager->target = 'all-tag-list'; + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('tag/list_pager'); + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/my/pager", name="home.tag.my_pager") + */ + public function myPagerAction() + { + $service = new FollowListService(); + + $pager = $service->handle(); + + $pager->target = 'my-tag-list'; + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('tag/my_pager'); + $this->view->setVar('pager', $pager); + } + + /** + * @Post("/{id:[0-9]+}/follow", name="home.tag.follow") + */ + public function followAction($id) + { + $service = new TagFollowService(); + + $data = $service->handle($id); + + $msg = $data['action'] == 'do' ? '关注成功' : '取消关注成功'; + + return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); + } + +} diff --git a/app/Http/Home/Controllers/UserConsoleController.php b/app/Http/Home/Controllers/UserConsoleController.php index c38d6209..1f215146 100644 --- a/app/Http/Home/Controllers/UserConsoleController.php +++ b/app/Http/Home/Controllers/UserConsoleController.php @@ -5,6 +5,7 @@ namespace App\Http\Home\Controllers; use App\Repos\WeChatSubscribe as WeChatSubscribeRepo; use App\Services\Logic\Account\OAuthProvider as OAuthProviderService; use App\Services\Logic\User\Console\AccountInfo as AccountInfoService; +use App\Services\Logic\User\Console\AnswerList as AnswerListService; use App\Services\Logic\User\Console\ArticleList as ArticleListService; use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService; use App\Services\Logic\User\Console\ConnectList as ConnectListService; @@ -24,6 +25,7 @@ use App\Services\Logic\User\Console\PointHistory as PointHistoryService; use App\Services\Logic\User\Console\PointRedeemList as PointRedeemListService; use App\Services\Logic\User\Console\ProfileInfo as ProfileInfoService; use App\Services\Logic\User\Console\ProfileUpdate as ProfileUpdateService; +use App\Services\Logic\User\Console\QuestionList as QuestionListService; use App\Services\Logic\User\Console\RefundList as RefundListService; use App\Services\Logic\User\Console\ReviewList as ReviewListService; use Phalcon\Mvc\Dispatcher; @@ -154,6 +156,32 @@ class UserConsoleController extends Controller $this->view->setVar('pager', $pager); } + /** + * @Get("/questions", name="home.uc.questions") + */ + public function questionsAction() + { + $service = new QuestionListService(); + + $pager = $service->handle(); + + $this->view->pick('user/console/questions'); + $this->view->setVar('pager', $pager); + } + + /** + * @Get("/answers", name="home.uc.answers") + */ + public function answersAction() + { + $service = new AnswerListService(); + + $pager = $service->handle(); + + $this->view->pick('user/console/answers'); + $this->view->setVar('pager', $pager); + } + /** * @Get("/favorites", name="home.uc.favorites") */ diff --git a/app/Http/Home/Controllers/WidgetController.php b/app/Http/Home/Controllers/WidgetController.php new file mode 100644 index 00000000..b129692d --- /dev/null +++ b/app/Http/Home/Controllers/WidgetController.php @@ -0,0 +1,58 @@ +handle(); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('widget/my_tags'); + $this->view->setVar('tags', $pager->items); + } + + /** + * @Get("/hot/questions", name="home.widget.hot_questions") + */ + public function hotQuestionsAction() + { + $service = new HotQuestionListService(); + + $questions = $service->handle(); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('widget/hot_questions'); + $this->view->setVar('questions', $questions); + } + + /** + * @Get("/top/answerers", name="home.widget.top_answerers") + */ + public function topAnswerersAction() + { + $service = new TopAnswererListService(); + + $answerers = $service->handle(); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('widget/top_answerers'); + $this->view->setVar('answerers', $answerers); + } + +} diff --git a/app/Http/Home/Services/Answer.php b/app/Http/Home/Services/Answer.php new file mode 100644 index 00000000..099a72da --- /dev/null +++ b/app/Http/Home/Services/Answer.php @@ -0,0 +1,24 @@ +checkAnswer($id); + } + +} diff --git a/app/Http/Home/Services/Question.php b/app/Http/Home/Services/Question.php new file mode 100644 index 00000000..800432d6 --- /dev/null +++ b/app/Http/Home/Services/Question.php @@ -0,0 +1,61 @@ +afterFetch(); + + return $question; + } + + public function getQuestion($id) + { + return $this->checkQuestion($id); + } + + public function getXmTags($id) + { + $tagRepo = new TagRepo(); + + $allTags = $tagRepo->findAll(['published' => 1], 'priority'); + + if ($allTags->count() == 0) return []; + + $questionTagIds = []; + + if ($id > 0) { + $question = $this->checkQuestion($id); + if (!empty($question->tags)) { + $questionTagIds = kg_array_column($question->tags, 'id'); + } + } + + $list = []; + + foreach ($allTags as $tag) { + $selected = in_array($tag->id, $questionTagIds); + $list[] = [ + 'name' => $tag->name, + 'value' => $tag->id, + 'selected' => $selected, + ]; + } + + return $list; + } + + +} diff --git a/app/Http/Home/Services/QuestionQuery.php b/app/Http/Home/Services/QuestionQuery.php new file mode 100644 index 00000000..19f854d3 --- /dev/null +++ b/app/Http/Home/Services/QuestionQuery.php @@ -0,0 +1,64 @@ +baseUrl = $this->url->get(['for' => 'home.question.list']); + } + + public function handleSorts() + { + $params = $this->getParams(); + + $result = []; + + $sorts = QuestionModel::sortTypes(); + + foreach ($sorts as $key => $value) { + $params['sort'] = $key; + $result[] = [ + 'id' => $key, + 'name' => $value, + 'url' => $this->baseUrl . $this->buildParams($params), + ]; + } + + return $result; + } + + public function getParams() + { + $query = $this->request->getQuery(); + + $params = []; + + $validator = new QuestionQueryValidator(); + + if (isset($query['tag_id'])) { + $validator->checkTag($query['tag_id']); + $params['tag_id'] = $query['tag_id']; + } + + if (isset($query['sort'])) { + $validator->checkSort($query['sort']); + $params['sort'] = $query['sort']; + } + + return $params; + } + + protected function buildParams($params) + { + return $params ? '?' . http_build_query($params) : ''; + } + +} diff --git a/app/Http/Home/Views/answer/add.volt b/app/Http/Home/Views/answer/add.volt new file mode 100644 index 00000000..6fd31eab --- /dev/null +++ b/app/Http/Home/Views/answer/add.volt @@ -0,0 +1,34 @@ +{% extends 'templates/layer.volt' %} + +{% block content %} + +
+
+
+
+ +
+
+
+
+ + +
+
+
+ +{% endblock %} + +{% block link_css %} + + {{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }} + +{% endblock %} + +{% block include_js %} + + {{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }} + {{ js_include('home/js/answer.js') }} + {{ js_include('home/js/vditor.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/answer/edit.volt b/app/Http/Home/Views/answer/edit.volt new file mode 100644 index 00000000..f447c9de --- /dev/null +++ b/app/Http/Home/Views/answer/edit.volt @@ -0,0 +1,33 @@ +{% extends 'templates/layer.volt' %} + +{% block content %} + +
+
+
+
+ +
+
+
+
+ +
+
+
+ +{% endblock %} + +{% block link_css %} + + {{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }} + +{% endblock %} + +{% block include_js %} + + {{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }} + {{ js_include('home/js/answer.js') }} + {{ js_include('home/js/vditor.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/article/comment.volt b/app/Http/Home/Views/article/comment.volt index 91731085..6d9b24ee 100644 --- a/app/Http/Home/Views/article/comment.volt +++ b/app/Http/Home/Views/article/comment.volt @@ -9,7 +9,7 @@ diff --git a/app/Http/Home/Views/article/edit.volt b/app/Http/Home/Views/article/edit.volt index 5cfe7fcd..e19cc01b 100644 --- a/app/Http/Home/Views/article/edit.volt +++ b/app/Http/Home/Views/article/edit.volt @@ -2,7 +2,7 @@ {% block content %} - {% set title = article.id > 0 ? '编辑文章' : '撰写文章' %} + {% set title = article.id > 0 ? '编辑文章' : '写文章' %} {% set action_url = article.id > 0 ? url({'for':'home.article.update','id':article.id}) : url({'for':'home.article.create'}) %} {% set source_url_display = article.source_type == 1 ? 'display:none' : 'display:block' %} @@ -30,8 +30,7 @@
- - +
@@ -41,25 +40,6 @@
基本信息
-
- -
- - -
- -
-
- -
- -
-
diff --git a/app/Http/Home/Views/article/hot_authors.volt b/app/Http/Home/Views/article/hot_authors.volt index ec92de94..8cfd91fa 100644 --- a/app/Http/Home/Views/article/hot_authors.volt +++ b/app/Http/Home/Views/article/hot_authors.volt @@ -1,4 +1,4 @@ -{% if authors %} +{% if authors|length > 0 %}
推荐作者
diff --git a/app/Http/Home/Views/article/list.volt b/app/Http/Home/Views/article/list.volt index c3e60e54..e540ac25 100644 --- a/app/Http/Home/Views/article/list.volt +++ b/app/Http/Home/Views/article/list.volt @@ -2,10 +2,10 @@ {% block content %} - {% set category_val = request.get('category_id','int','all') %} {% set sort_val = request.get('sort','trim','latest') %} {% set pager_url = url({'for':'home.article.pager'}, params) %} - {% set hot_author_url = url({'for':'home.article.hot_authors'}) %} + {% set hot_authors_url = url({'for':'home.article.hot_authors'}) %} + {% set my_tags_url = url({'for':'home.widget.my_tags'},{'type':'article'}) %}