mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-07-19 14:46:14 +08:00
阶段提交
This commit is contained in:
parent
661034f3c1
commit
863505dcca
12
CHANGELOG.md
12
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)
|
||||
|
||||
### 更新
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
41
app/Http/Admin/Views/answer/add.volt
Normal file
41
app/Http/Admin/Views/answer/add.volt
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.answer.create'}) }}">
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>回答问题</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block" style="margin:0;">
|
||||
<input class="layui-input" type="text" name="title" value="{{ question.title }}" readonly="readonly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block" style="margin:0;">
|
||||
<div id="vditor"></div>
|
||||
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-input-block kg-center" style="margin:0;">
|
||||
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
<input type="hidden" name="referer" value="{{ referer }}">
|
||||
<input type="hidden" name="question_id" value="{{ question.id }}">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% 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 %}
|
40
app/Http/Admin/Views/answer/edit.volt
Normal file
40
app/Http/Admin/Views/answer/edit.volt
Normal file
@ -0,0 +1,40 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.answer.update','id':answer.id}) }}">
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>编辑答案</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block" style="margin:0;">
|
||||
<input class="layui-input" type="text" name="title" value="{{ question.title }}" readonly="readonly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block" style="margin:0;">
|
||||
<div id="vditor"></div>
|
||||
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ answer.content }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-input-block kg-center" style="margin:0;">
|
||||
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
<input type="hidden" name="referer" value="{{ referer }}">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% 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 %}
|
64
app/Http/Admin/Views/answer/list.volt
Normal file
64
app/Http/Admin/Views/answer/list.volt
Normal file
@ -0,0 +1,64 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/answer') }}
|
||||
|
||||
<table class="layui-table kg-table layui-form">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>信息</th>
|
||||
<th>评论</th>
|
||||
<th>点赞</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<P>问题:<a href="{{ question_url }}" target="_blank">{{ item.question.title }}</a></P>
|
||||
<p>回答:{{ item.summary }}</p>
|
||||
<p>作者:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a> 创建:{{ date('Y-m-d',item.create_time) }}</p>
|
||||
</td>
|
||||
<td>{{ item.comment_count }}</td>
|
||||
<td>{{ item.like_count }}</td>
|
||||
<td>{{ publish_status(item.published) }}</td>
|
||||
<td class="center">
|
||||
<div class="layui-dropdown">
|
||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||
<ul>
|
||||
{% if item.published == 1 %}
|
||||
<li><a href="{{ review_url }}">审核回答</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ edit_url }}">编辑回答</a></li>
|
||||
{% if item.deleted == 0 %}
|
||||
<a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除回答</a>
|
||||
{% else %}
|
||||
<a href="javascript:" class="kg-restore" data-url="{{ restore_url }}">还原回答</a>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
78
app/Http/Admin/Views/answer/review.volt
Normal file
78
app/Http/Admin/Views/answer/review.volt
Normal file
@ -0,0 +1,78 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>审核内容</legend>
|
||||
</fieldset>
|
||||
|
||||
<div class="kg-mod-preview">
|
||||
<div class="title">{{ question.title }}</div>
|
||||
<div class="content markdown-body">{{ answer.content }}</div>
|
||||
</div>
|
||||
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>审核意见</legend>
|
||||
</fieldset>
|
||||
|
||||
<form class="layui-form kg-form kg-review-form" method="POST" action="{{ url({'for':'admin.answer.review','id':answer.id}) }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">审核</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="type" value="approve" title="通过" lay-filter="review">
|
||||
<input type="radio" name="type" value="reject" title="拒绝" lay-filter="review">
|
||||
</div>
|
||||
</div>
|
||||
<div id="reason-block" style="display:none;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">理由</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="reason">
|
||||
<option value="">请选择</option>
|
||||
{% for value,name in reasons %}
|
||||
<option value="{{ value }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button id="kg-submit" class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block link_css %}
|
||||
|
||||
{{ css_link('home/css/markdown.css') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script>
|
||||
|
||||
layui.use(['jquery', 'form'], function () {
|
||||
|
||||
var $ = layui.jquery;
|
||||
var form = layui.form;
|
||||
|
||||
form.on('radio(review)', function (data) {
|
||||
var block = $('#reason-block');
|
||||
if (data.value === 'approve') {
|
||||
block.hide();
|
||||
} else {
|
||||
block.show();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
58
app/Http/Admin/Views/answer/search.volt
Normal file
58
app/Http/Admin/Views/answer/search.volt
Normal file
@ -0,0 +1,58 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.answer.list'}) }}">
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>搜索回答</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">回答编号</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="id" placeholder="回答编号精确匹配">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">问题编号</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="question_id" placeholder="问题编号精确匹配">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">用户编号</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="owner_id" placeholder="用户编号精确匹配">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">发布状态</label>
|
||||
<div class="layui-input-block">
|
||||
{% for value,title in publish_types %}
|
||||
<input type="radio" name="published" value="{{ value }}" title="{{ title }}">
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">匿名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="anonymous" value="1" title="是">
|
||||
<input type="radio" name="anonymous" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">删除</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="deleted" value="1" title="是">
|
||||
<input type="radio" name="deleted" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="true">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
@ -30,10 +30,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">允许评论</label>
|
||||
<label class="layui-form-label">关闭评论</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="allow_comment" value="1" title="是" {% if article.allow_comment == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="allow_comment" value="0" title="否" {% if article.allow_comment == 0 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="closed" value="1" title="是" {% if article.closed == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="closed" value="0" title="否" {% if article.closed == 0 %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
|
@ -38,13 +38,13 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文章</th>
|
||||
<th>状态</th>
|
||||
<th>浏览</th>
|
||||
<th>评论</th>
|
||||
<th>浏览</th>
|
||||
<th>点赞</th>
|
||||
<th>收藏</th>
|
||||
<th>状态</th>
|
||||
<th>推荐</th>
|
||||
<th>评论</th>
|
||||
<th>关闭</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -75,13 +75,13 @@
|
||||
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
|
||||
</p>
|
||||
</td>
|
||||
<td>{{ publish_status(item.published) }}</td>
|
||||
<td>{{ item.view_count }}</td>
|
||||
<td>{{ item.comment_count }}</td>
|
||||
<td>{{ item.like_count }}</td>
|
||||
<td>{{ item.favorite_count }}</td>
|
||||
<td>{{ publish_status(item.published) }}</td>
|
||||
<td><input type="checkbox" name="featured" value="1" lay-skin="switch" lay-text="是|否" lay-filter="featured" data-url="{{ update_url }}" {% if item.featured == 1 %}checked="checked"{% endif %}></td>
|
||||
<td><input type="checkbox" name="comment" value="1" lay-skin="switch" lay-text="开|关" lay-filter="comment" data-url="{{ update_url }}" {% if item.allow_comment == 1 %}checked="checked"{% endif %}></td>
|
||||
<td><input type="checkbox" name="comment" value="1" lay-skin="switch" lay-text="是|否" lay-filter="closed" data-url="{{ update_url }}" {% if item.closed == 1 %}checked="checked"{% endif %}></td>
|
||||
<td class="center">
|
||||
<div class="layui-dropdown">
|
||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||
@ -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});
|
||||
},
|
||||
|
@ -36,7 +36,7 @@
|
||||
<div class="layui-input-block">
|
||||
<select name="reason">
|
||||
<option value="">请选择</option>
|
||||
{% for value,name in reject_options %}
|
||||
{% for value,name in reasons %}
|
||||
<option value="{{ value }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
@ -46,6 +46,20 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">允许评论</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="closed" value="0" title="是">
|
||||
<input type="radio" name="closed" value="1" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">仅我可见</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="private" value="1" title="是">
|
||||
<input type="radio" name="private" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">推荐</label>
|
||||
<div class="layui-input-block">
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
{{ partial('macros/common') }}
|
||||
|
||||
<table class="layui-table kg-table layui-form">
|
||||
<table class="layui-table kg-table layui-form" lay-size="lg">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col width="50%">
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
@ -15,31 +15,23 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>评论</th>
|
||||
<th>点赞</th>
|
||||
<th>用户</th>
|
||||
<th>终端</th>
|
||||
<th>发布</th>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<p>内容:<a href="javascript:" title="{{ item.content }}">{{ substr(item.content,0,30) }}</a></p>
|
||||
<p>时间:{{ date('Y-m-d H:i',item.create_time) }},点赞:{{ item.like_count }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>昵称:{{ item.owner.name }}</p>
|
||||
<p>编号:{{ item.owner.id }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>类型:{{ client_type(item.client_type) }}</p>
|
||||
<p>地址:<a href="javascript:" class="kg-ip2region" data-ip="{{ item.client_ip }}">查看</a></p>
|
||||
</td>
|
||||
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}></td>
|
||||
<td>{{ item.content }}</td>
|
||||
<td>{{ item.like_count }}</td>
|
||||
<td><a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></td>
|
||||
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
||||
<td class="center">
|
||||
{% if item.deleted == 0 %}
|
||||
<a href="javascript:" class="layui-badge layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>
|
||||
@ -54,10 +46,4 @@
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('admin/js/ip2region.js') }}
|
||||
|
||||
{% endblock %}
|
11
app/Http/Admin/Views/macros/answer.volt
Normal file
11
app/Http/Admin/Views/macros/answer.volt
Normal file
@ -0,0 +1,11 @@
|
||||
{%- macro publish_status(type) %}
|
||||
{% if type == 1 %}
|
||||
审核中
|
||||
{% elseif type == 2 %}
|
||||
已发布
|
||||
{% elseif type == 3 %}
|
||||
未通过
|
||||
{% else %}
|
||||
未知
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
@ -12,11 +12,11 @@
|
||||
|
||||
{%- macro source_info(type,url) %}
|
||||
{% if type == 1 %}
|
||||
原创
|
||||
<span class="layui-badge">原创</span>
|
||||
{% elseif type == 2 %}
|
||||
<a href="{{ url }}" target="_blank">转载</a>
|
||||
<a class="layui-badge layui-bg-blue" href="{{ url }}" target="_blank">转载</a>
|
||||
{% elseif type == 3 %}
|
||||
翻译
|
||||
<span class="layui-badge layui-bg-gray">翻译</span>
|
||||
{% else %}
|
||||
N/A
|
||||
{% endif %}
|
||||
|
55
app/Http/Admin/Views/moderation/answers.volt
Normal file
55
app/Http/Admin/Views/moderation/answers.volt
Normal file
@ -0,0 +1,55 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/answer') }}
|
||||
|
||||
<div class="kg-nav">
|
||||
<div class="kg-nav-left">
|
||||
<span class="layui-breadcrumb">
|
||||
<a><cite>回答审核</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="layui-table kg-table layui-form">
|
||||
<colgroup>
|
||||
<col width="50%">
|
||||
<col>
|
||||
<col>
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>回答</th>
|
||||
<th>作者</th>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<P>问题:<a href="{{ question_url }}" target="_blank">{{ item.question.title }}</a></P>
|
||||
<p class="layui-elip">回答:{{ substr(item.summary,0,32) }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||
<p>编号:{{ item.owner.id }}</p>
|
||||
</td>
|
||||
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||
<td class="center">
|
||||
<a href="{{ review_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
@ -2,7 +2,6 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/common') }}
|
||||
{{ partial('macros/article') }}
|
||||
|
||||
<div class="kg-nav">
|
||||
@ -18,14 +17,12 @@
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文章</th>
|
||||
<th>作者</th>
|
||||
<th>终端</th>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
@ -36,9 +33,8 @@
|
||||
{% set review_url = url({'for':'admin.article.review','id':item.id}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<p>标题:{{ item.title }}</p>
|
||||
<p>标题:{{ item.title }} {{ source_info(item.source_type,item.source_url) }}</p>
|
||||
<p class="meta">
|
||||
<span>来源:{{ source_info(item.source_type,item.source_url) }}</span>
|
||||
{% if item.tags %}
|
||||
<span>标签:{{ tags_info(item.tags) }}</span>
|
||||
{% endif %}
|
||||
@ -48,17 +44,7 @@
|
||||
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||
<p>编号:{{ item.owner.id }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>类型:{{ client_type(item.client_type) }}</p>
|
||||
<p>地址:<a href="javascript:" class="kg-ip2region" title="查看位置" data-ip="{{ item.client_ip }}">{{ item.client_ip }}</a></p>
|
||||
</td>
|
||||
<td>
|
||||
{% 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 %}
|
||||
</td>
|
||||
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||
<td class="center">
|
||||
<a href="{{ review_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||
</td>
|
||||
@ -69,10 +55,4 @@
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('admin/js/ip2region.js') }}
|
||||
|
||||
{% endblock %}
|
@ -2,7 +2,6 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/common') }}
|
||||
{{ partial('macros/question') }}
|
||||
|
||||
<div class="kg-nav">
|
||||
@ -18,14 +17,12 @@
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>问题</th>
|
||||
<th>作者</th>
|
||||
<th>终端</th>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
@ -47,17 +44,7 @@
|
||||
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||
<p>编号:{{ item.owner.id }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>类型:{{ client_type(item.client_type) }}</p>
|
||||
<p>地址:<a href="javascript:" class="kg-ip2region" title="查看位置" data-ip="{{ item.client_ip }}">{{ item.client_ip }}</a></p>
|
||||
</td>
|
||||
<td>
|
||||
{% 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 %}
|
||||
</td>
|
||||
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||
<td class="center">
|
||||
<a href="{{ review_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||
</td>
|
||||
@ -68,10 +55,4 @@
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('admin/js/ip2region.js') }}
|
||||
|
||||
{% endblock %}
|
@ -37,12 +37,12 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>问题</th>
|
||||
<th>状态</th>
|
||||
<th>浏览</th>
|
||||
<th>回答</th>
|
||||
<th>浏览</th>
|
||||
<th>点赞</th>
|
||||
<th>收藏</th>
|
||||
<th>讨论</th>
|
||||
<th>状态</th>
|
||||
<th>关闭</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -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}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<p>标题:<a href="{{ edit_url }}">{{ item.title }}</a>({{ item.id }})</p>
|
||||
@ -72,12 +73,12 @@
|
||||
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
|
||||
</p>
|
||||
</td>
|
||||
<td>{{ publish_status(item.published) }}</td>
|
||||
<td>{{ item.view_count }}</td>
|
||||
<td>{{ item.answer_count }}</td>
|
||||
<td>{{ item.view_count }}</td>
|
||||
<td>{{ item.like_count }}</td>
|
||||
<td>{{ item.favorite_count }}</td>
|
||||
<td><input type="checkbox" name="closed" value="1" lay-skin="switch" lay-text="开|关" lay-filter="discuss" data-url="{{ update_url }}" {% if item.closed == 0 %}checked="checked"{% endif %}></td>
|
||||
<td>{{ publish_status(item.published) }}</td>
|
||||
<td><input type="checkbox" name="closed" value="1" lay-skin="switch" lay-text="是|否" lay-filter="closed" data-url="{{ update_url }}" {% if item.closed == 1 %}checked="checked"{% endif %}></td>
|
||||
<td class="center">
|
||||
<div class="layui-dropdown">
|
||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||
@ -87,6 +88,7 @@
|
||||
{% elseif item.published == 2 %}
|
||||
<li><a href="{{ preview_url }}" target="_blank">预览问题</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ answer_add_url }}">回答问题</a></li>
|
||||
<li><a href="{{ edit_url }}">编辑问题</a></li>
|
||||
{% if item.deleted == 0 %}
|
||||
<li><a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除问题</a></li>
|
||||
@ -94,7 +96,7 @@
|
||||
<li><a href="javascript:" class="kg-restore" data-url="{{ restore_url }}">还原问题</a></li>
|
||||
{% endif %}
|
||||
<hr>
|
||||
<li><a href="javascript:" class="kg-answer" data-url="{{ answer_url }}">回答管理</a></li>
|
||||
<li><a href="{{ answer_list_url }}">回答管理</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
@ -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',
|
||||
|
@ -36,7 +36,7 @@
|
||||
<div class="layui-input-block">
|
||||
<select name="reason">
|
||||
<option value="">请选择</option>
|
||||
{% for value,name in reject_options %}
|
||||
{% for value,name in reasons %}
|
||||
<option value="{{ value }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
@ -33,6 +33,7 @@
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
@ -42,7 +43,8 @@
|
||||
<th>用户角色</th>
|
||||
<th>课程</th>
|
||||
<th>文章</th>
|
||||
<th>收藏</th>
|
||||
<th>提问</th>
|
||||
<th>回答</th>
|
||||
<th>活跃时间</th>
|
||||
<th>注册时间</th>
|
||||
<th>操作</th>
|
||||
@ -51,6 +53,7 @@
|
||||
<tbody>
|
||||
{% 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}) %}
|
||||
<tr>
|
||||
<td class="center">
|
||||
@ -71,15 +74,17 @@
|
||||
</td>
|
||||
<td>{{ item.course_count }}</td>
|
||||
<td>{{ item.article_count }}</td>
|
||||
<td>{{ item.favorite_count }}</td>
|
||||
<td>{{ item.question_count }}</td>
|
||||
<td>{{ item.answer_count }}</td>
|
||||
<td>{{ date('Y-m-d',item.active_time) }}</td>
|
||||
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
||||
<td class="center">
|
||||
<div class="layui-dropdown">
|
||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||
<ul>
|
||||
<li><a href="{{ preview_url }}" target="_blank">预览</a></li>
|
||||
<li><a href="{{ edit_url }}">编辑</a></li>
|
||||
<li><a href="{{ preview_url }}" target="_blank">用户主页</a></li>
|
||||
<li><a href="javascript:" class="kg-online" data-url="{{ online_url }}">在线记录</a></li>
|
||||
<li><a href="{{ edit_url }}">编辑用户</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
@ -90,4 +95,29 @@
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script>
|
||||
|
||||
layui.define(['jquery', 'layer'], function () {
|
||||
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
|
||||
$('.kg-online').on('click', function () {
|
||||
var url = $(this).data('url');
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: '在线记录',
|
||||
area: ['800px', '600px'],
|
||||
content: url
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
42
app/Http/Admin/Views/user/online.volt
Normal file
42
app/Http/Admin/Views/user/online.volt
Normal file
@ -0,0 +1,42 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/common') }}
|
||||
|
||||
<table class="layui-table kg-table">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>终端类型</th>
|
||||
<th>终端地址</th>
|
||||
<th>活跃时间</th>
|
||||
<th>创建时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in pager.items %}
|
||||
<tr>
|
||||
<td>{{ client_type(item.client_type) }}</td>
|
||||
<td><a href="javascript:" title="查看位置" class="layui-badge layui-bg-gray kg-ip2region">{{ item.client_ip }}</a></td>
|
||||
<td>{{ date('Y-m-d H:i',item.active_time) }}</td>
|
||||
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('admin/js/ip2region.js') }}
|
||||
|
||||
{% endblock %}
|
@ -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);
|
||||
}
|
||||
|
@ -33,6 +33,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tips" data-url="{{ url({'for':'home.answer.tips'}) }}"></div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block link_css %}
|
||||
@ -47,4 +49,28 @@
|
||||
{{ js_include('home/js/answer.js') }}
|
||||
{{ js_include('home/js/vditor.js') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script>
|
||||
|
||||
layui.use(['jquery', 'layer'], function () {
|
||||
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var $tips = $('#tips');
|
||||
|
||||
if ($tips.length > 0) {
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: '答题指南',
|
||||
content: $tips.data('url'),
|
||||
area: ['600px', '320px'],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
20
app/Http/Home/Views/answer/tips.volt
Normal file
20
app/Http/Home/Views/answer/tips.volt
Normal file
@ -0,0 +1,20 @@
|
||||
{% extends 'templates/layer.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="answer-tips">
|
||||
<h3 class="suggest-text">适合作为回答的</h3>
|
||||
<ul class="suggest-list">
|
||||
<li><i class="layui-icon layui-icon-ok-circle"></i> 经过验证的有效解决办法</li>
|
||||
<li><i class="layui-icon layui-icon-ok-circle"></i> 自己的经验指引,对解决问题有帮助</li>
|
||||
<li><i class="layui-icon layui-icon-ok-circle"></i> 遵循 Markdown 语法排版,表达语义正确</li>
|
||||
</ul>
|
||||
<h3 class="not-suggest-text">不该作为回答的</h3>
|
||||
<ul class="not-suggest-list">
|
||||
<li><i class="layui-icon layui-icon-close-fill"></i> 询问内容细节或回复楼层</li>
|
||||
<li><i class="layui-icon layui-icon-close-fill"></i> 与题目无关的内容</li>
|
||||
<li><i class="layui-icon layui-icon-close-fill"></i> “赞” “顶” “同问” 等毫无意义的内容</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -67,10 +67,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">允许评论</label>
|
||||
<label class="layui-form-label">关闭评论</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="allow_comment" value="1" title="是" {% if article.allow_comment == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="allow_comment" value="0" title="否" {% if article.allow_comment == 0 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="closed" value="1" title="是" {% if article.closed == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="closed" value="0" title="否" {% if article.closed == 0 %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
|
@ -65,7 +65,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="comment-anchor"></div>
|
||||
{% if article.allow_comment == 1 %}
|
||||
{% if article.closed == 0 %}
|
||||
<div class="article-comment wrap">
|
||||
{{ partial('article/comment') }}
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<p>评价内容:{{ info.review.content }}</p>
|
||||
{% elseif type == 184 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a> 通过了审核</p>
|
||||
<p>你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a> 审核已通过</p>
|
||||
{% elseif type == 185 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a> 审核未通过</p>
|
||||
@ -33,7 +33,7 @@
|
||||
<p>{{ sender.name }} 喜欢了你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a></p>
|
||||
{% elseif type == 204 %}
|
||||
{% set question_url = url({'for':'home.question.show','id':info.question.id}) %}
|
||||
<p>你的提问 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a> 通过了审核</p>
|
||||
<p>你的提问 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a> 审核已通过</p>
|
||||
{% elseif type == 205 %}
|
||||
{% set question_url = url({'for':'home.question.show','id':info.question.id}) %}
|
||||
<p>你的提问 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a> 审核未通过</p>
|
||||
@ -48,12 +48,18 @@
|
||||
{% elseif type == 209 %}
|
||||
{% set question_url = url({'for':'home.question.show','id':info.question.id}) %}
|
||||
<p>{{ sender.name }} 喜欢了你的问题 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a></p>
|
||||
{% elseif type == 209 %}
|
||||
{% elseif type == 224 %}
|
||||
{% set question_url = url({'for':'home.question.show','id':info.question.id}) %}
|
||||
<p>{{ sender.name }} 喜欢了你的问题 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a></p>
|
||||
<p>你对问题 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a> 的回答,审核已通过</p>
|
||||
<p>回答内容:{{ info.answer.summary }}</p>
|
||||
{% elseif type == 225 %}
|
||||
{% set question_url = url({'for':'home.question.show','id':info.question.id}) %}
|
||||
<p>你对问题 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a> 的回答,审核未通过</p>
|
||||
<p>回答内容:{{ info.answer.summary }}</p>
|
||||
{% elseif type == 228 %}
|
||||
{% set question_url = url({'for':'home.question.show','id':info.question.id}) %}
|
||||
<p>{{ sender.name }} 喜欢了你对问题 <a href="{{ question_url }}" target="_blank">{{ info.question.title }}</a> 的回答</p>
|
||||
<p>回答内容:{{ info.answer.summary }}</p>
|
||||
{% elseif type == 506 %}
|
||||
<p>{{ sender.name }} 回复了你的评论:{{ info.comment.content }}</p>
|
||||
<p>回复内容:{{ info.reply.content }}</p>
|
||||
|
@ -59,21 +59,21 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="answer-anchor"></div>
|
||||
<div class="answer-tips">
|
||||
{{ partial('question/answer_tips') }}
|
||||
<div class="answer-wrap wrap center">
|
||||
{% if question.me.answered == 0 %}
|
||||
<button class="layui-btn layui-btn-fluid btn-answer" data-url="{{ answer_add_url }}">回答问题</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if question.answer_count > 0 %}
|
||||
<div class="layout-content">
|
||||
<div class="content-wrap wrap">
|
||||
<div class="layui-tab layui-tab-brief search-tab" lay-filter="answer">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this" data-url="{{ answer_list_url }}?sort=popular">热门回答</li>
|
||||
<li data-url="{{ answer_list_url }}?sort=latest">最新回答</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<div id="answer-list" data-url="{{ answer_list_url }}?sort=popular"></div>
|
||||
</div>
|
||||
<div class="answer-wrap wrap">
|
||||
<div class="layui-tab layui-tab-brief search-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this" data-url="{{ answer_list_url }}?sort=popular">热门回答</li>
|
||||
<li data-url="{{ answer_list_url }}?sort=latest">最新回答</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<div id="answer-list" data-url="{{ answer_list_url }}?sort=popular"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,11 +99,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if question.me.answered == 0 %}
|
||||
<div class="sidebar wrap">
|
||||
<button class="layui-btn layui-btn-fluid btn-answer" data-url="{{ answer_add_url }}">回答问题</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="sidebar" id="sidebar-related" data-url="{{ question_related_url }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -144,11 +144,11 @@ class Article extends Model
|
||||
public $deleted = 0;
|
||||
|
||||
/**
|
||||
* 允许评论
|
||||
* 关闭标识
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $allow_comment = 1;
|
||||
public $closed = 0;
|
||||
|
||||
/**
|
||||
* 文字数
|
||||
|
@ -41,4 +41,20 @@ class Reason
|
||||
];
|
||||
}
|
||||
|
||||
public static function answerRejectOptions()
|
||||
{
|
||||
return [
|
||||
101 => '答非所问',
|
||||
102 => '内容质量差',
|
||||
103 => '内容不实',
|
||||
104 => '低俗色情',
|
||||
105 => '广告软文',
|
||||
106 => '恶意对比',
|
||||
107 => '涉嫌歧视,恶意抹黑',
|
||||
108 => '配图引起不适',
|
||||
109 => '内容涉嫌违法',
|
||||
110 => '其它问题',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
@ -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([
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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'])) {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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'],
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
41
app/Services/Logic/Notice/System/AnswerApproved.php
Normal file
41
app/Services/Logic/Notice/System/AnswerApproved.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\Notice\System;
|
||||
|
||||
use App\Models\Answer as AnswerModel;
|
||||
use App\Models\Notification as NotificationModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Question as QuestionRepo;
|
||||
use App\Services\Logic\Service as LogicService;
|
||||
|
||||
class AnswerApproved extends LogicService
|
||||
{
|
||||
|
||||
public function handle(AnswerModel $answer, UserModel $sender)
|
||||
{
|
||||
$answerSummary = kg_substr($answer->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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
42
app/Services/Logic/Notice/System/AnswerRejected.php
Normal file
42
app/Services/Logic/Notice/System/AnswerRejected.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\Notice\System;
|
||||
|
||||
use App\Models\Answer as AnswerModel;
|
||||
use App\Models\Notification as NotificationModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Question as QuestionRepo;
|
||||
use App\Services\Logic\Service as LogicService;
|
||||
|
||||
class AnswerRejected extends LogicService
|
||||
{
|
||||
|
||||
public function handle(AnswerModel $answer, UserModel $sender, $reason)
|
||||
{
|
||||
$answerSummary = kg_substr($answer->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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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'] = '无效的拒绝理由';
|
||||
|
||||
/**
|
||||
|
@ -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 = [
|
||||
|
@ -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 {
|
||||
|
@ -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 () {
|
||||
|
@ -80,4 +80,10 @@ layui.use(['jquery', 'helper'], function () {
|
||||
});
|
||||
});
|
||||
|
||||
$('.icon-reply').on('click', function () {
|
||||
$('html').animate({
|
||||
scrollTop: $('#comment-anchor').offset().top
|
||||
}, 500);
|
||||
});
|
||||
|
||||
});
|
@ -58,4 +58,10 @@ layui.use(['jquery', 'helper'], function () {
|
||||
});
|
||||
});
|
||||
|
||||
$('.icon-reply').on('click', function () {
|
||||
$('html').animate({
|
||||
scrollTop: $('#comment-anchor').offset().top
|
||||
}, 500);
|
||||
});
|
||||
|
||||
});
|
@ -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();
|
||||
});
|
||||
|
@ -91,4 +91,10 @@ layui.use(['jquery', 'helper'], function () {
|
||||
});
|
||||
});
|
||||
|
||||
$('.icon-reply').on('click', function () {
|
||||
$('html').animate({
|
||||
scrollTop: $('#answer-anchor').offset().top
|
||||
}, 500);
|
||||
});
|
||||
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user