From 863505dcca2e4c9307c6831a5a77415d299a54b6 Mon Sep 17 00:00:00 2001 From: koogua Date: Wed, 12 May 2021 20:41:24 +0800 Subject: [PATCH] =?UTF-8?q?=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 --- CHANGELOG.md | 12 + .../Admin/Controllers/AnswerController.php | 122 +++++++++- .../Admin/Controllers/ArticleController.php | 4 +- .../Controllers/ModerationController.php | 12 + .../Admin/Controllers/QuestionController.php | 4 +- app/Http/Admin/Controllers/UserController.php | 12 + app/Http/Admin/Services/Answer.php | 208 +++++++++++++++++- app/Http/Admin/Services/Article.php | 51 +++-- app/Http/Admin/Services/AuthNode.php | 65 +++++- app/Http/Admin/Services/Consult.php | 65 +++++- app/Http/Admin/Services/Moderation.php | 41 ++++ app/Http/Admin/Services/Question.php | 43 ++-- app/Http/Admin/Services/Review.php | 42 ++-- app/Http/Admin/Services/User.php | 20 ++ app/Http/Admin/Views/answer/add.volt | 41 ++++ app/Http/Admin/Views/answer/edit.volt | 40 ++++ app/Http/Admin/Views/answer/list.volt | 64 ++++++ app/Http/Admin/Views/answer/review.volt | 78 +++++++ app/Http/Admin/Views/answer/search.volt | 58 +++++ app/Http/Admin/Views/article/edit_basic.volt | 6 +- app/Http/Admin/Views/article/list.volt | 18 +- app/Http/Admin/Views/article/review.volt | 2 +- app/Http/Admin/Views/article/search.volt | 14 ++ app/Http/Admin/Views/comment/list.volt | 32 +-- app/Http/Admin/Views/macros/answer.volt | 11 + app/Http/Admin/Views/macros/article.volt | 6 +- app/Http/Admin/Views/moderation/answers.volt | 55 +++++ app/Http/Admin/Views/moderation/articles.volt | 24 +- .../Admin/Views/moderation/questions.volt | 21 +- app/Http/Admin/Views/question/list.volt | 24 +- app/Http/Admin/Views/question/review.volt | 2 +- app/Http/Admin/Views/user/list.volt | 38 +++- app/Http/Admin/Views/user/online.volt | 42 ++++ .../Home/Controllers/AnswerController.php | 13 ++ app/Http/Home/Views/answer/add.volt | 26 +++ app/Http/Home/Views/answer/tips.volt | 20 ++ app/Http/Home/Views/article/edit.volt | 6 +- app/Http/Home/Views/article/show.volt | 2 +- app/Http/Home/Views/macros/notification.volt | 14 +- app/Http/Home/Views/question/show.volt | 31 ++- app/Library/AppInfo.php | 2 +- app/Models/Article.php | 4 +- app/Models/Reason.php | 16 ++ app/Repos/Answer.php | 7 +- app/Repos/Article.php | 15 +- app/Repos/Chapter.php | 9 + app/Repos/Course.php | 10 +- app/Repos/Online.php | 33 +++ app/Repos/Question.php | 17 +- app/Repos/User.php | 16 +- app/Services/Logic/Answer/AnswerCreate.php | 68 ++++-- app/Services/Logic/Answer/AnswerDelete.php | 44 ++-- app/Services/Logic/Answer/AnswerUpdate.php | 8 +- app/Services/Logic/Article/ArticleCreate.php | 17 +- .../Logic/Article/ArticleDataTrait.php | 4 +- app/Services/Logic/Article/ArticleDelete.php | 16 +- app/Services/Logic/Article/ArticleInfo.php | 2 +- app/Services/Logic/Article/ArticleList.php | 2 +- app/Services/Logic/Consult/ConsultCreate.php | 29 ++- app/Services/Logic/Consult/ConsultDelete.php | 32 ++- .../Logic/Notice/System/AnswerAccepted.php | 3 + .../Logic/Notice/System/AnswerApproved.php | 41 ++++ .../Logic/Notice/System/AnswerLiked.php | 3 + .../Logic/Notice/System/AnswerRejected.php | 42 ++++ .../Logic/Notice/System/ArticleCommented.php | 4 +- .../Logic/Notice/System/CommentLiked.php | 4 +- .../Logic/Notice/System/CommentReplied.php | 8 +- .../Logic/Notice/System/ConsultLiked.php | 4 +- .../Logic/Notice/System/QuestionAnswered.php | 4 +- .../Logic/Notice/System/ReviewLiked.php | 4 +- .../Logic/Question/QuestionCreate.php | 16 +- .../Logic/Question/QuestionDelete.php | 16 +- app/Services/Logic/Review/ReviewCreate.php | 25 ++- app/Services/Logic/Review/ReviewDelete.php | 17 +- app/Validators/Answer.php | 33 ++- app/Validators/Article.php | 4 +- config/errors.php | 2 +- db/migrations/20210430023157.php | 65 +++++- public/static/home/css/common.css | 6 +- public/static/home/js/answer.js | 3 +- public/static/home/js/article.show.js | 6 + public/static/home/js/chapter.show.js | 6 + public/static/home/js/comment.js | 6 - public/static/home/js/question.show.js | 6 + 84 files changed, 1695 insertions(+), 373 deletions(-) create mode 100644 app/Http/Admin/Views/answer/add.volt create mode 100644 app/Http/Admin/Views/answer/edit.volt create mode 100644 app/Http/Admin/Views/answer/list.volt create mode 100644 app/Http/Admin/Views/answer/review.volt create mode 100644 app/Http/Admin/Views/answer/search.volt create mode 100644 app/Http/Admin/Views/macros/answer.volt create mode 100644 app/Http/Admin/Views/moderation/answers.volt create mode 100644 app/Http/Admin/Views/user/online.volt create mode 100644 app/Http/Home/Views/answer/tips.volt create mode 100644 app/Services/Logic/Notice/System/AnswerApproved.php create mode 100644 app/Services/Logic/Notice/System/AnswerRejected.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ce29a7..965bfae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +### [v1.3.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.4)(2021-05-13) + +### 更新 + +- 前台增加问答功能 +- 优化标签功能 +- 优化文章功能以及全文搜索 +- 优化课程评价,咨询,文章等相关统计 +- 后台增加提问和回答审核功能 +- 后台增加查看用户在线记录 修正后台编辑角色权限错误 +- 优化前台界面 + ### [v1.3.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.3)(2021-04-30) ### 更新 diff --git a/app/Http/Admin/Controllers/AnswerController.php b/app/Http/Admin/Controllers/AnswerController.php index f2f3e38e..e22ca8f3 100644 --- a/app/Http/Admin/Controllers/AnswerController.php +++ b/app/Http/Admin/Controllers/AnswerController.php @@ -3,6 +3,7 @@ namespace App\Http\Admin\Controllers; use App\Http\Admin\Services\Answer as AnswerService; +use App\Http\Admin\Services\Question as QuestionService; /** * @RoutePrefix("/admin/answer") @@ -15,7 +16,11 @@ class AnswerController extends Controller */ public function searchAction() { + $answerService = new AnswerService(); + $publishTypes = $answerService->getPublishTypes(); + + $this->view->setVar('publish_types', $publishTypes); } /** @@ -30,6 +35,78 @@ class AnswerController extends Controller $this->view->setVar('pager', $pager); } + /** + * @Get("/add", name="admin.answer.add") + */ + public function addAction() + { + $id = $this->request->getQuery('question_id', 'int', 0); + + $questionService = new QuestionService(); + + $question = $questionService->getQuestion($id); + + $referer = $this->request->getHTTPReferer(); + + $this->view->setVar('question', $question); + $this->view->setVar('referer', $referer); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="admin.answer.edit") + */ + public function editAction($id) + { + $answerService = new AnswerService(); + + $answer = $answerService->getAnswer($id); + + $questionService = new QuestionService(); + + $question = $questionService->getQuestion($answer->question_id); + + $referer = $this->request->getHTTPReferer(); + + $this->view->setVar('referer', $referer); + $this->view->setVar('question', $question); + $this->view->setVar('answer', $answer); + } + + /** + * @Get("/{id:[0-9]+}/show", name="admin.answer.show") + */ + public function showAction($id) + { + $answerService = new AnswerService(); + + $answer = $answerService->getAnswer($id); + + $this->view->setVar('answer', $answer); + } + + /** + * @Post("/create", name="admin.answer.create") + */ + public function createAction() + { + $answerService = new AnswerService(); + + $answerService->createAnswer(); + + $location = $this->request->getPost('referer'); + + if (empty($location)) { + $location = $this->url->get(['for' => 'admin.question.list']); + } + + $content = [ + 'location' => $location, + 'msg' => '回答问题成功', + ]; + + return $this->jsonSuccess($content); + } + /** * @Post("/{id:[0-9]+}/update", name="admin.answer.update") */ @@ -39,7 +116,16 @@ class AnswerController extends Controller $answerService->updateAnswer($id); - $content = ['msg' => '更新回答成功']; + $location = $this->request->getPost('referer'); + + if (empty($location)) { + $location = $this->url->get(['for' => 'admin.answer.list']); + } + + $content = [ + 'location' => $location, + 'msg' => '更新回答成功', + ]; return $this->jsonSuccess($content); } @@ -78,4 +164,38 @@ class AnswerController extends Controller return $this->jsonSuccess($content); } + /** + * @Route("/{id:[0-9]+}/review", name="admin.answer.review") + */ + public function reviewAction($id) + { + $answerService = new AnswerService(); + + $answer = $answerService->getAnswer($id); + + if ($this->request->isPost()) { + + $answerService->reviewAnswer($id); + + $location = $this->url->get(['for' => 'admin.mod.answers']); + + $content = [ + 'location' => $location, + 'msg' => '审核回答成功', + ]; + + return $this->jsonSuccess($content); + } + + $reasons = $answerService->getReasons(); + + $questionService = new QuestionService(); + + $question = $questionService->getQuestion($answer->question_id); + + $this->view->setVar('reasons', $reasons); + $this->view->setVar('question', $question); + $this->view->setVar('answer', $answer); + } + } diff --git a/app/Http/Admin/Controllers/ArticleController.php b/app/Http/Admin/Controllers/ArticleController.php index 26efde1d..26080afb 100644 --- a/app/Http/Admin/Controllers/ArticleController.php +++ b/app/Http/Admin/Controllers/ArticleController.php @@ -189,10 +189,10 @@ class ArticleController extends Controller return $this->jsonSuccess($content); } - $rejectOptions = $articleService->getRejectOptions(); + $reasons = $articleService->getReasons(); $article = $articleService->getArticle($id); - $this->view->setVar('reject_options', $rejectOptions); + $this->view->setVar('reasons', $reasons); $this->view->setVar('article', $article); } diff --git a/app/Http/Admin/Controllers/ModerationController.php b/app/Http/Admin/Controllers/ModerationController.php index f5fb5dbb..0ec36a01 100644 --- a/app/Http/Admin/Controllers/ModerationController.php +++ b/app/Http/Admin/Controllers/ModerationController.php @@ -34,4 +34,16 @@ class ModerationController extends Controller $this->view->setVar('pager', $pager); } + /** + * @Get("/answers", name="admin.mod.answers") + */ + public function answersAction() + { + $modService = new ModerationService(); + + $pager = $modService->getAnswers(); + + $this->view->setVar('pager', $pager); + } + } diff --git a/app/Http/Admin/Controllers/QuestionController.php b/app/Http/Admin/Controllers/QuestionController.php index ac959bfe..ef92e49d 100644 --- a/app/Http/Admin/Controllers/QuestionController.php +++ b/app/Http/Admin/Controllers/QuestionController.php @@ -185,10 +185,10 @@ class QuestionController extends Controller return $this->jsonSuccess($content); } - $rejectOptions = $questionService->getRejectOptions(); + $reasons = $questionService->getReasons(); $question = $questionService->getQuestion($id); - $this->view->setVar('reject_options', $rejectOptions); + $this->view->setVar('reasons', $reasons); $this->view->setVar('question', $question); } diff --git a/app/Http/Admin/Controllers/UserController.php b/app/Http/Admin/Controllers/UserController.php index 5d106768..b53e405d 100644 --- a/app/Http/Admin/Controllers/UserController.php +++ b/app/Http/Admin/Controllers/UserController.php @@ -94,6 +94,18 @@ class UserController extends Controller $this->view->setVar('admin_roles', $adminRoles); } + /** + * @Get("/{id:[0-9]+}/online", name="admin.user.online") + */ + public function onlineAction($id) + { + $userService = new UserService(); + + $pager = $userService->getOnlineLogs($id); + + $this->view->setVar('pager', $pager); + } + /** * @Post("/{id:[0-9]+}/update", name="admin.user.update") */ diff --git a/app/Http/Admin/Services/Answer.php b/app/Http/Admin/Services/Answer.php index 093304e6..b1fd1263 100644 --- a/app/Http/Admin/Services/Answer.php +++ b/app/Http/Admin/Services/Answer.php @@ -4,12 +4,32 @@ namespace App\Http\Admin\Services; use App\Builders\AnswerList as AnswerListBuilder; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Answer as AnswerModel; +use App\Models\Question as QuestionModel; +use App\Models\Reason as ReasonModel; +use App\Models\User as UserModel; use App\Repos\Answer as AnswerRepo; +use App\Repos\Question as QuestionRepo; +use App\Repos\User as UserRepo; +use App\Services\Logic\Notice\System\AnswerApproved as AnswerApprovedNotice; +use App\Services\Logic\Notice\System\AnswerRejected as AnswerRejectedNotice; +use App\Services\Logic\Notice\System\QuestionAnswered as QuestionAnsweredNotice; +use App\Services\Logic\Point\History\AnswerPost as AnswerPostPointHistory; use App\Validators\Answer as AnswerValidator; class Answer extends Service { + public function getPublishTypes() + { + return AnswerModel::publishTypes(); + } + + public function getReasons() + { + return ReasonModel::answerRejectOptions(); + } + public function getAnswers() { $pagerQuery = new PagerQuery(); @@ -34,6 +54,33 @@ class Answer extends Service return $this->findOrFail($id); } + public function createAnswer() + { + $post = $this->request->getPost(); + + $user = $this->getLoginUser(); + + $validator = new AnswerValidator(); + + $question = $validator->checkQuestion($post['question_id']); + + $answer = new AnswerModel(); + + $answer->owner_id = $user->id; + $answer->question_id = $question->id; + $answer->published = AnswerModel::PUBLISH_APPROVED; + $answer->content = $validator->checkContent($post['content']); + + $answer->create(); + + $this->recountQuestionAnswers($question); + $this->recountUserAnswers($user); + $this->handleAnswerPostPoint($answer); + $this->handleQuestionAnsweredNotice($answer); + + return $answer; + } + public function updateAnswer($id) { $answer = $this->findOrFail($id); @@ -49,7 +96,16 @@ class Answer extends Service } if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + + $question = $this->findQuestion($answer->question_id); + + $this->recountQuestionAnswers($question); + + $user = $this->findUser($answer->owner_id); + + $this->recountUserAnswers($user); } $answer->update($data); @@ -59,24 +115,91 @@ class Answer extends Service public function deleteAnswer($id) { - $page = $this->findOrFail($id); + $answer = $this->findOrFail($id); - $page->deleted = 1; + $answer->deleted = 1; - $page->update(); + $answer->update(); - return $page; + $question = $this->findQuestion($answer->question_id); + + $this->recountQuestionAnswers($question); + + $owner = $this->findUser($answer->owner_id); + + $this->recountUserAnswers($owner); + + return $answer; } public function restoreAnswer($id) { - $page = $this->findOrFail($id); + $answer = $this->findOrFail($id); - $page->deleted = 0; + $answer->deleted = 0; - $page->update(); + $answer->update(); - return $page; + $question = $this->findQuestion($answer->question_id); + + $this->recountQuestionAnswers($question); + + $owner = $this->findUser($answer->owner_id); + + $this->recountUserAnswers($owner); + + return $answer; + } + + public function reviewAnswer($id) + { + $type = $this->request->getPost('type', ['trim', 'string']); + $reason = $this->request->getPost('reason', ['trim', 'string']); + + $answer = $this->findOrFail($id); + + $validator = new AnswerValidator(); + + if ($type == 'approve') { + $answer->published = AnswerModel::PUBLISH_APPROVED; + } elseif ($type == 'reject') { + $validator->checkRejectReason($reason); + $answer->published = AnswerModel::PUBLISH_REJECTED; + } + + $answer->update(); + + $question = $this->findQuestion($answer->question_id); + + $this->recountQuestionAnswers($question); + + $owner = $this->findUser($answer->owner_id); + + $this->recountUserAnswers($owner); + + $sender = $this->getLoginUser(); + + if ($type == 'approve') { + + $this->handleAnswerPostPoint($answer); + $this->handleAnswerApprovedNotice($answer, $sender); + + $this->eventsManager->fire('Answer:afterApprove', $this, $answer); + + } elseif ($type == 'reject') { + + $options = ReasonModel::answerRejectOptions(); + + if (array_key_exists($reason, $options)) { + $reason = $options[$reason]; + } + + $this->handleAnswerRejectedNotice($answer, $sender, $reason); + + $this->eventsManager->fire('Answer:afterReject', $this, $answer); + } + + return $answer; } protected function findOrFail($id) @@ -86,13 +209,29 @@ class Answer extends Service return $validator->checkAnswer($id); } + protected function findQuestion($id) + { + $questionRepo = new QuestionRepo(); + + return $questionRepo->findById($id); + } + + protected function findUser($id) + { + $userRepo = new UserRepo(); + + return $userRepo->findById($id); + } + protected function handleAnswers($pager) { if ($pager->total_items > 0) { $builder = new AnswerListBuilder(); - $pipeA = $pager->items->toArray(); + $items = $pager->items->toArray(); + + $pipeA = $builder->handleQuestions($items); $pipeB = $builder->handleUsers($pipeA); $pipeC = $builder->objects($pipeB); @@ -102,4 +241,55 @@ class Answer extends Service return $pager; } + protected function recountQuestionAnswers(QuestionModel $question) + { + $questionRepo = new QuestionRepo(); + + $answerCount = $questionRepo->countAnswers($question->id); + + $question->answer_count = $answerCount; + + $question->update(); + } + + protected function recountUserAnswers(UserModel $user) + { + $userRepo = new UserRepo(); + + $answerCount = $userRepo->countAnswers($user->id); + + $user->answer_count = $answerCount; + + $user->update(); + } + + protected function handleQuestionAnsweredNotice(AnswerModel $answer) + { + $notice = new QuestionAnsweredNotice(); + + $notice->handle($answer); + } + + protected function handleAnswerApprovedNotice(AnswerModel $answer, UserModel $sender) + { + $notice = new AnswerApprovedNotice(); + + $notice->handle($answer, $sender); + } + + protected function handleAnswerRejectedNotice(AnswerModel $answer, UserModel $sender, $reason) + { + $notice = new AnswerRejectedNotice(); + + $notice->handle($answer, $sender, $reason); + + } + + protected function handleAnswerPostPoint(AnswerModel $answer) + { + $service = new AnswerPostPointHistory(); + + $service->handle($answer); + } + } diff --git a/app/Http/Admin/Services/Article.php b/app/Http/Admin/Services/Article.php index 84c2d84a..fa39df84 100644 --- a/app/Http/Admin/Services/Article.php +++ b/app/Http/Admin/Services/Article.php @@ -30,7 +30,7 @@ class Article extends Service { $tagRepo = new TagRepo(); - $allTags = $tagRepo->findAll(['published' => 1], 'priority'); + $allTags = $tagRepo->findAll(['published' => 1]); if ($allTags->count() == 0) return []; @@ -78,7 +78,7 @@ class Article extends Service return ArticleModel::sourceTypes(); } - public function getRejectOptions() + public function getReasons() { return ReasonModel::articleRejectOptions(); } @@ -124,13 +124,14 @@ class Article extends Service $article = new ArticleModel(); + $article->published = ArticleModel::PUBLISH_APPROVED; $article->owner_id = $user->id; $article->category_id = $category->id; $article->title = $title; $article->create(); - $this->incrUserArticleCount($user); + $this->recountUserArticles($user); $this->eventsManager->fire('Article:afterCreate', $this, $article); @@ -168,8 +169,8 @@ class Article extends Service } } - if (isset($post['allow_comment'])) { - $data['allow_comment'] = $post['allow_comment']; + if (isset($post['closed'])) { + $data['closed'] = $validator->checkCloseStatus($post['closed']); } if (isset($post['private'])) { @@ -181,7 +182,12 @@ class Article extends Service } if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + + $owner = $this->findUser($article->owner_id); + + $this->recountUserArticles($owner); } if (isset($post['xm_tag_ids'])) { @@ -209,7 +215,7 @@ class Article extends Service $owner = $userRepo->findById($article->owner_id); - $this->decrUserArticleCount($owner); + $this->recountUserArticles($owner); $this->rebuildArticleIndex($article); @@ -230,7 +236,7 @@ class Article extends Service $owner = $userRepo->findById($article->owner_id); - $this->incrUserArticleCount($owner); + $this->recountUserArticles($owner); $this->rebuildArticleIndex($article); @@ -249,14 +255,22 @@ class Article extends Service $validator = new ArticleValidator(); if ($type == 'approve') { + $article->published = ArticleModel::PUBLISH_APPROVED; + } elseif ($type == 'reject') { + $validator->checkRejectReason($reason); + $article->published = ArticleModel::PUBLISH_REJECTED; } $article->update(); + $owner = $this->findUser($article->owner_id); + + $this->recountUserArticles($owner); + $sender = $this->getLoginUser(); if ($type == 'approve') { @@ -296,6 +310,13 @@ class Article extends Service return $validator->checkArticle($id); } + protected function findUser($id) + { + $userRepo = new UserRepo(); + + return $userRepo->findById($id); + } + protected function handleArticles($pager) { if ($pager->total_items > 0) { @@ -315,21 +336,17 @@ class Article extends Service return $pager; } - protected function incrUserArticleCount(UserModel $user) + protected function recountUserArticles(UserModel $user) { - $user->article_count += 1; + $userRepo = new UserRepo(); + + $articleCount = $userRepo->countArticles($user->id); + + $user->article_count = $articleCount; $user->update(); } - protected function decrUserArticleCount(UserModel $user) - { - if ($user->article_count > 0) { - $user->article_count -= 1; - $user->update(); - } - } - protected function rebuildArticleCache(ArticleModel $article) { $cache = new ArticleCache(); diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 656a2bd4..b62914ae 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -294,7 +294,7 @@ class AuthNode extends Service ], [ 'id' => '1-10', - 'title' => '问答管理', + 'title' => '问题管理', 'type' => 'menu', 'children' => [ [ @@ -341,6 +341,55 @@ class AuthNode extends Service ], ], ], + [ + 'id' => '1-11', + 'title' => '回答管理', + 'type' => 'menu', + 'children' => [ + [ + 'id' => '1-11-1', + 'title' => '回答列表', + 'type' => 'menu', + 'route' => 'admin.answer.list', + ], + [ + 'id' => '1-11-2', + 'title' => '搜索回答', + 'type' => 'menu', + 'route' => 'admin.answer.search', + ], + [ + 'id' => '1-11-3', + 'title' => '添加答案', + 'type' => 'button', + 'route' => 'admin.answer.add', + ], + [ + 'id' => '1-11-4', + 'title' => '编辑回答', + 'type' => 'button', + 'route' => 'admin.answer.edit', + ], + [ + 'id' => '1-11-5', + 'title' => '删除回答', + 'type' => 'button', + 'route' => 'admin.answer.delete', + ], + [ + 'id' => '1-11-9', + 'title' => '回答详情', + 'type' => 'button', + 'route' => 'admin.answer.show', + ], + [ + 'id' => '1-11-10', + 'title' => '审核回答', + 'type' => 'button', + 'route' => 'admin.answer.review', + ], + ], + ], [ 'id' => '1-8', 'title' => '标签管理', @@ -436,6 +485,12 @@ class AuthNode extends Service 'type' => 'menu', 'route' => 'admin.mod.questions', ], + [ + 'id' => '2-10-3', + 'title' => '回答列表', + 'type' => 'menu', + 'route' => 'admin.mod.answers', + ], ], ], [ @@ -900,7 +955,13 @@ class AuthNode extends Service 'title' => '编辑用户', 'type' => 'button', 'route' => 'admin.user.edit', - ] + ], + [ + 'id' => '4-1-5', + 'title' => '在线记录', + 'type' => 'button', + 'route' => 'admin.user.online', + ], ], ], [ diff --git a/app/Http/Admin/Services/Consult.php b/app/Http/Admin/Services/Consult.php index 667b99a7..cb93d9a5 100644 --- a/app/Http/Admin/Services/Consult.php +++ b/app/Http/Admin/Services/Consult.php @@ -4,7 +4,10 @@ namespace App\Http\Admin\Services; use App\Builders\ConsultList as ConsultListBuilder; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Chapter as ChapterModel; use App\Models\Consult as ConsultModel; +use App\Models\Course as CourseModel; +use App\Repos\Chapter as ChapterRepo; use App\Repos\Consult as ConsultRepo; use App\Repos\Course as CourseRepo; use App\Services\Logic\Notice\ConsultReply as ConsultReplyNotice; @@ -74,6 +77,7 @@ class Consult extends Service if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); + $this->handleItemConsults($consult); } $consult->update($data); @@ -93,13 +97,7 @@ class Consult extends Service $consult->update(); - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($consult->course_id); - - $course->consult_count -= 1; - - $course->update(); + $this->handleItemConsults($consult); } public function restoreConsult($id) @@ -110,13 +108,20 @@ class Consult extends Service $consult->update(); - $courseRepo = new CourseRepo(); + $this->handleItemConsults($consult); + } - $course = $courseRepo->findById($consult->course_id); + protected function handleItemConsults(ConsultModel $consult) + { + if ($consult->course_id > 0) { + $course = $this->findCourse($consult->course_id); + $this->recountCourseConsults($course); + } - $course->consult_count += 1; - - $course->update(); + if ($consult->chapter_id > 0) { + $chapter = $this->findChapter($consult->chapter_id); + $this->recountChapterConsults($chapter); + } } protected function handleReplyNotice(ConsultModel $consult) @@ -133,6 +138,42 @@ class Consult extends Service return $validator->checkConsult($id); } + protected function findCourse($id) + { + $courseRepo = new CourseRepo(); + + return $courseRepo->findById($id); + } + + protected function findChapter($id) + { + $chapterRepo = new ChapterRepo(); + + return $chapterRepo->findById($id); + } + + protected function recountCourseConsults(CourseModel $course) + { + $courseRepo = new CourseRepo(); + + $consultCount = $courseRepo->countConsults($course->id); + + $course->consult_count = $consultCount; + + $course->update(); + } + + protected function recountChapterConsults(ChapterModel $chapter) + { + $chapterRepo = new ChapterRepo(); + + $consultCount = $chapterRepo->countConsults($chapter->id); + + $chapter->consult_count = $consultCount; + + $chapter->update(); + } + protected function handleConsults($pager) { if ($pager->total_items > 0) { diff --git a/app/Http/Admin/Services/Moderation.php b/app/Http/Admin/Services/Moderation.php index bef5a221..fbf3cfa7 100644 --- a/app/Http/Admin/Services/Moderation.php +++ b/app/Http/Admin/Services/Moderation.php @@ -2,11 +2,14 @@ namespace App\Http\Admin\Services; +use App\Builders\AnswerList as AnswerListBuilder; use App\Builders\ArticleList as ArticleListBuilder; use App\Builders\QuestionList as QuestionListBuilder; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Answer as AnswerModel; use App\Models\Article as ArticleModel; use App\Models\Question as QuestionModel; +use App\Repos\Answer as AnswerRepo; use App\Repos\Article as ArticleRepo; use App\Repos\Question as QuestionRepo; @@ -53,6 +56,26 @@ class Moderation extends Service return $this->handleQuestions($pager); } + public function getAnswers() + { + $pagerQuery = new PagerQuery(); + + $params = $pagerQuery->getParams(); + + $params['published'] = AnswerModel::PUBLISH_PENDING; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $answerRepo = new AnswerRepo(); + + $pager = $answerRepo->paginate($params, $sort, $page, $limit); + + return $this->handleAnswers($pager); + } + protected function handleArticles($pager) { if ($pager->total_items > 0) { @@ -91,4 +114,22 @@ class Moderation extends Service return $pager; } + protected function handleAnswers($pager) + { + if ($pager->total_items > 0) { + + $builder = new AnswerListBuilder(); + + $items = $pager->items->toArray(); + + $pipeA = $builder->handleQuestions($items); + $pipeB = $builder->handleUsers($pipeA); + $pipeC = $builder->objects($pipeB); + + $pager->items = $pipeC; + } + + return $pager; + } + } diff --git a/app/Http/Admin/Services/Question.php b/app/Http/Admin/Services/Question.php index 1fe04d72..eda8ffd3 100644 --- a/app/Http/Admin/Services/Question.php +++ b/app/Http/Admin/Services/Question.php @@ -29,7 +29,7 @@ class Question extends Service { $tagRepo = new TagRepo(); - $allTags = $tagRepo->findAll(['published' => 1], 'priority'); + $allTags = $tagRepo->findAll(['published' => 1]); if ($allTags->count() == 0) return []; @@ -72,7 +72,7 @@ class Question extends Service return QuestionModel::publishTypes(); } - public function getRejectOptions() + public function getReasons() { return ReasonModel::questionRejectOptions(); } @@ -117,12 +117,13 @@ class Question extends Service $question = new QuestionModel(); + $question->published = QuestionModel::PUBLISH_APPROVED; $question->owner_id = $user->id; $question->title = $title; $question->create(); - $this->incrUserQuestionCount($user); + $this->recountUserQuestions($user); $this->eventsManager->fire('Question:afterCreate', $this, $question); @@ -161,7 +162,12 @@ class Question extends Service } if (isset($post['published'])) { + $data['published'] = $validator->checkPublishStatus($post['published']); + + $owner = $this->findUser($question->owner_id); + + $this->recountUserQuestions($owner); } if (isset($post['xm_tag_ids'])) { @@ -189,7 +195,7 @@ class Question extends Service $owner = $userRepo->findById($question->owner_id); - $this->decrUserQuestionCount($owner); + $this->recountUserQuestions($owner); $this->rebuildQuestionIndex($question); @@ -210,7 +216,7 @@ class Question extends Service $owner = $userRepo->findById($question->owner_id); - $this->incrUserQuestionCount($owner); + $this->recountUserQuestions($owner); $this->rebuildQuestionIndex($question); @@ -237,6 +243,10 @@ class Question extends Service $question->update(); + $owner = $this->findUser($question->owner_id); + + $this->recountUserQuestions($owner); + $sender = $this->getLoginUser(); if ($type == 'approve') { @@ -276,6 +286,13 @@ class Question extends Service return $validator->checkQuestion($id); } + protected function findUser($id) + { + $userRepo = new UserRepo(); + + return $userRepo->findById($id); + } + protected function handleQuestions($pager) { if ($pager->total_items > 0) { @@ -295,21 +312,17 @@ class Question extends Service return $pager; } - protected function incrUserQuestionCount(UserModel $user) + protected function recountUserQuestions(UserModel $user) { - $user->question_count += 1; + $userRepo = new UserRepo(); + + $questionCount = $userRepo->countQuestions($user->id); + + $user->question_count = $questionCount; $user->update(); } - protected function decrUserQuestionCount(UserModel $user) - { - if ($user->question_count > 0) { - $user->question_count -= 1; - $user->update(); - } - } - protected function rebuildQuestionCache(QuestionModel $question) { $cache = new QuestionCache(); diff --git a/app/Http/Admin/Services/Review.php b/app/Http/Admin/Services/Review.php index 5ae43fb6..e04c41fb 100644 --- a/app/Http/Admin/Services/Review.php +++ b/app/Http/Admin/Services/Review.php @@ -4,6 +4,7 @@ namespace App\Http\Admin\Services; use App\Builders\ReviewList as ReviewListBuilder; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Course as CourseModel; use App\Repos\Course as CourseRepo; use App\Repos\Review as ReviewRepo; use App\Services\CourseStat as CourseStatService; @@ -47,6 +48,8 @@ class Review extends Service { $review = $this->findOrFail($id); + $course = $this->findCourse($review->course_id); + $post = $this->request->getPost(); $validator = new ReviewValidator(); @@ -71,11 +74,12 @@ class Review extends Service if (isset($post['published'])) { $data['published'] = $validator->checkPublishStatus($post['published']); + $this->recountCourseReviews($course); } $review->update($data); - $this->updateCourseRating($review->course_id); + $this->updateCourseRating($course); return $review; } @@ -88,7 +92,9 @@ class Review extends Service $review->update(); - $this->decrCourseReviewCount($review->course_id); + $course = $this->findCourse($review->course_id); + + $this->recountCourseReviews($course); } public function restoreReview($id) @@ -99,7 +105,9 @@ class Review extends Service $review->update(); - $this->incrCourseReviewCount($review->course_id); + $course = $this->findCourse($review->course_id); + + $this->recountCourseReviews($course); } protected function findOrFail($id) @@ -109,33 +117,29 @@ class Review extends Service return $validator->checkReview($id); } - protected function incrCourseReviewCount($courseId) + protected function findCourse($id) { $courseRepo = new CourseRepo(); - $course = $courseRepo->findById($courseId); + return $courseRepo->findById($id); + } - $course->review_count -= 1; + protected function recountCourseReviews(CourseModel $course) + { + $courseRepo = new CourseRepo(); + + $reviewCount = $courseRepo->countReviews($course->id); + + $course->review_count = $reviewCount; $course->update(); } - protected function decrCourseReviewCount($courseId) - { - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($courseId); - - $course->review_count += 1; - - $course->update(); - } - - protected function updateCourseRating($courseId) + protected function updateCourseRating(CourseModel $course) { $service = new CourseStatService(); - $service->updateRating($courseId); + $service->updateRating($course->id); } protected function handleReviews($pager) diff --git a/app/Http/Admin/Services/User.php b/app/Http/Admin/Services/User.php index 7ebf83c9..ac95ca4e 100644 --- a/app/Http/Admin/Services/User.php +++ b/app/Http/Admin/Services/User.php @@ -10,6 +10,7 @@ use App\Models\Account as AccountModel; use App\Models\ImUser as ImUserModel; use App\Models\User as UserModel; use App\Repos\Account as AccountRepo; +use App\Repos\Online as OnlineRepo; use App\Repos\Role as RoleRepo; use App\Repos\User as UserRepo; use App\Validators\Account as AccountValidator; @@ -30,6 +31,25 @@ class User extends Service return $roleRepo->findAll(['deleted' => 0]); } + public function getOnlineLogs($id) + { + $user = $this->findOrFail($id); + + $pageQuery = new PaginateQuery(); + + $params = $pageQuery->getParams(); + + $params['user_id'] = $user->id; + + $sort = $pageQuery->getSort(); + $page = $pageQuery->getPage(); + $limit = $pageQuery->getLimit(); + + $onlineRepo = new OnlineRepo(); + + return $onlineRepo->paginate($params, $sort, $page, $limit); + } + public function getUsers() { $pageQuery = new PaginateQuery(); diff --git a/app/Http/Admin/Views/answer/add.volt b/app/Http/Admin/Views/answer/add.volt new file mode 100644 index 00000000..4b6a5dcf --- /dev/null +++ b/app/Http/Admin/Views/answer/add.volt @@ -0,0 +1,41 @@ +{% extends 'templates/main.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('admin/js/vditor.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/answer/edit.volt b/app/Http/Admin/Views/answer/edit.volt new file mode 100644 index 00000000..d11df81c --- /dev/null +++ b/app/Http/Admin/Views/answer/edit.volt @@ -0,0 +1,40 @@ +{% extends 'templates/main.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('admin/js/vditor.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/answer/list.volt b/app/Http/Admin/Views/answer/list.volt new file mode 100644 index 00000000..697a5f19 --- /dev/null +++ b/app/Http/Admin/Views/answer/list.volt @@ -0,0 +1,64 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {{ partial('macros/answer') }} + + + + + + + + + + + + + + + + + + + + {% for item in pager.items %} + {% set question_url = url({'for':'home.question.show','id':item.question.id}) %} + {% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %} + {% set review_url = url({'for':'admin.answer.review','id':item.id}) %} + {% set edit_url = url({'for':'admin.answer.edit','id':item.id}) %} + {% set delete_url = url({'for':'admin.answer.delete','id':item.id}) %} + {% set restore_url = url({'for':'admin.answer.restore','id':item.id}) %} + + + + + + + + {% endfor %} + +
信息评论点赞状态操作
+

问题:{{ item.question.title }}

+

回答:{{ item.summary }}

+

作者:{{ item.owner.name }} 创建:{{ date('Y-m-d',item.create_time) }}

+
{{ item.comment_count }}{{ item.like_count }}{{ publish_status(item.published) }} +
+ + +
+
+ + {{ partial('partials/pager') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/answer/review.volt b/app/Http/Admin/Views/answer/review.volt new file mode 100644 index 00000000..c94e5e6e --- /dev/null +++ b/app/Http/Admin/Views/answer/review.volt @@ -0,0 +1,78 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + +
+ 审核内容 +
+ +
+
{{ question.title }}
+
{{ answer.content }}
+
+ +
+ 审核意见 +
+ +
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+
+ +{% endblock %} + +{% block link_css %} + + {{ css_link('home/css/markdown.css') }} + +{% endblock %} + +{% block inline_js %} + + + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/answer/search.volt b/app/Http/Admin/Views/answer/search.volt new file mode 100644 index 00000000..cd0e7b6f --- /dev/null +++ b/app/Http/Admin/Views/answer/search.volt @@ -0,0 +1,58 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + +
+
+ 搜索回答 +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {% for value,title in publish_types %} + + {% endfor %} +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/article/edit_basic.volt b/app/Http/Admin/Views/article/edit_basic.volt index 1889a8b1..ebf4ea31 100644 --- a/app/Http/Admin/Views/article/edit_basic.volt +++ b/app/Http/Admin/Views/article/edit_basic.volt @@ -30,10 +30,10 @@
- +
- - + +
diff --git a/app/Http/Admin/Views/article/list.volt b/app/Http/Admin/Views/article/list.volt index 10cdcdb6..b13ccdea 100644 --- a/app/Http/Admin/Views/article/list.volt +++ b/app/Http/Admin/Views/article/list.volt @@ -38,13 +38,13 @@ 文章 - 状态 - 浏览 评论 + 浏览 点赞 收藏 + 状态 推荐 - 评论 + 关闭 操作 @@ -75,13 +75,13 @@ 创建:{{ date('Y-m-d',item.create_time) }}

- {{ publish_status(item.published) }} {{ item.view_count }} {{ item.comment_count }} {{ item.like_count }} {{ item.favorite_count }} + {{ publish_status(item.published) }} - +
@@ -147,16 +147,16 @@ }); }); - form.on('switch(comment)', function (data) { + form.on('switch(closed)', function (data) { var checked = $(this).is(':checked'); - var allowComment = checked ? 1 : 0; + var closed = checked ? 1 : 0; var url = $(this).data('url'); - var tips = allowComment === 1 ? '确定要开启评论?' : '确定要关闭评论?'; + var tips = closed === 1 ? '确定要关闭评论?' : '确定要开启评论?'; layer.confirm(tips, function () { $.ajax({ type: 'POST', url: url, - data: {allow_comment: allowComment}, + data: {closed: closed}, success: function (res) { layer.msg(res.msg, {icon: 1}); }, diff --git a/app/Http/Admin/Views/article/review.volt b/app/Http/Admin/Views/article/review.volt index 55ac9f67..f24ca97d 100644 --- a/app/Http/Admin/Views/article/review.volt +++ b/app/Http/Admin/Views/article/review.volt @@ -36,7 +36,7 @@
diff --git a/app/Http/Admin/Views/article/search.volt b/app/Http/Admin/Views/article/search.volt index a156ef3c..a21a904d 100644 --- a/app/Http/Admin/Views/article/search.volt +++ b/app/Http/Admin/Views/article/search.volt @@ -46,6 +46,20 @@ {% endfor %}
+
+ +
+ + +
+
+
+ +
+ + +
+
diff --git a/app/Http/Admin/Views/comment/list.volt b/app/Http/Admin/Views/comment/list.volt index 64071a58..539ebbd9 100644 --- a/app/Http/Admin/Views/comment/list.volt +++ b/app/Http/Admin/Views/comment/list.volt @@ -4,9 +4,9 @@ {{ partial('macros/common') }} - +
- + @@ -15,31 +15,23 @@ + - - + {% for item in pager.items %} + {% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %} {% set update_url = url({'for':'admin.comment.update','id':item.id}) %} {% set delete_url = url({'for':'admin.comment.delete','id':item.id}) %} {% set restore_url = url({'for':'admin.comment.restore','id':item.id}) %} - - - - + + + + - - @@ -36,9 +33,8 @@ {% set review_url = url({'for':'admin.article.review','id':item.id}) %} - - + @@ -69,10 +55,4 @@ {{ partial('partials/pager') }} -{% endblock %} - -{% block include_js %} - - {{ js_include('admin/js/ip2region.js') }} - {% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/moderation/questions.volt b/app/Http/Admin/Views/moderation/questions.volt index 6e45647a..4d0a0a6e 100644 --- a/app/Http/Admin/Views/moderation/questions.volt +++ b/app/Http/Admin/Views/moderation/questions.volt @@ -2,7 +2,6 @@ {% block content %} - {{ partial('macros/common') }} {{ partial('macros/question') }}
@@ -18,14 +17,12 @@
- - @@ -47,17 +44,7 @@

昵称:{{ item.owner.name }}

编号:{{ item.owner.id }}

- - + @@ -68,10 +55,4 @@ {{ partial('partials/pager') }} -{% endblock %} - -{% block include_js %} - - {{ js_include('admin/js/ip2region.js') }} - {% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/question/list.volt b/app/Http/Admin/Views/question/list.volt index 7d8d209a..cfabc02a 100644 --- a/app/Http/Admin/Views/question/list.volt +++ b/app/Http/Admin/Views/question/list.volt @@ -37,12 +37,12 @@ - - + - + + @@ -55,7 +55,8 @@ {% set update_url = url({'for':'admin.question.update','id':item.id}) %} {% set delete_url = url({'for':'admin.question.delete','id':item.id}) %} {% set restore_url = url({'for':'admin.question.restore','id':item.id}) %} - {% set answer_url = url({'for':'admin.answer.list'},{'item_id':item.id}) %} + {% set answer_add_url = url({'for':'admin.answer.add'},{'question_id':item.id}) %} + {% set answer_list_url = url({'for':'admin.answer.list'},{'question_id':item.id}) %} - - + - + + @@ -117,11 +119,11 @@ var form = layui.form; var layer = layui.layer; - form.on('switch(discuss)', function (data) { + form.on('switch(closed)', function (data) { var checked = $(this).is(':checked'); - var allowDiscuss = checked ? 1 : 0; + var closed = checked ? 1 : 0; var url = $(this).data('url'); - var tips = allowDiscuss === 1 ? '确定要开启讨论?' : '确定要关闭讨论?'; + var tips = closed === 1 ? '确定要关闭讨论?' : '确定要开启讨论?'; layer.confirm(tips, function () { $.ajax({ type: 'POST', diff --git a/app/Http/Admin/Views/question/review.volt b/app/Http/Admin/Views/question/review.volt index 8ad1193b..e1491b8a 100644 --- a/app/Http/Admin/Views/question/review.volt +++ b/app/Http/Admin/Views/question/review.volt @@ -36,7 +36,7 @@
diff --git a/app/Http/Admin/Views/user/list.volt b/app/Http/Admin/Views/user/list.volt index a9ff78cd..76ea12ba 100644 --- a/app/Http/Admin/Views/user/list.volt +++ b/app/Http/Admin/Views/user/list.volt @@ -33,6 +33,7 @@
+ @@ -42,7 +43,8 @@ - + + @@ -51,6 +53,7 @@ {% for item in pager.items %} {% set preview_url = url({'for':'home.user.show','id':item.id}) %} + {% set online_url = url({'for':'admin.user.online','id':item.id}) %} {% set edit_url = url({'for':'admin.user.edit','id':item.id}) %} - + + @@ -90,4 +95,29 @@ {{ partial('partials/pager') }} +{% endblock %} + +{% block inline_js %} + + + {% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/user/online.volt b/app/Http/Admin/Views/user/online.volt new file mode 100644 index 00000000..86f0af78 --- /dev/null +++ b/app/Http/Admin/Views/user/online.volt @@ -0,0 +1,42 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {{ partial('macros/common') }} + +
评论点赞 用户终端发布时间 操作
-

内容:{{ substr(item.content,0,30) }}

-

时间:{{ date('Y-m-d H:i',item.create_time) }},点赞:{{ item.like_count }}

-
-

昵称:{{ item.owner.name }}

-

编号:{{ item.owner.id }}

-
-

类型:{{ client_type(item.client_type) }}

-

地址:查看

-
{{ item.content }}{{ item.like_count }}{{ item.owner.name }}{{ date('Y-m-d',item.create_time) }} {% if item.deleted == 0 %} 删除 @@ -54,10 +46,4 @@ {{ partial('partials/pager') }} -{% endblock %} - -{% block include_js %} - - {{ js_include('admin/js/ip2region.js') }} - {% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/macros/answer.volt b/app/Http/Admin/Views/macros/answer.volt new file mode 100644 index 00000000..05de2d9f --- /dev/null +++ b/app/Http/Admin/Views/macros/answer.volt @@ -0,0 +1,11 @@ +{%- macro publish_status(type) %} + {% if type == 1 %} + 审核中 + {% elseif type == 2 %} + 已发布 + {% elseif type == 3 %} + 未通过 + {% else %} + 未知 + {% endif %} +{%- endmacro %} \ No newline at end of file diff --git a/app/Http/Admin/Views/macros/article.volt b/app/Http/Admin/Views/macros/article.volt index 9d31a831..c904f6ba 100644 --- a/app/Http/Admin/Views/macros/article.volt +++ b/app/Http/Admin/Views/macros/article.volt @@ -12,11 +12,11 @@ {%- macro source_info(type,url) %} {% if type == 1 %} - 原创 + 原创 {% elseif type == 2 %} - 转载 + 转载 {% elseif type == 3 %} - 翻译 + 翻译 {% else %} N/A {% endif %} diff --git a/app/Http/Admin/Views/moderation/answers.volt b/app/Http/Admin/Views/moderation/answers.volt new file mode 100644 index 00000000..ffeab7d9 --- /dev/null +++ b/app/Http/Admin/Views/moderation/answers.volt @@ -0,0 +1,55 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {{ partial('macros/answer') }} + +
+
+ + 回答审核 + +
+
+ + + + + + + + + + + + + + + + + + {% for item in pager.items %} + {% set question_url = url({'for':'home.question.show','id':item.question.id}) %} + {% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %} + {% set review_url = url({'for':'admin.answer.review','id':item.id}) %} + + + + + + + {% endfor %} + +
回答作者时间操作
+

问题:{{ item.question.title }}

+

回答:{{ substr(item.summary,0,32) }}

+
+

昵称:{{ item.owner.name }}

+

编号:{{ item.owner.id }}

+
{{ date('Y-m-d H:i',item.create_time) }} + 详情 +
+ + {{ partial('partials/pager') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/moderation/articles.volt b/app/Http/Admin/Views/moderation/articles.volt index 63b3782a..f9e1a631 100644 --- a/app/Http/Admin/Views/moderation/articles.volt +++ b/app/Http/Admin/Views/moderation/articles.volt @@ -2,7 +2,6 @@ {% block content %} - {{ partial('macros/common') }} {{ partial('macros/article') }}
@@ -18,14 +17,12 @@
文章 作者终端 时间 操作
-

标题:{{ item.title }}

+

标题:{{ item.title }} {{ source_info(item.source_type,item.source_url) }}

- 来源:{{ source_info(item.source_type,item.source_url) }} {% if item.tags %} 标签:{{ tags_info(item.tags) }} {% endif %} @@ -48,17 +44,7 @@

昵称:{{ item.owner.name }}

编号:{{ item.owner.id }}

-

类型:{{ client_type(item.client_type) }}

-

地址:{{ item.client_ip }}

-
- {% if item.update_time > 0 %} - {{ date('Y-m-d H:i:s',item.update_time) }} - {% else %} - {{ date('Y-m-d H:i:s',item.create_time) }} - {% endif %} - {{ date('Y-m-d H:i',item.create_time) }} 详情
问题 作者终端 时间 操作
-

类型:{{ client_type(item.client_type) }}

-

地址:{{ item.client_ip }}

-
- {% if item.update_time > 0 %} - {{ date('Y-m-d H:i:s',item.update_time) }} - {% else %} - {{ date('Y-m-d H:i:s',item.create_time) }} - {% endif %} - {{ date('Y-m-d H:i',item.create_time) }} 详情
问题状态浏览 回答浏览 点赞 收藏讨论状态关闭 操作

标题:{{ item.title }}({{ item.id }})

@@ -72,12 +73,12 @@ 创建:{{ date('Y-m-d',item.create_time) }}

{{ publish_status(item.published) }}{{ item.view_count }} {{ item.answer_count }}{{ item.view_count }} {{ item.like_count }} {{ item.favorite_count }}{{ publish_status(item.published) }}
@@ -87,6 +88,7 @@ {% elseif item.published == 2 %}
  • 预览问题
  • {% endif %} +
  • 回答问题
  • 编辑问题
  • {% if item.deleted == 0 %}
  • 删除问题
  • @@ -94,7 +96,7 @@
  • 还原问题
  • {% endif %}
    -
  • 回答管理
  • +
  • 回答管理
  • 用户角色 课程 文章收藏提问回答 活跃时间 注册时间 操作
    @@ -71,15 +74,17 @@ {{ item.course_count }} {{ item.article_count }}{{ item.favorite_count }}{{ item.question_count }}{{ item.answer_count }} {{ date('Y-m-d',item.active_time) }} {{ date('Y-m-d',item.create_time) }}
    + + + + + + + + + + + + + + + + {% for item in pager.items %} + + + + + + + {% endfor %} + +
    终端类型终端地址活跃时间创建时间
    {{ client_type(item.client_type) }}{{ item.client_ip }}{{ date('Y-m-d H:i',item.active_time) }}{{ date('Y-m-d H:i',item.create_time) }}
    + + {{ partial('partials/pager') }} + +{% endblock %} + +{% block include_js %} + + {{ js_include('admin/js/ip2region.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Controllers/AnswerController.php b/app/Http/Home/Controllers/AnswerController.php index df126771..f51c77d6 100644 --- a/app/Http/Home/Controllers/AnswerController.php +++ b/app/Http/Home/Controllers/AnswerController.php @@ -10,6 +10,7 @@ use App\Services\Logic\Answer\AnswerDelete as AnswerDeleteService; use App\Services\Logic\Answer\AnswerInfo as AnswerInfoService; use App\Services\Logic\Answer\AnswerLike as AnswerLikeService; use App\Services\Logic\Answer\AnswerUpdate as AnswerUpdateService; +use Phalcon\Mvc\View; /** * @RoutePrefix("/answer") @@ -17,6 +18,14 @@ use App\Services\Logic\Answer\AnswerUpdate as AnswerUpdateService; class AnswerController extends Controller { + /** + * @Get("/tips", name="home.answer.tips") + */ + public function tipsAction() + { + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + } + /** * @Get("/add", name="home.answer.add") */ @@ -28,6 +37,8 @@ class AnswerController extends Controller $question = $service->getQuestion($id); + $this->seo->prependTitle('回答问题'); + $this->view->setVar('question', $question); } @@ -44,6 +55,8 @@ class AnswerController extends Controller $question = $service->getQuestion($answer->question_id); + $this->seo->prependTitle('编辑回答'); + $this->view->setVar('question', $question); $this->view->setVar('answer', $answer); } diff --git a/app/Http/Home/Views/answer/add.volt b/app/Http/Home/Views/answer/add.volt index 0a7bc5c9..085ae598 100644 --- a/app/Http/Home/Views/answer/add.volt +++ b/app/Http/Home/Views/answer/add.volt @@ -33,6 +33,8 @@
    +
    + {% endblock %} {% block link_css %} @@ -47,4 +49,28 @@ {{ js_include('home/js/answer.js') }} {{ js_include('home/js/vditor.js') }} +{% endblock %} + +{% block inline_js %} + + + {% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/answer/tips.volt b/app/Http/Home/Views/answer/tips.volt new file mode 100644 index 00000000..cc8ba34e --- /dev/null +++ b/app/Http/Home/Views/answer/tips.volt @@ -0,0 +1,20 @@ +{% extends 'templates/layer.volt' %} + +{% block content %} + +
    +

    适合作为回答的

    +
      +
    • 经过验证的有效解决办法
    • +
    • 自己的经验指引,对解决问题有帮助
    • +
    • 遵循 Markdown 语法排版,表达语义正确
    • +
    +

    不该作为回答的

    +
      +
    • 询问内容细节或回复楼层
    • +
    • 与题目无关的内容
    • +
    • “赞” “顶” “同问” 等毫无意义的内容
    • +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/article/edit.volt b/app/Http/Home/Views/article/edit.volt index e19cc01b..f88d3a5f 100644 --- a/app/Http/Home/Views/article/edit.volt +++ b/app/Http/Home/Views/article/edit.volt @@ -67,10 +67,10 @@
    - +
    - - + +
    diff --git a/app/Http/Home/Views/article/show.volt b/app/Http/Home/Views/article/show.volt index b152742d..7b145569 100644 --- a/app/Http/Home/Views/article/show.volt +++ b/app/Http/Home/Views/article/show.volt @@ -65,7 +65,7 @@ {% endif %}
    - {% if article.allow_comment == 1 %} + {% if article.closed == 0 %}
    {{ partial('article/comment') }}
    diff --git a/app/Http/Home/Views/macros/notification.volt b/app/Http/Home/Views/macros/notification.volt index 67415610..1b8e7171 100644 --- a/app/Http/Home/Views/macros/notification.volt +++ b/app/Http/Home/Views/macros/notification.volt @@ -16,7 +16,7 @@

    评价内容:{{ info.review.content }}

    {% elseif type == 184 %} {% set article_url = url({'for':'home.article.show','id':info.article.id}) %} -

    你的文章 {{ info.article.title }} 通过了审核

    +

    你的文章 {{ info.article.title }} 审核已通过

    {% elseif type == 185 %} {% set article_url = url({'for':'home.article.show','id':info.article.id}) %}

    你的文章 {{ info.article.title }} 审核未通过

    @@ -33,7 +33,7 @@

    {{ sender.name }} 喜欢了你的文章 {{ info.article.title }}

    {% elseif type == 204 %} {% set question_url = url({'for':'home.question.show','id':info.question.id}) %} -

    你的提问 {{ info.question.title }} 通过了审核

    +

    你的提问 {{ info.question.title }} 审核已通过

    {% elseif type == 205 %} {% set question_url = url({'for':'home.question.show','id':info.question.id}) %}

    你的提问 {{ info.question.title }} 审核未通过

    @@ -48,12 +48,18 @@ {% elseif type == 209 %} {% set question_url = url({'for':'home.question.show','id':info.question.id}) %}

    {{ sender.name }} 喜欢了你的问题 {{ info.question.title }}

    - {% elseif type == 209 %} + {% elseif type == 224 %} {% set question_url = url({'for':'home.question.show','id':info.question.id}) %} -

    {{ sender.name }} 喜欢了你的问题 {{ info.question.title }}

    +

    你对问题 {{ info.question.title }} 的回答,审核已通过

    +

    回答内容:{{ info.answer.summary }}

    + {% elseif type == 225 %} + {% set question_url = url({'for':'home.question.show','id':info.question.id}) %} +

    你对问题 {{ info.question.title }} 的回答,审核未通过

    +

    回答内容:{{ info.answer.summary }}

    {% elseif type == 228 %} {% set question_url = url({'for':'home.question.show','id':info.question.id}) %}

    {{ sender.name }} 喜欢了你对问题 {{ info.question.title }} 的回答

    +

    回答内容:{{ info.answer.summary }}

    {% elseif type == 506 %}

    {{ sender.name }} 回复了你的评论:{{ info.comment.content }}

    回复内容:{{ info.reply.content }}

    diff --git a/app/Http/Home/Views/question/show.volt b/app/Http/Home/Views/question/show.volt index 985319dc..da806f6a 100644 --- a/app/Http/Home/Views/question/show.volt +++ b/app/Http/Home/Views/question/show.volt @@ -59,21 +59,21 @@ {% endif %}
    -
    - {{ partial('question/answer_tips') }} +
    + {% if question.me.answered == 0 %} + + {% endif %}
    {% if question.answer_count > 0 %} -
    -
    -
    -
      -
    • 热门回答
    • -
    • 最新回答
    • -
    -
    -
    -
    -
    +
    +
    +
      +
    • 热门回答
    • +
    • 最新回答
    • +
    +
    +
    +
    @@ -99,11 +99,6 @@
    - {% if question.me.answered == 0 %} - - {% endif %}
    diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php index e20d18d7..757a4408 100644 --- a/app/Library/AppInfo.php +++ b/app/Library/AppInfo.php @@ -11,7 +11,7 @@ class AppInfo protected $link = 'https://koogua.com'; - protected $version = '1.3.3'; + protected $version = '1.3.4'; public function __get($name) { diff --git a/app/Models/Article.php b/app/Models/Article.php index 7f03e9b4..644696cf 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -144,11 +144,11 @@ class Article extends Model public $deleted = 0; /** - * 允许评论 + * 关闭标识 * * @var int */ - public $allow_comment = 1; + public $closed = 0; /** * 文字数 diff --git a/app/Models/Reason.php b/app/Models/Reason.php index ef5c569d..9b1ea57c 100644 --- a/app/Models/Reason.php +++ b/app/Models/Reason.php @@ -41,4 +41,20 @@ class Reason ]; } + public static function answerRejectOptions() + { + return [ + 101 => '答非所问', + 102 => '内容质量差', + 103 => '内容不实', + 104 => '低俗色情', + 105 => '广告软文', + 106 => '恶意对比', + 107 => '涉嫌歧视,恶意抹黑', + 108 => '配图引起不适', + 109 => '内容涉嫌违法', + 110 => '其它问题', + ]; + } + } \ No newline at end of file diff --git a/app/Repos/Answer.php b/app/Repos/Answer.php index 2339c001..8a036a3a 100644 --- a/app/Repos/Answer.php +++ b/app/Repos/Answer.php @@ -90,13 +90,16 @@ class Answer extends Repository public function countAnswers() { - return (int)AnswerModel::count(['conditions' => 'deleted = 0']); + return (int)AnswerModel::count([ + 'conditions' => 'published = :published: AND deleted = 0', + 'bind' => ['published' => AnswerModel::PUBLISH_APPROVED], + ]); } public function countLikes($answerId) { return (int)AnswerLikeModel::count([ - 'conditions' => 'answer_id = :answer_id:', + 'conditions' => 'answer_id = :answer_id: AND deleted = 0', 'bind' => ['answer_id' => $answerId], ]); } diff --git a/app/Repos/Article.php b/app/Repos/Article.php index 0ff01ff4..bbd4f36e 100644 --- a/app/Repos/Article.php +++ b/app/Repos/Article.php @@ -64,6 +64,10 @@ class Article extends Repository $builder->andWhere('published = :published:', ['published' => $where['published']]); } + if (isset($where['closed'])) { + $builder->andWhere('closed = :closed:', ['closed' => $where['closed']]); + } + if (isset($where['deleted'])) { $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); } @@ -79,9 +83,6 @@ class Article extends Repository case 'popular': $orderBy = 'score DESC'; break; - case 'comment': - $orderBy = 'comment_count DESC'; - break; default: $orderBy = 'id DESC'; break; @@ -146,15 +147,15 @@ class Article extends Repository public function countComments($articleId) { return (int)CommentModel::count([ - 'conditions' => 'item_id = ?1 AND item_type = ?2 AND deleted = 0', - 'bind' => [1 => $articleId, 2 => CommentModel::ITEM_ARTICLE], + 'conditions' => 'item_id = ?1 AND item_type = ?2 AND published = ?3 AND deleted = 0', + 'bind' => [1 => $articleId, 2 => CommentModel::ITEM_ARTICLE, 3 => CommentModel::PUBLISH_APPROVED], ]); } public function countLikes($articleId) { return (int)ArticleLikeModel::count([ - 'conditions' => 'article_id = :article_id:', + 'conditions' => 'article_id = :article_id: AND deleted = 0', 'bind' => ['article_id' => $articleId], ]); } @@ -162,7 +163,7 @@ class Article extends Repository public function countFavorites($articleId) { return (int)ArticleFavoriteModel::count([ - 'conditions' => 'article_id = :article_id:', + 'conditions' => 'article_id = :article_id: AND deleted = 0', 'bind' => ['article_id' => $articleId], ]); } diff --git a/app/Repos/Chapter.php b/app/Repos/Chapter.php index e67fa3ef..7e26cef0 100644 --- a/app/Repos/Chapter.php +++ b/app/Repos/Chapter.php @@ -10,6 +10,7 @@ use App\Models\ChapterRead as ChapterReadModel; use App\Models\ChapterUser as ChapterUserModel; use App\Models\ChapterVod as ChapterVodModel; use App\Models\Comment as CommentModel; +use App\Models\Consult as ConsultModel; use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -161,6 +162,14 @@ class Chapter extends Repository ]); } + public function countConsults($chapterId) + { + return (int)ConsultModel::count([ + 'conditions' => 'chapter_id = :chapter_id: AND published = 1 AND deleted = 0', + 'bind' => ['chapter_id' => $chapterId], + ]); + } + public function countUsers($chapterId) { return (int)ChapterUserModel::count([ diff --git a/app/Repos/Course.php b/app/Repos/Course.php index 581c80a8..750afae0 100644 --- a/app/Repos/Course.php +++ b/app/Repos/Course.php @@ -294,7 +294,9 @@ class Course extends Repository public function countCourses() { - return (int)CourseModel::count(['conditions' => 'deleted = 0']); + return (int)CourseModel::count([ + 'conditions' => 'published = 1 AND deleted = 0', + ]); } public function countLessons($courseId) @@ -324,7 +326,7 @@ class Course extends Repository public function countConsults($courseId) { return (int)ConsultModel::count([ - 'conditions' => 'course_id = :course_id: AND published = 1', + 'conditions' => 'course_id = :course_id: AND published = 1 AND deleted = 0', 'bind' => ['course_id' => $courseId], ]); } @@ -332,7 +334,7 @@ class Course extends Repository public function countReviews($courseId) { return (int)ReviewModel::count([ - 'conditions' => 'course_id = :course_id: AND published = 1', + 'conditions' => 'course_id = :course_id: AND published = 1 AND deleted = 0', 'bind' => ['course_id' => $courseId], ]); } @@ -340,7 +342,7 @@ class Course extends Repository public function countFavorites($courseId) { return (int)CourseFavoriteModel::count([ - 'conditions' => 'course_id = :course_id:', + 'conditions' => 'course_id = :course_id: AND deleted = 0', 'bind' => ['course_id' => $courseId], ]); } diff --git a/app/Repos/Online.php b/app/Repos/Online.php index 7a16ce30..eebad626 100644 --- a/app/Repos/Online.php +++ b/app/Repos/Online.php @@ -2,6 +2,7 @@ namespace App\Repos; +use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder; use App\Models\Online as OnlineModel; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -9,6 +10,38 @@ use Phalcon\Mvc\Model\ResultsetInterface; class Online extends Repository { + public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15) + { + $builder = $this->modelsManager->createBuilder(); + + $builder->from(OnlineModel::class); + + $builder->where('1 = 1'); + + if (!empty($where['user_id'])) { + $builder->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); + } + + switch ($sort) { + case 'oldest': + $orderBy = 'id ASC'; + break; + default: + $orderBy = 'id DESC'; + break; + } + + $builder->orderBy($orderBy); + + $pager = new PagerQueryBuilder([ + 'builder' => $builder, + 'page' => $page, + 'limit' => $limit, + ]); + + return $pager->paginate(); + } + /** * @param int $userId * @param string $activeDate diff --git a/app/Repos/Question.php b/app/Repos/Question.php index 4434775a..0e0c99dd 100644 --- a/app/Repos/Question.php +++ b/app/Repos/Question.php @@ -147,29 +147,32 @@ class Question extends Repository public function countQuestions() { - return (int)QuestionModel::count(['conditions' => 'deleted = 0']); + return (int)QuestionModel::count([ + 'conditions' => 'published = :published: AND deleted = 0', + 'bind' => ['published' => QuestionModel::PUBLISH_APPROVED], + ]); } public function countAnswers($questionId) { return (int)AnswerModel::count([ - 'conditions' => 'question_id = :question_id: AND deleted = 0', - 'bind' => ['question_id' => $questionId], + 'conditions' => 'question_id = ?1 AND published = ?2 AND deleted = 0', + 'bind' => [1 => $questionId, 2 => AnswerModel::PUBLISH_APPROVED], ]); } public function countComments($questionId) { return (int)CommentModel::count([ - 'conditions' => 'item_id = ?1 AND item_type = ?2 AND deleted = 0', - 'bind' => [1 => $questionId, 2 => CommentModel::ITEM_QUESTION], + 'conditions' => 'item_id = ?1 AND item_type = ?2 AND published = ?3 AND deleted = 0', + 'bind' => [1 => $questionId, 2 => CommentModel::ITEM_QUESTION, 3 => CommentModel::PUBLISH_APPROVED], ]); } public function countFavorites($questionId) { return (int)QuestionFavoriteModel::count([ - 'conditions' => 'question_id = :question_id:', + 'conditions' => 'question_id = :question_id: AND deleted = 0', 'bind' => ['question_id' => $questionId], ]); } @@ -177,7 +180,7 @@ class Question extends Repository public function countLikes($questionId) { return (int)QuestionLikeModel::count([ - 'conditions' => 'question_id = :question_id:', + 'conditions' => 'question_id = :question_id: AND deleted = 0', 'bind' => ['question_id' => $questionId], ]); } diff --git a/app/Repos/User.php b/app/Repos/User.php index d1199ce0..c6b19ea9 100644 --- a/app/Repos/User.php +++ b/app/Repos/User.php @@ -178,31 +178,31 @@ class User extends Repository public function countArticles($userId) { return (int)ArticleModel::count([ - 'conditions' => 'owner_id = :owner_id: AND published = 1', - 'bind' => ['owner_id' => $userId], + 'conditions' => 'owner_id = ?1 AND published = ?2', + 'bind' => [1 => $userId, 2 => ArticleModel::PUBLISH_APPROVED], ]); } public function countQuestions($userId) { return (int)QuestionModel::count([ - 'conditions' => 'owner_id = :owner_id: AND published = 1', - 'bind' => ['owner_id' => $userId], + 'conditions' => 'owner_id = ?1 AND published = ?2', + 'bind' => [1 => $userId, 2 => QuestionModel::PUBLISH_APPROVED], ]); } public function countAnswers($userId) { return (int)AnswerModel::count([ - 'conditions' => 'owner_id = :owner_id: AND published = 1', - 'bind' => ['owner_id' => $userId], + 'conditions' => 'owner_id = ?1 AND published = ?2', + 'bind' => [1 => $userId, 2 => AnswerModel::PUBLISH_APPROVED], ]); } public function countCourseFavorites($userId) { return (int)CourseFavoriteModel::count([ - 'conditions' => 'user_id = :user_id:', + 'conditions' => 'user_id = :user_id: AND deleted = 0', 'bind' => ['user_id' => $userId], ]); } @@ -210,7 +210,7 @@ class User extends Repository public function countArticleFavorites($userId) { return (int)ArticleFavoriteModel::count([ - 'conditions' => 'user_id = :user_id:', + 'conditions' => 'user_id = :user_id: AND deleted = 0', 'bind' => ['user_id' => $userId], ]); } diff --git a/app/Services/Logic/Answer/AnswerCreate.php b/app/Services/Logic/Answer/AnswerCreate.php index d7d6bf18..ec848439 100644 --- a/app/Services/Logic/Answer/AnswerCreate.php +++ b/app/Services/Logic/Answer/AnswerCreate.php @@ -5,11 +5,14 @@ namespace App\Services\Logic\Answer; use App\Models\Answer as AnswerModel; use App\Models\Question as QuestionModel; use App\Models\User as UserModel; +use App\Repos\Question as QuestionRepo; +use App\Repos\User as UserRepo; use App\Services\Logic\AnswerTrait; use App\Services\Logic\Notice\System\QuestionAnswered as QuestionAnsweredNotice; use App\Services\Logic\Point\History\AnswerPost as AnswerPostPointHistory; use App\Services\Logic\QuestionTrait; use App\Services\Logic\Service as LogicService; +use App\Services\Sync\QuestionScore as QuestionScoreSync; use App\Traits\Client as ClientTrait; use App\Validators\Answer as AnswerValidator; @@ -28,16 +31,13 @@ class AnswerCreate extends LogicService $user = $this->getLoginUser(); - $answer = new AnswerModel(); - $validator = new AnswerValidator(); $validator->checkIfAllowAnswer($question, $user); - /** - * @todo 引入自动审核机制 - */ - $answer->published = AnswerModel::PUBLISH_APPROVED; + $answer = new AnswerModel(); + + $answer->published = $this->getPublishStatus($user); $answer->content = $validator->checkContent($post['content']); $answer->client_type = $this->getClientType(); @@ -47,48 +47,74 @@ class AnswerCreate extends LogicService $answer->create(); - $question->last_answer_id = $answer->id; - $question->last_replier_id = $answer->owner_id; - $question->last_reply_time = $answer->create_time; + $this->recountQuestionAnswers($question); + $this->recountUserAnswers($user); - $question->update(); + if ($answer->published == AnswerModel::PUBLISH_APPROVED) { - $this->incrUserAnswerCount($user); + $question->last_answer_id = $answer->id; + $question->last_replier_id = $answer->owner_id; + $question->last_reply_time = $answer->create_time; - $this->incrQuestionAnswerCount($question); + $question->update(); - $this->handleAnswerPoint($answer); - - $this->handleAnswerNotice($answer); + $this->syncQuestionScore($question); + $this->handleAnswerPostPoint($answer); + $this->handleQuestionAnsweredNotice($answer); + } $this->eventsManager->fire('Answer:afterCreate', $this, $answer); return $answer; } - protected function incrQuestionAnswerCount(QuestionModel $question) + protected function getPublishStatus(UserModel $user) { - $question->answer_count += 1; + return $user->answer_count > 2 ? AnswerModel::PUBLISH_APPROVED : AnswerModel::PUBLISH_PENDING; + } + + protected function recountQuestionAnswers(QuestionModel $question) + { + $questionRepo = new QuestionRepo(); + + $answerCount = $questionRepo->countAnswers($question->id); + + $question->answer_count = $answerCount; $question->update(); } - protected function incrUserAnswerCount(UserModel $user) + protected function recountUserAnswers(UserModel $user) { - $user->answer_count += 1; + $userRepo = new UserRepo(); + + $answerCount = $userRepo->countAnswers($user->id); + + $user->answer_count = $answerCount; $user->update(); } - protected function handleAnswerNotice(AnswerModel $answer) + protected function syncQuestionScore(QuestionModel $question) { + $sync = new QuestionScoreSync(); + + $sync->addItem($question->id); + } + + protected function handleQuestionAnsweredNotice(AnswerModel $answer) + { + if ($answer->published != AnswerModel::PUBLISH_APPROVED) return; + $notice = new QuestionAnsweredNotice(); $notice->handle($answer); } - protected function handleAnswerPoint(AnswerModel $answer) + protected function handleAnswerPostPoint(AnswerModel $answer) { + if ($answer->published != AnswerModel::PUBLISH_APPROVED) return; + $service = new AnswerPostPointHistory(); $service->handle($answer); diff --git a/app/Services/Logic/Answer/AnswerDelete.php b/app/Services/Logic/Answer/AnswerDelete.php index ee9f52bf..a707efb3 100644 --- a/app/Services/Logic/Answer/AnswerDelete.php +++ b/app/Services/Logic/Answer/AnswerDelete.php @@ -4,13 +4,14 @@ namespace App\Services\Logic\Answer; use App\Models\Question as QuestionModel; use App\Models\User as UserModel; +use App\Repos\Question as QuestionRepo; +use App\Repos\User as UserRepo; use App\Services\Logic\AnswerTrait; -use App\Services\Logic\Question\QuestionScore as QuestionScoreService; use App\Services\Logic\QuestionTrait; use App\Services\Logic\Service as LogicService; +use App\Services\Sync\QuestionScore as QuestionScoreSync; use App\Validators\Answer as AnswerValidator; - class AnswerDelete extends LogicService { @@ -29,40 +30,47 @@ class AnswerDelete extends LogicService $validator->checkOwner($user->id, $answer->owner_id); + $validator->checkIfAllowDelete($answer); + $answer->deleted = 1; $answer->update(); - $this->decrQuestionAnswerCount($question); - - $this->updateQuestionScore($question); + $this->recountQuestionAnswers($question); + $this->recountUserAnswers($user); $this->eventsManager->fire('Answer:afterDelete', $this, $answer); return $answer; } - protected function decrUserAnswerCount(UserModel $user) + protected function recountQuestionAnswers(QuestionModel $question) { - if ($user->answer_count > 0) { - $user->answer_count -= 1; - $user->update(); - } + $questionRepo = new QuestionRepo(); + + $answerCount = $questionRepo->countAnswers($question->id); + + $question->answer_count = $answerCount; + + $question->update(); } - protected function decrQuestionAnswerCount(QuestionModel $question) + protected function recountUserAnswers(UserModel $user) { - if ($question->answer_count > 0) { - $question->answer_count -= 1; - $question->update(); - } + $userRepo = new UserRepo(); + + $answerCount = $userRepo->countAnswers($user->id); + + $user->answer_count = $answerCount; + + $user->update(); } - protected function updateQuestionScore(QuestionModel $question) + protected function syncQuestionScore(QuestionModel $question) { - $service = new QuestionScoreService(); + $sync = new QuestionScoreSync(); - $service->handle($question); + $sync->addItem($question->id); } } diff --git a/app/Services/Logic/Answer/AnswerUpdate.php b/app/Services/Logic/Answer/AnswerUpdate.php index deaf5c75..2ecf551b 100644 --- a/app/Services/Logic/Answer/AnswerUpdate.php +++ b/app/Services/Logic/Answer/AnswerUpdate.php @@ -23,13 +23,13 @@ class AnswerUpdate extends LogicService $answer = $this->checkAnswer($id); - $question = $this->checkQuestion($answer->question_id); - $user = $this->getLoginUser(); $validator = new AnswerValidator(); - $validator->checkIfAllowEdit($answer, $user); + $validator->checkOwner($user->id, $answer->owner_id); + + $validator->checkIfAllowEdit($answer); $answer->content = $validator->checkContent($post['content']); $answer->client_type = $this->getClientType(); @@ -37,8 +37,6 @@ class AnswerUpdate extends LogicService $answer->update(); - $this->syncQuestionScore($question); - $this->eventsManager->fire('Answer:afterUpdate', $this, $answer); return $answer; diff --git a/app/Services/Logic/Article/ArticleCreate.php b/app/Services/Logic/Article/ArticleCreate.php index 17b3faaa..616d7857 100644 --- a/app/Services/Logic/Article/ArticleCreate.php +++ b/app/Services/Logic/Article/ArticleCreate.php @@ -4,6 +4,7 @@ namespace App\Services\Logic\Article; use App\Models\Article as ArticleModel; use App\Models\User as UserModel; +use App\Repos\User as UserRepo; use App\Services\Logic\Service as LogicService; class ArticleCreate extends LogicService @@ -21,6 +22,7 @@ class ArticleCreate extends LogicService $data = $this->handlePostData($post); + $data['published'] = $this->getPublishStatus($user); $data['owner_id'] = $user->id; $article->create($data); @@ -29,16 +31,25 @@ class ArticleCreate extends LogicService $this->saveTags($article, $post['xm_tag_ids']); } - $this->incrUserArticleCount($user); + $this->recountUserArticles($user); $this->eventsManager->fire('Article:afterCreate', $this, $article); return $article; } - protected function incrUserArticleCount(UserModel $user) + protected function getPublishStatus(UserModel $user) { - $user->article_count += 1; + return $user->article_count > 100 ? ArticleModel::PUBLISH_APPROVED : ArticleModel::PUBLISH_PENDING; + } + + protected function recountUserArticles(UserModel $user) + { + $userRepo = new UserRepo(); + + $articleCount = $userRepo->countArticles($user->id); + + $user->article_count = $articleCount; $user->update(); } diff --git a/app/Services/Logic/Article/ArticleDataTrait.php b/app/Services/Logic/Article/ArticleDataTrait.php index 379e0bd6..8dc2bfba 100644 --- a/app/Services/Logic/Article/ArticleDataTrait.php +++ b/app/Services/Logic/Article/ArticleDataTrait.php @@ -37,8 +37,8 @@ trait ArticleDataTrait } } - if (isset($post['allow_comment'])) { - $data['allow_comment'] = $validator->checkAllowCommentStatus($post['allow_comment']); + if (isset($post['closed'])) { + $data['closed'] = $validator->checkCloseStatus($post['closed']); } if (isset($post['private'])) { diff --git a/app/Services/Logic/Article/ArticleDelete.php b/app/Services/Logic/Article/ArticleDelete.php index ebb85128..3affcb7c 100644 --- a/app/Services/Logic/Article/ArticleDelete.php +++ b/app/Services/Logic/Article/ArticleDelete.php @@ -4,6 +4,7 @@ namespace App\Services\Logic\Article; use App\Models\Article as ArticleModel; use App\Models\User as UserModel; +use App\Repos\User as UserRepo; use App\Services\Logic\ArticleTrait; use App\Services\Logic\Service as LogicService; use App\Services\Sync\ArticleIndex as ArticleIndexSync; @@ -28,19 +29,22 @@ class ArticleDelete extends LogicService $article->update(); - $this->decrUserArticleCount($user); + $this->recountUserArticles($user); $this->rebuildArticleIndex($article); $this->eventsManager->fire('Article:afterDelete', $this, $article); } - protected function decrUserArticleCount(UserModel $user) + protected function recountUserArticles(UserModel $user) { - if ($user->article_count > 0) { - $user->article_count -= 1; - $user->update(); - } + $userRepo = new UserRepo(); + + $articleCount = $userRepo->countArticles($user->id); + + $user->article_count = $articleCount; + + $user->update(); } protected function rebuildArticleIndex(ArticleModel $article) diff --git a/app/Services/Logic/Article/ArticleInfo.php b/app/Services/Logic/Article/ArticleInfo.php index bbf6c933..29d9cb6a 100644 --- a/app/Services/Logic/Article/ArticleInfo.php +++ b/app/Services/Logic/Article/ArticleInfo.php @@ -51,7 +51,7 @@ class ArticleInfo extends LogicService 'owner' => $owner, 'me' => $me, 'private' => $article->private, - 'allow_comment' => $article->allow_comment, + 'closed' => $article->closed, 'source_type' => $article->source_type, 'source_url' => $article->source_url, 'word_count' => $article->word_count, diff --git a/app/Services/Logic/Article/ArticleList.php b/app/Services/Logic/Article/ArticleList.php index 100b685c..ae58b3ea 100644 --- a/app/Services/Logic/Article/ArticleList.php +++ b/app/Services/Logic/Article/ArticleList.php @@ -82,7 +82,7 @@ class ArticleList extends LogicService 'owner' => $owner, 'private' => $article['private'], 'published' => $article['published'], - 'allow_comment' => $article['allow_comment'], + 'closed' => $article['closed'], 'view_count' => $article['view_count'], 'like_count' => $article['like_count'], 'comment_count' => $article['comment_count'], diff --git a/app/Services/Logic/Consult/ConsultCreate.php b/app/Services/Logic/Consult/ConsultCreate.php index ff6d8260..6336a490 100644 --- a/app/Services/Logic/Consult/ConsultCreate.php +++ b/app/Services/Logic/Consult/ConsultCreate.php @@ -6,6 +6,8 @@ use App\Models\Chapter as ChapterModel; use App\Models\Consult as ConsultModel; use App\Models\Course as CourseModel; use App\Models\User as UserModel; +use App\Repos\Chapter as ChapterRepo; +use App\Repos\Course as CourseRepo; use App\Services\Logic\ChapterTrait; use App\Services\Logic\CourseTrait; use App\Services\Logic\Notice\DingTalk\ConsultCreate as ConsultCreateNotice; @@ -76,10 +78,8 @@ class ConsultCreate extends LogicService $consult->create(); - $this->incrCourseConsultCount($course); - + $this->recountCourseConsults($course); $this->incrUserDailyConsultCount($user); - $this->handleConsultCreateNotice($consult); $this->eventsManager->fire('Consult:afterCreate', $this, $consult); @@ -116,12 +116,9 @@ class ConsultCreate extends LogicService $consult->create(); - $this->incrCourseConsultCount($course); - - $this->incrChapterConsultCount($chapter); - + $this->recountCourseConsults($course); + $this->recountChapterConsults($chapter); $this->incrUserDailyConsultCount($user); - $this->handleConsultCreateNotice($consult); $this->eventsManager->fire('Consult:afterCreate', $this, $consult); @@ -146,16 +143,24 @@ class ConsultCreate extends LogicService return $priority; } - protected function incrCourseConsultCount(CourseModel $course) + protected function recountCourseConsults(CourseModel $course) { - $course->consult_count += 1; + $courseRepo = new CourseRepo(); + + $consultCount = $courseRepo->countConsults($course->id); + + $course->consult_count = $consultCount; $course->update(); } - protected function incrChapterConsultCount(ChapterModel $chapter) + protected function recountChapterConsults(ChapterModel $chapter) { - $chapter->consult_count += 1; + $chapterRepo = new ChapterRepo(); + + $consultCount = $chapterRepo->countConsults($chapter->id); + + $chapter->consult_count = $consultCount; $chapter->update(); } diff --git a/app/Services/Logic/Consult/ConsultDelete.php b/app/Services/Logic/Consult/ConsultDelete.php index 26aaee0a..42ab290a 100644 --- a/app/Services/Logic/Consult/ConsultDelete.php +++ b/app/Services/Logic/Consult/ConsultDelete.php @@ -4,6 +4,8 @@ namespace App\Services\Logic\Consult; 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\Logic\ChapterTrait; use App\Services\Logic\ConsultTrait; use App\Services\Logic\CourseTrait; @@ -35,33 +37,39 @@ class ConsultDelete extends LogicService $course = $this->checkCourse($consult->course_id); - $this->decrCourseConsultCount($course); + $this->recountCourseConsults($course); } if ($consult->chapter_id > 0) { $chapter = $this->checkChapter($consult->chapter_id); - $this->decrChapterConsultCount($chapter); + $this->recountChapterConsults($chapter); } $this->eventsManager->fire('Consult:afterDelete', $this, $consult); } - protected function decrCourseConsultCount(CourseModel $course) + protected function recountCourseConsults(CourseModel $course) { - if ($course->consult_count > 0) { - $course->consult_count -= 1; - $course->update(); - } + $courseRepo = new CourseRepo(); + + $consultCount = $courseRepo->countConsults($course->id); + + $course->consult_count = $consultCount; + + $course->update(); } - protected function decrChapterConsultCount(ChapterModel $chapter) + protected function recountChapterConsults(ChapterModel $chapter) { - if ($chapter->consult_count > 0) { - $chapter->consult_count -= 1; - $chapter->update(); - } + $chapterRepo = new ChapterRepo(); + + $consultCount = $chapterRepo->countConsults($chapter->id); + + $chapter->consult_count = $consultCount; + + $chapter->update(); } } diff --git a/app/Services/Logic/Notice/System/AnswerAccepted.php b/app/Services/Logic/Notice/System/AnswerAccepted.php index 113b9893..3acd421a 100644 --- a/app/Services/Logic/Notice/System/AnswerAccepted.php +++ b/app/Services/Logic/Notice/System/AnswerAccepted.php @@ -13,6 +13,8 @@ class AnswerAccepted extends LogicService public function handle(AnswerModel $answer, UserModel $sender) { + $answerSummary = kg_substr($answer->summary, 0, 32); + $questionRepo = new QuestionRepo(); $question = $questionRepo->findById($answer->question_id); @@ -25,6 +27,7 @@ class AnswerAccepted extends LogicService $notification->event_type = NotificationModel::TYPE_ANSWER_ACCEPTED; $notification->event_info = [ 'question' => ['id' => $question->id, 'title' => $question->title], + 'answer' => ['id' => $answer->id, 'summary' => $answerSummary], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/AnswerApproved.php b/app/Services/Logic/Notice/System/AnswerApproved.php new file mode 100644 index 00000000..4c882c43 --- /dev/null +++ b/app/Services/Logic/Notice/System/AnswerApproved.php @@ -0,0 +1,41 @@ +summary, 0, 32); + + $question = $this->findQuestion($answer->question_id); + + $notification = new NotificationModel(); + + $notification->sender_id = $sender->id; + $notification->receiver_id = $answer->owner_id; + $notification->event_id = $answer->id; + $notification->event_type = NotificationModel::TYPE_ANSWER_APPROVED; + $notification->event_info = [ + 'question' => ['id' => $question->id, 'title' => $question->title], + 'answer' => ['id' => $answer->id, 'summary' => $answerSummary], + ]; + + $notification->create(); + } + + protected function findQuestion($id) + { + $questionRepo = new QuestionRepo(); + + return $questionRepo->findById($id); + } + +} diff --git a/app/Services/Logic/Notice/System/AnswerLiked.php b/app/Services/Logic/Notice/System/AnswerLiked.php index a49bd8c7..40c826ae 100644 --- a/app/Services/Logic/Notice/System/AnswerLiked.php +++ b/app/Services/Logic/Notice/System/AnswerLiked.php @@ -13,6 +13,8 @@ class AnswerLiked extends LogicService public function handle(AnswerModel $answer, UserModel $sender) { + $answerSummary = kg_substr($answer->summary, 0, 32); + $questionRepo = new QuestionRepo(); $question = $questionRepo->findById($answer->question_id); @@ -25,6 +27,7 @@ class AnswerLiked extends LogicService $notification->event_type = NotificationModel::TYPE_ANSWER_LIKED; $notification->event_info = [ 'question' => ['id' => $question->id, 'title' => $question->title], + 'answer' => ['id' => $answer->id, 'summary' => $answerSummary], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/AnswerRejected.php b/app/Services/Logic/Notice/System/AnswerRejected.php new file mode 100644 index 00000000..1df58962 --- /dev/null +++ b/app/Services/Logic/Notice/System/AnswerRejected.php @@ -0,0 +1,42 @@ +summary, 0, 32); + + $question = $this->findQuestion($answer->question_id); + + $notification = new NotificationModel(); + + $notification->sender_id = $sender->id; + $notification->receiver_id = $answer->owner_id; + $notification->event_id = $answer->id; + $notification->event_type = NotificationModel::TYPE_ANSWER_REJECTED; + $notification->event_info = [ + 'question' => ['id' => $question->id, 'title' => $question->title], + 'answer' => ['id' => $answer->id, 'summary' => $answerSummary], + 'reason' => $reason, + ]; + + $notification->create(); + } + + protected function findQuestion($id) + { + $questionRepo = new QuestionRepo(); + + return $questionRepo->findById($id); + } + +} diff --git a/app/Services/Logic/Notice/System/ArticleCommented.php b/app/Services/Logic/Notice/System/ArticleCommented.php index 4bf1b412..7f5927e7 100644 --- a/app/Services/Logic/Notice/System/ArticleCommented.php +++ b/app/Services/Logic/Notice/System/ArticleCommented.php @@ -13,7 +13,7 @@ class ArticleCommented extends LogicService public function handle(CommentModel $comment) { - $comment->content = kg_substr($comment->content, 0, 32); + $commentContent = kg_substr($comment->content, 0, 32); $article = $this->findArticle($comment->item_id); @@ -25,7 +25,7 @@ class ArticleCommented extends LogicService $notification->event_type = NotificationModel::TYPE_ARTICLE_COMMENTED; $notification->event_info = [ 'article' => ['id' => $article->id, 'title' => $article->title], - 'comment' => ['id' => $comment->id, 'content' => $comment->content], + 'comment' => ['id' => $comment->id, 'content' => $commentContent], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/CommentLiked.php b/app/Services/Logic/Notice/System/CommentLiked.php index 538f7dfe..8a771b4c 100644 --- a/app/Services/Logic/Notice/System/CommentLiked.php +++ b/app/Services/Logic/Notice/System/CommentLiked.php @@ -12,7 +12,7 @@ class CommentLiked extends LogicService public function handle(CommentModel $comment, UserModel $sender) { - $comment->content = kg_substr($comment->content, 0, 32); + $commentContent = kg_substr($comment->content, 0, 32); $notification = new NotificationModel(); @@ -22,7 +22,7 @@ class CommentLiked extends LogicService $notification->event_type = NotificationModel::TYPE_COMMENT_LIKED; $notification->event_info = [ 'sender' => ['id' => $sender->id, 'name' => $sender->name], - 'comment' => ['id' => $comment->id, 'content' => $comment->content], + 'comment' => ['id' => $comment->id, 'content' => $commentContent], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/CommentReplied.php b/app/Services/Logic/Notice/System/CommentReplied.php index 3c41f672..b4d87513 100644 --- a/app/Services/Logic/Notice/System/CommentReplied.php +++ b/app/Services/Logic/Notice/System/CommentReplied.php @@ -13,11 +13,11 @@ class CommentReplied extends LogicService public function handle(CommentModel $reply) { - $reply->content = kg_substr($reply->content, 0, 32); + $replyContent = kg_substr($reply->content, 0, 32); $comment = $this->findComment($reply->parent_id); - $comment->content = kg_substr($comment->content, 0, 32); + $commentContent = kg_substr($comment->content, 0, 32); $notification = new NotificationModel(); @@ -26,8 +26,8 @@ class CommentReplied extends LogicService $notification->event_id = $reply->id; $notification->event_type = NotificationModel::TYPE_COMMENT_REPLIED; $notification->event_info = [ - 'comment' => ['id' => $comment->id, 'content' => $comment->content], - 'reply' => ['id' => $reply->id, 'content' => $reply->content], + 'comment' => ['id' => $comment->id, 'content' => $commentContent], + 'reply' => ['id' => $reply->id, 'content' => $replyContent], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/ConsultLiked.php b/app/Services/Logic/Notice/System/ConsultLiked.php index 759d9661..1503454b 100644 --- a/app/Services/Logic/Notice/System/ConsultLiked.php +++ b/app/Services/Logic/Notice/System/ConsultLiked.php @@ -13,7 +13,7 @@ class ConsultLiked extends LogicService public function handle(ConsultModel $consult, UserModel $sender) { - $consult->question = kg_substr($consult->question, 0, 32); + $consultQuestion = kg_substr($consult->question, 0, 32); $course = $this->findCourse($consult->course_id); @@ -25,7 +25,7 @@ class ConsultLiked extends LogicService $notification->event_type = NotificationModel::TYPE_CONSULT_LIKED; $notification->event_info = [ 'course' => ['id' => $course->id, 'title' => $course->title], - 'consult' => ['id' => $consult->id, 'question' => $consult->question], + 'consult' => ['id' => $consult->id, 'question' => $consultQuestion], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/QuestionAnswered.php b/app/Services/Logic/Notice/System/QuestionAnswered.php index 0a75087a..a263b376 100644 --- a/app/Services/Logic/Notice/System/QuestionAnswered.php +++ b/app/Services/Logic/Notice/System/QuestionAnswered.php @@ -13,6 +13,8 @@ class QuestionAnswered extends LogicService public function handle(AnswerModel $answer) { + $answerSummary = kg_substr($answer->summary, 0, 32); + $question = $this->findQuestion($answer->question_id); $notification = new NotificationModel(); @@ -23,7 +25,7 @@ class QuestionAnswered extends LogicService $notification->event_type = NotificationModel::TYPE_QUESTION_ANSWERED; $notification->event_info = [ 'question' => ['id' => $question->id, 'title' => $question->title], - 'answer' => ['id' => $answer->id, 'summary' => $answer->summary], + 'answer' => ['id' => $answer->id, 'summary' => $answerSummary], ]; $notification->create(); diff --git a/app/Services/Logic/Notice/System/ReviewLiked.php b/app/Services/Logic/Notice/System/ReviewLiked.php index 9de50536..23fce6b9 100644 --- a/app/Services/Logic/Notice/System/ReviewLiked.php +++ b/app/Services/Logic/Notice/System/ReviewLiked.php @@ -13,7 +13,7 @@ class ReviewLiked extends LogicService public function handle(ReviewModel $review, UserModel $sender) { - $review->content = kg_substr($review->content, 0, 32); + $reviewContent = kg_substr($review->content, 0, 32); $course = $this->findCourse($review->course_id); @@ -25,7 +25,7 @@ class ReviewLiked extends LogicService $notification->event_type = NotificationModel::TYPE_REVIEW_LIKED; $notification->event_info = [ 'course' => ['id' => $course->id, 'title' => $course->title], - 'review' => ['id' => $review->id, 'content' => $review->content], + 'review' => ['id' => $review->id, 'content' => $reviewContent], ]; $notification->create(); diff --git a/app/Services/Logic/Question/QuestionCreate.php b/app/Services/Logic/Question/QuestionCreate.php index bf0f0cd1..96cd6b92 100644 --- a/app/Services/Logic/Question/QuestionCreate.php +++ b/app/Services/Logic/Question/QuestionCreate.php @@ -4,6 +4,7 @@ namespace App\Services\Logic\Question; use App\Models\Question as QuestionModel; use App\Models\User as UserModel; +use App\Repos\User as UserRepo; use App\Services\Logic\Service as LogicService; class QuestionCreate extends LogicService @@ -29,16 +30,25 @@ class QuestionCreate extends LogicService $this->saveTags($question, $post['xm_tag_ids']); } - $this->incrUserQuestionCount($user); + $this->recountUserQuestions($user); $this->eventsManager->fire('Question:afterCreate', $this, $question); return $question; } - protected function incrUserQuestionCount(UserModel $user) + protected function getPublishStatus(UserModel $user) { - $user->question_count += 1; + return $user->question_count > 3 ? QuestionModel::PUBLISH_APPROVED : QuestionModel::PUBLISH_PENDING; + } + + protected function recountUserQuestions(UserModel $user) + { + $userRepo = new UserRepo(); + + $questionCount = $userRepo->countQuestions($user->id); + + $user->question_count = $questionCount; $user->update(); } diff --git a/app/Services/Logic/Question/QuestionDelete.php b/app/Services/Logic/Question/QuestionDelete.php index 1409d4e0..85b720bc 100644 --- a/app/Services/Logic/Question/QuestionDelete.php +++ b/app/Services/Logic/Question/QuestionDelete.php @@ -4,6 +4,7 @@ namespace App\Services\Logic\Question; use App\Models\Question as QuestionModel; use App\Models\User as UserModel; +use App\Repos\User as UserRepo; use App\Services\Logic\QuestionTrait; use App\Services\Logic\Service as LogicService; use App\Services\Sync\QuestionIndex as QuestionIndexSync; @@ -30,7 +31,7 @@ class QuestionDelete extends LogicService $question->update(); - $this->decrUserQuestionCount($user); + $this->recountUserQuestions($user); $this->rebuildQuestionIndex($question); @@ -39,12 +40,15 @@ class QuestionDelete extends LogicService return $question; } - protected function decrUserQuestionCount(UserModel $user) + protected function recountUserQuestions(UserModel $user) { - if ($user->question_count > 0) { - $user->question_count -= 1; - $user->update(); - } + $userRepo = new UserRepo(); + + $questionCount = $userRepo->countQuestions($user->id); + + $user->question_count = $questionCount; + + $user->update(); } protected function rebuildQuestionIndex(QuestionModel $question) diff --git a/app/Services/Logic/Review/ReviewCreate.php b/app/Services/Logic/Review/ReviewCreate.php index 3cd6090e..001044a3 100644 --- a/app/Services/Logic/Review/ReviewCreate.php +++ b/app/Services/Logic/Review/ReviewCreate.php @@ -5,6 +5,7 @@ namespace App\Services\Logic\Review; use App\Models\Course as CourseModel; use App\Models\CourseUser as CourseUserModel; use App\Models\Review as ReviewModel; +use App\Repos\Course as CourseRepo; use App\Services\CourseStat as CourseStatService; use App\Services\Logic\CourseTrait; use App\Services\Logic\Point\History\CourseReview as CourseReviewPointHistory; @@ -48,17 +49,15 @@ class ReviewCreate extends LogicService $data['rating1'] = $validator->checkRating($post['rating1']); $data['rating2'] = $validator->checkRating($post['rating2']); $data['rating3'] = $validator->checkRating($post['rating3']); + $data['published'] = 1; $review = new ReviewModel(); $review->create($data); $this->updateCourseUserReview($courseUser); - - $this->incrCourseReviewCount($course); - + $this->recountCourseReviews($course); $this->updateCourseRating($course); - $this->handleReviewPoint($review); $this->eventsManager->fire('Review:afterCreate', $this, $review); @@ -73,13 +72,6 @@ class ReviewCreate extends LogicService $courseUser->update(); } - protected function incrCourseReviewCount(CourseModel $course) - { - $course->review_count += 1; - - $course->update(); - } - protected function updateCourseRating(CourseModel $course) { $service = new CourseStatService(); @@ -87,6 +79,17 @@ class ReviewCreate extends LogicService $service->updateRating($course->id); } + protected function recountCourseReviews(CourseModel $course) + { + $courseRepo = new CourseRepo(); + + $reviewCount = $courseRepo->countReviews($course->id); + + $course->review_count = $reviewCount; + + $course->update(); + } + protected function handleReviewPoint(ReviewModel $review) { $service = new CourseReviewPointHistory(); diff --git a/app/Services/Logic/Review/ReviewDelete.php b/app/Services/Logic/Review/ReviewDelete.php index 7553eb36..f40bee79 100644 --- a/app/Services/Logic/Review/ReviewDelete.php +++ b/app/Services/Logic/Review/ReviewDelete.php @@ -3,6 +3,7 @@ namespace App\Services\Logic\Review; use App\Models\Course as CourseModel; +use App\Repos\Course as CourseRepo; use App\Services\CourseStat as CourseStatService; use App\Services\Logic\CourseTrait; use App\Services\Logic\ReviewTrait; @@ -31,19 +32,21 @@ class ReviewDelete extends LogicService $review->update(); - $this->decrCourseReviewCount($course); - + $this->recountCourseReviews($course); $this->updateCourseRating($course); $this->eventsManager->fire('Review:afterDelete', $this, $review); } - protected function decrCourseReviewCount(CourseModel $course) + protected function recountCourseReviews(CourseModel $course) { - if ($course->review_count > 0) { - $course->review_count -= 1; - $course->update(); - } + $courseRepo = new CourseRepo(); + + $reviewCount = $courseRepo->countReviews($course->id); + + $course->review_count = $reviewCount; + + $course->update(); } protected function updateCourseRating(CourseModel $course) diff --git a/app/Validators/Answer.php b/app/Validators/Answer.php index a0d3aa0b..0c3cecd5 100644 --- a/app/Validators/Answer.php +++ b/app/Validators/Answer.php @@ -6,6 +6,7 @@ use App\Caches\MaxAnswerId as MaxAnswerIdCache; use App\Exceptions\BadRequest as BadRequestException; use App\Models\Answer as AnswerModel; use App\Models\Question as QuestionModel; +use App\Models\Reason as ReasonModel; use App\Models\User as UserModel; use App\Repos\Answer as AnswerRepo; use App\Repos\Question as QuestionRepo; @@ -41,6 +42,13 @@ class Answer extends Validator } } + public function checkQuestion($id) + { + $validator = new Question(); + + return $validator->checkQuestion($id); + } + public function checkContent($content) { $value = $this->filter->sanitize($content, ['trim', 'string']); @@ -67,6 +75,13 @@ class Answer extends Validator return $status; } + public function checkRejectReason($reason) + { + if (!array_key_exists($reason, ReasonModel::answerRejectOptions())) { + throw new BadRequestException('answer.invalid_reject_reason'); + } + } + public function checkIfAllowAnswer(QuestionModel $question, UserModel $user) { $allowed = true; @@ -88,13 +103,25 @@ class Answer extends Validator } } - public function checkIfAllowEdit(AnswerModel $answer, UserModel $user) + public function checkIfAllowEdit(AnswerModel $answer) { - $this->checkOwner($user->id, $answer->owner_id); + if ($answer->accepted == 1) { + throw new BadRequestException('answer.edit_not_allowed'); + } - if (time() - $answer->create_time > 3600) { + $case1 = $answer->published == AnswerModel::PUBLISH_APPROVED; + $case2 = time() - $answer->create_time > 3600; + + if ($case1 && $case2) { throw new BadRequestException('answer.edit_not_allowed'); } } + public function checkIfAllowDelete(AnswerModel $answer) + { + if ($answer->accepted == 1) { + throw new BadRequestException('answer.delete_not_allowed'); + } + } + } diff --git a/app/Validators/Article.php b/app/Validators/Article.php index 2155bf3c..077c4c04 100644 --- a/app/Validators/Article.php +++ b/app/Validators/Article.php @@ -149,10 +149,10 @@ class Article extends Validator return $status; } - public function checkAllowCommentStatus($status) + public function checkCloseStatus($status) { if (!in_array($status, [0, 1])) { - throw new BadRequestException('article.invalid_allow_comment_status'); + throw new BadRequestException('article.invalid_close_status'); } return $status; diff --git a/config/errors.php b/config/errors.php index 5ccfac7f..c17ca271 100644 --- a/config/errors.php +++ b/config/errors.php @@ -120,7 +120,7 @@ $error['article.invalid_source_url'] = '无效的来源网址'; $error['article.invalid_feature_status'] = '无效的推荐状态'; $error['article.invalid_publish_status'] = '无效的发布状态'; $error['article.invalid_private_status'] = '无效的私有状态'; -$error['article.invalid_allow_comment_status'] = '无效的允许评论状态'; +$error['article.invalid_close_status'] = '无效的关闭状态'; $error['article.invalid_reject_reason'] = '无效的拒绝理由'; /** diff --git a/db/migrations/20210430023157.php b/db/migrations/20210430023157.php index be24a4c8..e6c6ec3c 100644 --- a/db/migrations/20210430023157.php +++ b/db/migrations/20210430023157.php @@ -23,6 +23,7 @@ final class V20210430023157 extends AbstractMigration $this->handleRoleRoutes(); $this->handleQuestionNav(); $this->handleArticleCover(); + $this->handleArticleClosed(); } public function down() @@ -405,13 +406,29 @@ final class V20210430023157 extends AbstractMigration 'comment' => '用户编号', 'after' => 'question_id', ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '删除标识', + 'after' => 'user_id', + ]) ->addColumn('create_time', 'integer', [ 'null' => false, 'default' => '0', 'limit' => MysqlAdapter::INT_REGULAR, 'signed' => false, 'comment' => '创建时间', - 'after' => 'user_id', + 'after' => 'deleted', + ]) + ->addColumn('update_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '更新时间', + 'after' => 'create_time', ]) ->addIndex(['user_id'], [ 'name' => 'user_id', @@ -458,13 +475,29 @@ final class V20210430023157 extends AbstractMigration 'comment' => '标签编号', 'after' => 'question_id', ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '删除标识', + 'after' => 'user_id', + ]) ->addColumn('create_time', 'integer', [ 'null' => false, 'default' => '0', 'limit' => MysqlAdapter::INT_REGULAR, 'signed' => false, 'comment' => '创建时间', - 'after' => 'user_id', + 'after' => 'deleted', + ]) + ->addColumn('update_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '更新时间', + 'after' => 'create_time', ]) ->addIndex(['question_id', 'user_id'], [ 'name' => 'question_user', @@ -667,13 +700,29 @@ final class V20210430023157 extends AbstractMigration 'comment' => '标签编号', 'after' => 'answer_id', ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '删除标识', + 'after' => 'user_id', + ]) ->addColumn('create_time', 'integer', [ 'null' => false, 'default' => '0', 'limit' => MysqlAdapter::INT_REGULAR, 'signed' => false, 'comment' => '创建时间', - 'after' => 'user_id', + 'after' => 'deleted', + ]) + ->addColumn('update_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '更新时间', + 'after' => 'create_time', ]) ->addIndex(['answer_id', 'user_id'], [ 'name' => 'answer_user', @@ -824,6 +873,7 @@ final class V20210430023157 extends AbstractMigration protected function modifyArticleTable() { $this->table('kg_article') + ->renameColumn('allow_comment', 'closed') ->addColumn('report_count', 'integer', [ 'null' => false, 'default' => '0', @@ -891,6 +941,15 @@ final class V20210430023157 extends AbstractMigration ->execute(); } + protected function handleArticleClosed() + { + $this->getQueryBuilder() + ->update('kg_article') + ->set('closed', 0) + ->where(['closed' => 1]) + ->execute(); + } + protected function handleQuestionNav() { $data = [ diff --git a/public/static/home/css/common.css b/public/static/home/css/common.css index f6d6707a..48a069ed 100644 --- a/public/static/home/css/common.css +++ b/public/static/home/css/common.css @@ -574,11 +574,11 @@ display: flex; justify-content: space-between; margin-bottom: 20px; - color: #999; + color: #666; } .article-info .meta a { - color: #999; + color: #666; } .article-info .meta .left span { @@ -612,6 +612,8 @@ .answer-tips h3 { margin-bottom: 5px; + font-weight: normal; + font-size: inherit; } .answer-tips ul { diff --git a/public/static/home/js/answer.js b/public/static/home/js/answer.js index 33faccce..f0b0dd36 100644 --- a/public/static/home/js/answer.js +++ b/public/static/home/js/answer.js @@ -1,7 +1,6 @@ -layui.use(['jquery', 'layer', 'helper'], function () { +layui.use(['jquery', 'helper'], function () { var $ = layui.jquery; - var layer = layui.layer; var helper = layui.helper; $('body').on('click', '.answer-report', function () { diff --git a/public/static/home/js/article.show.js b/public/static/home/js/article.show.js index ed9ee605..fa4175ec 100644 --- a/public/static/home/js/article.show.js +++ b/public/static/home/js/article.show.js @@ -80,4 +80,10 @@ layui.use(['jquery', 'helper'], function () { }); }); + $('.icon-reply').on('click', function () { + $('html').animate({ + scrollTop: $('#comment-anchor').offset().top + }, 500); + }); + }); \ No newline at end of file diff --git a/public/static/home/js/chapter.show.js b/public/static/home/js/chapter.show.js index 8c778e47..39d33773 100644 --- a/public/static/home/js/chapter.show.js +++ b/public/static/home/js/chapter.show.js @@ -58,4 +58,10 @@ layui.use(['jquery', 'helper'], function () { }); }); + $('.icon-reply').on('click', function () { + $('html').animate({ + scrollTop: $('#comment-anchor').offset().top + }, 500); + }); + }); \ No newline at end of file diff --git a/public/static/home/js/comment.js b/public/static/home/js/comment.js index 79b3c699..f40ba9f9 100644 --- a/public/static/home/js/comment.js +++ b/public/static/home/js/comment.js @@ -83,12 +83,6 @@ layui.use(['jquery', 'form', 'layer', 'helper'], function () { return false; }); - $('.icon-comment').on('click', function () { - $('html').animate({ - scrollTop: $('#comment-anchor').offset().top - }, 500); - }); - $('#btn-cancel-comment').on('click', function () { $('#comment-footer').hide(); }); diff --git a/public/static/home/js/question.show.js b/public/static/home/js/question.show.js index da459caf..e4d39804 100644 --- a/public/static/home/js/question.show.js +++ b/public/static/home/js/question.show.js @@ -91,4 +91,10 @@ layui.use(['jquery', 'helper'], function () { }); }); + $('.icon-reply').on('click', function () { + $('html').animate({ + scrollTop: $('#answer-anchor').offset().top + }, 500); + }); + }); \ No newline at end of file