diff --git a/CHANGELOG.md b/CHANGELOG.md index 00dd33e0..51ce29a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +### [v1.3.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.3)(2021-04-30) + +### 更新 + +- 前台增加文章发布功能 +- 增加文章,咨询,评价,评论相关事件站内提醒 +- 增加文章,咨询,评价,评论事件埋点 +- 后台首页增加若干统计项目 +- 后台增加文章审核功能 +- 重构积分历史记录 +- 优化在线统计方式 +- 优化前台界面 + ### [v1.3.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.2)(2021-04-20) ### 更新 diff --git a/app/Builders/NotificationList.php b/app/Builders/NotificationList.php new file mode 100644 index 00000000..21f684f1 --- /dev/null +++ b/app/Builders/NotificationList.php @@ -0,0 +1,44 @@ +getUsers($notifications); + + foreach ($notifications as $key => $notification) { + $notifications[$key]['sender'] = $users[$notification['sender_id']] ?? new \stdClass(); + $notifications[$key]['receiver'] = $users[$notification['receiver_id']] ?? new \stdClass(); + } + + return $notifications; + } + + public function getUsers(array $notifications) + { + $senderIds = kg_array_column($notifications, 'sender_id'); + $receiverIds = kg_array_column($notifications, 'receiver_id'); + $ids = array_merge($senderIds, $receiverIds); + + $userRepo = new UserRepo(); + + $users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']); + + $baseUrl = kg_cos_url(); + + $result = []; + + foreach ($users->toArray() as $user) { + $user['avatar'] = $baseUrl . $user['avatar']; + $result[$user['id']] = $user; + } + + return $result; + } + +} diff --git a/app/Caches/ArticleRelatedList.php b/app/Caches/ArticleRelatedList.php index de602a27..78c97f4b 100644 --- a/app/Caches/ArticleRelatedList.php +++ b/app/Caches/ArticleRelatedList.php @@ -40,7 +40,7 @@ class ArticleRelatedList extends Cache $where = [ 'tag_id' => $tagIds[$randKey], - 'published' => 1, + 'published' => ArticleModel::PUBLISH_APPROVED, ]; $pager = $articleRepo->paginate($where); diff --git a/app/Caches/ModerationStat.php b/app/Caches/ModerationStat.php new file mode 100644 index 00000000..518b76f3 --- /dev/null +++ b/app/Caches/ModerationStat.php @@ -0,0 +1,35 @@ +lifetime; + } + + public function getKey($id = null) + { + return 'moderation_stat'; + } + + public function getContent($id = null) + { + $statRepo = new StatRepo(); + + $articleCount = $statRepo->countPendingArticles(); + $commentCount = $statRepo->countPendingComments(); + + return [ + 'article_count' => $articleCount, + 'comment_count' => $commentCount, + ]; + } + +} diff --git a/app/Caches/SiteGlobalStat.php b/app/Caches/SiteGlobalStat.php index a1a66d98..37738ff9 100644 --- a/app/Caches/SiteGlobalStat.php +++ b/app/Caches/SiteGlobalStat.php @@ -2,10 +2,11 @@ namespace App\Caches; +use App\Repos\Article as ArticleRepo; +use App\Repos\Comment as CommentRepo; use App\Repos\Consult as ConsultRepo; use App\Repos\Course as CourseRepo; use App\Repos\ImGroup as GroupRepo; -use App\Repos\Order as OrderRepo; use App\Repos\Package as PackageRepo; use App\Repos\Review as ReviewRepo; use App\Repos\Topic as TopicRepo; @@ -14,7 +15,7 @@ use App\Repos\User as UserRepo; class SiteGlobalStat extends Cache { - protected $lifetime = 2 * 3600; + protected $lifetime = 15 * 60; public function getLifetime() { @@ -29,9 +30,10 @@ class SiteGlobalStat extends Cache public function getContent($id = null) { $courseRepo = new CourseRepo(); + $articleRepo = new ArticleRepo(); + $commentRepo = new CommentRepo(); $consultRepo = new ConsultRepo(); $groupRepo = new GroupRepo(); - $orderRepo = new OrderRepo(); $packageRepo = new PackageRepo(); $reviewRepo = new ReviewRepo(); $topicRepo = new TopicRepo(); @@ -39,9 +41,11 @@ class SiteGlobalStat extends Cache return [ 'course_count' => $courseRepo->countCourses(), + 'article_count' => $articleRepo->countArticles(), + 'comment_count' => $commentRepo->countComments(), 'consult_count' => $consultRepo->countConsults(), 'group_count' => $groupRepo->countGroups(), - 'order_count' => $orderRepo->countOrders(), + 'vip_count' => $userRepo->countVipUsers(), 'package_count' => $packageRepo->countPackages(), 'review_count' => $reviewRepo->countReviews(), 'topic_count' => $topicRepo->countTopics(), diff --git a/app/Caches/SiteTodayStat.php b/app/Caches/SiteTodayStat.php index 13c0ae66..53f845c2 100644 --- a/app/Caches/SiteTodayStat.php +++ b/app/Caches/SiteTodayStat.php @@ -7,7 +7,7 @@ use App\Repos\Stat as StatRepo; class SiteTodayStat extends Cache { - protected $lifetime = 1 * 3600; + protected $lifetime = 15 * 60; public function getLifetime() { @@ -26,15 +26,19 @@ class SiteTodayStat extends Cache $date = date('Y-m-d'); $saleCount = $statRepo->countDailySales($date); + $refundCount = $statRepo->countDailyRefunds($date); $saleAmount = $statRepo->sumDailySales($date); $refundAmount = $statRepo->sumDailyRefunds($date); - $registerCount = $statRepo->countDailyRegisteredUser($date); + $registerCount = $statRepo->countDailyRegisteredUsers($date); + $pointRedeemCount = $statRepo->countDailyPointRedeems($date); return [ 'sale_count' => $saleCount, + 'refund_count' => $refundCount, 'sale_amount' => $saleAmount, 'refund_amount' => $refundAmount, 'register_count' => $registerCount, + 'point_redeem_count' => $pointRedeemCount, ]; } diff --git a/app/Console/Tasks/DeliverTask.php b/app/Console/Tasks/DeliverTask.php index 7c57b30d..e6505c09 100644 --- a/app/Console/Tasks/DeliverTask.php +++ b/app/Console/Tasks/DeliverTask.php @@ -14,7 +14,7 @@ use App\Repos\ImGroupUser as ImGroupUserRepo; use App\Repos\Order as OrderRepo; use App\Repos\User as UserRepo; use App\Services\Logic\Notice\OrderFinish as OrderFinishNotice; -use App\Services\Logic\Point\PointHistory as PointHistoryService; +use App\Services\Logic\Point\History\OrderConsume as OrderConsumePointHistory; use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -205,9 +205,9 @@ class DeliverTask extends Task protected function handleOrderConsumePoint(OrderModel $order) { - $service = new PointHistoryService(); + $service = new OrderConsumePointHistory(); - $service->handleOrderConsume($order); + $service->handle($order); } protected function handleOrderFinishNotice(OrderModel $order) diff --git a/app/Console/Tasks/PointGiftDeliverTask.php b/app/Console/Tasks/PointGiftDeliverTask.php index 0f0778ab..e7f67354 100644 --- a/app/Console/Tasks/PointGiftDeliverTask.php +++ b/app/Console/Tasks/PointGiftDeliverTask.php @@ -14,7 +14,7 @@ use App\Repos\ImGroupUser as ImGroupUserRepo; use App\Repos\PointGift as PointGiftRepo; use App\Repos\PointRedeem as PointRedeemRepo; use App\Services\Logic\Notice\DingTalk\PointRedeem as PointRedeemNotice; -use App\Services\Logic\Point\PointHistory as PointHistoryService; +use App\Services\Logic\Point\History\PointRefund as PointRefundPointHistory; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -54,9 +54,6 @@ class PointGiftDeliverTask extends Task case PointGiftModel::TYPE_GOODS: $this->handleGoodsRedeem($redeem); break; - case PointGiftModel::TYPE_CASH: - $this->handleCashRedeem($redeem); - break; } $task->status = TaskModel::STATUS_FINISHED; @@ -169,16 +166,11 @@ class PointGiftDeliverTask extends Task $notice->createTask($redeem); } - protected function handleCashRedeem(PointRedeemModel $redeem) - { - - } - protected function handlePointRefund(PointRedeemModel $redeem) { - $service = new PointHistoryService(); + $service = new PointRefundPointHistory(); - $service->handlePointRefund($redeem); + $service->handle($redeem); } /** diff --git a/app/Console/Tasks/SyncLearningTask.php b/app/Console/Tasks/SyncLearningTask.php index 2ba7a0e8..f4845a3d 100644 --- a/app/Console/Tasks/SyncLearningTask.php +++ b/app/Console/Tasks/SyncLearningTask.php @@ -10,7 +10,7 @@ use App\Repos\ChapterUser as ChapterUserRepo; use App\Repos\Course as CourseRepo; use App\Repos\CourseUser as CourseUserRepo; use App\Repos\Learning as LearningRepo; -use App\Services\Logic\Point\PointHistory as PointHistoryService; +use App\Services\Logic\Point\History\ChapterStudy as ChapterStudyPointHistory; use App\Services\Sync\Learning as LearningSyncService; class SyncLearningTask extends Task @@ -184,9 +184,9 @@ class SyncLearningTask extends Task */ protected function handleStudyPoint(ChapterUserModel $chapterUser) { - $service = new PointHistoryService(); + $service = new ChapterStudyPointHistory(); - $service->handleChapterStudy($chapterUser); + $service->handle($chapterUser); } } diff --git a/app/Http/Admin/Controllers/ArticleController.php b/app/Http/Admin/Controllers/ArticleController.php index 14a3b3af..7fd4a3b4 100644 --- a/app/Http/Admin/Controllers/ArticleController.php +++ b/app/Http/Admin/Controllers/ArticleController.php @@ -31,10 +31,12 @@ class ArticleController extends Controller { $articleService = new ArticleService(); + $publishTypes = $articleService->getPublishTypes(); $sourceTypes = $articleService->getSourceTypes(); $categories = $articleService->getCategories(); $xmTags = $articleService->getXmTags(0); + $this->view->setVar('publish_types', $publishTypes); $this->view->setVar('source_types', $sourceTypes); $this->view->setVar('categories', $categories); $this->view->setVar('xm_tags', $xmTags); @@ -52,6 +54,19 @@ class ArticleController extends Controller $this->view->setVar('pager', $pager); } + /** + * @Get("/list/pending", name="admin.article.pending_list") + */ + public function pendingListAction() + { + $articleService = new ArticleService(); + + $pager = $articleService->getPendingArticles(); + + $this->view->pick('article/pending_list'); + $this->view->setVar('pager', $pager); + } + /** * @Get("/add", name="admin.article.add") */ @@ -64,6 +79,40 @@ class ArticleController extends Controller $this->view->setVar('categories', $categories); } + /** + * @Get("/{id:[0-9]+}/edit", name="admin.article.edit") + */ + public function editAction($id) + { + $articleService = new ArticleService(); + + $publishTypes = $articleService->getPublishTypes(); + $sourceTypes = $articleService->getSourceTypes(); + $categories = $articleService->getCategories(); + $article = $articleService->getArticle($id); + $xmTags = $articleService->getXmTags($id); + + $this->view->setVar('publish_types', $publishTypes); + $this->view->setVar('source_types', $sourceTypes); + $this->view->setVar('categories', $categories); + $this->view->setVar('article', $article); + $this->view->setVar('xm_tags', $xmTags); + } + + /** + * @Get("/{id:[0-9]+}/show", name="admin.article.show") + */ + public function showAction($id) + { + $articleService = new ArticleService(); + + $rejectOptions = $articleService->getRejectOptions(); + $article = $articleService->getArticle($id); + + $this->view->setVar('reject_options', $rejectOptions); + $this->view->setVar('article', $article); + } + /** * @Post("/create", name="admin.article.create") */ @@ -86,24 +135,6 @@ class ArticleController extends Controller return $this->jsonSuccess($content); } - /** - * @Get("/{id:[0-9]+}/edit", name="admin.article.edit") - */ - public function editAction($id) - { - $articleService = new ArticleService(); - - $sourceTypes = $articleService->getSourceTypes(); - $categories = $articleService->getCategories(); - $article = $articleService->getArticle($id); - $xmTags = $articleService->getXmTags($id); - - $this->view->setVar('source_types', $sourceTypes); - $this->view->setVar('categories', $categories); - $this->view->setVar('article', $article); - $this->view->setVar('xm_tags', $xmTags); - } - /** * @Post("/{id:[0-9]+}/update", name="admin.article.update") */ @@ -152,4 +183,23 @@ class ArticleController extends Controller return $this->jsonSuccess($content); } + /** + * @Post("/{id:[0-9]+}/review", name="admin.article.review") + */ + public function reviewAction($id) + { + $articleService = new ArticleService(); + + $articleService->reviewArticle($id); + + $location = $this->url->get(['for' => 'admin.article.pending_list']); + + $content = [ + 'location' => $location, + 'msg' => '审核文章成功', + ]; + + return $this->jsonSuccess($content); + } + } diff --git a/app/Http/Admin/Controllers/IndexController.php b/app/Http/Admin/Controllers/IndexController.php index c5ff04b4..72e5071b 100644 --- a/app/Http/Admin/Controllers/IndexController.php +++ b/app/Http/Admin/Controllers/IndexController.php @@ -37,11 +37,13 @@ class IndexController extends Controller $globalStat = $indexService->getGlobalStat(); $todayStat = $indexService->getTodayStat(); + $modStat = $indexService->getModerationStat(); $appInfo = $indexService->getAppInfo(); $serverInfo = $indexService->getServerInfo(); $this->view->setVar('global_stat', $globalStat); $this->view->setVar('today_stat', $todayStat); + $this->view->setVar('mod_stat', $modStat); $this->view->setVar('app_info', $appInfo); $this->view->setVar('server_info', $serverInfo); } diff --git a/app/Http/Admin/Controllers/ModerationController.php b/app/Http/Admin/Controllers/ModerationController.php new file mode 100644 index 00000000..75c81808 --- /dev/null +++ b/app/Http/Admin/Controllers/ModerationController.php @@ -0,0 +1,25 @@ +getArticles(); + + $this->view->setVar('pager', $pager); + } + +} diff --git a/app/Http/Admin/Services/Article.php b/app/Http/Admin/Services/Article.php index 8c4dabdd..98674a74 100644 --- a/app/Http/Admin/Services/Article.php +++ b/app/Http/Admin/Services/Article.php @@ -9,16 +9,31 @@ use App\Library\Utils\Word as WordUtil; use App\Models\Article as ArticleModel; use App\Models\ArticleTag as ArticleTagModel; use App\Models\Category as CategoryModel; +use App\Models\Reason as ReasonModel; +use App\Models\User as UserModel; use App\Repos\Article as ArticleRepo; use App\Repos\ArticleTag as ArticleTagRepo; use App\Repos\Category as CategoryRepo; use App\Repos\Tag as TagRepo; +use App\Repos\User as UserRepo; +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; use App\Services\Sync\ArticleIndex as ArticleIndexSync; use App\Validators\Article as ArticleValidator; class Article extends Service { + public function getArticleModel() + { + $article = new ArticleModel(); + + $article->afterFetch(); + + return $article; + } + public function getXmTags($id) { $tagRepo = new TagRepo(); @@ -61,11 +76,21 @@ class Article extends Service ]); } + public function getPublishTypes() + { + return ArticleModel::publishTypes(); + } + public function getSourceTypes() { return ArticleModel::sourceTypes(); } + public function getRejectOptions() + { + return ReasonModel::articleRejectOptions(); + } + public function getArticles() { $pagerQuery = new PagerQuery(); @@ -98,7 +123,7 @@ class Article extends Service { $post = $this->request->getPost(); - $loginUser = $this->getLoginUser(); + $user = $this->getLoginUser(); $validator = new ArticleValidator(); @@ -107,12 +132,16 @@ class Article extends Service $article = new ArticleModel(); - $article->owner_id = $loginUser->id; + $article->owner_id = $user->id; $article->category_id = $category->id; $article->title = $title; $article->create(); + $this->incrUserArticleCount($user); + + $this->eventsManager->fire('Article:afterCreate', $this, $article); + return $article; } @@ -159,6 +188,10 @@ class Article extends Service $data['allow_comment'] = $post['allow_comment']; } + if (isset($post['private'])) { + $data['private'] = $validator->checkPrivateStatus($post['private']); + } + if (isset($post['featured'])) { $data['featured'] = $validator->checkFeatureStatus($post['featured']); } @@ -173,24 +206,99 @@ class Article extends Service $article->update($data); + $this->rebuildArticleIndex($article); + + $this->eventsManager->fire('Article:afterUpdate', $this, $article); + return $article; } public function deleteArticle($id) { $article = $this->findOrFail($id); + $article->deleted = 1; + $article->update(); + $userRepo = new UserRepo(); + + $owner = $userRepo->findById($article->owner_id); + + $this->decrUserArticleCount($owner); + + $this->rebuildArticleIndex($article); + + $this->eventsManager->fire('Article:afterDelete', $this, $article); + return $article; } public function restoreArticle($id) { $article = $this->findOrFail($id); + $article->deleted = 0; + $article->update(); + $userRepo = new UserRepo(); + + $owner = $userRepo->findById($article->owner_id); + + $this->incrUserArticleCount($owner); + + $this->rebuildArticleIndex($article); + + $this->eventsManager->fire('Article:afterRestore', $this, $article); + + return $article; + } + + public function reviewArticle($id) + { + $type = $this->request->getPost('type', ['trim', 'string']); + $reason = $this->request->getPost('reason', ['trim', 'string']); + + $article = $this->findOrFail($id); + + if ($type == 'approve') { + $article->published = ArticleModel::PUBLISH_APPROVED; + } elseif ($type == 'reject') { + $article->published = ArticleModel::PUBLISH_REJECTED; + } + + $article->update(); + + $sender = $this->getLoginUser(); + + if ($type == 'approve') { + + $this->rebuildArticleIndex($article); + + $this->handlePostPoint($article); + + $notice = new ArticleApprovedNotice(); + + $notice->handle($article, $sender); + + $this->eventsManager->fire('Article:afterApprove', $this, $article); + + } elseif ($type == 'reject') { + + $options = ReasonModel::articleRejectOptions(); + + if (array_key_exists($reason, $options)) { + $reason = $options[$reason]; + } + + $notice = new ArticleRejectedNotice(); + + $notice->handle($article, $sender, $reason); + + $this->eventsManager->fire('Article:afterReject', $this, $article); + } + return $article; } @@ -201,24 +309,15 @@ class Article extends Service return $validator->checkArticle($id); } - protected function rebuildArticleCache(ArticleModel $article) - { - $cache = new ArticleCache(); - - $cache->rebuild($article->id); - } - - protected function rebuildArticleIndex(ArticleModel $article) - { - $sync = new ArticleIndexSync(); - - $sync->addItem($article->id); - } - protected function saveTags(ArticleModel $article, $tagIds) { $originTagIds = []; + /** + * 修改数据后,afterFetch设置的属性会失效,重新执行 + */ + $article->afterFetch(); + if ($article->tags) { $originTagIds = kg_array_column($article->tags, 'id'); } @@ -284,4 +383,42 @@ class Article extends Service return $pager; } + protected function incrUserArticleCount(UserModel $user) + { + $user->article_count += 1; + + $user->update(); + } + + protected function decrUserArticleCount(UserModel $user) + { + if ($user->article_count > 0) { + $user->article_count -= 1; + $user->update(); + } + } + + protected function rebuildArticleCache(ArticleModel $article) + { + $cache = new ArticleCache(); + + $cache->rebuild($article->id); + } + + protected function rebuildArticleIndex(ArticleModel $article) + { + $sync = new ArticleIndexSync(); + + $sync->addItem($article->id); + } + + protected function handlePostPoint(ArticleModel $article) + { + if ($article->published != ArticleModel::PUBLISH_APPROVED) return; + + $service = new ArticlePostPointHistory(); + + $service->handle($article); + } + } diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 4e260dc9..1c7871e3 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -140,36 +140,36 @@ class AuthNode extends Service ], [ 'id' => '1-4', - 'title' => '话题管理', + 'title' => '专题管理', 'type' => 'menu', 'children' => [ [ 'id' => '1-4-1', - 'title' => '话题列表', + 'title' => '专题列表', 'type' => 'menu', 'route' => 'admin.topic.list', ], [ 'id' => '1-4-5', - 'title' => '搜索话题', + 'title' => '搜索专题', 'type' => 'menu', 'route' => 'admin.topic.search', ], [ 'id' => '1-4-2', - 'title' => '添加话题', + 'title' => '添加专题', 'type' => 'menu', 'route' => 'admin.topic.add', ], [ 'id' => '1-4-3', - 'title' => '编辑话题', + 'title' => '编辑专题', 'type' => 'button', 'route' => 'admin.topic.edit', ], [ 'id' => '1-4-4', - 'title' => '删除话题', + 'title' => '删除专题', 'type' => 'button', 'route' => 'admin.topic.delete', ], @@ -272,6 +272,12 @@ class AuthNode extends Service 'type' => 'button', 'route' => 'admin.article.edit', ], + [ + 'id' => '1-7-6', + 'title' => '文章分类', + 'type' => 'menu', + 'route' => 'admin.article.category', + ], [ 'id' => '1-7-5', 'title' => '删除文章', @@ -279,10 +285,16 @@ class AuthNode extends Service 'route' => 'admin.article.delete', ], [ - 'id' => '1-7-6', - 'title' => '文章分类', - 'type' => 'menu', - 'route' => 'admin.article.category', + 'id' => '1-7-9', + 'title' => '文章详情', + 'type' => 'button', + 'route' => 'admin.article.review', + ], + [ + 'id' => '1-7-10', + 'title' => '审核文章', + 'type' => 'button', + 'route' => 'admin.article.review', ], ], ], @@ -364,6 +376,19 @@ class AuthNode extends Service 'id' => '2', 'title' => '运营管理', 'children' => [ + [ + 'id' => '2-10', + 'title' => '审核队列', + 'type' => 'menu', + 'children' => [ + [ + 'id' => '2-10-1', + 'title' => '文章列表', + 'type' => 'menu', + 'route' => 'admin.mod.articles', + ], + ], + ], [ 'id' => '2-1', 'title' => '学员管理', diff --git a/app/Http/Admin/Services/Index.php b/app/Http/Admin/Services/Index.php index b67928c3..bb529fb8 100644 --- a/app/Http/Admin/Services/Index.php +++ b/app/Http/Admin/Services/Index.php @@ -2,6 +2,7 @@ namespace App\Http\Admin\Services; +use App\Caches\ModerationStat; use App\Caches\SiteGlobalStat; use App\Caches\SiteTodayStat; use App\Library\AppInfo; @@ -53,6 +54,13 @@ class Index extends Service return $cache->get(); } + public function getModerationStat() + { + $cache = new ModerationStat(); + + return $cache->get(); + } + public function getReleases() { $url = 'https://koogua.com/api-releases.json'; diff --git a/app/Http/Admin/Services/Moderation.php b/app/Http/Admin/Services/Moderation.php new file mode 100644 index 00000000..68b69cc0 --- /dev/null +++ b/app/Http/Admin/Services/Moderation.php @@ -0,0 +1,52 @@ +getParams(); + + $params['published'] = ArticleModel::PUBLISH_PENDING; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $articleRepo = new ArticleRepo(); + + $pager = $articleRepo->paginate($params, $sort, $page, $limit); + + return $this->handleArticles($pager); + } + + protected function handleArticles($pager) + { + if ($pager->total_items > 0) { + + $builder = new ArticleListBuilder(); + + $items = $pager->items->toArray(); + + $pipeA = $builder->handleArticles($items); + $pipeB = $builder->handleCategories($pipeA); + $pipeC = $builder->handleUsers($pipeB); + $pipeD = $builder->objects($pipeC); + + $pager->items = $pipeD; + } + + return $pager; + } + +} diff --git a/app/Http/Admin/Views/article/edit_basic.volt b/app/Http/Admin/Views/article/edit_basic.volt index 70d75e4a..8857b583 100644 --- a/app/Http/Admin/Views/article/edit_basic.volt +++ b/app/Http/Admin/Views/article/edit_basic.volt @@ -50,6 +50,14 @@ +
+ +
+ {% for value,title in publish_types %} + + {% endfor %} +
+
@@ -57,6 +65,13 @@
+
+ +
+ + +
+
diff --git a/app/Http/Admin/Views/article/list.volt b/app/Http/Admin/Views/article/list.volt index 3e7f8cfd..95eab222 100644 --- a/app/Http/Admin/Views/article/list.volt +++ b/app/Http/Admin/Views/article/list.volt @@ -38,13 +38,13 @@ 文章 + 状态 浏览 评论 点赞 收藏 推荐 评论 - 发布 操作 @@ -74,13 +74,13 @@ 创建:{{ date('Y-m-d',item.create_time) }}

+ {{ publish_status(item.published) }} {{ item.view_count }} {{ item.comment_count }} {{ item.like_count }} {{ item.favorite_count }} -
diff --git a/app/Http/Admin/Views/article/search.volt b/app/Http/Admin/Views/article/search.volt index b9a3a17c..69b89d9f 100644 --- a/app/Http/Admin/Views/article/search.volt +++ b/app/Http/Admin/Views/article/search.volt @@ -41,6 +41,14 @@
+
+ +
+ {% for value,title in publish_types %} + + {% endfor %} +
+
@@ -56,13 +64,6 @@
-
- -
- - -
-
diff --git a/app/Http/Admin/Views/article/show.volt b/app/Http/Admin/Views/article/show.volt new file mode 100644 index 00000000..106706e9 --- /dev/null +++ b/app/Http/Admin/Views/article/show.volt @@ -0,0 +1,82 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {% set list_url = url({'for':'admin.article.pending_list'}) %} + + + +
+
+ +
+
{{ article.content|parse_markdown }}
+
+
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+
+ +{% endblock %} + +{% block link_css %} + + {{ css_link('home/css/markdown.css') }} + +{% endblock %} + +{% block inline_js %} + + + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/index/main.volt b/app/Http/Admin/Views/index/main.volt index 147865b3..1d45d94f 100644 --- a/app/Http/Admin/Views/index/main.volt +++ b/app/Http/Admin/Views/index/main.volt @@ -7,6 +7,7 @@
{{ partial('index/main_global_stat') }} {{ partial('index/main_today_stat') }} + {{ partial('index/main_mod_stat') }} {{ partial('index/main_app_trend') }}
diff --git a/app/Http/Admin/Views/index/main_global_stat.volt b/app/Http/Admin/Views/index/main_global_stat.volt index a3864c8f..f4198c5a 100644 --- a/app/Http/Admin/Views/index/main_global_stat.volt +++ b/app/Http/Admin/Views/index/main_global_stat.volt @@ -2,54 +2,78 @@
全局统计
-
-
-
课程数
-
{{ global_stat.course_count }}
-
-
-
+
用户数
{{ global_stat.user_count }}
-
+
-
群组数
-
{{ global_stat.group_count }}
+
会员数
+
{{ global_stat.vip_count }}
-
+
-
订单数
-
{{ global_stat.order_count }}
+
课程数
+
{{ global_stat.course_count }}
-
-
-
评价数
-
{{ global_stat.review_count }}
-
-
-
-
-
咨询数
-
{{ global_stat.consult_count }}
-
-
-
+
套餐数
{{ global_stat.package_count }}
-
+
专题数
{{ global_stat.topic_count }}
+
+
+
群组数
+
{{ global_stat.group_count }}
+
+
+
+
+
评价数
+
{{ global_stat.review_count }}
+
+
+
+
+
咨询数
+
{{ global_stat.consult_count }}
+
+
+
+
+
文章数
+
{{ global_stat.article_count }}
+
+
+
+
+
提问数
+
0
+
+
+
+
+
回答数
+
0
+
+
+
+
+
评论数
+
{{ global_stat.comment_count }}
+
+
\ No newline at end of file diff --git a/app/Http/Admin/Views/index/main_mod_stat.volt b/app/Http/Admin/Views/index/main_mod_stat.volt new file mode 100644 index 00000000..60c96ce2 --- /dev/null +++ b/app/Http/Admin/Views/index/main_mod_stat.volt @@ -0,0 +1,39 @@ +
+
审核队列
+
+
+
+
+
文章
+ +
+
+
+
+
提问
+
+ 0 +
+
+
+
+
+
回答
+
+ 0 +
+
+
+
+
+
评论
+
+ 0 +
+
+
+
+
+
\ No newline at end of file diff --git a/app/Http/Admin/Views/index/main_today_stat.volt b/app/Http/Admin/Views/index/main_today_stat.volt index 3f13f9eb..0adedf1f 100644 --- a/app/Http/Admin/Views/index/main_today_stat.volt +++ b/app/Http/Admin/Views/index/main_today_stat.volt @@ -2,25 +2,37 @@
今日统计
-
+
用户注册
{{ today_stat.register_count }}
-
+
+
+
积分兑换
+
{{ today_stat.point_redeem_count }}
+
+
+
成交订单
{{ today_stat.sale_count }}
-
+
+
+
成功退款
+
{{ today_stat.refund_count }}
+
+
+
销售金额
{{ '¥%0.2f'|format(today_stat.sale_amount) }}
-
+
退款金额
{{ '¥%0.2f'|format(today_stat.refund_amount) }}
diff --git a/app/Http/Admin/Views/macros/article.volt b/app/Http/Admin/Views/macros/article.volt index fad0df84..9d31a831 100644 --- a/app/Http/Admin/Views/macros/article.volt +++ b/app/Http/Admin/Views/macros/article.volt @@ -1,3 +1,15 @@ +{%- macro publish_status(type) %} + {% if type == 1 %} + 审核中 + {% elseif type == 2 %} + 已发布 + {% elseif type == 3 %} + 未通过 + {% else %} + 未知 + {% endif %} +{%- endmacro %} + {%- macro source_info(type,url) %} {% if type == 1 %} 原创 diff --git a/app/Http/Admin/Views/moderation/articles.volt b/app/Http/Admin/Views/moderation/articles.volt new file mode 100644 index 00000000..87a3229b --- /dev/null +++ b/app/Http/Admin/Views/moderation/articles.volt @@ -0,0 +1,79 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {{ partial('macros/article') }} + +
+
+ + 文章审核 + +
+
+ + + + + + + + + + + + + + + + + + + + + + {% for item in pager.items %} + {% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %} + {% set show_url = url({'for':'admin.article.show','id':item.id}) %} + + + + + + + + + {% endfor %} + +
文章作者来源评论时间操作
+

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

+

+ {% if item.category.id is defined %} + 分类:{{ item.category.name }} + {% endif %} + {% if item.tags %} + 标签:{{ tags_info(item.tags) }} + {% endif %} +

+
+

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

+

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

+
{{ source_info(item.source_type,item.source_url) }} + {% if item.allow_comment == 1 %} + 开启 + {% else %} + 关闭 + {% endif %} + + {% if item.update_time > 0 %} + {{ date('Y-m-d H:i:s',item.update_time) }} + {% else %} + {{ date('Y-m-d H:i:s',item.create_time) }} + {% endif %} + + 详情 +
+ + {{ partial('partials/pager') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/public/forbidden.volt b/app/Http/Admin/Views/public/forbidden.volt index 28975e59..975b2413 100644 --- a/app/Http/Admin/Views/public/forbidden.volt +++ b/app/Http/Admin/Views/public/forbidden.volt @@ -8,7 +8,7 @@

4 - 0 + 0 3

diff --git a/app/Http/Admin/Views/setting/point.volt b/app/Http/Admin/Views/setting/point.volt index ff566cb7..7dafc75f 100644 --- a/app/Http/Admin/Views/setting/point.volt +++ b/app/Http/Admin/Views/setting/point.volt @@ -97,6 +97,24 @@ N/A + + 发表评论 + + + + + + + + + 发表文章 + + + + + + +
diff --git a/app/Http/Admin/Views/topic/add.volt b/app/Http/Admin/Views/topic/add.volt index ccda82ce..e470d1cb 100644 --- a/app/Http/Admin/Views/topic/add.volt +++ b/app/Http/Admin/Views/topic/add.volt @@ -4,7 +4,7 @@
- 添加话题 + 添加专题
diff --git a/app/Http/Admin/Views/topic/edit.volt b/app/Http/Admin/Views/topic/edit.volt index 5b09f72f..681d128f 100644 --- a/app/Http/Admin/Views/topic/edit.volt +++ b/app/Http/Admin/Views/topic/edit.volt @@ -4,7 +4,7 @@
- 编辑话题 + 编辑专题
diff --git a/app/Http/Admin/Views/topic/list.volt b/app/Http/Admin/Views/topic/list.volt index fb7f0bc1..522e6975 100644 --- a/app/Http/Admin/Views/topic/list.volt +++ b/app/Http/Admin/Views/topic/list.volt @@ -8,15 +8,15 @@ diff --git a/app/Http/Admin/Views/topic/search.volt b/app/Http/Admin/Views/topic/search.volt index 7a4c97c5..96577795 100644 --- a/app/Http/Admin/Views/topic/search.volt +++ b/app/Http/Admin/Views/topic/search.volt @@ -4,7 +4,7 @@
- 搜索话题 + 搜索专题
diff --git a/app/Http/Api/Controllers/UserConsoleController.php b/app/Http/Api/Controllers/UserConsoleController.php index 671ff955..2d921bc7 100644 --- a/app/Http/Api/Controllers/UserConsoleController.php +++ b/app/Http/Api/Controllers/UserConsoleController.php @@ -8,6 +8,10 @@ use App\Services\Logic\User\Console\CourseList as CourseListService; use App\Services\Logic\User\Console\FavoriteList as FavoriteListService; use App\Services\Logic\User\Console\FriendList as FriendListService; use App\Services\Logic\User\Console\GroupList as GroupListService; +use App\Services\Logic\User\Console\NotificationList as NotificationListService; +use App\Services\Logic\User\Console\NotificationRead as NotificationReadService; +use App\Services\Logic\User\Console\NotifyStats as NotifyStatsService; +use App\Services\Logic\User\Console\Online as OnlineService; use App\Services\Logic\User\Console\OrderList as OrderListService; use App\Services\Logic\User\Console\ProfileInfo as ProfileInfoService; use App\Services\Logic\User\Console\ProfileUpdate as ProfileUpdateService; @@ -140,6 +144,34 @@ class UserConsoleController extends Controller return $this->jsonSuccess(['pager' => $pager]); } + /** + * @Get("/notifications", name="api.uc.notifications") + */ + public function notificationsAction() + { + $service = new NotificationListService(); + + $pager = $service->handle(); + + $service = new NotificationReadService(); + + $service->handle(); + + return $this->jsonSuccess(['pager' => $pager]); + } + + /** + * @Get("/notify/stats", name="api.uc.notify_stats") + */ + public function notifyStatsAction() + { + $service = new NotifyStatsService(); + + $stats = $service->handle(); + + return $this->jsonSuccess(['stats' => $stats]); + } + /** * @Post("/profile/update", name="api.uc.update_profile") */ @@ -152,4 +184,16 @@ class UserConsoleController extends Controller return $this->jsonSuccess(); } + /** + * @Post("/online", name="api.uc.online") + */ + public function onlineAction() + { + $service = new OnlineService(); + + $service->handle(); + + return $this->jsonSuccess(); + } + } diff --git a/app/Http/Home/Controllers/ArticleController.php b/app/Http/Home/Controllers/ArticleController.php index cfa16689..ba081e4b 100644 --- a/app/Http/Home/Controllers/ArticleController.php +++ b/app/Http/Home/Controllers/ArticleController.php @@ -2,6 +2,7 @@ namespace App\Http\Home\Controllers; +use App\Http\Home\Services\Article as ArticleService; use App\Http\Home\Services\ArticleQuery as ArticleQueryService; use App\Services\Logic\Article\ArticleFavorite as ArticleFavoriteService; use App\Services\Logic\Article\ArticleInfo as ArticleInfoService; @@ -65,6 +66,47 @@ class ArticleController extends Controller $this->view->setVar('authors', $authors); } + /** + * @Get("/add", name="home.article.add") + */ + public function addAction() + { + $service = new ArticleService(); + + $sourceTypes = $service->getSourceTypes(); + $categories = $service->getCategories(); + $article = $service->getArticleModel(); + $xmTags = $service->getXmTags(0); + + $this->seo->prependTitle('撰写文章'); + + $this->view->pick('article/edit'); + $this->view->setVar('source_types', $sourceTypes); + $this->view->setVar('categories', $categories); + $this->view->setVar('article', $article); + $this->view->setVar('xm_tags', $xmTags); + } + + /** + * @Get("/{id:[0-9]+}/edit", name="home.article.edit") + */ + public function editAction($id) + { + $service = new ArticleService(); + + $sourceTypes = $service->getSourceTypes(); + $categories = $service->getCategories(); + $article = $service->getArticle($id); + $xmTags = $service->getXmTags($id); + + $this->seo->prependTitle('编辑文章'); + + $this->view->setVar('source_types', $sourceTypes); + $this->view->setVar('categories', $categories); + $this->view->setVar('article', $article); + $this->view->setVar('xm_tags', $xmTags); + } + /** * @Get("/{id:[0-9]+}", name="home.article.show") */ @@ -74,6 +116,12 @@ class ArticleController extends Controller $article = $service->handle($id); + $owned = $this->authUser->id == $article['owner']['id']; + + if ($article['private'] == 1 && !$owned) { + $this->response->redirect(['for' => 'home.error.403']); + } + $this->seo->prependTitle($article['title']); $this->view->setVar('article', $article); @@ -105,6 +153,61 @@ class ArticleController extends Controller $this->view->setVar('comments', $comments); } + /** + * @Post("/create", name="home.article.create") + */ + public function createAction() + { + $service = new ArticleService(); + + $service->createArticle(); + + $location = $this->url->get(['for' => 'home.uc.articles']); + + $content = [ + 'location' => $location, + 'msg' => '创建文章成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/update", name="home.article.update") + */ + public function updateAction($id) + { + $service = new ArticleService(); + + $service->updateArticle($id); + + $location = $this->url->get(['for' => 'home.uc.articles']); + + $content = [ + 'location' => $location, + 'msg' => '更新文章成功', + ]; + + return $this->jsonSuccess($content); + } + + /** + * @Post("/{id:[0-9]+}/delete", name="home.article.delete") + */ + public function deleteAction($id) + { + $service = new ArticleService(); + + $service->deleteArticle($id); + + $content = [ + 'location' => $this->request->getHTTPReferer(), + 'msg' => '删除文章成功', + ]; + + return $this->jsonSuccess($content); + } + /** * @Post("/{id:[0-9]+}/favorite", name="home.article.favorite") */ @@ -133,4 +236,5 @@ class ArticleController extends Controller return $this->jsonSuccess(['data' => $data, 'msg' => $msg]); } + } diff --git a/app/Http/Home/Controllers/TeacherConsoleController.php b/app/Http/Home/Controllers/TeacherConsoleController.php index 53164fe3..1fec5622 100644 --- a/app/Http/Home/Controllers/TeacherConsoleController.php +++ b/app/Http/Home/Controllers/TeacherConsoleController.php @@ -26,6 +26,17 @@ class TeacherConsoleController extends Controller return true; } + public function initialize() + { + parent::initialize(); + + $authUser = $this->getAuthUser(false); + + $this->seo->prependTitle('教学中心'); + + $this->view->setVar('auth_user', $authUser); + } + /** * @Get("/", name="home.tc.index") */ diff --git a/app/Http/Home/Controllers/UploadController.php b/app/Http/Home/Controllers/UploadController.php index 351f25cb..929be935 100644 --- a/app/Http/Home/Controllers/UploadController.php +++ b/app/Http/Home/Controllers/UploadController.php @@ -41,11 +41,54 @@ class UploadController extends Controller return $this->jsonSuccess(['data' => $data]); } + /** + * @Post("/cover/img", name="home.upload.cover_img") + */ + public function uploadCoverImageAction() + { + $service = new StorageService(); + + $file = $service->uploadCoverImage(); + + if (!$file) { + return $this->jsonError(['msg' => '上传文件失败']); + } + + $data = [ + 'src' => $service->getImageUrl($file->path), + 'title' => $file->name, + ]; + + return $this->jsonSuccess(['data' => $data]); + } + + /** + * @Post("/content/img", name="home.upload.content_img") + */ + public function uploadContentImageAction() + { + $service = new StorageService(); + + $file = $service->uploadContentImage(); + + if (!$file) { + return $this->jsonError(['msg' => '上传文件失败']); + } + + $data = [ + 'src' => $service->getImageUrl($file->path), + 'title' => $file->name, + ]; + + return $this->jsonSuccess(['data' => $data]); + } + /** * @Post("/im/img", name="home.upload.im_img") */ public function uploadImImageAction() { + } /** diff --git a/app/Http/Home/Controllers/UserConsoleController.php b/app/Http/Home/Controllers/UserConsoleController.php index b5cad788..c38d6209 100644 --- a/app/Http/Home/Controllers/UserConsoleController.php +++ b/app/Http/Home/Controllers/UserConsoleController.php @@ -15,6 +15,10 @@ use App\Services\Logic\User\Console\CourseList as CourseListService; use App\Services\Logic\User\Console\FavoriteList as FavoriteListService; use App\Services\Logic\User\Console\FriendList as FriendListService; use App\Services\Logic\User\Console\GroupList as GroupListService; +use App\Services\Logic\User\Console\NotificationList as NotificationListService; +use App\Services\Logic\User\Console\NotificationRead as NotificationReadService; +use App\Services\Logic\User\Console\NotifyStats as NotifyStatsService; +use App\Services\Logic\User\Console\Online as OnlineService; use App\Services\Logic\User\Console\OrderList as OrderListService; use App\Services\Logic\User\Console\PointHistory as PointHistoryService; use App\Services\Logic\User\Console\PointRedeemList as PointRedeemListService; @@ -48,6 +52,8 @@ class UserConsoleController extends Controller $authUser = $this->getAuthUser(false); + $this->seo->prependTitle('用户中心'); + $this->view->setVar('auth_user', $authUser); } @@ -268,6 +274,23 @@ class UserConsoleController extends Controller $this->view->setVar('pager', $pager); } + /** + * @Get("/notifications", name="home.uc.notifications") + */ + public function notificationsAction() + { + $service = new NotificationListService(); + + $pager = $service->handle(); + + $service = new NotificationReadService(); + + $service->handle(); + + $this->view->pick('user/console/notifications'); + $this->view->setVar('pager', $pager); + } + /** * @Get("/subscribe", name="home.uc.subscribe") */ @@ -283,6 +306,18 @@ class UserConsoleController extends Controller $this->view->setVar('subscribed', $subscribed); } + /** + * @Get("/notify/stats", name="home.uc.notify_stats") + */ + public function notifyStatsAction() + { + $service = new NotifyStatsService(); + + $stats = $service->handle(); + + return $this->jsonSuccess(['stats' => $stats]); + } + /** * @Post("/profile/update", name="home.uc.update_profile") */ @@ -335,4 +370,16 @@ class UserConsoleController extends Controller return $this->jsonSuccess($content); } + /** + * @Post("/online", name="home.uc.online") + */ + public function onlineAction() + { + $service = new OnlineService(); + + $service->handle(); + + return $this->jsonSuccess(); + } + } diff --git a/app/Http/Home/Services/Article.php b/app/Http/Home/Services/Article.php new file mode 100644 index 00000000..ac172aa2 --- /dev/null +++ b/app/Http/Home/Services/Article.php @@ -0,0 +1,144 @@ +request->getPost(); + + $user = $this->getLoginUser(); + + $article = new ArticleModel(); + + $data = $this->handlePostData($post); + + $data['client_type'] = $this->getClientType(); + $data['client_ip'] = $this->getClientIp(); + $data['owner_id'] = $user->id; + + $article->create($data); + + if (isset($post['xm_tag_ids'])) { + $this->saveTags($article, $post['xm_tag_ids']); + } + + $this->incrUserArticleCount($user); + + $this->eventsManager->fire('Article:afterCreate', $this, $article); + + return $article; + } + + public function updateArticle($id) + { + $post = $this->request->getPost(); + + $article = $this->findOrFail($id); + + $data = $this->handlePostData($post); + + $data['client_type'] = $this->getClientType(); + $data['client_ip'] = $this->getClientIp(); + + if ($article->published == ArticleModel::PUBLISH_REJECTED) { + $data['published'] = ArticleModel::PUBLISH_PENDING; + } + + /** + * 当通过审核后,禁止修改部分文章属性 + */ + if ($article->published == ArticleModel::PUBLISH_APPROVED) { + unset( + $data['title'], + $data['content'], + $data['cover'], + $data['source_type'], + $data['source_url'], + $data['category_id'], + $post['xm_tag_ids'], + ); + } + + $article->update($data); + + if (isset($post['xm_tag_ids'])) { + $this->saveTags($article, $post['xm_tag_ids']); + } + + $this->eventsManager->fire('Article:afterUpdate', $this, $article); + + return $article; + } + + public function deleteArticle($id) + { + $article = $this->findOrFail($id); + + $user = $this->getLoginUser(); + + $validator = new ArticleValidator(); + + $validator->checkOwner($user->id, $article->owner_id); + + $article->deleted = 1; + + $article->update(); + + $this->decrUserArticleCount($user); + + $this->rebuildArticleIndex($article); + + $this->eventsManager->fire('Article:afterDelete', $this, $article); + + return $article; + } + + protected function handlePostData($post) + { + $data = []; + + $validator = new ArticleValidator(); + + $data['title'] = $validator->checkTitle($post['title']); + $data['content'] = $validator->checkContent($post['content']); + $data['word_count'] = WordUtil::getWordCount($data['content']); + + if (isset($post['category_id'])) { + $category = $validator->checkCategory($post['category_id']); + $data['category_id'] = $category->id; + } + + if (isset($post['cover'])) { + $data['cover'] = $validator->checkCover($post['cover']); + } + + if (isset($post['source_type'])) { + $data['source_type'] = $validator->checkSourceType($post['source_type']); + if ($post['source_type'] != ArticleModel::SOURCE_ORIGIN) { + $data['source_url'] = $validator->checkSourceUrl($post['source_url']); + } + } + + if (isset($post['allow_comment'])) { + $data['allow_comment'] = $validator->checkAllowCommentStatus($post['allow_comment']); + } + + if (isset($post['private'])) { + $data['private'] = $validator->checkPrivateStatus($post['private']); + } + + return $data; + } + +} diff --git a/app/Http/Home/Views/article/edit.volt b/app/Http/Home/Views/article/edit.volt new file mode 100644 index 00000000..5cfe7fcd --- /dev/null +++ b/app/Http/Home/Views/article/edit.volt @@ -0,0 +1,125 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {% set title = article.id > 0 ? '编辑文章' : '撰写文章' %} + {% set action_url = article.id > 0 ? url({'for':'home.article.update','id':article.id}) : url({'for':'home.article.create'}) %} + {% set source_url_display = article.source_type == 1 ? 'display:none' : 'display:block' %} + + + + +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+
基本信息
+
+
+
+ +
+ + +
+ +
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +{% endblock %} + +{% block link_css %} + + {{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }} + +{% endblock %} + +{% block include_js %} + + {{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }} + {{ js_include('lib/xm-select.js') }} + {{ js_include('home/js/article.edit.js') }} + {{ js_include('home/js/vditor.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/comment/info.volt b/app/Http/Home/Views/comment/info.volt index f64a65fd..3e8884b4 100644 --- a/app/Http/Home/Views/comment/info.volt +++ b/app/Http/Home/Views/comment/info.volt @@ -54,7 +54,7 @@
- 删除 + 删除
diff --git a/app/Http/Home/Views/error/maintain.volt b/app/Http/Home/Views/error/maintain.volt index 91c1e5f2..7baf340f 100644 --- a/app/Http/Home/Views/error/maintain.volt +++ b/app/Http/Home/Views/error/maintain.volt @@ -9,7 +9,7 @@

5 - 0 + 0 3

diff --git a/app/Http/Home/Views/error/show400.volt b/app/Http/Home/Views/error/show400.volt index ddde2a54..8ab88475 100644 --- a/app/Http/Home/Views/error/show400.volt +++ b/app/Http/Home/Views/error/show400.volt @@ -9,7 +9,7 @@

4 - 0 + 0 0

diff --git a/app/Http/Home/Views/error/show401.volt b/app/Http/Home/Views/error/show401.volt index cd322fbf..c483a510 100644 --- a/app/Http/Home/Views/error/show401.volt +++ b/app/Http/Home/Views/error/show401.volt @@ -8,7 +8,7 @@

4 - 0 + 0 1

diff --git a/app/Http/Home/Views/error/show403.volt b/app/Http/Home/Views/error/show403.volt index 6a1dbbd3..c4ef6f50 100644 --- a/app/Http/Home/Views/error/show403.volt +++ b/app/Http/Home/Views/error/show403.volt @@ -8,7 +8,7 @@

4 - 0 + 0 3

diff --git a/app/Http/Home/Views/error/show404.volt b/app/Http/Home/Views/error/show404.volt index b815f72a..c20a6f27 100644 --- a/app/Http/Home/Views/error/show404.volt +++ b/app/Http/Home/Views/error/show404.volt @@ -8,7 +8,7 @@

4 - 0 + 0 4

diff --git a/app/Http/Home/Views/error/show500.volt b/app/Http/Home/Views/error/show500.volt index 68580147..5a8cff68 100644 --- a/app/Http/Home/Views/error/show500.volt +++ b/app/Http/Home/Views/error/show500.volt @@ -8,7 +8,7 @@

5 - 0 + 0 0

diff --git a/app/Http/Home/Views/error/show503.volt b/app/Http/Home/Views/error/show503.volt index b3452e80..4cf9059a 100644 --- a/app/Http/Home/Views/error/show503.volt +++ b/app/Http/Home/Views/error/show503.volt @@ -8,7 +8,7 @@

5 - 0 + 0 3

diff --git a/app/Http/Home/Views/macros/article.volt b/app/Http/Home/Views/macros/article.volt index 7171e5a4..4c87a2b5 100644 --- a/app/Http/Home/Views/macros/article.volt +++ b/app/Http/Home/Views/macros/article.volt @@ -8,4 +8,16 @@ {% else %} 未知 {% endif %} +{%- endmacro %} + +{%- macro publish_status(type) %} + {% if type == 1 %} + 审核中 + {% elseif type == 2 %} + 已发布 + {% elseif type == 3 %} + 未通过 + {% else %} + 未知 + {% endif %} {%- endmacro %} \ No newline at end of file diff --git a/app/Http/Home/Views/macros/notification.volt b/app/Http/Home/Views/macros/notification.volt new file mode 100644 index 00000000..8b50b462 --- /dev/null +++ b/app/Http/Home/Views/macros/notification.volt @@ -0,0 +1,41 @@ +{%- macro event_info(notify) %} + + {% set sender = notify.sender %} + {% set type = notify.event_type %} + {% set info = notify.event_info %} + + {% if type == 0 %} +

未知类型

+ {% elseif type == 147 %} + {% set course_url = url({'for':'home.course.show','id':info.course.id}) %} +

{{ sender.name }} 喜欢了你在课程 {{ info.course.title }} 中的咨询

+

咨询内容:{{ info.consult.question }}

+ {% elseif type == 167 %} + {% set course_url = url({'for':'home.course.show','id':info.course.id}) %} +

{{ sender.name }} 喜欢了你在课程 {{ info.course.title }} 中的评价

+

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

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

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

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

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

+

拒绝原因:{{ info.reason }}

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

{{ sender.name }} 评论了你的文章 {{ info.article.title }}

+

评论内容:{{ info.comment.content }}

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

{{ sender.name }} 收藏了你的文章 {{ info.article.title }}

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

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

+ {% elseif type == 506 %} +

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

+

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

+ {% elseif type == 507 %} +

{{ sender.name }} 喜欢了你的评论:{{ info.comment.content }}

+ {% endif %} + +{%- endmacro %} \ No newline at end of file diff --git a/app/Http/Home/Views/macros/point.volt b/app/Http/Home/Views/macros/point.volt index 323c30bc..51918bf5 100644 --- a/app/Http/Home/Views/macros/point.volt +++ b/app/Http/Home/Views/macros/point.volt @@ -43,32 +43,40 @@ 课程评价 {% elseif value == 8 %} 微聊讨论 + {% elseif value == 9 %} + 发布评论 + {% elseif value == 10 %} + 发布文章 {% endif %} {%- endmacro %} -{%- macro event_detail_info(history) %} - {% set event_info = history.event_info %} - {% if history.event_type == 1 %} -

{{ event_info.order.subject }}

- {% elseif history.event_type == 2 %} - {% set gift_url = url({'for':'home.point_gift.show','id':event_info.point_redeem.gift_id}) %} -

{{ event_info.point_redeem.gift_name }}

- {% elseif history.event_type == 3 %} - {% set gift_url = url({'for':'home.point_gift.show','id':event_info.point_redeem.gift_id}) %} -

{{ event_info.point_redeem.gift_name }}

- {% elseif history.event_type == 4 %} +{%- macro event_detail_info(type,info) %} + {% if type == 1 %} +

{{ info.order.subject }}

+ {% elseif type == 2 %} + {% set gift_url = url({'for':'home.point_gift.show','id':info.point_redeem.gift_id}) %} +

{{ info.point_redeem.gift_name }}

+ {% elseif type == 3 %} + {% set gift_url = url({'for':'home.point_gift.show','id':info.point_redeem.gift_id}) %} +

{{ info.point_redeem.gift_name }}

+ {% elseif type == 4 %} N/A - {% elseif history.event_type == 5 %} + {% elseif type == 5 %} N/A - {% elseif history.event_type == 6 %} - {% set course_url = url({'for':'home.course.show','id':event_info.course.id}) %} - {% set chapter_url = url({'for':'home.chapter.show','id':event_info.chapter.id}) %} -

课程:{{ event_info.course.title }}

-

章节:{{ event_info.chapter.title }}

- {% elseif history.event_type == 7 %} - {% set course_url = url({'for':'home.course.show','id':event_info.course.id}) %} -

{{ event_info.course.title }}

- {% elseif history.event_type == 8 %} + {% elseif type == 6 %} + {% set course_url = url({'for':'home.course.show','id':info.course.id}) %} + {% set chapter_url = url({'for':'home.chapter.show','id':info.chapter.id}) %} +

课程:{{ info.course.title }}

+

章节:{{ info.chapter.title }}

+ {% elseif type == 7 %} + {% set course_url = url({'for':'home.course.show','id':info.course.id}) %} +

{{ info.course.title }}

+ {% elseif type == 8 %} N/A + {% elseif type == 9 %} + N/A + {% elseif type == 10 %} + {% set article_url = url({'for':'home.article.show','id':info.article.id}) %} +

{{ info.article.title }}

{% endif %} {%- endmacro %} \ No newline at end of file diff --git a/app/Http/Home/Views/partials/header.volt b/app/Http/Home/Views/partials/header.volt index 1222e867..3a1ee5a3 100644 --- a/app/Http/Home/Views/partials/header.volt +++ b/app/Http/Home/Views/partials/header.volt @@ -39,6 +39,16 @@ 微聊 {% if auth_user.id > 0 %} +
  • + 创建 +
    +
    提问题
    +
    写文章
    +
    +
  • +
  • + 消息 +
  • {{ auth_user.name }}
    diff --git a/app/Http/Home/Views/user/console/articles.volt b/app/Http/Home/Views/user/console/articles.volt index 38a77673..7c96198e 100644 --- a/app/Http/Home/Views/user/console/articles.volt +++ b/app/Http/Home/Views/user/console/articles.volt @@ -4,12 +4,20 @@ {{ partial('macros/article') }} + {% set published_types = {'0':'全部','1':'审核中','2':'已发布','3':'未通过'} %} + {% set published = request.get('published','trim','0') %} +
    {{ partial('user/console/menu') }}
    我的文章 + {% for key,value in published_types %} + {% set class = (published == key) ? 'layui-btn layui-btn-xs' : 'none' %} + {% set url = (key == '0') ? url({'for':'home.uc.articles'}) : url({'for':'home.uc.articles'},{'published':key}) %} + {{ value }} + {% endfor %}
    {% if pager.total_pages > 0 %} @@ -19,6 +27,7 @@ + @@ -27,24 +36,34 @@ + {% for item in pager.items %} - {% set article_url = url({'for':'home.article.show','id':item.id}) %} + {% set show_url = url({'for':'home.article.show','id':item.id}) %} + {% set edit_url = url({'for':'home.article.edit','id':item.id}) %} + {% set delete_url = url({'for':'home.article.delete','id':item.id}) %} + {% endfor %} diff --git a/app/Http/Home/Views/user/console/notifications.volt b/app/Http/Home/Views/user/console/notifications.volt new file mode 100644 index 00000000..60cbff10 --- /dev/null +++ b/app/Http/Home/Views/user/console/notifications.volt @@ -0,0 +1,51 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + {{ partial('macros/notification') }} + +
    +
    {{ partial('user/console/menu') }}
    +
    +
    +
    + 消息提醒 +
    +
    + {% for item in pager.items %} + {% set sender_url = url({'for':'home.user.show','id':item.sender.id}) %} + {% set receiver_url = url({'for':'home.user.show','id':item.receiver.id}) %} +
    +
    + + {{ item.sender.name }} + +
    +
    + +
    {{ event_info(item) }}
    + +
    +
    + {% endfor %} +
    + {{ partial('partials/pager') }} +
    +
    +
    + +{% endblock %} + +{% block include_js %} + + {{ js_include('home/js/user.console.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/user/console/point_history.volt b/app/Http/Home/Views/user/console/point_history.volt index a9cc481b..bbff9658 100644 --- a/app/Http/Home/Views/user/console/point_history.volt +++ b/app/Http/Home/Views/user/console/point_history.volt @@ -32,7 +32,7 @@ - + {% endfor %} diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php index 3639ff96..e20d18d7 100644 --- a/app/Library/AppInfo.php +++ b/app/Library/AppInfo.php @@ -11,7 +11,7 @@ class AppInfo protected $link = 'https://koogua.com'; - protected $version = '1.3.2'; + protected $version = '1.3.3'; public function __get($name) { diff --git a/app/Listeners/Account.php b/app/Listeners/Account.php index c7e710a2..a4a31729 100644 --- a/app/Listeners/Account.php +++ b/app/Listeners/Account.php @@ -4,7 +4,7 @@ namespace App\Listeners; use App\Models\User as UserModel; use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService; -use App\Services\Logic\Point\PointHistory as PointHistoryService; +use App\Services\Logic\Point\History\AccountRegister as AccountRegisterPointHistory; use Phalcon\Events\Event as PhEvent; class Account extends Listener @@ -27,9 +27,9 @@ class Account extends Listener protected function handleRegisterPoint(UserModel $user) { - $service = new PointHistoryService(); + $service = new AccountRegisterPointHistory(); - $service->handleAccountRegister($user); + $service->handle($user); } protected function handleLoginNotice(UserModel $user) diff --git a/app/Listeners/Article.php b/app/Listeners/Article.php new file mode 100644 index 00000000..c168d6ef --- /dev/null +++ b/app/Listeners/Article.php @@ -0,0 +1,66 @@ +handleImDiscuss($message); + $service->handle($message); $tomorrow = strtotime($todayDate) + 86400; diff --git a/app/Listeners/Listener.php b/app/Listeners/Listener.php index 39e5c5c1..235266f7 100644 --- a/app/Listeners/Listener.php +++ b/app/Listeners/Listener.php @@ -3,11 +3,14 @@ namespace App\Listeners; use App\Services\Service as AppService; +use App\Traits\Auth as AuthTrait; use Phalcon\Mvc\User\Plugin as UserPlugin; class Listener extends UserPlugin { + use AuthTrait; + public function getConfig() { $appService = new AppService(); diff --git a/app/Listeners/Review.php b/app/Listeners/Review.php index e88ccfc7..2e9ad277 100644 --- a/app/Listeners/Review.php +++ b/app/Listeners/Review.php @@ -3,7 +3,6 @@ namespace App\Listeners; use App\Models\Review as ReviewModel; -use App\Services\Logic\Point\PointHistory as PointHistoryService; use Phalcon\Events\Event as PhEvent; class Review extends Listener @@ -11,14 +10,42 @@ class Review extends Listener public function afterCreate(PhEvent $event, $source, ReviewModel $review) { - $this->handleReviewPoint($review); + } - protected function handleReviewPoint(ReviewModel $review) + public function afterUpdate(PhEvent $event, $source, ReviewModel $review) + { + + } + + public function afterDelete(PhEvent $event, $source, ReviewModel $review) + { + + } + + public function afterRestore(PhEvent $event, $source, ReviewModel $review) + { + + } + + public function afterApprove(PhEvent $event, $source, ReviewModel $review) + { + + } + + public function afterReject(PhEvent $event, $source, ReviewModel $review) + { + + } + + public function afterLike(PhEvent $event, $source, ReviewModel $review) + { + + } + + public function afterUndoLike(PhEvent $event, $source, ReviewModel $review) { - $service = new PointHistoryService(); - $service->handleCourseReview($review); } } \ No newline at end of file diff --git a/app/Listeners/Site.php b/app/Listeners/Site.php index 1841332a..088d7756 100644 --- a/app/Listeners/Site.php +++ b/app/Listeners/Site.php @@ -2,11 +2,7 @@ namespace App\Listeners; -use App\Library\Utils\Lock as LockUtil; -use App\Models\Online as OnlineModel; use App\Models\User as UserModel; -use App\Repos\Online as OnlineRepo; -use App\Services\Logic\Point\PointHistory as PointHistoryService; use App\Traits\Client as ClientTrait; use Phalcon\Events\Event as PhEvent; @@ -17,102 +13,7 @@ class Site extends Listener public function afterView(PhEvent $event, $source, UserModel $user) { - if ($user->id > 0) { - $this->handleOnline($user); - - $this->handleVisitPoint($user); - - /** - * 更新会重置afterFetch,重新执行 - */ - $user->afterFetch(); - } - } - - protected function handleOnline(UserModel $user) - { - $now = time(); - - if ($now - $user->active_time < 900) { - return; - } - - $itemId = "user_online:{$user->id}"; - - $clientType = $this->getClientType(); - $clientIp = $this->getClientIp(); - - $lockId = LockUtil::addLock($itemId); - - if ($lockId === false) return; - - $user->active_time = $now; - - $user->update(); - - $onlineRepo = new OnlineRepo(); - - $records = $onlineRepo->findByUserDate($user->id, date('Ymd')); - - if ($records->count() > 0) { - $online = null; - foreach ($records as $record) { - $case1 = $record->client_type == $clientType; - $case2 = $record->client_ip == $clientIp; - if ($case1 && $case2) { - $online = $record; - break; - } - } - if ($online) { - $online->active_time = $now; - $online->update(); - } else { - $this->createOnline($user->id, $clientType, $clientIp); - } - } else { - $this->createOnline($user->id, $clientType, $clientIp); - } - - LockUtil::releaseLock($itemId, $lockId); - } - - protected function createOnline($userId, $clientType, $clientIp) - { - $online = new OnlineModel(); - - $online->user_id = $userId; - $online->client_type = $clientType; - $online->client_ip = $clientIp; - $online->active_time = time(); - - $online->create(); - - return $online; - } - - protected function handleVisitPoint(UserModel $user) - { - $todayDate = date('Ymd'); - - $keyName = sprintf('site_visit:%s:%s', $user->id, $todayDate); - - $cache = $this->getCache(); - - $content = $cache->get($keyName); - - if ($content) return; - - $service = new PointHistoryService(); - - $service->handleSiteVisit($user); - - $tomorrow = strtotime($todayDate) + 86400; - - $lifetime = $tomorrow - time(); - - $cache->save($keyName, 1, $lifetime); } } \ No newline at end of file diff --git a/app/Models/Article.php b/app/Models/Article.php index 4b2c2432..25ef2301 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -17,6 +17,13 @@ class Article extends Model const SOURCE_REPRINT = 2; // 转载 const SOURCE_TRANSLATE = 3; // 翻译 + /** + * 发布状态 + */ + const PUBLISH_PENDING = 1; // 审核中 + const PUBLISH_APPROVED = 2; // 已发布 + const PUBLISH_REJECTED = 3; // 未通过 + /** * 主键编号 * @@ -87,6 +94,27 @@ class Article extends Model */ public $source_url = ''; + /** + * 终端类型 + * + * @var integer + */ + public $client_type = 0; + + /** + * 终端IP + * + * @var integer + */ + public $client_ip = ''; + + /** + * 私有标识 + * + * @var int + */ + public $private = 0; + /** * 推荐标识 * @@ -99,7 +127,7 @@ class Article extends Model * * @var int */ - public $published = 0; + public $published = self::PUBLISH_PENDING; /** * 删除标识 @@ -191,7 +219,7 @@ class Article extends Model $this->cover = self::getCoverPath($this->cover); } - if (is_array($this->tags)) { + if (is_array($this->tags) || is_object($this->tags)) { $this->tags = kg_json_encode($this->tags); } @@ -213,14 +241,10 @@ class Article extends Model $this->summary = kg_parse_summary($this->content); } - if (is_array($this->tags)) { + if (is_array($this->tags) || is_array($this->tags)) { $this->tags = kg_json_encode($this->tags); } - if ($this->deleted == 1) { - $this->published = 0; - } - $this->update_time = time(); } @@ -260,6 +284,15 @@ class Article extends Model ]; } + public static function publishTypes() + { + return [ + self::PUBLISH_PENDING => '审核中', + self::PUBLISH_APPROVED => '已发布', + self::PUBLISH_REJECTED => '未通过', + ]; + } + public static function sortTypes() { return [ diff --git a/app/Models/Chapter.php b/app/Models/Chapter.php index 7ae5a945..01fd31bd 100644 --- a/app/Models/Chapter.php +++ b/app/Models/Chapter.php @@ -124,7 +124,7 @@ class Chapter extends Model /** * 扩展属性 * - * @var string|array + * @var array|string */ public $attrs = []; @@ -235,7 +235,7 @@ class Chapter extends Model } } - if (is_array($this->attrs)) { + if (is_array($this->attrs) || is_object($this->attrs)) { $this->attrs = kg_json_encode($this->attrs); } @@ -244,7 +244,7 @@ class Chapter extends Model public function beforeUpdate() { - if (is_array($this->attrs)) { + if (is_array($this->attrs) || is_object($this->attrs)) { $this->attrs = kg_json_encode($this->attrs); } diff --git a/app/Models/ChapterVod.php b/app/Models/ChapterVod.php index be1b0335..852fa32d 100644 --- a/app/Models/ChapterVod.php +++ b/app/Models/ChapterVod.php @@ -63,7 +63,7 @@ class ChapterVod extends Model public function beforeCreate() { - if (is_array($this->file_transcode)) { + if (is_array($this->file_transcode) || is_object($this->file_transcode)) { $this->file_transcode = kg_json_encode($this->file_transcode); } @@ -72,7 +72,7 @@ class ChapterVod extends Model public function beforeUpdate() { - if (is_array($this->file_transcode)) { + if (is_array($this->file_transcode) || is_object($this->file_transcode)) { $this->file_transcode = kg_json_encode($this->file_transcode); } diff --git a/app/Models/Comment.php b/app/Models/Comment.php index 2b9c1393..34eb95a0 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -13,7 +13,15 @@ class Comment extends Model */ const ITEM_CHAPTER = 1; // 章节 const ITEM_ARTICLE = 2; // 文章 - const ITEM_ANSWER = 3; // 回答 + const ITEM_QUESTION = 3; // 问题 + const ITEM_ANSWER = 4; // 回答 + + /** + * 发布状态 + */ + const PUBLISH_PENDING = 1; // 审核中 + const PUBLISH_APPROVED = 2; // 已发布 + const PUBLISH_REJECTED = 3; // 未通过 /** * 主键编号 @@ -83,7 +91,7 @@ class Comment extends Model * * @var integer */ - public $published = 1; + public $published = self::PUBLISH_PENDING; /** * 删除标识 @@ -144,10 +152,6 @@ class Comment extends Model public function beforeUpdate() { - if ($this->deleted == 1) { - $this->published = 0; - } - $this->update_time = time(); } @@ -167,4 +171,13 @@ class Comment extends Model ]; } + public static function publishTypes() + { + return [ + self::PUBLISH_PENDING => '审核中', + self::PUBLISH_APPROVED => '已发布', + self::PUBLISH_REJECTED => '未通过', + ]; + } + } diff --git a/app/Models/Consult.php b/app/Models/Consult.php index ee24fea3..30888a24 100644 --- a/app/Models/Consult.php +++ b/app/Models/Consult.php @@ -49,6 +49,20 @@ class Consult extends Model */ public $replier_id = 0; + /** + * 终端类型 + * + * @var integer + */ + public $client_type = 0; + + /** + * 终端IP + * + * @var integer + */ + public $client_ip = ''; + /** * 提问 * diff --git a/app/Models/Course.php b/app/Models/Course.php index d8c50fed..8a775adb 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -190,7 +190,7 @@ class Course extends Model /** * 扩展属性 * - * @var string|array + * @var array|string */ public $attrs = []; @@ -311,7 +311,7 @@ class Course extends Model } } - if (is_array($this->attrs)) { + if (is_array($this->attrs) || is_object($this->attrs)) { $this->attrs = kg_json_encode($this->attrs); } @@ -342,7 +342,7 @@ class Course extends Model $this->summary = kg_parse_summary($this->details); } - if (is_array($this->attrs)) { + if (is_array($this->attrs) || is_object($this->attrs)) { $this->attrs = kg_json_encode($this->attrs); } diff --git a/app/Models/FlashSale.php b/app/Models/FlashSale.php index ec226754..81c6ab62 100644 --- a/app/Models/FlashSale.php +++ b/app/Models/FlashSale.php @@ -125,11 +125,11 @@ class FlashSale extends Model public function beforeCreate() { - if (is_array($this->item_info)) { + 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)) { + if (is_array($this->schedules) || is_object($this->schedules)) { $this->schedules = kg_json_encode($this->schedules); } @@ -138,11 +138,11 @@ class FlashSale extends Model public function beforeUpdate() { - if (is_array($this->item_info)) { + 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)) { + if (is_array($this->schedules) || is_object($this->schedules)) { $this->schedules = kg_json_encode($this->schedules); } diff --git a/app/Models/ImNotice.php b/app/Models/ImNotice.php index 258f77fb..31b29225 100644 --- a/app/Models/ImNotice.php +++ b/app/Models/ImNotice.php @@ -53,9 +53,9 @@ class ImNotice extends Model /** * 条目内容 * - * @var string + * @var array|string */ - public $item_info = ''; + public $item_info = []; /** * 阅读标识 @@ -85,7 +85,7 @@ class ImNotice extends Model public function beforeCreate() { - if (!empty($this->item_info)) { + if (is_array($this->item_info)) { $this->item_info = kg_json_encode($this->item_info); } @@ -94,7 +94,7 @@ class ImNotice extends Model public function beforeUpdate() { - if (is_array($this->item_info) && !empty($this->item_info)) { + if (is_array($this->item_info)) { $this->item_info = kg_json_encode($this->item_info); } @@ -103,7 +103,7 @@ class ImNotice extends Model public function afterFetch() { - if (is_string($this->item_info) && !empty($this->item_info)) { + if (is_string($this->item_info)) { $this->item_info = json_decode($this->item_info, true); } } diff --git a/app/Models/Notification.php b/app/Models/Notification.php new file mode 100644 index 00000000..b7cfab4e --- /dev/null +++ b/app/Models/Notification.php @@ -0,0 +1,206 @@ +event_info) || is_object($this->event_info)) { + $this->event_info = kg_json_encode($this->event_info); + } + + $this->create_time = time(); + } + + public function beforeUpdate() + { + 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() + { + if (is_string($this->event_info)) { + $this->event_info = json_decode($this->event_info, true); + } + } + +} \ No newline at end of file diff --git a/app/Models/Order.php b/app/Models/Order.php index 161ed023..5e6ab4f5 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -80,7 +80,7 @@ class Order extends Model /** * 条目信息 * - * @var string|array + * @var array|string */ public $item_info = []; @@ -101,7 +101,7 @@ class Order extends Model /** * 促销信息 * - * @var string|array + * @var array|string */ public $promotion_info = []; @@ -170,11 +170,11 @@ class Order extends Model { $this->sn = date('YmdHis') . rand(1000, 9999); - if (is_array($this->item_info)) { + 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)) { + if (is_array($this->promotion_info) || is_object($this->promotion_info)) { $this->promotion_info = kg_json_encode($this->promotion_info); } @@ -183,11 +183,11 @@ class Order extends Model public function beforeUpdate() { - if (is_array($this->item_info)) { + 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)) { + if (is_array($this->promotion_info) || is_object($this->promotion_info)) { $this->promotion_info = kg_json_encode($this->promotion_info); } diff --git a/app/Models/PointGift.php b/app/Models/PointGift.php index 0a1a5db1..63c1de87 100644 --- a/app/Models/PointGift.php +++ b/app/Models/PointGift.php @@ -68,7 +68,7 @@ class PointGift extends Model /** * 属性 * - * @var string|array + * @var array|string */ public $attrs = []; @@ -162,7 +162,7 @@ class PointGift extends Model } } - if (is_array($this->attrs)) { + if (is_array($this->attrs) || is_object($this->attrs)) { $this->attrs = kg_json_encode($this->attrs); } @@ -181,7 +181,7 @@ class PointGift extends Model $this->cover = self::getCoverPath($this->cover); } - if (is_array($this->attrs)) { + if (is_array($this->attrs) || is_object($this->attrs)) { $this->attrs = kg_json_encode($this->attrs); } diff --git a/app/Models/PointHistory.php b/app/Models/PointHistory.php index 1cb0aa91..dbf817b5 100644 --- a/app/Models/PointHistory.php +++ b/app/Models/PointHistory.php @@ -16,6 +16,10 @@ class PointHistory extends Model const EVENT_CHAPTER_STUDY = 6; // 课时学习 const EVENT_COURSE_REVIEW = 7; // 课程评价 const EVENT_IM_DISCUSS = 8; // 微聊讨论 + const EVENT_COMMENT_POST = 9; // 发布评论 + const EVENT_ARTICLE_POST = 10; // 发布文章 + const EVENT_QUESTION_POST = 11; // 发布问题 + const EVENT_ANSWER_POST = 12; // 发布答案 /** * 主键编号 @@ -50,12 +54,12 @@ class PointHistory extends Model * * @var int */ - public $event_type = ''; + public $event_type = 0; /** * 事件内容 * - * @var string|array + * @var array|string */ public $event_info = []; @@ -87,7 +91,7 @@ class PointHistory extends Model public function beforeCreate() { - if (is_array($this->event_info) && !empty($this->event_info)) { + if (is_array($this->event_info) || is_object($this->event_info)) { $this->event_info = kg_json_encode($this->event_info); } @@ -96,12 +100,16 @@ class PointHistory extends Model public function beforeUpdate() { + 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() { - if (is_string($this->event_info) && !empty($this->event_info)) { + if (is_string($this->event_info)) { $this->event_info = json_decode($this->event_info, true); } } diff --git a/app/Models/Reason.php b/app/Models/Reason.php new file mode 100644 index 00000000..8ff03c0a --- /dev/null +++ b/app/Models/Reason.php @@ -0,0 +1,26 @@ + '内容质量差', + 102 => '旧闻重提', + 103 => '内容不实', + 104 => '标题夸张', + 105 => '题文不符', + 106 => '低俗色情', + 107 => '广告软文', + 108 => '封面反感', + 109 => '归类与主题不符', + 110 => '抄袭他人作品', + 111 => '内容涉嫌违法', + 112 => '其它问题', + ]; + } + +} \ No newline at end of file diff --git a/app/Models/Review.php b/app/Models/Review.php index 304f00e5..fc518ff8 100644 --- a/app/Models/Review.php +++ b/app/Models/Review.php @@ -28,6 +28,20 @@ class Review extends Model */ public $owner_id = 0; + /** + * 终端类型 + * + * @var integer + */ + public $client_type = 0; + + /** + * 终端IP + * + * @var integer + */ + public $client_ip = ''; + /** * 评价内容 * diff --git a/app/Models/Role.php b/app/Models/Role.php index 3bc132e2..faf819fc 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -103,7 +103,7 @@ class Role extends Model public function beforeCreate() { - if (is_array($this->routes)) { + if (is_array($this->routes) || is_object($this->routes)) { $this->routes = kg_json_encode($this->routes); } @@ -112,7 +112,7 @@ class Role extends Model public function beforeUpdate() { - if (is_array($this->routes)) { + if (is_array($this->routes) || is_object($this->routes)) { $this->routes = kg_json_encode($this->routes); } diff --git a/app/Models/Slide.php b/app/Models/Slide.php index 8719172d..cbe149bf 100644 --- a/app/Models/Slide.php +++ b/app/Models/Slide.php @@ -131,7 +131,7 @@ class Slide extends Model $this->cover = self::getCoverPath($this->cover); } - if (is_array($this->target_attrs)) { + if (is_array($this->target_attrs) || is_object($this->target_attrs)) { $this->target_attrs = kg_json_encode($this->target_attrs); } @@ -144,7 +144,7 @@ class Slide extends Model $this->cover = self::getCoverPath($this->cover); } - if (is_array($this->target_attrs)) { + if (is_array($this->target_attrs) || is_object($this->target_attrs)) { $this->target_attrs = kg_json_encode($this->target_attrs); } diff --git a/app/Models/Task.php b/app/Models/Task.php index cc1f51ab..8ac27e11 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -73,7 +73,7 @@ class Task extends Model /** * 条目内容 * - * @var string|array + * @var array|string */ public $item_info = []; @@ -126,7 +126,7 @@ class Task extends Model public function beforeCreate() { - if (is_array($this->item_info)) { + if (is_array($this->item_info) || is_object($this->item_info)) { $this->item_info = kg_json_encode($this->item_info); } @@ -135,7 +135,7 @@ class Task extends Model public function beforeUpdate() { - if (is_array($this->item_info)) { + if (is_array($this->item_info) || is_object($this->item_info)) { $this->item_info = kg_json_encode($this->item_info); } diff --git a/app/Providers/Volt.php b/app/Providers/Volt.php index 40ebfa7f..53991622 100644 --- a/app/Providers/Volt.php +++ b/app/Providers/Volt.php @@ -66,6 +66,10 @@ class Volt extends Provider return 'kg_object_array(' . $resolvedArgs . ')'; }); + $compiler->addFilter('parse_markdown', function ($resolvedArgs) { + return 'kg_parse_markdown(' . $resolvedArgs . ')'; + }); + $compiler->addFilter('duration', function ($resolvedArgs) { return 'kg_duration(' . $resolvedArgs . ')'; }); diff --git a/app/Repos/Article.php b/app/Repos/Article.php index 986aeb6b..620e2f31 100644 --- a/app/Repos/Article.php +++ b/app/Repos/Article.php @@ -7,6 +7,7 @@ use App\Models\Article as ArticleModel; use App\Models\ArticleFavorite as ArticleFavoriteModel; use App\Models\ArticleLike as ArticleLikeModel; use App\Models\ArticleTag as ArticleTagModel; +use App\Models\Comment as CommentModel; use App\Models\Tag as TagModel; use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Resultset; @@ -43,16 +44,20 @@ class Article extends Repository $builder->andWhere('owner_id = :owner_id:', ['owner_id' => $where['owner_id']]); } + if (isset($where['source_type'])) { + $builder->andWhere('source_type = :source_type:', ['source_type' => $where['source_type']]); + } + if (!empty($where['title'])) { $builder->andWhere('title LIKE :title:', ['title' => "%{$where['title']}%"]); } - if (isset($where['featured'])) { - $builder->andWhere('featured = :featured:', ['featured' => $where['featured']]); + if (isset($where['private'])) { + $builder->andWhere('private = :private:', ['private' => $where['private']]); } - if (isset($where['source_type'])) { - $builder->andWhere('source_type = :source_type:', ['source_type' => $where['source_type']]); + if (isset($where['featured'])) { + $builder->andWhere('featured = :featured:', ['featured' => $where['featured']]); } if (isset($where['published'])) { @@ -140,9 +145,9 @@ class Article extends Repository public function countComments($articleId) { - return (int)ArticleCommentModel::count([ - 'conditions' => 'article_id = :article_id: AND deleted = 0', - 'bind' => ['article_id' => $articleId], + return (int)CommentModel::count([ + 'conditions' => 'item_id = ?1 AND item_type = ?2 AND deleted = 0', + 'bind' => [1 => $articleId, 2 => CommentModel::ITEM_ARTICLE], ]); } diff --git a/app/Repos/Chapter.php b/app/Repos/Chapter.php index 6ba09f22..e67fa3ef 100644 --- a/app/Repos/Chapter.php +++ b/app/Repos/Chapter.php @@ -9,6 +9,7 @@ use App\Models\ChapterOffline as ChapterOfflineModel; use App\Models\ChapterRead as ChapterReadModel; use App\Models\ChapterUser as ChapterUserModel; use App\Models\ChapterVod as ChapterVodModel; +use App\Models\Comment as CommentModel; use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -176,4 +177,12 @@ class Chapter extends Repository ]); } + public function countComments($chapterId) + { + return (int)CommentModel::count([ + 'conditions' => 'item_id = ?1 AND item_type = ?2 AND deleted = 0', + 'bind' => [1 => $chapterId, 2 => CommentModel::ITEM_CHAPTER], + ]); + } + } diff --git a/app/Repos/Comment.php b/app/Repos/Comment.php index 89cecccd..8674057a 100644 --- a/app/Repos/Comment.php +++ b/app/Repos/Comment.php @@ -92,4 +92,9 @@ class Comment extends Repository ->execute(); } + public function countComments() + { + return (int)CommentModel::count(['conditions' => 'deleted = 0']); + } + } diff --git a/app/Repos/Notification.php b/app/Repos/Notification.php new file mode 100644 index 00000000..a5e4789b --- /dev/null +++ b/app/Repos/Notification.php @@ -0,0 +1,106 @@ +modelsManager->createBuilder(); + + $builder->from(NotificationModel::class); + + $builder->where('1 = 1'); + + if (!empty($where['sender_id'])) { + $builder->andWhere('sender_id = :sender_id:', ['sender_id' => $where['sender_id']]); + } + + if (!empty($where['receiver_id'])) { + $builder->andWhere('receiver_id = :receiver_id:', ['receiver_id' => $where['receiver_id']]); + } + + if (!empty($where['event_id'])) { + $builder->andWhere('event_id = :event_id:', ['event_id' => $where['event_id']]); + } + + if (!empty($where['event_type'])) { + $builder->andWhere('event_type = :event_type:', ['event_type' => $where['event_type']]); + } + + if (isset($where['viewed'])) { + $builder->andWhere('viewed = :viewed:', ['viewed' => $where['viewed']]); + } + + if (isset($where['deleted'])) { + $builder->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + switch ($sort) { + case 'oldest': + $orderBy = 'id ASC'; + break; + default: + $orderBy = 'id DESC'; + break; + } + + $builder->orderBy($orderBy); + + $pager = new PagerQueryBuilder([ + 'builder' => $builder, + 'page' => $page, + 'limit' => $limit, + ]); + + return $pager->paginate(); + } + + /** + * @param int $id + * @return NotificationModel|Model|bool + */ + public function findById($id) + { + return NotificationModel::findFirst([ + 'conditions' => 'id = :id:', + 'bind' => ['id' => $id], + ]); + } + + /** + * @param array $ids + * @param string|array $columns + * @return ResultsetInterface|Resultset|NotificationModel[] + */ + public function findByIds($ids, $columns = '*') + { + return NotificationModel::query() + ->columns($columns) + ->inWhere('id', $ids) + ->execute(); + } + + public function findByUserEvent($senderId, $eventId, $eventType) + { + return NotificationModel::findFirst([ + 'conditions' => 'sender_id = ?1 AND event_id = ?2 AND event_type = ?3', + 'bind' => [1 => $senderId, 2 => $eventId, 3 => $eventType], + ]); + } + + public function markAllAsViewed($userId) + { + $phql = sprintf('UPDATE %s SET viewed = 1 WHERE receiver_id = :user_id: AND viewed = 0', NotificationModel::class); + + return $this->modelsManager->executeQuery($phql, ['user_id' => $userId]); + } + +} diff --git a/app/Repos/PointHistory.php b/app/Repos/PointHistory.php index 234425ca..2acb67a3 100644 --- a/app/Repos/PointHistory.php +++ b/app/Repos/PointHistory.php @@ -102,4 +102,21 @@ class PointHistory extends Repository ->execute(); } + /** + * @param int $userId + * @param int $eventType + * @param string $date + * @return int + */ + public function sumUserDailyEventPoints($userId, $eventType, $date) + { + $createTime = strtotime($date); + + return (int)PointHistoryModel::sum([ + 'column' => 'event_point', + 'conditions' => 'user_id = ?1 AND event_type = ?2 AND create_time > ?3', + 'bind' => [1 => $userId, 2 => $eventType, 3 => $createTime], + ]); + } + } diff --git a/app/Repos/Stat.php b/app/Repos/Stat.php index bb5731ce..127e7a76 100644 --- a/app/Repos/Stat.php +++ b/app/Repos/Stat.php @@ -2,9 +2,12 @@ namespace App\Repos; +use App\Models\Article as ArticleModel; +use App\Models\Comment as CommentModel; use App\Models\Online as OnlineModel; use App\Models\Order as OrderModel; use App\Models\OrderStatus as OrderStatusModel; +use App\Models\PointRedeem as PointRedeemModel; use App\Models\Refund as RefundModel; use App\Models\User as UserModel; use Phalcon\Mvc\Model\Resultset; @@ -13,7 +16,23 @@ use Phalcon\Mvc\Model\ResultsetInterface; class Stat extends Repository { - public function countDailyRegisteredUser($date) + public function countPendingArticles() + { + return (int)ArticleModel::count([ + 'conditions' => 'published = :published: AND deleted = 0', + 'bind' => ['published' => ArticleModel::PUBLISH_PENDING], + ]); + } + + public function countPendingComments() + { + return (int)CommentModel::count([ + 'conditions' => 'published = :published: AND deleted = 0', + 'bind' => ['published' => CommentModel::PUBLISH_PENDING], + ]); + } + + public function countDailyRegisteredUsers($date) { $startTime = strtotime($date); @@ -74,6 +93,22 @@ class Stat extends Repository ]); } + public function countDailyPointRedeems($date) + { + $startTime = strtotime($date); + + $endTime = $startTime + 86400; + + return (int)PointRedeemModel::count([ + 'conditions' => 'status = ?1 AND create_time BETWEEN ?2 AND ?3', + 'bind' => [ + 1 => PointRedeemModel::STATUS_PENDING, + 2 => $startTime, + 3 => $endTime, + ], + ]); + } + public function sumDailySales($date) { $sql = "SELECT sum(o.amount) AS total_amount FROM %s AS os JOIN %s AS o ON os.order_id = o.id "; diff --git a/app/Repos/User.php b/app/Repos/User.php index 8ce2068e..8602a3b7 100644 --- a/app/Repos/User.php +++ b/app/Repos/User.php @@ -8,6 +8,7 @@ use App\Models\ArticleFavorite as ArticleFavoriteModel; use App\Models\CourseFavorite as CourseFavoriteModel; use App\Models\CourseUser as CourseUserModel; use App\Models\ImUser as ImUserModel; +use App\Models\Notification as NotificationModel; use App\Models\User as UserModel; use App\Models\UserBalance as UserBalanceModel; use App\Models\UserContact as UserContactModel; @@ -156,7 +157,12 @@ class User extends Repository public function countUsers() { - return (int)UserModel::count(['conditions' => 'deleted = 0']); + return (int)UserModel::count(); + } + + public function countVipUsers() + { + return (int)UserModel::count(['conditions' => 'vip = 1']); } public function countCourses($userId) @@ -191,4 +197,12 @@ class User extends Repository ]); } + public function countUnreadNotifications($userId) + { + return (int)NotificationModel::count([ + 'conditions' => 'receiver_id = :user_id: AND viewed = 0', + 'bind' => ['user_id' => $userId], + ]); + } + } diff --git a/app/Services/Logic/Article/ArticleFavorite.php b/app/Services/Logic/Article/ArticleFavorite.php index dc1554c6..40c42f68 100644 --- a/app/Services/Logic/Article/ArticleFavorite.php +++ b/app/Services/Logic/Article/ArticleFavorite.php @@ -7,6 +7,7 @@ use App\Models\ArticleFavorite as ArticleFavoriteModel; use App\Models\User as UserModel; use App\Repos\ArticleFavorite as ArticleFavoriteRepo; use App\Services\Logic\ArticleTrait; +use App\Services\Logic\Notice\System\ArticleFavorited as ArticleFavoritedNotice; use App\Services\Logic\Service as LogicService; use App\Validators\UserLimit as UserLimitValidator; @@ -41,8 +42,13 @@ class ArticleFavorite extends LogicService $favorite->create(); $this->incrArticleFavoriteCount($article); + $this->incrUserFavoriteCount($user); + $this->handleFavoriteNotice($article, $user); + + $this->eventsManager->fire('Article:afterFavorite', $this, $article); + } else { $action = 'undo'; @@ -50,7 +56,10 @@ class ArticleFavorite extends LogicService $favorite->delete(); $this->decrArticleFavoriteCount($article); + $this->decrUserFavoriteCount($user); + + $this->eventsManager->fire('Article:afterUndoFavorite', $this, $article); } return [ @@ -89,4 +98,11 @@ class ArticleFavorite extends LogicService } } + protected function handleFavoriteNotice(ArticleModel $article, UserModel $sender) + { + $notice = new ArticleFavoritedNotice(); + + $notice->handle($article, $sender); + } + } diff --git a/app/Services/Logic/Article/ArticleInfo.php b/app/Services/Logic/Article/ArticleInfo.php index edc52bde..bbf6c933 100644 --- a/app/Services/Logic/Article/ArticleInfo.php +++ b/app/Services/Logic/Article/ArticleInfo.php @@ -27,6 +27,8 @@ class ArticleInfo extends LogicService $this->incrArticleViewCount($article); + $this->eventsManager->fire('Article:afterView', $this, $article); + return $result; } @@ -48,6 +50,7 @@ class ArticleInfo extends LogicService 'category' => $category, 'owner' => $owner, 'me' => $me, + 'private' => $article->private, 'allow_comment' => $article->allow_comment, 'source_type' => $article->source_type, 'source_url' => $article->source_url, @@ -57,6 +60,7 @@ class ArticleInfo extends LogicService 'comment_count' => $article->comment_count, 'favorite_count' => $article->favorite_count, 'create_time' => $article->create_time, + 'update_time' => $article->update_time, ]; } diff --git a/app/Services/Logic/Article/ArticleLike.php b/app/Services/Logic/Article/ArticleLike.php index 810b9566..86f8b4c1 100644 --- a/app/Services/Logic/Article/ArticleLike.php +++ b/app/Services/Logic/Article/ArticleLike.php @@ -7,6 +7,7 @@ use App\Models\ArticleLike as ArticleLikeModel; use App\Models\User as UserModel; use App\Repos\ArticleLike as ArticleLikeRepo; use App\Services\Logic\ArticleTrait; +use App\Services\Logic\Notice\System\ArticleLiked as ArticleLikedNotice; use App\Services\Logic\Service as LogicService; use App\Validators\UserLimit as UserLimitValidator; @@ -42,6 +43,10 @@ class ArticleLike extends LogicService $this->incrArticleLikeCount($article); + $this->handleLikeNotice($article, $user); + + $this->eventsManager->fire('Article:afterLike', $this, $article); + } else { $action = 'undo'; @@ -49,6 +54,8 @@ class ArticleLike extends LogicService $articleLike->delete(); $this->decrArticleLikeCount($article); + + $this->eventsManager->fire('Article:afterUndoLike', $this, $article); } $this->incrUserDailyArticleLikeCount($user); @@ -79,4 +86,11 @@ class ArticleLike extends LogicService $this->eventsManager->fire('UserDailyCounter:incrArticleLikeCount', $this, $user); } + protected function handleLikeNotice(ArticleModel $article, UserModel $sender) + { + $notice = new ArticleLikedNotice(); + + $notice->handle($article, $sender); + } + } diff --git a/app/Services/Logic/Article/ArticleList.php b/app/Services/Logic/Article/ArticleList.php index 87ecf956..07ffcd70 100644 --- a/app/Services/Logic/Article/ArticleList.php +++ b/app/Services/Logic/Article/ArticleList.php @@ -4,6 +4,7 @@ namespace App\Services\Logic\Article; use App\Builders\ArticleList as ArticleListBuilder; use App\Library\Paginator\Query as PagerQuery; +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; @@ -19,7 +20,9 @@ class ArticleList extends LogicService $params = $this->checkQueryParams($params); - $params['published'] = 1; + $params['published'] = ArticleModel::PUBLISH_APPROVED; + $params['private'] = 0; + $params['deleted'] = 0; $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); @@ -74,6 +77,9 @@ class ArticleList extends LogicService 'tags' => $article['tags'], 'category' => $category, 'owner' => $owner, + 'private' => $article['private'], + 'published' => $article['published'], + 'allow_comment' => $article['allow_comment'], 'view_count' => $article['view_count'], 'like_count' => $article['like_count'], 'comment_count' => $article['comment_count'], diff --git a/app/Services/Logic/Article/CommentList.php b/app/Services/Logic/Article/CommentList.php index 2f311af2..194cec1c 100644 --- a/app/Services/Logic/Article/CommentList.php +++ b/app/Services/Logic/Article/CommentList.php @@ -23,9 +23,9 @@ class CommentList extends LogicService $params = $pagerQuery->getParams(); - $params['item_type'] = CommentModel::ITEM_ARTICLE; $params['item_id'] = $article->id; - $params['published'] = 1; + $params['item_type'] = CommentModel::ITEM_ARTICLE; + $params['published'] = CommentModel::PUBLISH_APPROVED; $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); diff --git a/app/Services/Logic/Chapter/CommentList.php b/app/Services/Logic/Chapter/CommentList.php index beca7675..f1ebe80e 100644 --- a/app/Services/Logic/Chapter/CommentList.php +++ b/app/Services/Logic/Chapter/CommentList.php @@ -23,9 +23,9 @@ class CommentList extends LogicService $params = $pagerQuery->getParams(); - $params['item_type'] = CommentModel::ITEM_CHAPTER; $params['item_id'] = $chapter->id; - $params['published'] = 1; + $params['item_type'] = CommentModel::ITEM_CHAPTER; + $params['published'] = CommentModel::PUBLISH_APPROVED; $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); diff --git a/app/Services/Logic/Comment/CommentCountTrait.php b/app/Services/Logic/Comment/CommentCountTrait.php index 1bdfc499..a4407656 100644 --- a/app/Services/Logic/Comment/CommentCountTrait.php +++ b/app/Services/Logic/Comment/CommentCountTrait.php @@ -6,12 +6,54 @@ use App\Models\Article as ArticleModel; use App\Models\Chapter as ChapterModel; use App\Models\Comment as CommentModel; use App\Models\User as UserModel; +use App\Services\Logic\Notice\System\ArticleCommented as ArticleCommentedNotice; +use App\Services\Logic\Notice\System\ChapterCommented as ChapterCommentedNotice; use Phalcon\Di as Di; use Phalcon\Events\Manager as EventsManager; trait CommentCountTrait { + protected function incrItemCommentCount(CommentModel $comment) + { + if ($comment->item_type == CommentModel::ITEM_CHAPTER) { + + $chapter = $this->checkChapter($comment->item_id); + + $this->incrChapterCommentCount($chapter); + + $notice = new ChapterCommentedNotice(); + + $notice->handle($comment); + + } elseif ($comment->item_type == CommentModel::ITEM_ARTICLE) { + + $article = $this->checkArticle($comment->item_id); + + $this->incrArticleCommentCount($article); + + $notice = new ArticleCommentedNotice(); + + $notice->handle($comment); + } + } + + protected function decrItemCommentCount(CommentModel $comment) + { + if ($comment->item_type == CommentModel::ITEM_CHAPTER) { + + $chapter = $this->checkChapter($comment->item_id); + + $this->decrChapterCommentCount($chapter); + + } elseif ($comment->item_type == CommentModel::ITEM_ARTICLE) { + + $article = $this->checkArticle($comment->item_id); + + $this->decrArticleCommentCount($article); + } + } + protected function incrCommentReplyCount(CommentModel $comment) { $comment->reply_count += 1; diff --git a/app/Services/Logic/Comment/CommentCreate.php b/app/Services/Logic/Comment/CommentCreate.php index 4c7c54e4..0a8ad40b 100644 --- a/app/Services/Logic/Comment/CommentCreate.php +++ b/app/Services/Logic/Comment/CommentCreate.php @@ -5,6 +5,9 @@ namespace App\Services\Logic\Comment; use App\Models\Comment as CommentModel; use App\Services\Logic\ArticleTrait; use App\Services\Logic\ChapterTrait; +use App\Services\Logic\Notice\System\ArticleCommented as ArticleCommentedNotice; +use App\Services\Logic\Notice\System\ChapterCommented as ChapterCommentedNotice; +use App\Services\Logic\Point\History\CommentPost as CommentPostPointHistory; use App\Services\Logic\Service as LogicService; use App\Traits\Client as ClientTrait; use App\Validators\Comment as CommentValidator; @@ -38,31 +41,63 @@ class CommentCreate extends LogicService 'item_id' => $post['item_id'], 'item_type' => $post['item_type'], 'owner_id' => $user->id, - 'published' => 1, ]; $data['content'] = $validator->checkContent($post['content']); $data['client_type'] = $this->getClientType(); $data['client_ip'] = $this->getClientIp(); - if ($post['item_type'] == CommentModel::ITEM_CHAPTER) { - - $chapter = $this->checkChapter($post['item_id']); - - $this->incrChapterCommentCount($chapter); - - } elseif ($post['item_type'] == CommentModel::ITEM_ARTICLE) { - - $article = $this->checkArticle($post['item_id']); - - $this->incrArticleCommentCount($article); - } + /** + * @todo 引入自动审核机制 + */ + $data['published'] = CommentModel::PUBLISH_APPROVED; $comment->create($data); $this->incrUserDailyCommentCount($user); + $this->incrItemCommentCount($comment); + + $this->handlePostNotice($comment); + + $this->handlePostPoint($comment); + + $this->eventsManager->fire('Comment:afterCreate', $this, $comment); + return $comment; } + protected function handlePostNotice(CommentModel $comment) + { + if ($comment->item_type == CommentModel::ITEM_CHAPTER) { + + $chapter = $this->checkChapter($comment->item_id); + + $this->incrChapterCommentCount($chapter); + + $notice = new ChapterCommentedNotice(); + + $notice->handle($comment); + + } elseif ($comment->item_type == CommentModel::ITEM_ARTICLE) { + + $article = $this->checkArticle($comment->item_id); + + $this->incrArticleCommentCount($article); + + $notice = new ArticleCommentedNotice(); + + $notice->handle($comment); + } + } + + protected function handlePostPoint(CommentModel $comment) + { + if ($comment->published != CommentModel::PUBLISH_APPROVED) return; + + $service = new CommentPostPointHistory(); + + $service->handle($comment); + } + } diff --git a/app/Services/Logic/Comment/CommentDelete.php b/app/Services/Logic/Comment/CommentDelete.php index dcd8847e..6aac600b 100644 --- a/app/Services/Logic/Comment/CommentDelete.php +++ b/app/Services/Logic/Comment/CommentDelete.php @@ -2,9 +2,6 @@ namespace App\Services\Logic\Comment; -use App\Models\Comment as CommentModel; -use App\Services\Logic\ArticleTrait; -use App\Services\Logic\ChapterTrait; use App\Services\Logic\CommentTrait; use App\Services\Logic\Service as LogicService; use App\Validators\Comment as CommentValidator; @@ -12,8 +9,6 @@ use App\Validators\Comment as CommentValidator; class CommentDelete extends LogicService { - use ArticleTrait; - use ChapterTrait; use CommentTrait; use CommentCountTrait; @@ -38,18 +33,9 @@ class CommentDelete extends LogicService $this->decrCommentReplyCount($parent); } - if ($comment->item_type == CommentModel::ITEM_CHAPTER) { + $this->decrItemCommentCount($comment); - $chapter = $this->checkChapter($comment->item_id); - - $this->decrChapterCommentCount($chapter); - - } elseif ($comment->item_type == CommentModel::ITEM_ARTICLE) { - - $article = $this->checkArticle($comment->item_id); - - $this->decrArticleCommentCount($article); - } + $this->eventsManager->fire('Comment:afterDelete', $this, $comment); } } diff --git a/app/Services/Logic/Comment/CommentLike.php b/app/Services/Logic/Comment/CommentLike.php index 380a2b02..356265c4 100644 --- a/app/Services/Logic/Comment/CommentLike.php +++ b/app/Services/Logic/Comment/CommentLike.php @@ -7,6 +7,7 @@ use App\Models\CommentLike as CommentLikeModel; use App\Models\User as UserModel; use App\Repos\CommentLike as CommentLikeRepo; use App\Services\Logic\CommentTrait; +use App\Services\Logic\Notice\System\CommentLiked as CommentLikedNotice; use App\Services\Logic\Service as LogicService; use App\Validators\UserLimit as UserLimitValidator; @@ -42,6 +43,12 @@ class CommentLike extends LogicService $this->incrCommentLikeCount($comment); + $this->incrUserDailyCommentLikeCount($user); + + $this->handleLikeNotice($comment, $user); + + $this->eventsManager->fire('Comment:afterLike', $this, $comment); + } else { $action = 'undo'; @@ -49,9 +56,9 @@ class CommentLike extends LogicService $commentLike->delete(); $this->decrCommentLikeCount($comment); - } - $this->incrUserDailyCommentLikeCount($user); + $this->eventsManager->fire('Comment:afterUndoLike', $this, $comment); + } return [ 'action' => $action, @@ -66,6 +73,7 @@ class CommentLike extends LogicService $comment->update(); } + protected function decrCommentLikeCount(CommentModel $comment) { if ($comment->like_count > 0) { @@ -79,4 +87,11 @@ class CommentLike extends LogicService $this->eventsManager->fire('UserDailyCounter:incrCommentLikeCount', $this, $user); } + protected function handleLikeNotice(CommentModel $comment, UserModel $sender) + { + $notice = new CommentLikedNotice(); + + $notice->handle($comment, $sender); + } + } diff --git a/app/Services/Logic/Comment/CommentList.php b/app/Services/Logic/Comment/CommentList.php index b6e9f931..5ab878f5 100644 --- a/app/Services/Logic/Comment/CommentList.php +++ b/app/Services/Logic/Comment/CommentList.php @@ -3,6 +3,7 @@ namespace App\Services\Logic\Comment; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Comment as CommentModel; use App\Repos\Comment as CommentRepo; use App\Services\Logic\Service as LogicService; @@ -18,7 +19,8 @@ class CommentList extends LogicService $params = $pagerQuery->getParams(); $params['parent_id'] = 0; - $params['published'] = 1; + $params['published'] = CommentModel::PUBLISH_APPROVED; + $params['deleted'] = 0; $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); diff --git a/app/Services/Logic/Comment/CommentListTrait.php b/app/Services/Logic/Comment/CommentListTrait.php index bef84037..908b5d4f 100644 --- a/app/Services/Logic/Comment/CommentListTrait.php +++ b/app/Services/Logic/Comment/CommentListTrait.php @@ -37,6 +37,7 @@ trait CommentListTrait 'like_count' => $comment['like_count'], 'reply_count' => $comment['reply_count'], 'create_time' => $comment['create_time'], + 'update_time' => $comment['update_time'], 'to_user' => $toUser, 'owner' => $owner, 'me' => $me, diff --git a/app/Services/Logic/Comment/CommentReply.php b/app/Services/Logic/Comment/CommentReply.php index fc31d0fe..c21e91ab 100644 --- a/app/Services/Logic/Comment/CommentReply.php +++ b/app/Services/Logic/Comment/CommentReply.php @@ -3,9 +3,9 @@ namespace App\Services\Logic\Comment; use App\Models\Comment as CommentModel; -use App\Services\Logic\ArticleTrait; -use App\Services\Logic\ChapterTrait; use App\Services\Logic\CommentTrait; +use App\Services\Logic\Notice\System\CommentReplied as CommentRepliedNotice; +use App\Services\Logic\Point\History\CommentPost as CommentPostPointHistory; use App\Services\Logic\Service as LogicService; use App\Traits\Client as ClientTrait; use App\Validators\Comment as CommentValidator; @@ -14,8 +14,6 @@ use App\Validators\UserLimit as UserLimitValidator; class CommentReply extends LogicService { - use ArticleTrait; - use ChapterTrait; use ClientTrait; use CommentTrait; use CommentCountTrait; @@ -41,7 +39,6 @@ class CommentReply extends LogicService 'item_id' => $comment->item_id, 'item_type' => $comment->item_type, 'owner_id' => $user->id, - 'published' => 1, ]; if ($comment->parent_id > 0) { @@ -54,28 +51,46 @@ class CommentReply extends LogicService $data['client_type'] = $this->getClientType(); $data['client_ip'] = $this->getClientIp(); - $comment = new CommentModel(); + /** + * @todo 引入自动审核机制 + */ + $data['published'] = CommentModel::PUBLISH_APPROVED; - $comment->create($data); + $reply = new CommentModel(); - $this->incrCommentReplyCount($parent); + $reply->create($data); - if ($comment->item_type == CommentModel::ITEM_CHAPTER) { - - $chapter = $this->checkChapter($comment->item_id); - - $this->incrChapterCommentCount($chapter); - - } elseif ($comment->item_type == CommentModel::ITEM_ARTICLE) { - - $article = $this->checkArticle($comment->item_id); - - $this->incrArticleCommentCount($article); - } + $parent = $this->checkComment($reply->parent_id); $this->incrUserDailyCommentCount($user); - return $comment; + $this->incrCommentReplyCount($parent); + + $this->incrItemCommentCount($reply); + + $this->handlePostNotice($reply); + + $this->handlePostPoint($reply); + + $this->eventsManager->fire('Comment:afterReply', $this, $reply); + + return $reply; + } + + protected function handlePostNotice(CommentModel $comment) + { + $notice = new CommentRepliedNotice(); + + $notice->handle($comment); + } + + protected function handlePostPoint(CommentModel $comment) + { + if ($comment->published != CommentModel::PUBLISH_APPROVED) return; + + $service = new CommentPostPointHistory(); + + $service->handle($comment); } } diff --git a/app/Services/Logic/Comment/ReplyList.php b/app/Services/Logic/Comment/ReplyList.php index 8834bd95..eac5b67a 100644 --- a/app/Services/Logic/Comment/ReplyList.php +++ b/app/Services/Logic/Comment/ReplyList.php @@ -3,6 +3,7 @@ namespace App\Services\Logic\Comment; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Comment as CommentModel; use App\Repos\Comment as CommentRepo; use App\Services\Logic\CommentTrait; use App\Services\Logic\Service as LogicService; @@ -20,7 +21,8 @@ class ReplyList extends LogicService $params = $pagerQuery->getParams(); $params['parent_id'] = $id; - $params['published'] = 1; + $params['published'] = CommentModel::PUBLISH_APPROVED; + $params['deleted'] = 0; $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); @@ -30,7 +32,7 @@ class ReplyList extends LogicService $pager = $commentRepo->paginate($params, $sort, $page, $limit); - return $this->handlePager($pager); + return $this->handleComments($pager); } } diff --git a/app/Services/Logic/Consult/ConsultCreate.php b/app/Services/Logic/Consult/ConsultCreate.php index 4db3c504..ff6d8260 100644 --- a/app/Services/Logic/Consult/ConsultCreate.php +++ b/app/Services/Logic/Consult/ConsultCreate.php @@ -10,14 +10,16 @@ use App\Services\Logic\ChapterTrait; use App\Services\Logic\CourseTrait; use App\Services\Logic\Notice\DingTalk\ConsultCreate as ConsultCreateNotice; use App\Services\Logic\Service as LogicService; +use App\Traits\Client as ClientTrait; use App\Validators\Consult as ConsultValidator; use App\Validators\UserLimit as UserLimitValidator; class ConsultCreate extends LogicService { - use CourseTrait; use ChapterTrait; + use ClientTrait; + use CourseTrait; public function handle() { @@ -40,7 +42,7 @@ class ConsultCreate extends LogicService return $this->handleChapterConsult($chapter, $user); - } else { + } elseif ($courseId > 0) { $course = $this->checkCourse($courseId); @@ -68,6 +70,8 @@ class ConsultCreate extends LogicService $consult->priority = $priority; $consult->course_id = $course->id; $consult->owner_id = $user->id; + $consult->client_type = $this->getClientType(); + $consult->client_ip = $this->getClientIp(); $consult->published = 1; $consult->create(); @@ -78,6 +82,8 @@ class ConsultCreate extends LogicService $this->handleConsultCreateNotice($consult); + $this->eventsManager->fire('Consult:afterCreate', $this, $consult); + return $consult; } @@ -104,6 +110,8 @@ class ConsultCreate extends LogicService $consult->course_id = $course->id; $consult->chapter_id = $chapter->id; $consult->owner_id = $user->id; + $consult->client_type = $this->getClientType(); + $consult->client_ip = $this->getClientIp(); $consult->published = 1; $consult->create(); @@ -116,6 +124,8 @@ class ConsultCreate extends LogicService $this->handleConsultCreateNotice($consult); + $this->eventsManager->fire('Consult:afterCreate', $this, $consult); + return $consult; } diff --git a/app/Services/Logic/Consult/ConsultDelete.php b/app/Services/Logic/Consult/ConsultDelete.php index 8529f3b3..26aaee0a 100644 --- a/app/Services/Logic/Consult/ConsultDelete.php +++ b/app/Services/Logic/Consult/ConsultDelete.php @@ -27,7 +27,9 @@ class ConsultDelete extends LogicService $validator->checkOwner($user->id, $consult->owner_id); - $consult->update(['deleted' => 1]); + $consult->deleted = 1; + + $consult->update(); if ($consult->course_id > 0) { @@ -42,6 +44,8 @@ class ConsultDelete extends LogicService $this->decrChapterConsultCount($chapter); } + + $this->eventsManager->fire('Consult:afterDelete', $this, $consult); } protected function decrCourseConsultCount(CourseModel $course) diff --git a/app/Services/Logic/Consult/ConsultLike.php b/app/Services/Logic/Consult/ConsultLike.php index 5cf737ab..636eb623 100644 --- a/app/Services/Logic/Consult/ConsultLike.php +++ b/app/Services/Logic/Consult/ConsultLike.php @@ -7,6 +7,7 @@ use App\Models\ConsultLike as ConsultLikeModel; use App\Models\User as UserModel; use App\Repos\ConsultLike as ConsultLikeRepo; use App\Services\Logic\ConsultTrait; +use App\Services\Logic\Notice\System\ConsultLiked as ConsultLikedNotice; use App\Services\Logic\Service as LogicService; use App\Validators\UserLimit as UserLimitValidator; @@ -42,6 +43,10 @@ class ConsultLike extends LogicService $this->incrConsultLikeCount($consult); + $this->handleLikeNotice($consult, $user); + + $this->eventsManager->fire('Consult:afterLike', $this, $consult); + } else { $action = 'undo'; @@ -49,6 +54,8 @@ class ConsultLike extends LogicService $consultLike->delete(); $this->decrConsultLikeCount($consult); + + $this->eventsManager->fire('Consult:afterUndoLike', $this, $consult); } $this->incrUserDailyConsultLikeCount($user); @@ -79,4 +86,11 @@ class ConsultLike extends LogicService $this->eventsManager->fire('UserDailyCounter:incrConsultLikeCount', $this, $user); } + protected function handleLikeNotice(ConsultModel $consult, UserModel $sender) + { + $notice = new ConsultLikedNotice(); + + $notice->handle($consult, $sender); + } + } diff --git a/app/Services/Logic/Consult/ConsultReply.php b/app/Services/Logic/Consult/ConsultReply.php index 25e88371..aaf2a2aa 100644 --- a/app/Services/Logic/Consult/ConsultReply.php +++ b/app/Services/Logic/Consult/ConsultReply.php @@ -42,6 +42,8 @@ class ConsultReply extends LogicService $this->handleReplyNotice($consult); } + $this->eventsManager->fire('Consult:afterReply', $this, $consult); + return $consult; } diff --git a/app/Services/Logic/Consult/ConsultUpdate.php b/app/Services/Logic/Consult/ConsultUpdate.php index 13e39837..f87bdf01 100644 --- a/app/Services/Logic/Consult/ConsultUpdate.php +++ b/app/Services/Logic/Consult/ConsultUpdate.php @@ -35,6 +35,8 @@ class ConsultUpdate extends LogicService $consult->update($data); + $this->eventsManager->fire('Consult:afterUpdate', $this, $consult); + return $consult; } diff --git a/app/Services/Logic/Course/ConsultListTrait.php b/app/Services/Logic/Course/ConsultListTrait.php index 2ea63229..d6d97699 100644 --- a/app/Services/Logic/Course/ConsultListTrait.php +++ b/app/Services/Logic/Course/ConsultListTrait.php @@ -36,6 +36,7 @@ trait ConsultListTrait 'like_count' => $consult['like_count'], 'reply_time' => $consult['reply_time'], 'create_time' => $consult['create_time'], + 'update_time' => $consult['update_time'], 'owner' => $owner, 'me' => $me, ]; diff --git a/app/Services/Logic/Course/ReviewList.php b/app/Services/Logic/Course/ReviewList.php index 5c198a19..d3979015 100644 --- a/app/Services/Logic/Course/ReviewList.php +++ b/app/Services/Logic/Course/ReviewList.php @@ -63,6 +63,7 @@ class ReviewList extends LogicService 'content' => $review['content'], 'like_count' => $review['like_count'], 'create_time' => $review['create_time'], + 'update_time' => $review['update_time'], 'owner' => $owner, 'me' => $me, ]; diff --git a/app/Services/Logic/Notice/System/ArticleApproved.php b/app/Services/Logic/Notice/System/ArticleApproved.php new file mode 100644 index 00000000..20975445 --- /dev/null +++ b/app/Services/Logic/Notice/System/ArticleApproved.php @@ -0,0 +1,28 @@ +sender_id = $sender->id; + $notification->receiver_id = $article->owner_id; + $notification->event_id = $article->id; + $notification->event_type = NotificationModel::TYPE_ARTICLE_APPROVED; + $notification->event_info = [ + 'article' => ['id' => $article->id, 'title' => $article->title], + ]; + + $notification->create(); + } + +} diff --git a/app/Services/Logic/Notice/System/ArticleCommented.php b/app/Services/Logic/Notice/System/ArticleCommented.php new file mode 100644 index 00000000..4bf1b412 --- /dev/null +++ b/app/Services/Logic/Notice/System/ArticleCommented.php @@ -0,0 +1,48 @@ +content = kg_substr($comment->content, 0, 32); + + $article = $this->findArticle($comment->item_id); + + $notification = new NotificationModel(); + + $notification->sender_id = $comment->owner_id; + $notification->receiver_id = $article->owner_id; + $notification->event_id = $comment->id; + $notification->event_type = NotificationModel::TYPE_ARTICLE_COMMENTED; + $notification->event_info = [ + 'article' => ['id' => $article->id, 'title' => $article->title], + 'comment' => ['id' => $comment->id, 'content' => $comment->content], + ]; + + $notification->create(); + } + + protected function findArticle($id) + { + $articleRepo = new ArticleRepo(); + + return $articleRepo->findById($id); + } + + protected function findUser($id) + { + $userRepo = new UserRepo(); + + return $userRepo->findById($id); + } + +} diff --git a/app/Services/Logic/Notice/System/ArticleDeleted.php b/app/Services/Logic/Notice/System/ArticleDeleted.php new file mode 100644 index 00000000..29e0077f --- /dev/null +++ b/app/Services/Logic/Notice/System/ArticleDeleted.php @@ -0,0 +1,29 @@ +sender_id = $sender->id; + $notification->receiver_id = $article->owner_id; + $notification->event_id = $article->id; + $notification->event_type = NotificationModel::TYPE_ARTICLE_DELETED; + $notification->event_info = [ + 'article' => ['id' => $article->id, 'title' => $article->title], + 'reason' => $reason ?: '', + ]; + + $notification->create(); + } + +} diff --git a/app/Services/Logic/Notice/System/ArticleFavorited.php b/app/Services/Logic/Notice/System/ArticleFavorited.php new file mode 100644 index 00000000..0e677a5d --- /dev/null +++ b/app/Services/Logic/Notice/System/ArticleFavorited.php @@ -0,0 +1,28 @@ +sender_id = $sender->id; + $notification->receiver_id = $article->owner_id; + $notification->event_id = $article->id; + $notification->event_type = NotificationModel::TYPE_ARTICLE_FAVORITED; + $notification->event_info = [ + 'article' => ['id' => $article->id, 'title' => $article->title], + ]; + + $notification->create(); + } + +} diff --git a/app/Services/Logic/Notice/System/ArticleLiked.php b/app/Services/Logic/Notice/System/ArticleLiked.php new file mode 100644 index 00000000..1c903134 --- /dev/null +++ b/app/Services/Logic/Notice/System/ArticleLiked.php @@ -0,0 +1,28 @@ +sender_id = $sender->id; + $notification->receiver_id = $article->owner_id; + $notification->event_id = $article->id; + $notification->event_type = NotificationModel::TYPE_ARTICLE_LIKED; + $notification->event_info = [ + 'article' => ['id' => $article->id, 'title' => $article->title], + ]; + + $notification->create(); + } + +} diff --git a/app/Services/Logic/Notice/System/ArticleRejected.php b/app/Services/Logic/Notice/System/ArticleRejected.php new file mode 100644 index 00000000..de5b15ca --- /dev/null +++ b/app/Services/Logic/Notice/System/ArticleRejected.php @@ -0,0 +1,29 @@ +sender_id = $sender->id; + $notification->receiver_id = $article->owner_id; + $notification->event_id = $article->id; + $notification->event_type = NotificationModel::TYPE_ARTICLE_REJECTED; + $notification->event_info = [ + 'article' => ['id' => $article->id, 'title' => $article->title], + 'reason' => $reason ?: '', + ]; + + $notification->create(); + } + +} diff --git a/app/Services/Logic/Notice/System/ChapterCommented.php b/app/Services/Logic/Notice/System/ChapterCommented.php new file mode 100644 index 00000000..84eaf2d0 --- /dev/null +++ b/app/Services/Logic/Notice/System/ChapterCommented.php @@ -0,0 +1,16 @@ +content = kg_substr($comment->content, 0, 32); + + $notification = new NotificationModel(); + + $notification->sender_id = $sender->id; + $notification->receiver_id = $comment->owner_id; + $notification->event_id = $comment->id; + $notification->event_type = NotificationModel::TYPE_COMMENT_LIKED; + $notification->event_info = [ + 'sender' => ['id' => $sender->id, 'name' => $sender->name], + 'comment' => ['id' => $comment->id, 'content' => $comment->content], + ]; + + $notification->create(); + } + +} diff --git a/app/Services/Logic/Notice/System/CommentReplied.php b/app/Services/Logic/Notice/System/CommentReplied.php new file mode 100644 index 00000000..3c41f672 --- /dev/null +++ b/app/Services/Logic/Notice/System/CommentReplied.php @@ -0,0 +1,50 @@ +content = kg_substr($reply->content, 0, 32); + + $comment = $this->findComment($reply->parent_id); + + $comment->content = kg_substr($comment->content, 0, 32); + + $notification = new NotificationModel(); + + $notification->sender_id = $reply->owner_id; + $notification->receiver_id = $comment->owner_id; + $notification->event_id = $reply->id; + $notification->event_type = NotificationModel::TYPE_COMMENT_REPLIED; + $notification->event_info = [ + 'comment' => ['id' => $comment->id, 'content' => $comment->content], + 'reply' => ['id' => $reply->id, 'content' => $reply->content], + ]; + + $notification->create(); + } + + protected function findComment($id) + { + $commentRepo = new CommentRepo(); + + return $commentRepo->findById($id); + } + + protected function findUser($id) + { + $userRepo = new UserRepo(); + + return $userRepo->findById($id); + } + +} diff --git a/app/Services/Logic/Notice/System/ConsultLiked.php b/app/Services/Logic/Notice/System/ConsultLiked.php new file mode 100644 index 00000000..759d9661 --- /dev/null +++ b/app/Services/Logic/Notice/System/ConsultLiked.php @@ -0,0 +1,41 @@ +question = kg_substr($consult->question, 0, 32); + + $course = $this->findCourse($consult->course_id); + + $notification = new NotificationModel(); + + $notification->sender_id = $sender->id; + $notification->receiver_id = $consult->owner_id; + $notification->event_id = $consult->id; + $notification->event_type = NotificationModel::TYPE_CONSULT_LIKED; + $notification->event_info = [ + 'course' => ['id' => $course->id, 'title' => $course->title], + 'consult' => ['id' => $consult->id, 'question' => $consult->question], + ]; + + $notification->create(); + } + + protected function findCourse($id) + { + $courseRepo = new CourseRepo(); + + return $courseRepo->findById($id); + } + +} diff --git a/app/Services/Logic/Notice/System/ReviewLiked.php b/app/Services/Logic/Notice/System/ReviewLiked.php new file mode 100644 index 00000000..9de50536 --- /dev/null +++ b/app/Services/Logic/Notice/System/ReviewLiked.php @@ -0,0 +1,41 @@ +content = kg_substr($review->content, 0, 32); + + $course = $this->findCourse($review->course_id); + + $notification = new NotificationModel(); + + $notification->sender_id = $sender->id; + $notification->receiver_id = $review->owner_id; + $notification->event_id = $review->id; + $notification->event_type = NotificationModel::TYPE_REVIEW_LIKED; + $notification->event_info = [ + 'course' => ['id' => $course->id, 'title' => $course->title], + 'review' => ['id' => $review->id, 'content' => $review->content], + ]; + + $notification->create(); + } + + protected function findCourse($id) + { + $courseRepo = new CourseRepo(); + + return $courseRepo->findById($id); + } + +} diff --git a/app/Services/Logic/Point/History/AccountRegister.php b/app/Services/Logic/Point/History/AccountRegister.php new file mode 100644 index 00000000..c027b493 --- /dev/null +++ b/app/Services/Logic/Point/History/AccountRegister.php @@ -0,0 +1,53 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['account_register']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['account_register']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $eventId = $user->id; + $eventType = PointHistoryModel::EVENT_ACCOUNT_REGISTER; + $eventInfo = new \stdClass(); + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $user->id; + $history->event_type = $eventType; + $history->event_point = $eventPoint; + $history->event_info = $eventInfo; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/ArticlePost.php b/app/Services/Logic/Point/History/ArticlePost.php new file mode 100644 index 00000000..f0fb3d69 --- /dev/null +++ b/app/Services/Logic/Point/History/ArticlePost.php @@ -0,0 +1,75 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['article_post']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['article_post']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $dailyPointLimit = $eventRule['article_post']['limit'] ?? 0; + + if ($dailyPointLimit <= 0) return; + + $eventId = $article->id; + $eventType = PointHistoryModel::EVENT_ARTICLE_POST; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + /** + * @todo 使用缓存优化 + */ + $dailyPoints = $historyRepo->sumUserDailyEventPoints($article->owner_id, $eventType, date('Ymd')); + + if ($dailyPoints >= $dailyPointLimit) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($article->owner_id); + + $eventInfo = [ + 'article' => [ + 'id' => $article->id, + 'title' => $article->title, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_info = $eventInfo; + $history->event_point = $eventPoint; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/ChapterStudy.php b/app/Services/Logic/Point/History/ChapterStudy.php new file mode 100644 index 00000000..e4927f83 --- /dev/null +++ b/app/Services/Logic/Point/History/ChapterStudy.php @@ -0,0 +1,78 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['chapter_study']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['chapter_study']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $eventId = $chapterUser->id; + $eventType = PointHistoryModel::EVENT_CHAPTER_STUDY; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($chapterUser->user_id); + + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($chapterUser->course_id); + + $chapterRepo = new ChapterRepo(); + + $chapter = $chapterRepo->findById($chapterUser->chapter_id); + + $eventInfo = [ + 'course' => [ + 'id' => $course->id, + 'title' => $course->title, + ], + 'chapter' => [ + 'id' => $chapter->id, + 'title' => $chapter->title, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_info = $eventInfo; + $history->event_point = $eventPoint; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/CommentPost.php b/app/Services/Logic/Point/History/CommentPost.php new file mode 100644 index 00000000..a193485b --- /dev/null +++ b/app/Services/Logic/Point/History/CommentPost.php @@ -0,0 +1,75 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['comment_post']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['comment_post']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $dailyPointLimit = $eventRule['comment_post']['limit'] ?? 0; + + if ($dailyPointLimit <= 0) return; + + $eventId = $comment->id; + $eventType = PointHistoryModel::EVENT_COMMENT_POST; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + /** + * @todo 使用缓存优化 + */ + $dailyPoints = $historyRepo->sumUserDailyEventPoints($comment->owner_id, $eventType, date('Ymd')); + + if ($dailyPoints >= $dailyPointLimit) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($comment->owner_id); + + $eventInfo = [ + 'comment' => [ + 'id' => $comment->id, + 'content' => $comment->content, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_info = $eventInfo; + $history->event_point = $eventPoint; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/CourseReview.php b/app/Services/Logic/Point/History/CourseReview.php new file mode 100644 index 00000000..8a888d35 --- /dev/null +++ b/app/Services/Logic/Point/History/CourseReview.php @@ -0,0 +1,69 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['course_review']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['course_review']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $eventId = $review->id; + $eventType = PointHistoryModel::EVENT_COURSE_REVIEW; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($review->owner_id); + + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($review->course_id); + + $eventInfo = [ + 'course' => [ + 'id' => $course->id, + 'title' => $course->title, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_info = $eventInfo; + $history->event_point = $eventPoint; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/ImDiscuss.php b/app/Services/Logic/Point/History/ImDiscuss.php new file mode 100644 index 00000000..e164609f --- /dev/null +++ b/app/Services/Logic/Point/History/ImDiscuss.php @@ -0,0 +1,58 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['im_discuss']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['im_discuss']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $eventId = $message->sender_id; + $eventType = PointHistoryModel::EVENT_IM_DISCUSS; + $eventInfo = new \stdClass(); + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findDailyEventHistory($eventId, $eventType, date('Ymd')); + + if ($history) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($message->sender_id); + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_info = $eventInfo; + $history->event_point = $eventPoint; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/OrderConsume.php b/app/Services/Logic/Point/History/OrderConsume.php new file mode 100644 index 00000000..5ea8e308 --- /dev/null +++ b/app/Services/Logic/Point/History/OrderConsume.php @@ -0,0 +1,64 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $ruleEnabled = $setting['consume_rule']['enabled'] ?? 0; + + if ($ruleEnabled == 0) return; + + $ruleRate = $setting['consume_rule']['rate'] ?? 0; + + if ($ruleRate <= 0) return; + + $eventId = $order->id; + $eventType = PointHistoryModel::EVENT_ORDER_CONSUME; + $eventPoint = $ruleRate * $order->amount; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($order->owner_id); + + $eventInfo = [ + 'order' => [ + 'sn' => $order->sn, + 'subject' => $order->subject, + 'amount' => $order->amount, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_point = $eventPoint; + $history->event_info = $eventInfo; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/PointRedeem.php b/app/Services/Logic/Point/History/PointRedeem.php new file mode 100644 index 00000000..2ade251c --- /dev/null +++ b/app/Services/Logic/Point/History/PointRedeem.php @@ -0,0 +1,58 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventId = $redeem->id; + $eventType = PointHistoryModel::EVENT_POINT_REDEEM; + $eventPoint = 0 - $redeem->gift_point; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($redeem->user_id); + + $eventInfo = [ + 'point_redeem' => [ + 'id' => $redeem->id, + 'gift_id' => $redeem->gift_id, + 'gift_name' => $redeem->gift_name, + 'gift_type' => $redeem->gift_type, + 'gift_point' => $redeem->gift_point, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_point = $eventPoint; + $history->event_info = $eventInfo; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/PointRefund.php b/app/Services/Logic/Point/History/PointRefund.php new file mode 100644 index 00000000..7369400d --- /dev/null +++ b/app/Services/Logic/Point/History/PointRefund.php @@ -0,0 +1,52 @@ +id; + $eventType = PointHistoryModel::EVENT_POINT_REFUND; + $eventPoint = $redeem->gift_point; + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findEventHistory($eventId, $eventType); + + if ($history) return; + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($redeem->user_id); + + $eventInfo = [ + 'point_redeem' => [ + 'id' => $redeem->id, + 'gift_id' => $redeem->gift_id, + 'gift_name' => $redeem->gift_name, + 'gift_type' => $redeem->gift_type, + 'gift_point' => $redeem->gift_point, + ] + ]; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_point = $eventPoint; + $history->event_info = $eventInfo; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/History/SiteVisit.php b/app/Services/Logic/Point/History/SiteVisit.php new file mode 100644 index 00000000..e3391022 --- /dev/null +++ b/app/Services/Logic/Point/History/SiteVisit.php @@ -0,0 +1,53 @@ +getSettings('point'); + + $pointEnabled = $setting['enabled'] ?? 0; + + if ($pointEnabled == 0) return; + + $eventRule = json_decode($setting['event_rule'], true); + + $eventEnabled = $eventRule['site_visit']['enabled'] ?? 0; + + if ($eventEnabled == 0) return; + + $eventPoint = $eventRule['site_visit']['point'] ?? 0; + + if ($eventPoint <= 0) return; + + $eventId = $user->id; + $eventType = PointHistoryModel::EVENT_SITE_VISIT; + $eventInfo = new \stdClass(); + + $historyRepo = new PointHistoryRepo(); + + $history = $historyRepo->findDailyEventHistory($eventId, $eventType, date('Ymd')); + + if ($history) return; + + $history = new PointHistoryModel(); + + $history->user_id = $user->id; + $history->user_name = $user->name; + $history->event_id = $eventId; + $history->event_type = $eventType; + $history->event_point = $eventPoint; + $history->event_info = $eventInfo; + + $this->handlePointHistory($history); + } + +} diff --git a/app/Services/Logic/Point/PointHistory.php b/app/Services/Logic/Point/PointHistory.php index f3d351f8..7d74fef7 100644 --- a/app/Services/Logic/Point/PointHistory.php +++ b/app/Services/Logic/Point/PointHistory.php @@ -2,395 +2,14 @@ namespace App\Services\Logic\Point; -use App\Models\ChapterUser as ChapterUserModel; -use App\Models\ImMessage as ImMessageModel; -use App\Models\Order as OrderModel; use App\Models\PointHistory as PointHistoryModel; -use App\Models\PointRedeem as PointRedeemModel; -use App\Models\Review as ReviewModel; -use App\Models\User as UserModel; use App\Models\UserBalance as UserBalanceModel; -use App\Repos\Chapter as ChapterRepo; -use App\Repos\Course as CourseRepo; -use App\Repos\PointHistory as PointHistoryRepo; use App\Repos\User as UserRepo; use App\Services\Logic\Service as LogicService; class PointHistory extends LogicService { - public function handleOrderConsume(OrderModel $order) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $ruleEnabled = $setting['consume_rule']['enabled'] ?? 0; - - if ($ruleEnabled == 0) return; - - $ruleRate = $setting['consume_rule']['rate'] ?? 0; - - if ($ruleRate <= 0) return; - - $eventId = $order->id; - $eventType = PointHistoryModel::EVENT_ORDER_CONSUME; - $eventPoint = $ruleRate * $order->amount; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findEventHistory($eventId, $eventType); - - if ($history) return; - - $userRepo = new UserRepo(); - - $user = $userRepo->findById($order->owner_id); - - $eventInfo = [ - 'order' => [ - 'sn' => $order->sn, - 'subject' => $order->subject, - 'amount' => $order->amount, - ] - ]; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_point = $eventPoint; - $history->event_info = $eventInfo; - - $this->handlePointHistory($history); - } - - public function handlePointRedeem(PointRedeemModel $redeem) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $eventId = $redeem->id; - $eventType = PointHistoryModel::EVENT_POINT_REDEEM; - $eventPoint = 0 - $redeem->gift_point; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findEventHistory($eventId, $eventType); - - if ($history) return; - - $userRepo = new UserRepo(); - - $user = $userRepo->findById($redeem->user_id); - - $eventInfo = [ - 'point_redeem' => [ - 'id' => $redeem->id, - 'gift_id' => $redeem->gift_id, - 'gift_name' => $redeem->gift_name, - 'gift_type' => $redeem->gift_type, - 'gift_point' => $redeem->gift_point, - ] - ]; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_point = $eventPoint; - $history->event_info = $eventInfo; - - $this->handlePointHistory($history); - } - - public function handlePointRefund(PointRedeemModel $redeem) - { - $eventId = $redeem->id; - $eventType = PointHistoryModel::EVENT_POINT_REFUND; - $eventPoint = $redeem->gift_point; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findEventHistory($eventId, $eventType); - - if ($history) return; - - $userRepo = new UserRepo(); - - $user = $userRepo->findById($redeem->user_id); - - $eventInfo = [ - 'point_redeem' => [ - 'id' => $redeem->id, - 'gift_id' => $redeem->gift_id, - 'gift_name' => $redeem->gift_name, - 'gift_type' => $redeem->gift_type, - 'gift_point' => $redeem->gift_point, - ] - ]; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_point = $eventPoint; - $history->event_info = $eventInfo; - - $this->handlePointHistory($history); - } - - public function handleSiteVisit(UserModel $user) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $eventRule = json_decode($setting['event_rule'], true); - - $eventEnabled = $eventRule['site_visit']['enabled'] ?? 0; - - if ($eventEnabled == 0) return; - - $eventPoint = $eventRule['site_visit']['point'] ?? 0; - - if ($eventPoint <= 0) return; - - $eventId = $user->id; - $eventType = PointHistoryModel::EVENT_SITE_VISIT; - $eventInfo = '每日访问'; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findDailyEventHistory($eventId, $eventType, date('Ymd')); - - if ($history) return; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_point = $eventPoint; - $history->event_info = $eventInfo; - - $this->handlePointHistory($history); - } - - public function handleAccountRegister(UserModel $user) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $eventRule = json_decode($setting['event_rule'], true); - - $eventEnabled = $eventRule['account_register']['enabled'] ?? 0; - - if ($eventEnabled == 0) return; - - $eventPoint = $eventRule['account_register']['point'] ?? 0; - - if ($eventPoint <= 0) return; - - $eventId = $user->id; - $eventType = PointHistoryModel::EVENT_ACCOUNT_REGISTER; - $eventInfo = '帐号注册'; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findDailyEventHistory($eventId, $eventType, date('Ymd')); - - if ($history) return; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $user->id; - $history->event_type = $eventType; - $history->event_point = $eventPoint; - $history->event_info = $eventInfo; - - $this->handlePointHistory($history); - } - - public function handleChapterStudy(ChapterUserModel $chapterUser) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $eventRule = json_decode($setting['event_rule'], true); - - $eventEnabled = $eventRule['chapter_study']['enabled'] ?? 0; - - if ($eventEnabled == 0) return; - - $eventPoint = $eventRule['chapter_study']['point'] ?? 0; - - if ($eventPoint <= 0) return; - - $eventId = $chapterUser->id; - $eventType = PointHistoryModel::EVENT_CHAPTER_STUDY; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findEventHistory($eventId, $eventType); - - if ($history) return; - - $userRepo = new UserRepo(); - - $user = $userRepo->findById($chapterUser->user_id); - - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($chapterUser->course_id); - - $chapterRepo = new ChapterRepo(); - - $chapter = $chapterRepo->findById($chapterUser->chapter_id); - - $eventInfo = [ - 'course' => [ - 'id' => $course->id, - 'title' => $course->title, - ], - 'chapter' => [ - 'id' => $chapter->id, - 'title' => $chapter->title, - ] - ]; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_info = $eventInfo; - $history->event_point = $eventPoint; - - $this->handlePointHistory($history); - } - - public function handleCourseReview(ReviewModel $review) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $eventRule = json_decode($setting['event_rule'], true); - - $eventEnabled = $eventRule['course_review']['enabled'] ?? 0; - - if ($eventEnabled == 0) return; - - $eventPoint = $eventRule['course_review']['point'] ?? 0; - - if ($eventPoint <= 0) return; - - $eventId = $review->id; - $eventType = PointHistoryModel::EVENT_COURSE_REVIEW; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findEventHistory($eventId, $eventType); - - if ($history) return; - - $userRepo = new UserRepo(); - - $user = $userRepo->findById($review->owner_id); - - $courseRepo = new CourseRepo(); - - $course = $courseRepo->findById($review->course_id); - - $eventInfo = [ - 'course' => [ - 'id' => $course->id, - 'title' => $course->title, - ] - ]; - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_info = $eventInfo; - $history->event_point = $eventPoint; - - $this->handlePointHistory($history); - } - - public function handleImDiscuss(ImMessageModel $message) - { - $setting = $this->getSettings('point'); - - $pointEnabled = $setting['enabled'] ?? 0; - - if ($pointEnabled == 0) return; - - $eventRule = json_decode($setting['event_rule'], true); - - $eventEnabled = $eventRule['im_discuss']['enabled'] ?? 0; - - if ($eventEnabled == 0) return; - - $eventPoint = $eventRule['im_discuss']['point'] ?? 0; - - if ($eventPoint <= 0) return; - - $eventId = $message->sender_id; - $eventType = PointHistoryModel::EVENT_IM_DISCUSS; - $eventInfo = '每日微聊'; - - $historyRepo = new PointHistoryRepo(); - - $history = $historyRepo->findDailyEventHistory($eventId, $eventType, date('Ymd')); - - if ($history) return; - - $userRepo = new UserRepo(); - - $user = $userRepo->findById($message->sender_id); - - $history = new PointHistoryModel(); - - $history->user_id = $user->id; - $history->user_name = $user->name; - $history->event_id = $eventId; - $history->event_type = $eventType; - $history->event_info = $eventInfo; - $history->event_point = $eventPoint; - - $this->handlePointHistory($history); - } - protected function handlePointHistory(PointHistoryModel $history) { $logger = $this->getLogger('point'); diff --git a/app/Services/Logic/Point/PointRedeem.php b/app/Services/Logic/Point/PointRedeem.php index 5e0f54be..fdc7c822 100644 --- a/app/Services/Logic/Point/PointRedeem.php +++ b/app/Services/Logic/Point/PointRedeem.php @@ -8,7 +8,7 @@ use App\Models\PointRedeem as PointRedeemModel; use App\Models\Task as TaskModel; use App\Models\User as UserModel; use App\Repos\User as UserRepo; -use App\Services\Logic\Point\PointHistory as PointHistoryService; +use App\Services\Logic\Point\History\PointRedeem as PointRedeemPointHistory; use App\Services\Logic\PointGiftTrait; use App\Services\Logic\Service as LogicService; use App\Validators\PointRedeem as PointRedeemValidator; @@ -123,9 +123,9 @@ class PointRedeem extends LogicService protected function handleRedeemPoint(PointRedeemModel $redeem) { - $service = new PointHistoryService(); + $service = new PointRedeemPointHistory(); - $service->handlePointRedeem($redeem); + $service->handle($redeem); } } diff --git a/app/Services/Logic/Review/ReviewCreate.php b/app/Services/Logic/Review/ReviewCreate.php index b564f485..3cd6090e 100644 --- a/app/Services/Logic/Review/ReviewCreate.php +++ b/app/Services/Logic/Review/ReviewCreate.php @@ -7,14 +7,17 @@ use App\Models\CourseUser as CourseUserModel; use App\Models\Review as ReviewModel; use App\Services\CourseStat as CourseStatService; use App\Services\Logic\CourseTrait; +use App\Services\Logic\Point\History\CourseReview as CourseReviewPointHistory; use App\Services\Logic\ReviewTrait; use App\Services\Logic\Service as LogicService; +use App\Traits\Client as ClientTrait; use App\Validators\CourseUser as CourseUserValidator; use App\Validators\Review as ReviewValidator; class ReviewCreate extends LogicService { + use ClientTrait; use CourseTrait; use ReviewTrait; @@ -35,6 +38,8 @@ class ReviewCreate extends LogicService $validator = new ReviewValidator(); $data = [ + 'client_type' => $this->getClientType(), + 'client_ip' => $this->getClientIp(), 'course_id' => $course->id, 'owner_id' => $user->id, ]; @@ -54,6 +59,8 @@ class ReviewCreate extends LogicService $this->updateCourseRating($course); + $this->handleReviewPoint($review); + $this->eventsManager->fire('Review:afterCreate', $this, $review); return $review; @@ -73,11 +80,18 @@ class ReviewCreate extends LogicService $course->update(); } - public function updateCourseRating(CourseModel $course) + protected function updateCourseRating(CourseModel $course) { $service = new CourseStatService(); $service->updateRating($course->id); } + protected function handleReviewPoint(ReviewModel $review) + { + $service = new CourseReviewPointHistory(); + + $service->handle($review); + } + } diff --git a/app/Services/Logic/Review/ReviewDelete.php b/app/Services/Logic/Review/ReviewDelete.php index d314e35f..7553eb36 100644 --- a/app/Services/Logic/Review/ReviewDelete.php +++ b/app/Services/Logic/Review/ReviewDelete.php @@ -27,11 +27,15 @@ class ReviewDelete extends LogicService $validator->checkOwner($user->id, $review->owner_id); - $review->update(['deleted' => 1]); + $review->deleted = 1; + + $review->update(); $this->decrCourseReviewCount($course); $this->updateCourseRating($course); + + $this->eventsManager->fire('Review:afterDelete', $this, $review); } protected function decrCourseReviewCount(CourseModel $course) diff --git a/app/Services/Logic/Review/ReviewLike.php b/app/Services/Logic/Review/ReviewLike.php index 43869bc8..36d1f334 100644 --- a/app/Services/Logic/Review/ReviewLike.php +++ b/app/Services/Logic/Review/ReviewLike.php @@ -6,6 +6,7 @@ use App\Models\Review as ReviewModel; use App\Models\ReviewLike as ReviewLikeModel; use App\Models\User as UserModel; use App\Repos\ReviewLike as ReviewLikeRepo; +use App\Services\Logic\Notice\System\ReviewLiked as ReviewLikedNotice; use App\Services\Logic\ReviewTrait; use App\Services\Logic\Service as LogicService; use App\Validators\UserLimit as UserLimitValidator; @@ -42,6 +43,10 @@ class ReviewLike extends LogicService $this->incrReviewLikeCount($review); + $this->handleLikeNotice($review, $user); + + $this->eventsManager->fire('Review:afterLike', $this, $review); + } else { $action = 'undo'; @@ -49,6 +54,8 @@ class ReviewLike extends LogicService $reviewLike->delete(); $this->decrReviewLikeCount($review); + + $this->eventsManager->fire('Review:afterUndoLike', $this, $review); } $this->incrUserDailyReviewLikeCount($user); @@ -79,4 +86,11 @@ class ReviewLike extends LogicService $this->eventsManager->fire('UserDailyCounter:incrReviewLikeCount', $this, $user); } + protected function handleLikeNotice(ReviewModel $review, UserModel $sender) + { + $notice = new ReviewLikedNotice(); + + $notice->handle($review, $sender); + } + } diff --git a/app/Services/Logic/User/ArticleList.php b/app/Services/Logic/User/ArticleList.php index 0e856e1a..ad596aa8 100644 --- a/app/Services/Logic/User/ArticleList.php +++ b/app/Services/Logic/User/ArticleList.php @@ -3,6 +3,7 @@ namespace App\Services\Logic\User; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Article as ArticleModel; use App\Repos\Article as ArticleRepo; use App\Services\Logic\Article\ArticleList as ArticleListService; use App\Services\Logic\Service as LogicService; @@ -22,7 +23,9 @@ class ArticleList extends LogicService $params = $pagerQuery->getParams(); $params['owner_id'] = $user->id; - $params['published'] = 1; + $params['published'] = ArticleModel::PUBLISH_APPROVED; + $params['private'] = 0; + $params['deleted'] = 0; $sort = $pagerQuery->getSort(); $page = $pagerQuery->getPage(); diff --git a/app/Services/Logic/User/Console/ArticleList.php b/app/Services/Logic/User/Console/ArticleList.php index a5ee2064..77522b4b 100644 --- a/app/Services/Logic/User/Console/ArticleList.php +++ b/app/Services/Logic/User/Console/ArticleList.php @@ -2,8 +2,10 @@ namespace App\Services\Logic\User\Console; +use App\Library\Paginator\Query as PagerQuery; +use App\Repos\Article as ArticleRepo; +use App\Services\Logic\Article\ArticleList as ArticleListService; use App\Services\Logic\Service as LogicService; -use App\Services\Logic\User\ArticleList as UserArticleListService; class ArticleList extends LogicService { @@ -12,9 +14,29 @@ class ArticleList extends LogicService { $user = $this->getLoginUser(); - $service = new UserArticleListService(); + $pagerQuery = new PagerQuery(); - return $service->handle($user->id); + $params = $pagerQuery->getParams(); + + $params['owner_id'] = $user->id; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $articleRepo = new ArticleRepo(); + + $pager = $articleRepo->paginate($params, $sort, $page, $limit); + + return $this->handleArticles($pager); + } + + protected function handleArticles($pager) + { + $service = new ArticleListService(); + + return $service->handleArticles($pager); } } diff --git a/app/Services/Logic/User/Console/NotificationList.php b/app/Services/Logic/User/Console/NotificationList.php new file mode 100644 index 00000000..2e135e5c --- /dev/null +++ b/app/Services/Logic/User/Console/NotificationList.php @@ -0,0 +1,73 @@ +getLoginUser(); + + $pagerQuery = new PagerQuery(); + + $params = $pagerQuery->getParams(); + + $params['receiver_id'] = $user->id; + $params['deleted'] = 0; + + $sort = $pagerQuery->getSort(); + $page = $pagerQuery->getPage(); + $limit = $pagerQuery->getLimit(); + + $notifyRepo = new NotificationRepo(); + + $pager = $notifyRepo->paginate($params, $sort, $page, $limit); + + return $this->handleNotifications($pager); + } + + protected function handleNotifications($pager) + { + if ($pager->total_items == 0) { + return $pager; + } + + $notifications = $pager->items->toArray(); + + $builder = new NotificationListBuilder(); + + $users = $builder->getUsers($notifications); + + $items = []; + + foreach ($notifications as $key => $value) { + + $value['event_info'] = json_decode($value['event_info'], true); + + $sender = $users[$value['sender_id']] ?? new \stdClass(); + $receiver = $users[$value['receiver_id']] ?? new \stdClass(); + + $items[] = [ + 'id' => $value['id'], + 'viewed' => $value['viewed'], + 'event_id' => $value['event_id'], + 'event_type' => $value['event_type'], + 'event_info' => $value['event_info'], + 'create_time' => $value['create_time'], + 'sender' => $sender, + 'receiver' => $receiver, + ]; + } + + $pager->items = $items; + + return $pager; + } + +} diff --git a/app/Services/Logic/User/Console/NotificationRead.php b/app/Services/Logic/User/Console/NotificationRead.php new file mode 100644 index 00000000..987a6c8d --- /dev/null +++ b/app/Services/Logic/User/Console/NotificationRead.php @@ -0,0 +1,20 @@ +getLoginUser(); + + $notifyRepo = new NotificationRepo(); + + $notifyRepo->markAllAsViewed($user->id); + } + +} diff --git a/app/Services/Logic/User/Console/NotifyStats.php b/app/Services/Logic/User/Console/NotifyStats.php new file mode 100644 index 00000000..f481c1c8 --- /dev/null +++ b/app/Services/Logic/User/Console/NotifyStats.php @@ -0,0 +1,27 @@ +getLoginUser(); + + $noticeCount = $this->getNoticeCount($user->id); + + return ['notice_count' => $noticeCount]; + } + + protected function getNoticeCount($userId) + { + $userRepo = new UserRepo(); + + return $userRepo->countUnreadNotifications($userId); + } + +} diff --git a/app/Services/Logic/User/Console/Online.php b/app/Services/Logic/User/Console/Online.php new file mode 100644 index 00000000..285fd700 --- /dev/null +++ b/app/Services/Logic/User/Console/Online.php @@ -0,0 +1,101 @@ +getLoginUser(); + + $this->handleVisitLog($user); + + $this->handleVisitPoint($user); + } + + protected function handleVisitLog(UserModel $user) + { + $now = time(); + + if ($now - $user->active_time < 900) return; + + $user->active_time = $now; + + $user->update(); + + $onlineRepo = new OnlineRepo(); + + $records = $onlineRepo->findByUserDate($user->id, date('Ymd')); + + $clientType = $this->getClientType(); + $clientIp = $this->getClientIp(); + + if ($records->count() > 0) { + $online = null; + foreach ($records as $record) { + $case1 = $record->client_type == $clientType; + $case2 = $record->client_ip == $clientIp; + if ($case1 && $case2) { + $online = $record; + break; + } + } + if ($online) { + $online->active_time = $now; + $online->update(); + } else { + $this->createOnline($user->id, $clientType, $clientIp); + } + } else { + $this->createOnline($user->id, $clientType, $clientIp); + } + } + + protected function createOnline($userId, $clientType, $clientIp) + { + $online = new OnlineModel(); + + $online->user_id = $userId; + $online->client_type = $clientType; + $online->client_ip = $clientIp; + $online->active_time = time(); + + $online->create(); + + return $online; + } + + protected function handleVisitPoint(UserModel $user) + { + $todayDate = date('Ymd'); + + $keyName = sprintf('site_visit:%s:%s', $user->id, $todayDate); + + $cache = $this->getCache(); + + $content = $cache->get($keyName); + + if ($content) return; + + $service = new SiteVisitPointHistory(); + + $service->handle($user); + + $tomorrow = strtotime($todayDate) + 86400; + + $lifetime = $tomorrow - time(); + + $cache->save($keyName, 1, $lifetime); + } + +} diff --git a/app/Services/Logic/User/Console/PointRedeemList.php b/app/Services/Logic/User/Console/PointRedeemList.php index b7f6e6a0..66df0c31 100644 --- a/app/Services/Logic/User/Console/PointRedeemList.php +++ b/app/Services/Logic/User/Console/PointRedeemList.php @@ -3,6 +3,7 @@ namespace App\Services\Logic\User\Console; use App\Library\Paginator\Query as PagerQuery; +use App\Models\PointRedeem as PointRedeemModel; use App\Repos\PointRedeem as PointRedeemRepo; use App\Services\Logic\Service as LogicService; use App\Services\Logic\UserTrait; @@ -42,25 +43,31 @@ class PointRedeemList extends LogicService $items = []; - foreach ($pager->items as $item) { + /** + * @var PointRedeemModel[] $redeems + */ + $redeems = $pager->items; + + foreach ($redeems as $redeem) { $items[] = [ - 'id' => $item->id, - 'status' => $item->status, - 'create_time' => $item->create_time, + 'id' => $redeem->id, + 'status' => $redeem->status, + 'create_time' => $redeem->create_time, + 'update_time' => $redeem->update_time, 'user' => [ - 'id' => $item->user_id, - 'name' => $item->user_name, + 'id' => $redeem->user_id, + 'name' => $redeem->user_name, ], 'gift' => [ - 'id' => $item->gift_id, - 'name' => $item->gift_name, - 'type' => $item->gift_type, - 'point' => $item->gift_point, + 'id' => $redeem->gift_id, + 'name' => $redeem->gift_name, + 'type' => $redeem->gift_type, + 'point' => $redeem->gift_point, ], 'contact' => [ - 'name' => $item->contact_name, - 'phone' => $item->contact_phone, - 'address' => $item->contact_address, + 'name' => $redeem->contact_name, + 'phone' => $redeem->contact_phone, + 'address' => $redeem->contact_address, ], ]; } diff --git a/app/Services/Logic/User/Console/ProfileInfo.php b/app/Services/Logic/User/Console/ProfileInfo.php index d4d81c10..9cb2e2f1 100644 --- a/app/Services/Logic/User/Console/ProfileInfo.php +++ b/app/Services/Logic/User/Console/ProfileInfo.php @@ -31,10 +31,12 @@ class ProfileInfo extends LogicService 'gender' => $user->gender, 'vip' => $user->vip, 'locked' => $user->locked, - 'vip_expiry_time' => $user->vip_expiry_time, - 'lock_expiry_time' => $user->lock_expiry_time, 'edu_role' => $user->edu_role, 'admin_role' => $user->admin_role, + 'vip_expiry_time' => $user->vip_expiry_time, + 'lock_expiry_time' => $user->lock_expiry_time, + 'create_time' => $user->create_time, + 'update_time' => $user->update_time, ]; } diff --git a/app/Services/Logic/User/Console/RefundList.php b/app/Services/Logic/User/Console/RefundList.php index da1b0af1..c693efde 100644 --- a/app/Services/Logic/User/Console/RefundList.php +++ b/app/Services/Logic/User/Console/RefundList.php @@ -69,6 +69,7 @@ class RefundList extends LogicService 'apply_note' => $refund['apply_note'], 'review_note' => $refund['review_note'], 'create_time' => $refund['create_time'], + 'update_time' => $refund['update_time'], ]; } diff --git a/app/Services/Logic/User/UserInfo.php b/app/Services/Logic/User/UserInfo.php index f3d1bcdc..d6a6a36f 100644 --- a/app/Services/Logic/User/UserInfo.php +++ b/app/Services/Logic/User/UserInfo.php @@ -41,6 +41,7 @@ class UserInfo extends LogicService 'group_count' => $imUser->group_count, 'active_time' => $user->active_time, 'create_time' => $user->create_time, + 'update_time' => $user->update_time, ]; } diff --git a/app/Validators/Article.php b/app/Validators/Article.php index 7fbcccb6..28ccfdef 100644 --- a/app/Validators/Article.php +++ b/app/Validators/Article.php @@ -156,11 +156,29 @@ class Article extends Validator public function checkPublishStatus($status) { - if (!in_array($status, [0, 1])) { + if (!array_key_exists($status, ArticleModel::publishTypes())) { throw new BadRequestException('article.invalid_publish_status'); } return $status; } + public function checkPrivateStatus($status) + { + if (!in_array($status, [0, 1])) { + throw new BadRequestException('article.invalid_private_status'); + } + + return $status; + } + + public function checkAllowCommentStatus($status) + { + if (!in_array($status, [0, 1])) { + throw new BadRequestException('article.invalid_allow_comment_status'); + } + + return $status; + } + } diff --git a/app/Validators/Comment.php b/app/Validators/Comment.php index a85c40d7..091ff909 100644 --- a/app/Validators/Comment.php +++ b/app/Validators/Comment.php @@ -77,7 +77,7 @@ class Comment extends Validator public function checkPublishStatus($status) { - if (!in_array($status, [0, 1])) { + if (!array_key_exists($status, CommentModel::publishTypes())) { throw new BadRequestException('comment.invalid_publish_status'); } diff --git a/config/errors.php b/config/errors.php index a0f0d98b..a27f6968 100644 --- a/config/errors.php +++ b/config/errors.php @@ -120,6 +120,8 @@ $error['article.invalid_source_type'] = '无效的来源类型'; $error['article.invalid_source_url'] = '无效的来源网址'; $error['course.invalid_feature_status'] = '无效的推荐状态'; $error['course.invalid_publish_status'] = '无效的发布状态'; +$error['course.invalid_private_status'] = '无效的私有状态'; +$error['course.invalid_allow_comment_status'] = '无效的允许评论状态'; /** * 评论相关 diff --git a/config/events.php b/config/events.php index f8caf678..56d4b99e 100644 --- a/config/events.php +++ b/config/events.php @@ -1,6 +1,9 @@ UserDailyCounter::class, 'ImMessage' => ImMessage::class, 'Account' => Account::class, + 'Article' => Article::class, + 'Comment' => Comment::class, + 'Consult' => Consult::class, 'Review' => Review::class, 'Trade' => Trade::class, 'Site' => Site::class, diff --git a/db/migrations/20201212112717_data_202012121830.php b/db/migrations/20201212112717_data_202012121830.php index e73b008e..dbeafdb1 100644 --- a/db/migrations/20201212112717_data_202012121830.php +++ b/db/migrations/20201212112717_data_202012121830.php @@ -7,6 +7,14 @@ final class Data202012121830 extends AbstractMigration public function up() { + $noticeTemplate = json_encode([ + 'account_login' => '', + 'order_finish' => '', + 'refund_finish' => '', + 'live_begin' => '', + 'consult_reply' => '', + ]); + $rows = [ [ 'section' => 'wechat.oa', @@ -41,7 +49,7 @@ final class Data202012121830 extends AbstractMigration [ 'section' => 'wechat.oa', 'item_key' => 'notice_template', - 'item_value' => '{"account_login":"","order_finish":"","refund_finish":"","live_begin":"","consult_reply":""}', + 'item_value' => $noticeTemplate, ], ]; diff --git a/db/migrations/20210422023157.php b/db/migrations/20210422023157.php new file mode 100644 index 00000000..9145c30a --- /dev/null +++ b/db/migrations/20210422023157.php @@ -0,0 +1,301 @@ +createNotificationTable(); + $this->modifyArticleTable(); + $this->modifyConsultTable(); + $this->modifyReviewTable(); + $this->handleArticlePublishStatus(); + $this->handleCommentPublishStatus(); + $this->handlePointHistoryEventInfo(); + $this->handleImNoticeItemInfo(); + $this->handlePointEventRules(); + $this->handleChapterAttrs(); + } + + public function down() + { + $this->table('kg_notification')->drop()->save(); + + $this->table('kg_article') + ->removeColumn('client_type') + ->removeColumn('client_ip') + ->save(); + + $this->table('kg_consult') + ->removeColumn('client_type') + ->removeColumn('client_ip') + ->save(); + + $this->table('kg_review') + ->removeColumn('client_type') + ->removeColumn('client_ip') + ->save(); + } + + protected function createNotificationTable() + { + $this->table('kg_notification', [ + 'id' => false, + 'primary_key' => ['id'], + 'engine' => 'InnoDB', + 'encoding' => 'utf8mb4', + 'collation' => 'utf8mb4_general_ci', + 'comment' => '', + 'row_format' => 'DYNAMIC', + ]) + ->addColumn('id', 'integer', [ + 'null' => false, + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'identity' => 'enable', + 'comment' => '主键编号', + ]) + ->addColumn('sender_id', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '发送方编号', + 'after' => 'id', + ]) + ->addColumn('receiver_id', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '接收方编号', + 'after' => 'sender_id', + ]) + ->addColumn('event_id', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '事件编号', + 'after' => 'receiver_id', + ]) + ->addColumn('event_type', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '事件类型', + 'after' => 'event_id', + ]) + ->addColumn('event_info', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 1500, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '事件内容', + 'after' => 'event_type', + ]) + ->addColumn('viewed', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '已读标识', + 'after' => 'event_info', + ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '删除标识', + 'after' => 'viewed', + ]) + ->addColumn('create_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '创建时间', + 'after' => 'deleted', + ]) + ->addColumn('update_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '更新时间', + 'after' => 'create_time', + ]) + ->addIndex(['sender_id'], [ + 'name' => 'sender_id', + 'unique' => false, + ]) + ->addIndex(['receiver_id'], [ + 'name' => 'receiver_id', + 'unique' => false, + ]) + ->create(); + } + + protected function modifyArticleTable() + { + $this->table('kg_article') + ->addColumn('client_type', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '终端类型', + 'after' => 'source_url', + ]) + ->addColumn('client_ip', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '终端IP', + 'after' => 'client_type', + ])->addColumn('private', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '私有标识', + 'after' => 'client_ip', + ])->save(); + } + + protected function modifyConsultTable() + { + $this->table('kg_consult') + ->addColumn('client_type', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '终端类型', + 'after' => 'replier_id', + ]) + ->addColumn('client_ip', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '终端IP', + 'after' => 'client_type', + ])->save(); + } + + protected function modifyReviewTable() + { + $this->table('kg_review') + ->addColumn('client_type', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '终端类型', + 'after' => 'owner_id', + ]) + ->addColumn('client_ip', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '终端IP', + 'after' => 'client_type', + ])->save(); + } + + protected function handleArticlePublishStatus() + { + $this->getQueryBuilder() + ->update('kg_article') + ->set('published', 2) + ->where(['published' => 1]) + ->execute(); + + $this->getQueryBuilder() + ->update('kg_article') + ->set('published', 1) + ->where(['published' => 0]) + ->execute(); + } + + protected function handleCommentPublishStatus() + { + $this->getQueryBuilder() + ->update('kg_comment') + ->set('published', 2) + ->where(['published' => 1]) + ->execute(); + + $this->getQueryBuilder() + ->update('kg_comment') + ->set('published', 1) + ->where(['published' => 0]) + ->execute(); + } + + protected function handlePointEventRules() + { + $setting = $this->getQueryBuilder() + ->select('*') + ->from('kg_setting') + ->where(['section' => 'point', 'item_key' => 'event_rule']) + ->execute()->fetch('assoc'); + + if (!$setting) return; + + $itemValue = json_decode($setting['item_value'], true); + + $itemValue['comment_post'] = ['point' => 2, 'enabled' => 1, 'limit' => 10]; + $itemValue['article_post'] = ['point' => 20, 'enabled' => 1, 'limit' => 50]; + $itemValue['question_post'] = ['point' => 5, 'enabled' => 1, 'limit' => 50]; + $itemValue['answer_post'] = ['point' => 5, 'enabled' => 1, 'limit' => 50]; + + $itemValue = json_encode($itemValue); + + $this->getQueryBuilder() + ->update('kg_setting') + ->where(['id' => $setting['id']]) + ->set('item_value', $itemValue) + ->execute(); + } + + protected function handleChapterAttrs() + { + $this->getQueryBuilder() + ->update('kg_chapter') + ->set('attrs', '{}') + ->where(['parent_id' => 0, 'attrs' => '']) + ->execute(); + } + + protected function handleImNoticeItemInfo() + { + $this->getQueryBuilder() + ->update('kg_im_notice') + ->set('item_info', '{}') + ->where(['item_info' => '']) + ->execute(); + } + + protected function handlePointHistoryEventInfo() + { + $this->getQueryBuilder() + ->update('kg_point_history') + ->set('event_info', '{}') + ->whereInList('event_type', [4, 5, 8]) + ->execute(); + } + +} diff --git a/public/static/admin/js/vditor.js b/public/static/admin/js/vditor.js index 3e08c2fb..6d0762ca 100644 --- a/public/static/admin/js/vditor.js +++ b/public/static/admin/js/vditor.js @@ -4,10 +4,51 @@ layui.use(['jquery'], function () { var $textarea = $('#vditor-textarea'); + var toolbar = [ + 'emoji', + 'headings', + 'bold', + 'italic', + 'strike', + 'link', + '|', + 'list', + 'ordered-list', + 'check', + 'outdent', + 'indent', + '|', + 'quote', + 'line', + 'code', + 'inline-code', + 'insert-before', + 'insert-after', + '|', + 'upload', + 'table', + '|', + 'undo', + 'redo', + '|', + 'fullscreen', + 'edit-mode', + { + name: 'more', + toolbar: [ + 'both', + 'export', + 'preview', + 'info', + 'help', + ], + }]; + var vditor = new Vditor('vditor', { mode: 'sv', minHeight: 300, outline: false, + toolbar: toolbar, resize: { enable: true }, @@ -20,6 +61,9 @@ layui.use(['jquery'], function () { autoSpace: true } }, + fullscreen: { + index: 9999 + }, counter: { enable: true, max: 30000 @@ -33,13 +77,13 @@ layui.use(['jquery'], function () { 'X-Requested-With': 'XMLHttpRequest' }, success: function (editor, responseText) { - console.log(editor, responseText); var json = JSON.parse(responseText); var img = '![](' + json.data.src + ')'; vditor.insertValue(img); } }, - value: $textarea.val() + value: $textarea.val(), + placeholder: '酷瓜云课堂,100%开源在线教育解决方案' }); /** diff --git a/public/static/home/css/common.css b/public/static/home/css/common.css index 53a8439d..31af3f88 100644 --- a/public/static/home/css/common.css +++ b/public/static/home/css/common.css @@ -132,6 +132,39 @@ list-style: decimal; } +.writer-content .layui-input-block, +.writer-sidebar .layui-input-block { + margin-left: 0; +} + +.writer-content { + padding-top: 30px; +} + +.writer-sidebar .layui-form-label { + float: none; + width: 100%; + display: block; + padding: 0; + margin-bottom: 10px; + text-align: inherit; +} + +.writer-sidebar .cover-wrap { + position: relative; +} + +.writer-sidebar .cover { + width: 290px; + height: 160px; +} + +.writer-sidebar .btn-change { + position: absolute; + top: 90px; + left: 120px; +} + #header { left: 0; top: 0; @@ -310,7 +343,7 @@ .search-course-card, .search-group-card { padding-bottom: 15px; margin-bottom: 15px; - border-bottom: 1px dashed #ccc; + border-bottom: 1px solid #f6f6f6; } .search-course-card:last-child, .search-group-card:last-child { @@ -427,7 +460,7 @@ .article-card { padding-bottom: 15px; margin-bottom: 15px; - border-bottom: 1px dashed #ccc; + border-bottom: 1px solid #f6f6f6; } .article-card:last-child { @@ -1970,6 +2003,18 @@ width: 10%; } +.notice-card .content { + white-space: nowrap; +} + +.notice-card .content p { + margin-bottom: 10px; +} + +.notice-card .content p:last-child { + margin-bottom: 0; +} + .im-user-list .user-card { height: 150px; font-size: 12px; diff --git a/public/static/home/js/article.edit.js b/public/static/home/js/article.edit.js new file mode 100644 index 00000000..0d198c42 --- /dev/null +++ b/public/static/home/js/article.edit.js @@ -0,0 +1,48 @@ +layui.use(['jquery', 'form', 'layer', 'upload'], function () { + + var $ = layui.jquery; + var form = layui.form; + var layer = layui.layer; + var upload = layui.upload; + + form.on('select(source_type)', function (data) { + var block = $('#source-url-block'); + if (data.value === '1') { + block.hide(); + } else { + block.show(); + } + }); + + var xmTags = JSON.parse($('input[name=xm_tags]').val()); + + xmSelect.render({ + el: '#xm-tag-ids', + name: 'xm_tag_ids', + max: 3, + data: xmTags, + filterable: true, + filterMethod: function (val, item, index, prop) { + return item.name.toLowerCase().indexOf(val.toLowerCase()) !== -1; + } + }); + + upload.render({ + elem: '#change-cover', + url: '/upload/cover/img', + accept: 'images', + acceptMime: 'image/*', + before: function () { + layer.load(); + }, + done: function (res, index, upload) { + $('#img-cover').attr('src', res.data.src); + $('input[name=cover]').val(res.data.src); + layer.closeAll('loading'); + }, + error: function (index, upload) { + layer.msg('上传文件失败', {icon: 2}); + } + }); + +}); \ No newline at end of file diff --git a/public/static/home/js/common.js b/public/static/home/js/common.js index fb062096..86f89ab6 100644 --- a/public/static/home/js/common.js +++ b/public/static/home/js/common.js @@ -47,6 +47,22 @@ layui.use(['jquery', 'form', 'element', 'layer', 'helper'], function () { }); }, 300000); + if (window.user.id > 0) { + setInterval(function () { + $.get('/uc/notify/stats', function (res) { + var $notifyDot = $('#notify-dot'); + if (res.stats.notice_count > 0) { + $notifyDot.addClass('layui-badge-dot'); + } else { + $notifyDot.removeClass('layui-badge-dot'); + } + }); + }, 30000); + setInterval(function () { + $.post('/uc/online'); + }, 30000); + } + form.on('submit(go)', function (data) { var submit = $(this); submit.attr('disabled', 'disabled').addClass('layui-btn-disabled'); diff --git a/public/static/home/js/vditor.js b/public/static/home/js/vditor.js new file mode 100644 index 00000000..f3158b6a --- /dev/null +++ b/public/static/home/js/vditor.js @@ -0,0 +1,96 @@ +layui.use(['jquery'], function () { + + var $ = layui.jquery; + + var $textarea = $('#vditor-textarea'); + + var toolbar = [ + 'emoji', + 'headings', + 'bold', + 'italic', + 'strike', + 'link', + '|', + 'list', + 'ordered-list', + 'check', + 'outdent', + 'indent', + '|', + 'quote', + 'line', + 'code', + 'inline-code', + 'insert-before', + 'insert-after', + '|', + 'upload', + 'table', + '|', + 'undo', + 'redo', + '|', + 'fullscreen', + 'edit-mode', + { + name: 'more', + toolbar: [ + 'both', + 'export', + 'preview', + 'info', + 'help', + ], + }]; + + var vditor = new Vditor('vditor', { + mode: 'sv', + minHeight: 450, + outline: false, + toolbar: toolbar, + resize: { + enable: true + }, + cache: { + enable: false + }, + preview: { + markdown: { + chinesePunct: true, + autoSpace: true + } + }, + fullscreen: { + index: 9999 + }, + counter: { + enable: true, + max: 30000 + }, + upload: { + url: '/upload/content/img', + max: 10 * 1024 * 1024, + accept: 'image/*', + headers: { + 'X-Csrf-Token': $('meta[name="csrf-token"]').attr('content'), + 'X-Requested-With': 'XMLHttpRequest' + }, + success: function (editor, responseText) { + var json = JSON.parse(responseText); + var img = '![](' + json.data.src + ')'; + vditor.insertValue(img); + } + }, + value: $textarea.val(), + placeholder: '酷瓜云课堂,100%开源在线教育解决方案' + }); + + /** + * 同步编辑器内容到表单 + */ + $('.kg-submit').on('click', function () { + $textarea.val(vditor.getValue()); + }); + +}); \ No newline at end of file
    点赞 评论 收藏操作
    -

    标题:{{ item.title }}

    +

    + 标题:{{ item.title }} + ({{ item.create_time|time_ago }}) +

    来源:{{ source_type(item.source_type) }} 分类:{{ item.category.name }} - 时间:{{ item.create_time|time_ago }} + 状态:{{ publish_status(item.published) }}

    {{ item.view_count }} {{ item.like_count }} {{ item.comment_count }} {{ item.favorite_count }} + 编辑 + 删除 +
    {{ event_type_info(item.event_type) }} {{ event_point_info(item.event_point) }}{{ event_detail_info(item) }}{{ event_detail_info(item.event_type,item.event_info) }} {{ date('Y-m-d',item.create_time) }}