1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-17 13:46:21 +08:00

Merge branch 'koogua/v1.3.6'

This commit is contained in:
koogua 2021-06-03 21:11:55 +08:00
commit c994c9e05e
95 changed files with 1116 additions and 773 deletions

View File

@ -1,3 +1,26 @@
### [v1.3.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.6)(2021-06-04)
### 更新
- 清理没有用到的引用
- 优化界面和CSS样式
- 优化视频无法获取时长处理逻辑
- 优化视频无法转码处理逻辑
- 优化评论审核机制
- 优化评论相关数据更新逻辑
- 优化文章,问答,评论数据更新
- 优化内容标签的更新逻辑
- 优化首页H5的跳转判断
- 优化单页的浏览权限
- 优化Model中的事件方法
- 优化kg_parse_summary函数
- 用户主页加入问答列表
- 修复无法关闭问题讨论
- 修复编辑群组的路由
- 直播去除FLV方式拉流
- xs.question.ini加入忽略列表
- kg_user表增加comment_count字段
### [v1.3.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.5)(2021-05-20)
### 更新

View File

@ -84,5 +84,5 @@ Tips: 请用手机注册一个新账号,用户中心 -> 关注订阅,扫码
### 开源助力
毫无保留的真开源不容易,如果对你有帮助,请给我们 **STAR**
毫无保留的真开源不容易,不要吝啬您的赞许和鼓励,请给我们 **STAR**

View File

@ -1,31 +0,0 @@
<?php
namespace App\Caches;
use App\Repos\App as AppRepo;
class App extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "app:{$id}";
}
public function getContent($id = null)
{
$appRepo = new AppRepo();
$result = $appRepo->findByAppKey($id);
return $result ?: null;
}
}

32
app/Caches/AppInfo.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace App\Caches;
class AppInfo extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "_APP_INFO_";
}
public function getContent($id = null)
{
$appInfo = new \App\Library\AppInfo();
return [
'name' => $appInfo->name,
'alias' => $appInfo->alias,
'link' => $appInfo->link,
'version' => $appInfo->version,
];
}
}

View File

@ -44,10 +44,12 @@ class VodEventTask extends Task
protected function handleNewFileUploadEvent($event)
{
$fileId = $event['FileUploadEvent']['FileId'];
$width = $event['FileUploadEvent']['MetaData']['Height'];
$height = $event['FileUploadEvent']['MetaData']['Width'];
$duration = $event['FileUploadEvent']['MetaData']['Duration'];
$fileId = $event['FileUploadEvent']['FileId'] ?? 0;
$width = $event['FileUploadEvent']['MetaData']['Height'] ?? 0;
$height = $event['FileUploadEvent']['MetaData']['Width'] ?? 0;
$duration = $event['FileUploadEvent']['MetaData']['Duration'] ?? 0;
if ($fileId == 0) return;
$chapterRepo = new ChapterRepo();
@ -55,6 +57,18 @@ class VodEventTask extends Task
if (!$chapter) return;
$attrs = $chapter->attrs;
/**
* 获取不到时长视为失败
*/
if ($duration == 0) {
$attrs['file']['id'] = $fileId;
$attrs['file']['status'] = ChapterModel::FS_FAILED;
$chapter->update(['attrs' => $attrs]);
return;
}
$vodService = new VodService();
if ($width == 0 && $height == 0) {
@ -63,13 +77,8 @@ class VodEventTask extends Task
$vodService->createTransVideoTask($fileId);
}
/**
* @var array $attrs
*/
$attrs = $chapter->attrs;
$attrs['file']['id'] = $fileId;
$attrs['file']['status'] = ChapterModel::FS_TRANSLATING;
$attrs['duration'] = (int)$duration;
$chapter->update(['attrs' => $attrs]);
@ -79,9 +88,9 @@ class VodEventTask extends Task
protected function handleProcedureStateChangedEvent($event)
{
$fileId = $event['ProcedureStateChangeEvent']['FileId'];
$fileId = $event['ProcedureStateChangeEvent']['FileId'] ?? 0;
$processResult = $event['ProcedureStateChangeEvent']['MediaProcessResultSet'];
if ($fileId == 0) return;
$chapterRepo = new ChapterRepo();
@ -89,6 +98,20 @@ class VodEventTask extends Task
if (!$chapter) return;
$attrs = $chapter->attrs;
$processResult = $event['ProcedureStateChangeEvent']['MediaProcessResultSet'] ?? [];
/**
* 获取不到处理结果视为失败
*/
if (empty($processResult)) {
$attrs['file']['id'] = $fileId;
$attrs['file']['status'] = ChapterModel::FS_FAILED;
$chapter->update(['attrs' => $attrs]);
return;
}
$failCount = $successCount = 0;
foreach ($processResult as $item) {
@ -112,15 +135,9 @@ class VodEventTask extends Task
$fileStatus = ChapterModel::FS_FAILED;
}
if ($fileStatus == ChapterModel::FS_TRANSLATING) {
return;
}
/**
* @var array $attrs
*/
$attrs = $chapter->attrs;
if ($fileStatus == ChapterModel::FS_TRANSLATING) return;
$attrs['file']['id'] = $fileId;
$attrs['file']['status'] = $fileStatus;
$chapter->update(['attrs' => $attrs]);

View File

@ -14,6 +14,7 @@ use App\Repos\Answer as AnswerRepo;
use App\Repos\Question as QuestionRepo;
use App\Repos\Report as ReportRepo;
use App\Repos\User as UserRepo;
use App\Services\Logic\Answer\AnswerDataTrait;
use App\Services\Logic\Answer\AnswerInfo as AnswerInfoService;
use App\Services\Logic\Notice\System\AnswerApproved as AnswerApprovedNotice;
use App\Services\Logic\Notice\System\AnswerRejected as AnswerRejectedNotice;
@ -24,6 +25,8 @@ use App\Validators\Answer as AnswerValidator;
class Answer extends Service
{
use AnswerDataTrait;
public function getPublishTypes()
{
return AnswerModel::publishTypes();
@ -94,13 +97,16 @@ class Answer extends Service
$answer = new AnswerModel();
$answer->owner_id = $user->id;
$answer->question_id = $question->id;
$answer->published = AnswerModel::PUBLISH_APPROVED;
$answer->client_type = $this->getClientType();
$answer->client_ip = $this->getClientIp();
$answer->content = $validator->checkContent($post['content']);
$answer->question_id = $question->id;
$answer->owner_id = $user->id;
$answer->create();
$this->saveDynamicAttrs($answer);
$this->recountQuestionAnswers($question);
$this->recountUserAnswers($user);
$this->handleAnswerPostPoint($answer);
@ -126,20 +132,21 @@ 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);
$this->saveDynamicAttrs($answer);
$question = $this->findQuestion($answer->question_id);
$this->recountQuestionAnswers($question);
$owner = $this->findUser($answer->owner_id);
$this->recountUserAnswers($owner);
$this->eventsManager->fire('Answer:afterUpdate', $this, $answer);
return $answer;

View File

@ -15,10 +15,10 @@ use App\Models\User as UserModel;
use App\Repos\Article as ArticleRepo;
use App\Repos\Category as CategoryRepo;
use App\Repos\Report as ReportRepo;
use App\Repos\Tag as TagRepo;
use App\Repos\User as UserRepo;
use App\Services\Logic\Article\ArticleDataTrait;
use App\Services\Logic\Article\ArticleInfo as ArticleInfoService;
use App\Services\Logic\Article\XmTagList as XmTagListService;
use App\Services\Logic\Notice\System\ArticleApproved as ArticleApprovedNotice;
use App\Services\Logic\Notice\System\ArticleRejected as ArticleRejectedNotice;
use App\Services\Logic\Point\History\ArticlePost as ArticlePostPointHistory;
@ -32,33 +32,9 @@ class Article extends Service
public function getXmTags($id)
{
$tagRepo = new TagRepo();
$service = new XmTagListService();
$allTags = $tagRepo->findAll(['published' => 1]);
if ($allTags->count() == 0) return [];
$articleTagIds = [];
if ($id > 0) {
$article = $this->findOrFail($id);
if (!empty($article->tags)) {
$articleTagIds = kg_array_column($article->tags, 'id');
}
}
$list = [];
foreach ($allTags as $tag) {
$selected = in_array($tag->id, $articleTagIds);
$list[] = [
'name' => $tag->name,
'value' => $tag->id,
'selected' => $selected,
];
}
return $list;
return $service->handle($id);
}
public function getCategories()
@ -152,11 +128,15 @@ class Article extends Service
$article = new ArticleModel();
$article->published = ArticleModel::PUBLISH_APPROVED;
$article->client_type = $this->getClientType();
$article->client_ip = $this->getClientIp();
$article->owner_id = $user->id;
$article->title = $title;
$article->create();
$this->saveDynamicAttrs($article);
$this->recountUserArticles($user);
$this->eventsManager->fire('Article:afterCreate', $this, $article);
@ -208,12 +188,7 @@ 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'])) {
@ -222,8 +197,14 @@ class Article extends Service
$article->update($data);
$this->saveDynamicAttrs($article);
$this->rebuildArticleIndex($article);
$owner = $this->findUser($article->owner_id);
$this->recountUserArticles($owner);
$this->eventsManager->fire('Article:afterUpdate', $this, $article);
return $article;
@ -237,14 +218,14 @@ class Article extends Service
$article->update();
$userRepo = new UserRepo();
$owner = $userRepo->findById($article->owner_id);
$this->recountUserArticles($owner);
$this->saveDynamicAttrs($article);
$this->rebuildArticleIndex($article);
$owner = $this->findUser($article->owner_id);
$this->recountUserArticles($owner);
$this->eventsManager->fire('Article:afterDelete', $this, $article);
return $article;
@ -258,14 +239,14 @@ class Article extends Service
$article->update();
$userRepo = new UserRepo();
$owner = $userRepo->findById($article->owner_id);
$this->recountUserArticles($owner);
$this->saveDynamicAttrs($article);
$this->rebuildArticleIndex($article);
$owner = $this->findUser($article->owner_id);
$this->recountUserArticles($owner);
$this->eventsManager->fire('Article:afterRestore', $this, $article);
return $article;

View File

@ -10,7 +10,6 @@ class AuthMenu extends Component
protected $authInfo;
protected $authNodes = [];
protected $ownedRoutes = [];
protected $owned1stLevelIds = [];
protected $owned2ndLevelIds = [];
protected $owned3rdLevelIds = [];

View File

@ -530,7 +530,7 @@ class AuthNode extends Service
],
],
[
'id' => '2-10',
'id' => '2-11',
'title' => '举报队列',
'type' => 'menu',
'children' => [

View File

@ -2,8 +2,9 @@
namespace App\Http\Admin\Services;
use App\Caches\SiteGlobalStat;
use App\Caches\SiteTodayStat;
use App\Caches\AppInfo as AppInfoCache;
use App\Caches\SiteGlobalStat as SiteGlobalStatCache;
use App\Caches\SiteTodayStat as SiteTodayStatCache;
use App\Library\AppInfo;
use App\Library\Utils\ServerInfo;
use App\Repos\Stat as StatRepo;
@ -28,7 +29,17 @@ class Index extends Service
public function getAppInfo()
{
return new AppInfo();
$cache = new AppInfoCache();
$content = $cache->get();
$appInfo = new AppInfo();
if ($appInfo->version != $content['version']) {
$cache->rebuild();
}
return $appInfo;
}
public function getServerInfo()
@ -42,14 +53,14 @@ class Index extends Service
public function getGlobalStat()
{
$cache = new SiteGlobalStat();
$cache = new SiteGlobalStatCache();
return $cache->get();
}
public function getTodayStat()
{
$cache = new SiteTodayStat();
$cache = new SiteTodayStatCache();
return $cache->get();
}

View File

@ -14,13 +14,13 @@ use App\Models\User as UserModel;
use App\Repos\Category as CategoryRepo;
use App\Repos\Question as QuestionRepo;
use App\Repos\Report as ReportRepo;
use App\Repos\Tag as TagRepo;
use App\Repos\User as UserRepo;
use App\Services\Logic\Notice\System\QuestionApproved as QuestionApprovedNotice;
use App\Services\Logic\Notice\System\QuestionRejected as QuestionRejectedNotice;
use App\Services\Logic\Point\History\QuestionPost as QuestionPostPointHistory;
use App\Services\Logic\Question\QuestionDataTrait;
use App\Services\Logic\Question\QuestionInfo as QuestionInfoService;
use App\Services\Logic\Question\XmTagList as XmTagListService;
use App\Services\Sync\QuestionIndex as QuestionIndexSync;
use App\Validators\Question as QuestionValidator;
@ -31,33 +31,9 @@ class Question extends Service
public function getXmTags($id)
{
$tagRepo = new TagRepo();
$service = new XmTagListService();
$allTags = $tagRepo->findAll(['published' => 1]);
if ($allTags->count() == 0) return [];
$questionTagIds = [];
if ($id > 0) {
$question = $this->findOrFail($id);
if (!empty($question->tags)) {
$questionTagIds = kg_array_column($question->tags, 'id');
}
}
$list = [];
foreach ($allTags as $tag) {
$selected = in_array($tag->id, $questionTagIds);
$list[] = [
'name' => $tag->name,
'value' => $tag->id,
'selected' => $selected,
];
}
return $list;
return $service->handle($id);
}
public function getCategories()
@ -146,11 +122,15 @@ class Question extends Service
$question = new QuestionModel();
$question->published = QuestionModel::PUBLISH_APPROVED;
$question->client_type = $this->getClientType();
$question->client_ip = $this->getClientIp();
$question->owner_id = $user->id;
$question->title = $title;
$question->create();
$this->saveDynamicAttrs($question);
$this->recountUserQuestions($user);
$this->eventsManager->fire('Question:afterCreate', $this, $question);
@ -190,12 +170,7 @@ 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'])) {
@ -204,8 +179,14 @@ class Question extends Service
$question->update($data);
$this->saveDynamicAttrs($question);
$this->rebuildQuestionIndex($question);
$owner = $this->findUser($question->owner_id);
$this->recountUserQuestions($owner);
$this->eventsManager->fire('Question:afterUpdate', $this, $question);
return $question;
@ -219,14 +200,14 @@ class Question extends Service
$question->update();
$userRepo = new UserRepo();
$owner = $userRepo->findById($question->owner_id);
$this->recountUserQuestions($owner);
$this->saveDynamicAttrs($question);
$this->rebuildQuestionIndex($question);
$owner = $this->findUser($question->owner_id);
$this->recountUserQuestions($owner);
$this->eventsManager->fire('Question:afterDelete', $this, $question);
return $question;
@ -240,14 +221,12 @@ class Question extends Service
$question->update();
$userRepo = new UserRepo();
$this->rebuildQuestionIndex($question);
$owner = $userRepo->findById($question->owner_id);
$owner = $this->findUser($question->owner_id);
$this->recountUserQuestions($owner);
$this->rebuildQuestionIndex($question);
$this->eventsManager->fire('Question:afterRestore', $this, $question);
return $question;

View File

@ -50,4 +50,4 @@
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
</form>

View File

@ -4,7 +4,7 @@
{% set back_url = url({'for':'admin.category.list'},{'type':type}) %}
{% set add_url = url({'for':'admin.category.add'},{'type':type,'parent_id':parent.id}) %}
{% set allow_add = (type == 1 and parent.level < 2) or (type == 2 and parent.level < 1) or (type == 3 and parent.level < 1) %}
{% set allow_add = (type == 1 and parent.level < 2) or (type == 2 and parent.level < 1) %}
<div class="kg-nav">
<div class="kg-nav-left">
@ -55,14 +55,12 @@
<td>{{ item.id }}</td>
{% if item.type == 1 %}
{% if item.level == 1 %}
<td><a href="{{ child_url }}">{{ item.name }}</a></td>
<td><a href="{{ child_url }}"><i class="layui-icon layui-icon-add-circle"></i> {{ item.name }}</a></td>
{% else %}
<td><a href="{{ edit_url }}">{{ item.name }}</a></td>
{% endif %}
{% elseif item.type == 2 %}
<td><a href="{{ edit_url }}">{{ item.name }}</a></td>
{% elseif item.type == 3 %}
<td><a href="{{ edit_url }}">{{ item.name }}</a></td>
{% endif %}
<td>{{ item.level }}</td>
<td>{{ item.child_count }}</td>

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

@ -87,8 +87,8 @@
<li><a href="{{ review_url }}">审核问题</a></li>
{% elseif item.published == 2 %}
<li><a href="{{ preview_url }}" target="_blank">预览问题</a></li>
<li><a href="{{ answer_add_url }}">回答问题</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>
@ -128,7 +128,7 @@
$.ajax({
type: 'POST',
url: url,
data: {closed: data.value},
data: {closed: closed},
success: function (res) {
layer.msg(res.msg, {icon: 1});
},

View File

@ -47,13 +47,19 @@ class AnswerController extends Controller
*/
public function showAction($id)
{
$service = new AnswerService();
$service = new AnswerInfoService();
$answer = $service->getAnswer($id);
$answer = $service->handle($id);
$questionId = $answer['question']['id'];
if ($answer['me']['owned'] == 0) {
$this->response->redirect(['for' => 'home.error.403']);
}
$location = $this->url->get(
['for' => 'home.question.show', 'id' => $answer->question_id],
['answer_id' => $answer->id],
['for' => 'home.question.show', 'id' => $questionId],
['answer_id' => $id],
);
$this->response->redirect($location);

View File

@ -13,7 +13,7 @@ class IndexController extends Controller
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
if ($this->isMobileBrowser()) {
if ($this->isMobileBrowser() && $this->h5Enabled()) {
$this->response->redirect('/h5', true);
@ -66,4 +66,11 @@ class IndexController extends Controller
$this->view->setVar('vip_courses', $service->getSimpleVipCourses());
}
protected function h5Enabled()
{
$file = public_path('h5/index.html');
return file_exists($file);
}
}

View File

@ -20,6 +20,10 @@ class PageController extends Controller
$page = $service->handle($id);
if ($page['me']['owned'] == 0) {
$this->response->redirect(['for' => 'home.error.403']);
}
$featuredCourses = $this->getFeaturedCourses();
$this->seo->prependTitle($page['title']);

View File

@ -2,10 +2,12 @@
namespace App\Http\Home\Controllers;
use App\Services\Logic\User\AnswerList as UserAnswerListService;
use App\Services\Logic\User\ArticleList as UserArticleListService;
use App\Services\Logic\User\CourseList as UserCourseListService;
use App\Services\Logic\User\FriendList as UserFriendListService;
use App\Services\Logic\User\GroupList as UserGroupListService;
use App\Services\Logic\User\QuestionList as UserQuestionListService;
use App\Services\Logic\User\UserInfo as UserInfoService;
use Phalcon\Mvc\View;
@ -61,6 +63,38 @@ class UserController extends Controller
$this->view->setVar('pager', $pager);
}
/**
* @Get("/{id:[0-9]+}/questions", name="home.user.questions")
*/
public function questionsAction($id)
{
$service = new UserQuestionListService();
$pager = $service->handle($id);
$pager->target = 'tab-questions';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('user/questions');
$this->view->setVar('pager', $pager);
}
/**
* @Get("/{id:[0-9]+}/answers", name="home.user.answers")
*/
public function answersAction($id)
{
$service = new UserAnswerListService();
$pager = $service->handle($id);
$pager->target = 'tab-answers';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('user/answers');
$this->view->setVar('pager', $pager);
}
/**
* @Get("/{id:[0-9]+}/friends", name="home.user.friends")
*/

View File

@ -6,7 +6,7 @@ use App\Models\Article as ArticleModel;
use App\Models\Category as CategoryModel;
use App\Models\Reason as ReasonModel;
use App\Repos\Category as CategoryRepo;
use App\Repos\Tag as TagRepo;
use App\Services\Logic\Article\XmTagList as XmTagListService;
use App\Services\Logic\ArticleTrait;
class Article extends Service
@ -25,33 +25,9 @@ class Article extends Service
public function getXmTags($id)
{
$tagRepo = new TagRepo();
$service = new XmTagListService();
$allTags = $tagRepo->findAll(['published' => 1], 'priority');
if ($allTags->count() == 0) return [];
$articleTagIds = [];
if ($id > 0) {
$article = $this->checkArticle($id);
if (!empty($article->tags)) {
$articleTagIds = kg_array_column($article->tags, 'id');
}
}
$list = [];
foreach ($allTags as $tag) {
$selected = in_array($tag->id, $articleTagIds);
$list[] = [
'name' => $tag->name,
'value' => $tag->id,
'selected' => $selected,
];
}
return $list;
return $service->handle($id);
}
public function getCategories()

View File

@ -5,7 +5,7 @@ namespace App\Http\Home\Services;
use App\Models\Category as CategoryModel;
use App\Models\Question as QuestionModel;
use App\Repos\Category as CategoryRepo;
use App\Repos\Tag as TagRepo;
use App\Services\Logic\Question\XmTagList as XmTagListService;
use App\Services\Logic\QuestionTrait;
use App\Services\Logic\Service as LogicService;
@ -36,33 +36,9 @@ class Question extends LogicService
public function getXmTags($id)
{
$tagRepo = new TagRepo();
$service = new XmTagListService();
$allTags = $tagRepo->findAll(['published' => 1], 'priority');
if ($allTags->count() == 0) return [];
$questionTagIds = [];
if ($id > 0) {
$question = $this->checkQuestion($id);
if (!empty($question->tags)) {
$questionTagIds = kg_array_column($question->tags, 'id');
}
}
$list = [];
foreach ($allTags as $tag) {
$selected = in_array($tag->id, $questionTagIds);
$list[] = [
'name' => $tag->name,
'value' => $tag->id,
'selected' => $selected,
];
}
return $list;
return $service->handle($id);
}
public function getQuestion($id)

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

@ -2,7 +2,7 @@
{% block content %}
{% set update_url = url({'for':'home.igm.update','id':group.id}) %}
{% set update_url = url({'for':'home.im_group.update','id':group.id}) %}
{% set name_readonly = group.type == 1 ? 'readonly="readonly"' : '' %}
<form class="layui-form" method="post" action="{{ update_url }}">

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

@ -3,7 +3,6 @@
{% for item in pager.items %}
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
{% set question_url = url({'for':'home.question.show','id':item.id}) %}
{% set solved_class = item.solved ? 'column solved' : 'column' %}
<div class="search-question-card article-card question-card">
<div class="info">
<div class="title layui-elip">

View File

@ -0,0 +1,25 @@
{% if pager.total_pages > 0 %}
<div class="question-list">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
{% set answer_url = url({'for':'home.answer.show','id':item.id}) %}
<div class="layui-col-md6">
<div class="article-card wrap">
<div class="info">
<div class="title layui-elip">
<a href="{{ answer_url }}" target="_blank">{{ item.question.title }}</a>
</div>
<div class="summary">{{ substr(item.summary,0,80) }}</div>
<div class="meta">
<span class="time">{{ item.create_time|time_ago }}</span>
<span class="like">{{ item.like_count }} 点赞</span>
<span class="comment">{{ item.comment_count }} 评论</span>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -1,23 +1,19 @@
{{ partial('macros/article') }}
{% if pager.total_pages > 0 %}
<div class="article-list clearfix">
<div class="article-list">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
<div class="layui-col-md3">
<div class="course-card">
<span class="type layui-badge layui-bg-green">{{ source_type(item.source_type) }}</span>
<div class="cover">
<a href="{{ article_url }}" target="_blank">
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}" title="{{ item.title }}">
</a>
</div>
<div class="layui-col-md6">
<div class="article-card wrap">
<div class="info">
<div class="title layui-elip">
<a href="{{ article_url }}" title="{{ item.title }}" target="_blank">{{ item.title }}</a>
</div>
<div class="summary">{{ substr(item.summary,0,80) }}</div>
<div class="meta">
<span class="time">{{ item.create_time|time_ago }}</span>
<span class="view">{{ item.view_count }} 浏览</span>
<span class="like">{{ item.like_count }} 点赞</span>
<span class="comment">{{ item.comment_count }} 评论</span>

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

@ -67,10 +67,4 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/user.console.js') }}
{% endblock %}

View File

@ -63,10 +63,4 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/user.console.js') }}
{% endblock %}

View File

@ -28,10 +28,4 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/user.console.js') }}
{% endblock %}

View File

@ -44,7 +44,7 @@
<td>{{ item.area }}</td>
<td>{{ item.active_time|time_ago }}</td>
<td class="center">
<button class="layui-btn layui-btn-sm kg-delete" data-url="{{ delete_url }}">删除</button>
<button class="layui-btn layui-btn-sm layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</button>
</td>
</tr>
{% endfor %}
@ -56,10 +56,4 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/my.im.js') }}
{% endblock %}

View File

@ -42,10 +42,4 @@
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/user.console.js') }}
{% endblock %}

View File

@ -0,0 +1,26 @@
{% if pager.total_pages > 0 %}
<div class="question-list">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
{% set question_url = url({'for':'home.question.show','id':item.id}) %}
<div class="layui-col-md6">
<div class="article-card wrap">
<div class="info">
<div class="title layui-elip">
<a href="{{ question_url }}" target="_blank">{{ item.title }}</a>
</div>
<div class="summary">{{ substr(item.summary,0,80) }}</div>
<div class="meta">
<span class="time">{{ item.create_time|time_ago }}</span>
<span class="view">{{ item.view_count }} 浏览</span>
<span class="like">{{ item.like_count }} 点赞</span>
<span class="answer">{{ item.answer_count }} 回答</span>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -43,11 +43,15 @@
{% set show_tab_courses = user.course_count > 0 %}
{% set show_tab_articles = user.article_count > 0 %}
{% set show_tab_questions = user.question_count > 0 %}
{% set show_tab_answers = user.answer_count > 0 %}
{% set show_tab_friends = user.friend_count > 0 %}
{% set show_tab_groups = user.group_count > 0 %}
{% set courses_url = url({'for':'home.user.courses','id':user.id}) %}
{% set articles_url = url({'for':'home.user.articles','id':user.id}) %}
{% set questions_url = url({'for':'home.user.questions','id':user.id}) %}
{% set answers_url = url({'for':'home.user.answers','id':user.id}) %}
{% set friends_url = url({'for':'home.user.friends','id':user.id}) %}
{% set groups_url = url({'for':'home.user.groups','id':user.id}) %}
@ -58,6 +62,12 @@
{% if show_tab_articles %}
<li>文章</li>
{% endif %}
{% if show_tab_questions %}
<li>提问</li>
{% endif %}
{% if show_tab_answers %}
<li>回答</li>
{% endif %}
{% if show_tab_friends %}
<li>好友</li>
{% endif %}
@ -70,6 +80,12 @@
{% if show_tab_articles %}
<div class="layui-tab-item" id="tab-articles" data-url="{{ articles_url }}"></div>
{% endif %}
{% if show_tab_questions %}
<div class="layui-tab-item" id="tab-questions" data-url="{{ questions_url }}"></div>
{% endif %}
{% if show_tab_answers %}
<div class="layui-tab-item" id="tab-answers" data-url="{{ answers_url }}"></div>
{% endif %}
{% if show_tab_friends %}
<div class="layui-tab-item" id="tab-friends" data-url="{{ friends_url }}"></div>
{% endif %}

View File

@ -11,7 +11,7 @@ class AppInfo
protected $link = 'https://koogua.com';
protected $version = '1.3.5';
protected $version = '1.3.6';
public function __get($name)
{

View File

@ -449,6 +449,8 @@ function kg_parse_summary($content, $length = 100)
$content = strip_tags($content);
$content = trim($content);
return kg_substr($content, 0, $length);
}

View File

@ -153,27 +153,11 @@ class Answer extends Model
public function beforeCreate()
{
if (empty($this->cover)) {
$this->cover = kg_parse_first_content_image($this->content);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->content);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if (empty($this->cover)) {
$this->cover = kg_parse_first_content_image($this->content);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->content);
}
$this->update_time = time();
}

View File

@ -225,14 +225,6 @@ class Article extends Model
public function beforeCreate()
{
if (empty($this->cover)) {
$this->cover = kg_parse_first_content_image($this->content);
}
if (is_array($this->tags) || is_object($this->tags)) {
$this->tags = kg_json_encode($this->tags);
}
$this->create_time = time();
}
@ -246,19 +238,14 @@ class Article extends Model
$sync->addItem($this->id);
}
if (empty($this->cover)) {
$this->cover = kg_parse_first_content_image($this->content);
}
$this->update_time = time();
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->content);
}
if (is_array($this->tags) || is_array($this->tags)) {
public function beforeSave()
{
if (is_array($this->tags) || is_object($this->tags)) {
$this->tags = kg_json_encode($this->tags);
}
$this->update_time = time();
}
public function afterCreate()

View File

@ -89,7 +89,7 @@ class Consult extends Model
*
* @var int
*/
public $priority = 0;
public $priority = self::PRIORITY_LOW;
/**
* 私密标识

View File

@ -129,7 +129,7 @@ class Course extends Model
*
* @var float
*/
public $origin_price;
public $origin_price = 0.00;
/**
* 优惠价格
@ -313,12 +313,6 @@ class Course extends Model
$this->attrs = kg_json_encode($this->attrs);
}
if (empty($this->cover)) {
$this->cover = kg_default_course_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
$this->create_time = time();
}
@ -332,22 +326,10 @@ class Course extends Model
$sync->addItem($this->id);
}
if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->details);
}
if (is_array($this->attrs) || is_object($this->attrs)) {
$this->attrs = kg_json_encode($this->attrs);
}
if (empty($this->origin_price)) {
$this->origin_price = 1.5 * $this->market_price;
}
if ($this->deleted == 1) {
$this->published = 0;
}
@ -355,6 +337,23 @@ class Course extends Model
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->cover)) {
$this->cover = kg_default_course_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->details);
}
if (empty($this->origin_price)) {
$this->origin_price = 1.5 * $this->market_price;
}
}
public function afterCreate()
{
$cache = new MaxCourseIdCache();

View File

@ -125,18 +125,19 @@ class FlashSale extends Model
public function beforeCreate()
{
if (is_array($this->item_info) || is_object($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
if (is_array($this->schedules) || is_object($this->schedules)) {
$this->schedules = kg_json_encode($this->schedules);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if ($this->deleted == 1) {
$this->published = 0;
}
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->item_info) || is_object($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
@ -145,12 +146,6 @@ class FlashSale extends Model
if (is_array($this->schedules) || is_object($this->schedules)) {
$this->schedules = kg_json_encode($this->schedules);
}
if ($this->deleted == 1) {
$this->published = 0;
}
$this->update_time = time();
}
public function afterCreate()

View File

@ -127,12 +127,6 @@ class ImGroup extends Model
public function beforeCreate()
{
if (empty($this->avatar)) {
$this->avatar = kg_default_group_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
$this->create_time = time();
}
@ -143,10 +137,6 @@ class ImGroup extends Model
$sync->addItem($this->id);
}
if (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
if ($this->deleted == 1) {
$this->published = 0;
}
@ -154,6 +144,15 @@ class ImGroup extends Model
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->avatar)) {
$this->avatar = kg_default_group_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
}
public function afterCreate()
{
$cache = new MaxImGroupIdCache();

View File

@ -85,20 +85,19 @@ class ImNotice extends Model
public function beforeCreate()
{
if (is_array($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -112,22 +112,21 @@ class ImUser extends Model
public function beforeCreate()
{
if (empty($this->avatar)) {
$this->avatar = kg_default_user_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if (Text::startsWith($this->avatar, 'http')) {
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->avatar)) {
$this->avatar = kg_default_user_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -180,20 +180,19 @@ class Notification extends Model
public function beforeCreate()
{
if (is_array($this->event_info) || is_object($this->event_info)) {
$this->event_info = kg_json_encode($this->event_info);
}
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->event_info) || is_object($this->event_info)) {
$this->event_info = kg_json_encode($this->event_info);
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -170,18 +170,15 @@ class Order extends Model
{
$this->sn = date('YmdHis') . rand(1000, 9999);
if (is_array($this->item_info) || is_object($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
if (is_array($this->promotion_info) || is_object($this->promotion_info)) {
$this->promotion_info = kg_json_encode($this->promotion_info);
}
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->item_info) || is_object($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
@ -190,8 +187,6 @@ class Order extends Model
if (is_array($this->promotion_info) || is_object($this->promotion_info)) {
$this->promotion_info = kg_json_encode($this->promotion_info);
}
$this->update_time = time();
}
public function afterSave()

View File

@ -105,21 +105,11 @@ class Package extends Model
public function beforeCreate()
{
if (empty($this->cover)) {
$this->cover = kg_default_package_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if ($this->deleted == 1) {
$this->published = 0;
}
@ -127,6 +117,15 @@ class Package extends Model
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->cover)) {
$this->cover = kg_default_package_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
}
public function afterCreate()
{
$cache = new MaxPackageIdCache();

View File

@ -166,21 +166,11 @@ class PointGift extends Model
$this->attrs = kg_json_encode($this->attrs);
}
if (empty($this->cover)) {
$this->cover = kg_default_gift_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if (is_array($this->attrs) || is_object($this->attrs)) {
$this->attrs = kg_json_encode($this->attrs);
}
@ -192,6 +182,15 @@ class PointGift extends Model
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->cover)) {
$this->cover = kg_default_gift_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
}
public function afterCreate()
{
$cache = new MaxPointGiftIdCache();

View File

@ -95,20 +95,19 @@ class PointHistory extends Model
public function beforeCreate()
{
if (is_array($this->event_info) || is_object($this->event_info)) {
$this->event_info = kg_json_encode($this->event_info);
}
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->event_info) || is_object($this->event_info)) {
$this->event_info = kg_json_encode($this->event_info);
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -239,14 +239,6 @@ class Question extends Model
public function beforeCreate()
{
if (is_array($this->tags) || is_object($this->tags)) {
$this->tags = kg_json_encode($this->tags);
}
if (empty($this->cover)) {
$this->cover = kg_parse_first_content_image($this->content);
}
$this->create_time = time();
}
@ -260,19 +252,14 @@ class Question extends Model
$sync->addItem($this->id);
}
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->tags) || is_object($this->tags)) {
$this->tags = kg_json_encode($this->tags);
}
if (empty($this->cover)) {
$this->cover = kg_parse_first_content_image($this->content);
}
if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->content);
}
$this->update_time = time();
}
public function afterCreate()

View File

@ -152,15 +152,11 @@ class Review extends Model
public function beforeCreate()
{
$this->rating = $this->getAvgRating();
$this->create_time = time();
}
public function beforeUpdate()
{
$this->rating = $this->getAvgRating();
if ($this->deleted == 1) {
$this->published = 0;
}
@ -168,6 +164,11 @@ class Review extends Model
$this->update_time = time();
}
public function beforeSave()
{
$this->rating = $this->getAvgRating();
}
protected function getAvgRating()
{
$sumRating = $this->rating1 + $this->rating2 + $this->rating3;

View File

@ -103,20 +103,19 @@ class Role extends Model
public function beforeCreate()
{
if (is_array($this->routes) || is_object($this->routes)) {
$this->routes = kg_json_encode($this->routes);
}
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->routes) || is_object($this->routes)) {
$this->routes = kg_json_encode($this->routes);
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -124,6 +124,20 @@ class Slide extends Model
}
public function beforeCreate()
{
$this->create_time = time();
}
public function beforeUpdate()
{
if ($this->deleted == 1) {
$this->published = 0;
}
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->cover)) {
$this->cover = kg_default_slide_cover_path();
@ -134,25 +148,6 @@ class Slide extends Model
if (is_array($this->target_attrs) || is_object($this->target_attrs)) {
$this->target_attrs = kg_json_encode($this->target_attrs);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}
if (is_array($this->target_attrs) || is_object($this->target_attrs)) {
$this->target_attrs = kg_json_encode($this->target_attrs);
}
if ($this->deleted == 1) {
$this->published = 0;
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -98,21 +98,11 @@ class Tag extends Model
public function beforeCreate()
{
if (empty($this->icon)) {
$this->icon = kg_default_icon_path();
} elseif (Text::startsWith($this->icon, 'http')) {
$this->icon = self::getIconPath($this->icon);
}
$this->create_time = time();
}
public function beforeUpdate()
{
if (Text::startsWith($this->icon, 'http')) {
$this->icon = self::getIconPath($this->icon);
}
if ($this->deleted == 1) {
$this->published = 0;
}
@ -120,6 +110,15 @@ class Tag extends Model
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->icon)) {
$this->icon = kg_default_icon_path();
} elseif (Text::startsWith($this->icon, 'http')) {
$this->icon = self::getIconPath($this->icon);
}
}
public function afterCreate()
{
$cache = new MaxTagIdCache();

View File

@ -126,20 +126,19 @@ class Task extends Model
public function beforeCreate()
{
if (is_array($this->item_info) || is_object($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
$this->create_time = time();
}
public function beforeUpdate()
{
$this->update_time = time();
}
public function beforeSave()
{
if (is_array($this->item_info) || is_object($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
$this->update_time = time();
}
public function afterFetch()

View File

@ -136,6 +136,13 @@ class User extends Model
*/
public $answer_count;
/**
* 评论数
*
* @var int
*/
public $comment_count;
/**
* 收藏数
*
@ -199,12 +206,6 @@ class User extends Model
public function beforeCreate()
{
if (empty($this->avatar)) {
$this->avatar = kg_default_user_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
$this->create_time = time();
}
@ -215,11 +216,16 @@ class User extends Model
$sync->addItem($this->id);
}
if (Text::startsWith($this->avatar, 'http')) {
$this->update_time = time();
}
public function beforeSave()
{
if (empty($this->avatar)) {
$this->avatar = kg_default_user_avatar_path();
} elseif (Text::startsWith($this->avatar, 'http')) {
$this->avatar = self::getAvatarPath($this->avatar);
}
$this->update_time = time();
}
public function afterCreate()

View File

@ -5,8 +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\ArticleFavorite as ArticleFavoriteModel;
use App\Models\CourseFavorite as CourseFavoriteModel;
use App\Models\Comment as CommentModel;
use App\Models\CourseUser as CourseUserModel;
use App\Models\ImUser as ImUserModel;
use App\Models\Notification as NotificationModel;
@ -199,19 +198,11 @@ class User extends Repository
]);
}
public function countCourseFavorites($userId)
public function countComments($userId)
{
return (int)CourseFavoriteModel::count([
'conditions' => 'user_id = :user_id: AND deleted = 0',
'bind' => ['user_id' => $userId],
]);
}
public function countArticleFavorites($userId)
{
return (int)ArticleFavoriteModel::count([
'conditions' => 'user_id = :user_id: AND deleted = 0',
'bind' => ['user_id' => $userId],
return (int)CommentModel::count([
'conditions' => 'owner_id = ?1 AND published = ?2',
'bind' => [1 => $userId, 2 => CommentModel::PUBLISH_APPROVED],
]);
}

View File

@ -12,16 +12,15 @@ 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;
public function handle()
{
@ -41,15 +40,15 @@ class AnswerCreate extends LogicService
$answer = new AnswerModel();
$answer->published = $this->getPublishStatus($user);
$answer->content = $validator->checkContent($post['content']);
$answer->client_type = $this->getClientType();
$answer->client_ip = $this->getClientIp();
$answer->question_id = $question->id;
$answer->owner_id = $user->id;
$data = $this->handlePostData($post);
$answer->create();
$data['published'] = $this->getPublishStatus($user);
$data['question_id'] = $question->id;
$data['owner_id'] = $user->id;
$answer->create($data);
$this->saveDynamicAttrs($answer);
$this->incrUserDailyAnswerCount($user);
$this->recountQuestionAnswers($question);
$this->recountUserAnswers($user);
@ -73,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

@ -0,0 +1,43 @@
<?php
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;
trait AnswerDataTrait
{
use ClientTrait;
protected function handlePostData($post)
{
$data = [];
$data['client_type'] = $this->getClientType();
$data['client_ip'] = $this->getClientIp();
$validator = new AnswerValidator();
$data['content'] = $validator->checkContent($post['content']);
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);
$answer->summary = kg_parse_summary($answer->content);
$answer->update();
}
}

View File

@ -75,8 +75,16 @@ class AnswerInfo extends LogicService
{
$me = [
'liked' => 0,
'owned' => 0,
];
$isOwner = $user->id == $answer->owner_id;
$approved = $answer->published = AnswerModel::PUBLISH_APPROVED;
if ($isOwner || $approved) {
$me['owned'] = 1;
}
if ($user->id > 0) {
$likeRepo = new AnswerLikeRepo();

View File

@ -0,0 +1,72 @@
<?php
namespace App\Services\Logic\Answer;
use App\Builders\AnswerList as AnswerListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\Answer as AnswerRepo;
use App\Services\Logic\Service as LogicService;
class AnswerList extends LogicService
{
public function handle()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$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);
}
public function handleAnswers($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new AnswerListBuilder();
$answers = $pager->items->toArray();
$questions = $builder->getQuestions($answers);
$users = $builder->getUsers($answers);
$items = [];
foreach ($answers as $answer) {
$question = $questions[$answer['question_id']] ?? new \stdClass();
$owner = $users[$answer['owner_id']] ?? new \stdClass();
$items[] = [
'id' => $answer['id'],
'summary' => $answer['summary'],
'published' => $answer['published'],
'accepted' => $answer['accepted'],
'comment_count' => $answer['comment_count'],
'like_count' => $answer['like_count'],
'create_time' => $answer['create_time'],
'update_time' => $answer['update_time'],
'question' => $question,
'owner' => $owner,
];
}
$pager->items = $items;
return $pager;
}
}

View File

@ -14,6 +14,7 @@ class AnswerUpdate extends LogicService
use ClientTrait;
use QuestionTrait;
use AnswerTrait;
use AnswerDataTrait;
public function handle($id)
{
@ -29,11 +30,11 @@ class AnswerUpdate extends LogicService
$validator->checkIfAllowEdit($answer);
$answer->content = $validator->checkContent($post['content']);
$answer->client_type = $this->getClientType();
$answer->client_ip = $this->getClientIp();
$data = $this->handlePostData($post);
$answer->update();
$answer->update($data);
$this->saveDynamicAttrs($answer);
$this->eventsManager->fire('Answer:afterUpdate', $this, $answer);

View File

@ -29,7 +29,6 @@ class ArticleCreate extends LogicService
$data = $this->handlePostData($post);
$data['published'] = $this->getPublishStatus($user);
$data['owner_id'] = $user->id;
$article->create($data);
@ -38,6 +37,7 @@ class ArticleCreate extends LogicService
$this->saveTags($article, $post['xm_tag_ids']);
}
$this->saveDynamicAttrs($article);
$this->incrUserDailyArticleCount($user);
$this->recountUserArticles($user);
@ -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;
@ -19,6 +20,9 @@ trait ArticleDataTrait
{
$data = [];
$data['client_type'] = $this->getClientType();
$data['client_ip'] = $this->getClientIp();
$validator = new ArticleValidator();
$data['title'] = $validator->checkTitle($post['title']);
@ -48,6 +52,25 @@ 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);
$article->summary = kg_parse_summary($article->content);
$article->update();
/**
* 重新执行afterFetch
*/
$article->afterFetch();
}
protected function saveTags(ArticleModel $article, $tagIds)
{
$originTagIds = [];

View File

@ -8,7 +8,6 @@ use App\Models\Article as ArticleModel;
use App\Repos\Article as ArticleRepo;
use App\Services\Logic\Service as LogicService;
use App\Validators\ArticleQuery as ArticleQueryValidator;
use Phalcon\Text;
class ArticleList extends LogicService
{
@ -52,18 +51,8 @@ class ArticleList extends LogicService
$items = [];
$baseUrl = kg_cos_url();
foreach ($articles as $article) {
if (!empty($article['cover']) && !Text::startsWith($article['cover'], 'http')) {
$article['cover'] = $baseUrl . $article['cover'];
}
if (empty($article['summary'])) {
$article['summary'] = kg_parse_summary($article['content']);
}
$article['tags'] = json_decode($article['tags'], true);
$category = $categories[$article['category_id']] ?? new \stdClass();

View File

@ -44,6 +44,8 @@ class ArticleUpdate extends LogicService
$this->saveTags($article, $post['xm_tag_ids']);
}
$this->saveDynamicAttrs($article);
$this->eventsManager->fire('Article:afterUpdate', $this, $article);
return $article;

View File

@ -0,0 +1,50 @@
<?php
namespace App\Services\Logic\Article;
use App\Repos\Article as ArticleRepo;
use App\Repos\Tag as TagRepo;
use App\Services\Logic\Service as LogicService;
class XmTagList extends LogicService
{
public function handle($id)
{
$tagRepo = new TagRepo();
$allTags = $tagRepo->findAll(['published' => 1]);
if ($allTags->count() == 0) return [];
$articleTagIds = [];
if ($id > 0) {
$article = $this->findArticle($id);
if (!empty($article->tags)) {
$articleTagIds = kg_array_column($article->tags, 'id');
}
}
$list = [];
foreach ($allTags as $tag) {
$selected = in_array($tag->id, $articleTagIds);
$list[] = [
'name' => $tag->name,
'value' => $tag->id,
'selected' => $selected,
];
}
return $list;
}
protected function findArticle($id)
{
$articleRepo = new ArticleRepo();
return $articleRepo->findById($id);
}
}

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,14 +32,15 @@ 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);
$item = $validator->checkItem($comment->item_type, $comment->item_id);
$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

@ -3,6 +3,7 @@
namespace App\Services\Logic\Page;
use App\Models\Page as PageModel;
use App\Models\User as UserModel;
use App\Services\Logic\PageTrait;
use App\Services\Logic\Service as LogicService;
@ -13,22 +14,38 @@ class PageInfo extends LogicService
public function handle($id)
{
$user = $this->getCurrentUser(true);
$page = $this->checkPage($id);
return $this->handlePage($page);
return $this->handlePage($page, $user);
}
protected function handlePage(PageModel $page)
protected function handlePage(PageModel $page, UserModel $user)
{
$page->content = kg_parse_markdown($page->content);
$me = $this->handleMeInfo($page, $user);
return [
'id' => $page->id,
'title' => $page->title,
'content' => $page->content,
'create_time' => $page->create_time,
'update_time' => $page->update_time,
'me' => $me,
];
}
protected function handleMeInfo(PageModel $page, UserModel $user)
{
$me = ['owned' => 0];
if ($page->published == 1) {
$me['owned'] = 1;
}
return $me;
}
}

View File

@ -28,7 +28,6 @@ class QuestionCreate extends LogicService
$data = $this->handlePostData($post);
$data['published'] = $this->getPublishStatus($user);
$data['owner_id'] = $user->id;
$question->create($data);
@ -37,6 +36,7 @@ class QuestionCreate extends LogicService
$this->saveTags($question, $post['xm_tag_ids']);
}
$this->saveDynamicAttrs($question);
$this->incrUserDailyQuestionCount($user);
$this->recountUserQuestions($user);
@ -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;
@ -36,7 +37,26 @@ 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);
$question->summary = kg_parse_summary($question->content);
$question->update();
/**
* 重新执行afterFetch
*/
$question->afterFetch();
}
protected function saveTags(QuestionModel $question, $tagIds)
{
$originTagIds = [];

View File

@ -8,7 +8,6 @@ use App\Models\Question as QuestionModel;
use App\Repos\Question as QuestionRepo;
use App\Services\Logic\Service as LogicService;
use App\Validators\QuestionQuery as QuestionQueryValidator;
use Phalcon\Text;
class QuestionList extends LogicService
{
@ -49,21 +48,12 @@ class QuestionList extends LogicService
$items = [];
$baseUrl = kg_cos_url();
foreach ($questions as $question) {
if (!empty($question['cover']) && !Text::startsWith($question['cover'], 'http')) {
$question['cover'] = $baseUrl . $question['cover'];
}
if (empty($question['summary'])) {
$question['summary'] = kg_parse_summary($question['content'], 80);
}
$question['tags'] = json_decode($question['tags'], true);
$owner = $users[$question['owner_id']] ?? new \stdClass();
$lastReplier = $users[$question['last_replier_id']] ?? new \stdClass();
$items[] = [

View File

@ -50,6 +50,8 @@ class QuestionUpdate extends LogicService
$this->saveTags($question, $post['xm_tag_ids']);
}
$this->saveDynamicAttrs($question);
$this->eventsManager->fire('Question:afterUpdate', $this, $question);
return $question;

View File

@ -0,0 +1,50 @@
<?php
namespace App\Services\Logic\Question;
use App\Repos\Question as QuestionRepo;
use App\Repos\Tag as TagRepo;
use App\Services\Logic\Service as LogicService;
class XmTagList extends LogicService
{
public function handle($id)
{
$tagRepo = new TagRepo();
$allTags = $tagRepo->findAll(['published' => 1]);
if ($allTags->count() == 0) return [];
$questionTagIds = [];
if ($id > 0) {
$question = $this->findQuestion($id);
if (!empty($question->tags)) {
$questionTagIds = kg_array_column($question->tags, 'id');
}
}
$list = [];
foreach ($allTags as $tag) {
$selected = in_array($tag->id, $questionTagIds);
$list[] = [
'name' => $tag->name,
'value' => $tag->id,
'selected' => $selected,
];
}
return $list;
}
protected function findQuestion($id)
{
$questionRepo = new QuestionRepo();
return $questionRepo->findById($id);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Services\Logic\User;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\Answer as AnswerModel;
use App\Repos\Answer as AnswerRepo;
use App\Services\Logic\Answer\AnswerList as AnswerListService;
use App\Services\Logic\Service as LogicService;
use App\Services\Logic\UserTrait;
class AnswerList extends LogicService
{
use UserTrait;
public function handle($id)
{
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['owner_id'] = $user->id;
$params['published'] = AnswerModel::PUBLISH_APPROVED;
$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 handleAnswers($pager)
{
$service = new AnswerListService();
return $service->handleAnswers($pager);
}
}

View File

@ -2,9 +2,9 @@
namespace App\Services\Logic\User\Console;
use App\Builders\AnswerList as AnswerListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\Answer as AnswerRepo;
use App\Services\Logic\Answer\AnswerList as AnswerListService;
use App\Services\Logic\Service as LogicService;
class AnswerList extends LogicService
@ -34,44 +34,9 @@ class AnswerList extends LogicService
protected function handleAnswers($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$service = new AnswerListService();
$builder = new AnswerListBuilder();
$answers = $pager->items->toArray();
$questions = $builder->getQuestions($answers);
$users = $builder->getUsers($answers);
$items = [];
foreach ($answers as $answer) {
$answer['summary'] = kg_parse_summary($answer['content'], 64);
$question = $questions[$answer['question_id']] ?? new \stdClass();
$owner = $users[$answer['owner_id']] ?? new \stdClass();
$items[] = [
'id' => $answer['id'],
'summary' => $answer['summary'],
'published' => $answer['published'],
'accepted' => $answer['accepted'],
'comment_count' => $answer['comment_count'],
'like_count' => $answer['like_count'],
'create_time' => $answer['create_time'],
'update_time' => $answer['update_time'],
'question' => $question,
'owner' => $owner,
];
}
$pager->items = $items;
return $pager;
return $service->handleAnswers($pager);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Services\Logic\User;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\Question as QuestionModel;
use App\Repos\Question as QuestionRepo;
use App\Services\Logic\Question\QuestionList as QuestionListService;
use App\Services\Logic\Service as LogicService;
use App\Services\Logic\UserTrait;
class QuestionList extends LogicService
{
use UserTrait;
public function handle($id)
{
$user = $this->checkUser($id);
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['owner_id'] = $user->id;
$params['published'] = QuestionModel::PUBLISH_APPROVED;
$params['deleted'] = 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$articleRepo = new QuestionRepo();
$pager = $articleRepo->paginate($params, $sort, $page, $limit);
return $this->handleQuestions($pager);
}
protected function handleQuestions($pager)
{
$service = new QuestionListService();
return $service->handleQuestions($pager);
}
}

View File

@ -37,6 +37,8 @@ class UserInfo extends LogicService
'locked' => $user->locked,
'course_count' => $user->course_count,
'article_count' => $user->article_count,
'question_count' => $user->question_count,
'answer_count' => $user->answer_count,
'friend_count' => $imUser->friend_count,
'group_count' => $imUser->group_count,
'active_time' => $user->active_time,

View File

@ -35,10 +35,6 @@ class ArticleDocument extends Component
*/
public function formatDocument(ArticleModel $article)
{
if (empty($article->summary)) {
$article->summary = kg_parse_summary($article->content);
}
if (is_array($article->tags) || is_object($article->tags)) {
$article->tags = kg_json_encode($article->tags);
}

View File

@ -61,10 +61,6 @@ class CourseDocument extends Component
$course->cover = CourseModel::getCoverPath($course->cover);
if (empty($course->summary)) {
$course->summary = kg_parse_summary($course->details);
}
return [
'id' => $course->id,
'title' => $course->title,

View File

@ -36,10 +36,6 @@ class QuestionDocument extends Component
*/
public function formatDocument(QuestionModel $question)
{
if (empty($question->summary)) {
$question->summary = kg_parse_summary($question->content);
}
if (is_array($question->tags) || is_object($question->tags)) {
$question->tags = kg_json_encode($question->tags);
}

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();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

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;
@ -265,6 +265,8 @@
.page-info {
min-height: 500px;
padding-top: 30px;
padding-bottom: 30px;
}
.help-list li {
@ -1763,6 +1765,20 @@
padding: 10px 8px;
}
.user-tab .article-card {
height: 110px;
overflow: hidden;
}
.user-tab .article-card .title {
border-bottom: 1px solid #f2f2f2;
padding-bottom: 10px;
}
.user-tab .article-card .summary {
max-height: 3em;
}
.user-card {
float: left;
width: 100%;
@ -1956,7 +1972,7 @@
}
.layui-table p {
line-height: 1.8em;
line-height: 2em;
}
.layui-table .center {

View File

@ -14,12 +14,11 @@ layui.use(['jquery', 'helper'], function () {
var options = {
live: true,
autoplay: true,
h5_flv: true,
width: 760,
height: 428,
};
var formats = ['rtmp', 'flv', 'm3u8'];
var formats = ['m3u8'];
var rates = ['od', 'hd', 'sd'];
$.each(formats, function (i, format) {

View File

@ -1,45 +0,0 @@
layui.use(['jquery', 'layer'], function () {
var $ = layui.jquery;
var layer = layui.layer;
$('.btn-edit-pwd').on('click', function () {
var url = $(this).data('url');
layer.open({
type: 2,
title: '修改密码',
content: [url, 'no'],
area: ['640px', '320px'],
cancel: function () {
window.location.reload();
}
});
});
$('.btn-edit-phone').on('click', function () {
var url = $(this).data('url');
layer.open({
type: 2,
title: '修改手机',
content: [url, 'no'],
area: ['640px', '420px'],
cancel: function () {
window.location.reload();
}
});
});
$('.btn-edit-email').on('click', function () {
var url = $(this).data('url');
layer.open({
type: 2,
title: '修改邮箱',
content: [url, 'no'],
area: ['640px', '420px'],
cancel: function () {
window.location.reload();
}
});
});
});

View File

@ -1,8 +1,7 @@
layui.use(['jquery', 'layer', 'helper'], function () {
layui.use(['jquery', 'layer'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var helper = layui.helper;
/**
* 查看咨询
@ -124,14 +123,4 @@ layui.use(['jquery', 'layer', 'helper'], function () {
});
});
if ($('#tab-courses').length > 0) {
var $tabCourses = $('#tab-courses');
helper.ajaxLoadHtml($tabCourses.data('url'), $tabCourses.attr('id'));
}
if ($('#tab-users').length > 0) {
var $tabUsers = $('#tab-users');
helper.ajaxLoadHtml($tabUsers.data('url'), $tabUsers.attr('id'));
}
});

View File

@ -13,6 +13,16 @@ layui.use(['jquery', 'helper'], function () {
helper.ajaxLoadHtml($tabArticles.data('url'), $tabArticles.attr('id'));
}
if ($('#tab-questions').length > 0) {
var $tabQuestions = $('#tab-questions');
helper.ajaxLoadHtml($tabQuestions.data('url'), $tabQuestions.attr('id'));
}
if ($('#tab-answers').length > 0) {
var $tabAnswers = $('#tab-answers');
helper.ajaxLoadHtml($tabAnswers.data('url'), $tabAnswers.attr('id'));
}
if ($('#tab-friends').length > 0) {
var $tabFriends = $('#tab-friends');
helper.ajaxLoadHtml($tabFriends.data('url'), $tabFriends.attr('id'));