1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-29 22:01:38 +08:00

v1.3.6阶段提交,cherry-pick

This commit is contained in:
koogua 2021-06-03 12:10:24 +08:00
parent 92c2225d47
commit 57f587f4c4
21 changed files with 250 additions and 142 deletions

View File

@ -55,7 +55,7 @@
<tr>
<td>{{ item.id }}</td>
<td>
<a href="{{ child_url }}">{{ item.title }}</a>
<a href="{{ child_url }}"><i class="layui-icon layui-icon-add-circle"></i> {{ item.title }}</a>
<span class="layui-badge layui-bg-green">章</span>
</td>
<td>{{ item.lesson_count }}</td>

View File

@ -12,19 +12,19 @@
<div class="layout-main">
<div class="writer-content wrap">
<form class="layui-form" method="POST" action="{{ url({'for':'home.answer.create'}) }}">
<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 class="layui-form-item first-form-item">
<div class="layui-input-block">
<input class="layui-input" type="text" name="title" value="{{ question.title }}" disabled="disabled">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block" style="margin:0;">
<div class="layui-input-block">
<div id="vditor"></div>
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
</div>
</div>
<div class="layui-form-item center">
<div class="layui-input-block" style="margin:0;">
<div class="layui-input-block">
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布回答</button>
<input type="hidden" name="question_id" value="{{ question.id }}">
</div>

View File

@ -12,19 +12,19 @@
<div class="layout-main">
<div class="writer-content wrap">
<form class="layui-form" method="POST" action="{{ url({'for':'home.answer.update','id':answer.id}) }}">
<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 class="layui-form-item first-form-item">
<div class="layui-input-block">
<input class="layui-input" type="text" name="title" value="{{ question.title }}" disabled="disabled">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block" style="margin:0;">
<div class="layui-input-block">
<div id="vditor"></div>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ answer.content }}</textarea>
</div>
</div>
<div class="layui-form-item center">
<div class="layui-input-block" style="margin:0;">
<div class="layui-input-block">
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布回答</button>
</div>
</div>

View File

@ -16,10 +16,10 @@
</span>
</div>
<form class="layui-form" method="POST" action="{{ action_url }}">
<div class="layout-main">
<div class="layout-main">
<form class="layui-form" method="POST" action="{{ action_url }}">
<div class="writer-content wrap">
<div class="layui-form-item">
<div class="layui-form-item first-form-item">
<div class="layui-input-block">
<input class="layui-input" type="text" name="title" value="{{ article.title }}" placeholder="请输入标题..." lay-verify="required">
</div>
@ -31,57 +31,57 @@
</div>
</div>
</div>
</div>
<div id="layer-publish" style="display:none;">
<div class="writer-sidebar">
<div class="layui-form-item">
<label class="layui-form-label">分类标签</label>
<div class="layui-input-block">
<div id="xm-tag-ids"></div>
<input type="hidden" name="xm_tags" value='{{ xm_tags|json_encode }}'>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">来源类型</label>
<div class="layui-input-block">
<select name="source_type" lay-filter="source_type" lay-verify="required">
<option value="">请选择</option>
{% for value,title in source_types %}
<option value="{{ value }}" {% if article.source_type == value %}selected="selected"{% endif %}>{{ title }}</option>
{% endfor %}
</select>
</div>
</div>
<div id="source-url-block" style="{{ source_url_display }}">
<div id="layer-publish" style="display:none;">
<div class="writer-sidebar">
<div class="layui-form-item">
<label class="layui-form-label">来源网址</label>
<label class="layui-form-label">分类标签</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="source_url" value="{{ article.source_url }}">
<div id="xm-tag-ids"></div>
<input type="hidden" name="xm_tags" value='{{ xm_tags|json_encode }}'>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">来源类型</label>
<div class="layui-input-block">
<select name="source_type" lay-filter="source_type" lay-verify="required">
<option value="">请选择</option>
{% for value,title in source_types %}
<option value="{{ value }}" {% if article.source_type == value %}selected="selected"{% endif %}>{{ title }}</option>
{% endfor %}
</select>
</div>
</div>
<div id="source-url-block" style="{{ source_url_display }}">
<div class="layui-form-item">
<label class="layui-form-label">来源网址</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="source_url" value="{{ article.source_url }}">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关闭评论</label>
<div class="layui-input-block">
<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">
<label class="layui-form-label">仅我可见</label>
<div class="layui-input-block">
<input type="radio" name="private" value="1" title="是" {% if article.private == 1 %}checked="checked"{% endif %}>
<input type="radio" name="private" value="0" title="否" {% if article.private == 0 %}checked="checked"{% endif %}>
</div>
</div>
<div class="layui-form-item last-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-fluid kg-submit" lay-submit="true" lay-filter="go">确认发布</button>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关闭评论</label>
<div class="layui-input-block">
<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">
<label class="layui-form-label">仅我可见</label>
<div class="layui-input-block">
<input type="radio" name="private" value="1" title="是" {% if article.private == 1 %}checked="checked"{% endif %}>
<input type="radio" name="private" value="0" title="否" {% if article.private == 0 %}checked="checked"{% endif %}>
</div>
</div>
<div class="layui-form-item last-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-fluid kg-submit" lay-submit="true" lay-filter="go">确认发布</button>
</div>
</div>
</div>
</div>
</form>
</form>
</div>
{% endblock %}

View File

@ -15,10 +15,10 @@
</span>
</div>
<form class="layui-form" method="POST" action="{{ action_url }}">
<div class="layout-main clearfix">
<div class="layout-main">
<form class="layui-form" method="POST" action="{{ action_url }}">
<div class="writer-content wrap">
<div class="layui-form-item">
<div class="layui-form-item first-form-item">
<div class="layui-input-block">
<input class="layui-input" type="text" name="title" value="{{ question.title }}" placeholder="请输入标题..." lay-verify="required">
</div>
@ -30,24 +30,24 @@
</div>
</div>
</div>
</div>
<div id="layer-publish" style="display:none;">
<div class="writer-sidebar">
<div class="layui-form-item">
<label class="layui-form-label">分类标签</label>
<div class="layui-input-block">
<div id="xm-tag-ids"></div>
<input type="hidden" name="xm_tags" value='{{ xm_tags|json_encode }}'>
<div id="layer-publish" style="display:none;">
<div class="writer-sidebar">
<div class="layui-form-item">
<label class="layui-form-label">分类标签</label>
<div class="layui-input-block">
<div id="xm-tag-ids"></div>
<input type="hidden" name="xm_tags" value='{{ xm_tags|json_encode }}'>
</div>
</div>
</div>
<div class="layui-form-item last-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-fluid kg-submit" lay-submit="true" lay-filter="go">确认发布</button>
<div class="layui-form-item last-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-fluid kg-submit" lay-submit="true" lay-filter="go">确认发布</button>
</div>
</div>
</div>
</div>
</div>
</form>
</form>
</div>
{% endblock %}

View File

@ -35,13 +35,12 @@
</thead>
<tbody>
{% for item in pager.items %}
{% set question_url = url({'for':'home.question.show','id':item.question.id}) %}
{% set answer_url = url({'for':'home.answer.show','id':item.id}) %}
{% set show_url = url({'for':'home.answer.show','id':item.id}) %}
{% set edit_url = url({'for':'home.answer.edit','id':item.id}) %}
{% set delete_url = url({'for':'home.answer.delete','id':item.id}) %}
<tr>
<td>
<p>提问:<a href="{{ question_url }}" target="_blank">{{ item.question.title }}</a></p>
<p>提问:<a href="{{ show_url }}" target="_blank">{{ item.question.title }}</a></p>
<p>回答:{{ substr(item.summary,0,32) }}</p>
<p class="meta">
创建:<span class="layui-badge layui-bg-gray">{{ item.create_time|time_ago }}</span>
@ -50,7 +49,6 @@
</td>
<td>{{ item.like_count }}</td>
<td>
<a href="{{ answer_url }}" class="layui-btn layui-btn-xs">详情</a>
<a href="{{ edit_url }}" class="layui-btn layui-btn-xs layui-bg-blue">修改</a>
<a href="javascript:" class="layui-btn layui-btn-xs layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>
</td>

View File

@ -136,6 +136,13 @@ class User extends Model
*/
public $answer_count;
/**
* 评论数
*
* @var int
*/
public $comment_count;
/**
* 收藏数
*

View File

@ -5,6 +5,7 @@ namespace App\Repos;
use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\Answer as AnswerModel;
use App\Models\Article as ArticleModel;
use App\Models\Comment as CommentModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\ImUser as ImUserModel;
use App\Models\Notification as NotificationModel;
@ -197,6 +198,14 @@ class User extends Repository
]);
}
public function countComments($userId)
{
return (int)CommentModel::count([
'conditions' => 'owner_id = ?1 AND published = ?2',
'bind' => [1 => $userId, 2 => CommentModel::PUBLISH_APPROVED],
]);
}
public function countUnreadNotifications($userId)
{
return (int)NotificationModel::count([

View File

@ -12,14 +12,12 @@ 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\Traits\Client as ClientTrait;
use App\Validators\Answer as AnswerValidator;
use App\Validators\UserLimit as UserLimitValidator;
class AnswerCreate extends LogicService
{
use ClientTrait;
use QuestionTrait;
use AnswerTrait;
use AnswerDataTrait;
@ -74,11 +72,6 @@ class AnswerCreate extends LogicService
return $answer;
}
protected function getPublishStatus(UserModel $user)
{
return $user->answer_count > 2 ? AnswerModel::PUBLISH_APPROVED : AnswerModel::PUBLISH_PENDING;
}
protected function incrUserDailyAnswerCount(UserModel $user)
{
$this->eventsManager->fire('UserDailyCounter:incrAnswerCount', $this, $user);

View File

@ -3,6 +3,7 @@
namespace App\Services\Logic\Answer;
use App\Models\Answer as AnswerModel;
use App\Models\User as UserModel;
use App\Traits\Client as ClientTrait;
use App\Validators\Answer as AnswerValidator;
@ -25,6 +26,11 @@ trait AnswerDataTrait
return $data;
}
protected function getPublishStatus(UserModel $user)
{
return $user->answer_count > 2 ? AnswerModel::PUBLISH_APPROVED : AnswerModel::PUBLISH_PENDING;
}
protected function saveDynamicAttrs(AnswerModel $answer)
{
$answer->cover = kg_parse_first_content_image($answer->content);

View File

@ -50,11 +50,6 @@ class ArticleCreate extends LogicService
return $article;
}
protected function getPublishStatus(UserModel $user)
{
return $user->article_count > 100 ? ArticleModel::PUBLISH_APPROVED : ArticleModel::PUBLISH_PENDING;
}
protected function incrUserDailyArticleCount(UserModel $user)
{
$this->eventsManager->fire('UserDailyCounter:incrArticleCount', $this, $user);

View File

@ -5,6 +5,7 @@ namespace App\Services\Logic\Article;
use App\Library\Utils\Word as WordUtil;
use App\Models\Article as ArticleModel;
use App\Models\ArticleTag as ArticleTagModel;
use App\Models\User as UserModel;
use App\Repos\ArticleTag as ArticleTagRepo;
use App\Repos\Tag as TagRepo;
use App\Traits\Client as ClientTrait;
@ -51,6 +52,11 @@ trait ArticleDataTrait
return $data;
}
protected function getPublishStatus(UserModel $user)
{
return $user->article_count > 100 ? ArticleModel::PUBLISH_APPROVED : ArticleModel::PUBLISH_PENDING;
}
protected function saveDynamicAttrs(ArticleModel $article)
{
$article->cover = kg_parse_first_content_image($article->content);

View File

@ -4,6 +4,7 @@ namespace App\Services\Logic\Comment;
use App\Models\Answer as AnswerModel;
use App\Models\Article as ArticleModel;
use App\Models\Chapter as ChapterModel;
use App\Models\Comment as CommentModel;
use App\Models\Question as QuestionModel;
use App\Models\User as UserModel;
@ -17,24 +18,38 @@ trait AfterCreateTrait
use CountTrait;
protected function incrItemCommentCount($item, CommentModel $comment, UserModel $user)
{
if ($comment->published != CommentModel::PUBLISH_APPROVED) return;
if ($item instanceof ChapterModel) {
$this->incrChapterCommentCount($item);
} elseif ($item instanceof ArticleModel) {
$this->incrArticleCommentCount($item);
} elseif ($item instanceof QuestionModel) {
$this->incrQuestionCommentCount($item);
} elseif ($item instanceof AnswerModel) {
$this->incrAnswerCommentCount($item);
}
$this->incrUserCommentCount($user);
}
protected function handleNoticeAndPoint($item, CommentModel $comment, UserModel $user)
{
if ($comment->published != CommentModel::PUBLISH_APPROVED) return;
if ($item instanceof ArticleModel) {
$this->incrArticleCommentCount($item);
if ($user->id != $item->owner_id) {
$this->handleArticleCommentedNotice($item, $comment);
$this->handleCommentPostPoint($comment);
}
} elseif ($item instanceof QuestionModel) {
$this->incrQuestionCommentCount($item);
if ($user->id != $item->owner_id) {
$this->handleQuestionCommentedNotice($item, $comment);
$this->handleCommentPostPoint($comment);
}
} elseif ($item instanceof AnswerModel) {
$this->incrAnswerCommentCount($item);
if ($user->id != $item->owner_id) {
$this->handleAnswerCommentedNotice($item, $comment);
$this->handleCommentPostPoint($comment);

View File

@ -4,7 +4,6 @@ namespace App\Services\Logic\Comment;
use App\Models\Comment as CommentModel;
use App\Services\Logic\Service as LogicService;
use App\Traits\Client as ClientTrait;
use App\Validators\Comment as CommentValidator;
use App\Validators\UserLimit as UserLimitValidator;
@ -12,8 +11,8 @@ class CommentCreate extends LogicService
{
use AfterCreateTrait;
use CommentDataTrait;
use CountTrait;
use ClientTrait;
public function handle()
{
@ -31,26 +30,19 @@ class CommentCreate extends LogicService
$comment = new CommentModel();
$data = [
'item_id' => $post['item_id'],
'item_type' => $post['item_type'],
'owner_id' => $user->id,
];
$data = $this->handlePostData($post);
$data['content'] = $validator->checkContent($post['content']);
$data['client_type'] = $this->getClientType();
$data['client_ip'] = $this->getClientIp();
/**
* @todo 引入自动审核机制
*/
$data['published'] = CommentModel::PUBLISH_APPROVED;
$data['item_id'] = $post['item_id'];
$data['item_type'] = $post['item_type'];
$data['owner_id'] = $user->id;
$data['published'] = $this->getPublishStatus($user);
$comment->create($data);
$this->incrUserDailyCommentCount($user);
if ($comment->published == CommentModel::PUBLISH_APPROVED) {
$this->incrItemCommentCount($item, $comment, $user);
$this->handleNoticeAndPoint($item, $comment, $user);
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Services\Logic\Comment;
use App\Models\Comment as CommentModel;
use App\Models\User as UserModel;
use App\Traits\Client as ClientTrait;
use App\Validators\Comment as CommentValidator;
trait CommentDataTrait
{
use ClientTrait;
protected function handlePostData($post)
{
$data = [];
$data['client_type'] = $this->getClientType();
$data['client_ip'] = $this->getClientIp();
$validator = new CommentValidator();
$data['content'] = $validator->checkContent($post['content']);
return $data;
}
protected function getPublishStatus(UserModel $user)
{
$case1 = $user->article_count > 2;
$case2 = $user->question_count > 2;
$case3 = $user->answer_count > 2;
$case4 = $user->comment_count > 2;
$status = CommentModel::PUBLISH_PENDING;
if ($case1 || $case2 || $case3 || $case4) {
$status = CommentModel::PUBLISH_APPROVED;
}
return $status;
}
}

View File

@ -5,7 +5,6 @@ namespace App\Services\Logic\Comment;
use App\Models\Comment as CommentModel;
use App\Services\Logic\CommentTrait;
use App\Services\Logic\Service as LogicService;
use App\Traits\Client as ClientTrait;
use App\Validators\Comment as CommentValidator;
use App\Validators\UserLimit as UserLimitValidator;
@ -13,9 +12,9 @@ class CommentReply extends LogicService
{
use AfterCreateTrait;
use CommentDataTrait;
use CommentTrait;
use CountTrait;
use ClientTrait;
public function handle($id)
{
@ -33,12 +32,13 @@ class CommentReply extends LogicService
$validator = new CommentValidator();
$data = [
'parent_id' => $parent->id,
'item_id' => $comment->item_id,
'item_type' => $comment->item_type,
'owner_id' => $user->id,
];
$data = $this->handlePostData($post);
$data['parent_id'] = $parent->id;
$data['item_id'] = $comment->item_id;
$data['item_type'] = $comment->item_type;
$data['owner_id'] = $user->id;
$data['published'] = $this->getPublishStatus($user);
$item = $validator->checkItem($comment->item_id, $comment->item_type);
@ -51,15 +51,6 @@ class CommentReply extends LogicService
$data['to_user_id'] = $comment->owner_id;
}
$data['content'] = $validator->checkContent($post['content']);
$data['client_type'] = $this->getClientType();
$data['client_ip'] = $this->getClientIp();
/**
* @todo 引入自动审核机制
*/
$data['published'] = CommentModel::PUBLISH_APPROVED;
$reply = new CommentModel();
$reply->create($data);
@ -68,6 +59,7 @@ class CommentReply extends LogicService
if ($reply->published == CommentModel::PUBLISH_APPROVED) {
$this->incrCommentReplyCount($parent);
$this->incrItemCommentCount($item, $reply, $user);
$this->handleNoticeAndPoint($item, $reply, $user);
}

View File

@ -56,6 +56,13 @@ trait CountTrait
$answer->update();
}
protected function incrUserCommentCount(UserModel $user)
{
$user->comment_count += 1;
$user->update();
}
protected function decrCommentReplyCount(CommentModel $comment)
{
if ($comment->reply_count > 0) {
@ -98,11 +105,19 @@ trait CountTrait
protected function decrAnswerCommentCount(AnswerModel $answer)
{
if ($answer->comment_count > 0) {
$answer->comment_count += 1;
$answer->comment_count -= 1;
$answer->update();
}
}
protected function decrUserCommentCount(UserModel $user)
{
if ($user->comment_count > 0) {
$user->comment_count -= 1;
$user->update();
}
}
protected function incrUserDailyCommentCount(UserModel $user)
{
/**

View File

@ -45,11 +45,6 @@ class QuestionCreate extends LogicService
return $question;
}
protected function getPublishStatus(UserModel $user)
{
return $user->question_count > 100 ? QuestionModel::PUBLISH_APPROVED : QuestionModel::PUBLISH_PENDING;
}
protected function incrUserDailyQuestionCount(UserModel $user)
{
$this->eventsManager->fire('UserDailyCounter:incrQuestionCount', $this, $user);

View File

@ -4,6 +4,7 @@ namespace App\Services\Logic\Question;
use App\Models\Question as QuestionModel;
use App\Models\QuestionTag as QuestionTagModel;
use App\Models\User as UserModel;
use App\Repos\QuestionTag as QuestionTagRepo;
use App\Repos\Tag as TagRepo;
use App\Traits\Client as ClientTrait;
@ -37,6 +38,11 @@ trait QuestionDataTrait
return $data;
}
protected function getPublishStatus(UserModel $user)
{
return $user->question_count > 100 ? QuestionModel::PUBLISH_APPROVED : QuestionModel::PUBLISH_PENDING;
}
protected function saveDynamicAttrs(QuestionModel $question)
{
$question->cover = kg_parse_first_content_image($question->content);

View File

@ -0,0 +1,34 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
use Phinx\Migration\AbstractMigration;
final class V20210602034627 extends AbstractMigration
{
public function up()
{
$this->modifyUserTable();
}
public function down()
{
$this->table('kg_user')
->removeColumn('comment_count')
->save();
}
protected function modifyUserTable()
{
$this->table('kg_user')
->addColumn('comment_count', 'integer', [
'null' => false,
'default' => '0',
'limit' => MysqlAdapter::INT_REGULAR,
'signed' => false,
'comment' => '评论数量',
'after' => 'answer_count',
])->save();
}
}

View File

@ -132,20 +132,20 @@
list-style: decimal;
}
.writer-sidebar {
padding: 20px;
}
.writer-content .first-form-item {
margin-top: 10px;
}
.writer-content .layui-input-block,
.writer-sidebar .layui-input-block,
.report-form .layui-input-block {
margin-left: 0;
}
.writer-content {
padding-top: 30px;
}
.writer-sidebar {
padding: 20px;
}
.writer-sidebar .layui-form-label,
.report-form .layui-form-label {
float: none;
@ -1972,7 +1972,7 @@
}
.layui-table p {
line-height: 1.8em;
line-height: 2em;
}
.layui-table .center {