mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-24 20:06:09 +08:00
Merge branch 'develop'
This commit is contained in:
commit
1ce43f6a75
13
CHANGELOG.md
13
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)
|
||||
|
||||
### 更新
|
||||
|
44
app/Builders/NotificationList.php
Normal file
44
app/Builders/NotificationList.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Builders;
|
||||
|
||||
use App\Repos\User as UserRepo;
|
||||
|
||||
class NotificationList extends Builder
|
||||
{
|
||||
|
||||
public function handleUsers(array $notifications)
|
||||
{
|
||||
$users = $this->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;
|
||||
}
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ class ArticleRelatedList extends Cache
|
||||
|
||||
$where = [
|
||||
'tag_id' => $tagIds[$randKey],
|
||||
'published' => 1,
|
||||
'published' => ArticleModel::PUBLISH_APPROVED,
|
||||
];
|
||||
|
||||
$pager = $articleRepo->paginate($where);
|
||||
|
35
app/Caches/ModerationStat.php
Normal file
35
app/Caches/ModerationStat.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Caches;
|
||||
|
||||
use App\Repos\Stat as StatRepo;
|
||||
|
||||
class ModerationStat extends Cache
|
||||
{
|
||||
|
||||
protected $lifetime = 15 * 60;
|
||||
|
||||
public function getLifetime()
|
||||
{
|
||||
return $this->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,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -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(),
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
25
app/Http/Admin/Controllers/ModerationController.php
Normal file
25
app/Http/Admin/Controllers/ModerationController.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Admin\Controllers;
|
||||
|
||||
use App\Http\Admin\Services\Moderation as ModerationService;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/admin/moderation")
|
||||
*/
|
||||
class ModerationController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/articles", name="admin.mod.articles")
|
||||
*/
|
||||
public function articlesAction()
|
||||
{
|
||||
$modService = new ModerationService();
|
||||
|
||||
$pager = $modService->getArticles();
|
||||
|
||||
$this->view->setVar('pager', $pager);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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' => '学员管理',
|
||||
|
@ -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';
|
||||
|
52
app/Http/Admin/Services/Moderation.php
Normal file
52
app/Http/Admin/Services/Moderation.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Admin\Services;
|
||||
|
||||
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;
|
||||
|
||||
class Moderation extends Service
|
||||
{
|
||||
|
||||
public function getArticles()
|
||||
{
|
||||
$pagerQuery = new PagerQuery();
|
||||
|
||||
$params = $pagerQuery->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;
|
||||
}
|
||||
|
||||
}
|
@ -50,6 +50,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">发布状态</label>
|
||||
<div class="layui-input-block">
|
||||
{% for value,title in publish_types %}
|
||||
<input type="radio" name="published" value="{{ value }}" title="{{ title }}" {% if article.published == value %}checked="checked"{% endif %} lay-filter="source_type">
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">允许评论</label>
|
||||
<div class="layui-input-block">
|
||||
@ -57,6 +65,13 @@
|
||||
<input type="radio" name="allow_comment" value="0" title="否" {% if article.allow_comment == 0 %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">仅我可见</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="private" value="1" title="是" {% if article.private == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="private" value="0" title="否" {% if article.private == 0 %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
|
@ -38,13 +38,13 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文章</th>
|
||||
<th>状态</th>
|
||||
<th>浏览</th>
|
||||
<th>评论</th>
|
||||
<th>点赞</th>
|
||||
<th>收藏</th>
|
||||
<th>推荐</th>
|
||||
<th>评论</th>
|
||||
<th>发布</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -74,13 +74,13 @@
|
||||
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
|
||||
</p>
|
||||
</td>
|
||||
<td>{{ publish_status(item.published) }}</td>
|
||||
<td>{{ item.view_count }}</td>
|
||||
<td>{{ item.comment_count }}</td>
|
||||
<td>{{ item.like_count }}</td>
|
||||
<td>{{ item.favorite_count }}</td>
|
||||
<td><input type="checkbox" name="featured" value="1" lay-skin="switch" lay-text="是|否" lay-filter="featured" data-url="{{ update_url }}" {% if item.featured == 1 %}checked="checked"{% endif %}></td>
|
||||
<td><input type="checkbox" name="comment" value="1" lay-skin="switch" lay-text="开|关" lay-filter="comment" data-url="{{ update_url }}" {% if item.allow_comment == 1 %}checked="checked"{% endif %}></td>
|
||||
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}></td>
|
||||
<td class="center">
|
||||
<div class="layui-dropdown">
|
||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||
|
@ -41,6 +41,14 @@
|
||||
<div id="xm-tag-ids"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">发布状态</label>
|
||||
<div class="layui-input-block">
|
||||
{% for value,title in publish_types %}
|
||||
<input type="radio" name="published" value="{{ value }}" title="{{ title }}">
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">来源类型</label>
|
||||
<div class="layui-input-block">
|
||||
@ -56,13 +64,6 @@
|
||||
<input type="radio" name="featured" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">发布</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="published" value="1" title="是">
|
||||
<input type="radio" name="published" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">删除</label>
|
||||
<div class="layui-input-block">
|
||||
|
82
app/Http/Admin/Views/article/show.volt
Normal file
82
app/Http/Admin/Views/article/show.volt
Normal file
@ -0,0 +1,82 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% set list_url = url({'for':'admin.article.pending_list'}) %}
|
||||
|
||||
<div class="kg-nav">
|
||||
<div class="kg-nav-left">
|
||||
<span class="layui-breadcrumb">
|
||||
<a href="{{ list_url }}">审核列表</a>
|
||||
<a><cite>{{ article.title }}</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.review','id':article.id}) }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">内容</label>
|
||||
<div class="layui-input-block">
|
||||
<div class="content markdown-body">{{ article.content|parse_markdown }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">审核</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="type" value="approve" title="通过" lay-filter="review">
|
||||
<input type="radio" name="type" value="reject" title="拒绝" lay-filter="review">
|
||||
</div>
|
||||
</div>
|
||||
<div id="reason-block" style="display:none;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">理由</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="reason">
|
||||
<option value="">请选择</option>
|
||||
{% for value,name in reject_options %}
|
||||
<option value="{{ value }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button id="kg-submit" class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block link_css %}
|
||||
|
||||
{{ css_link('home/css/markdown.css') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script>
|
||||
|
||||
layui.use(['jquery', 'form'], function () {
|
||||
|
||||
var $ = layui.jquery;
|
||||
var form = layui.form;
|
||||
|
||||
form.on('radio(review)', function (data) {
|
||||
var block = $('#reason-block');
|
||||
if (data.value === 'approve') {
|
||||
block.hide();
|
||||
} else {
|
||||
block.show();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -7,6 +7,7 @@
|
||||
<div class="layui-col-md8">
|
||||
{{ partial('index/main_global_stat') }}
|
||||
{{ partial('index/main_today_stat') }}
|
||||
{{ partial('index/main_mod_stat') }}
|
||||
{{ partial('index/main_app_trend') }}
|
||||
</div>
|
||||
<div class="layui-col-md4">
|
||||
|
@ -2,54 +2,78 @@
|
||||
<div class="layui-card-header">全局统计</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-row layui-col-space10">
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">课程数</div>
|
||||
<div class="count">{{ global_stat.course_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">用户数</div>
|
||||
<div class="count">{{ global_stat.user_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">群组数</div>
|
||||
<div class="count">{{ global_stat.group_count }}</div>
|
||||
<div class="name">会员数</div>
|
||||
<div class="count">{{ global_stat.vip_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">订单数</div>
|
||||
<div class="count">{{ global_stat.order_count }}</div>
|
||||
<div class="name">课程数</div>
|
||||
<div class="count">{{ global_stat.course_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">评价数</div>
|
||||
<div class="count">{{ global_stat.review_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">咨询数</div>
|
||||
<div class="count">{{ global_stat.consult_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">套餐数</div>
|
||||
<div class="count">{{ global_stat.package_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">专题数</div>
|
||||
<div class="count">{{ global_stat.topic_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">群组数</div>
|
||||
<div class="count">{{ global_stat.group_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">评价数</div>
|
||||
<div class="count">{{ global_stat.review_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">咨询数</div>
|
||||
<div class="count">{{ global_stat.consult_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">文章数</div>
|
||||
<div class="count">{{ global_stat.article_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">提问数</div>
|
||||
<div class="count">0</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">回答数</div>
|
||||
<div class="count">0</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">评论数</div>
|
||||
<div class="count">{{ global_stat.comment_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
39
app/Http/Admin/Views/index/main_mod_stat.volt
Normal file
39
app/Http/Admin/Views/index/main_mod_stat.volt
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="layui-card layui-text kg-stats">
|
||||
<div class="layui-card-header">审核队列</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-row layui-col-space10">
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">文章</div>
|
||||
<div class="count">
|
||||
<a href="{{ url({'for':'admin.mod.articles'}) }}">{{ mod_stat.article_count }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">提问</div>
|
||||
<div class="count">
|
||||
<a href="javascript:">0</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">回答</div>
|
||||
<div class="count">
|
||||
<a href="javascript:">0</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">评论</div>
|
||||
<div class="count">
|
||||
<a href="javascript:">0</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -2,25 +2,37 @@
|
||||
<div class="layui-card-header">今日统计</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-row layui-col-space10">
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">用户注册</div>
|
||||
<div class="count">{{ today_stat.register_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">积分兑换</div>
|
||||
<div class="count">{{ today_stat.point_redeem_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">成交订单</div>
|
||||
<div class="count">{{ today_stat.sale_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">成功退款</div>
|
||||
<div class="count">{{ today_stat.refund_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">销售金额</div>
|
||||
<div class="count">{{ '¥%0.2f'|format(today_stat.sale_amount) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-col-md2">
|
||||
<div class="kg-stat-card">
|
||||
<div class="name">退款金额</div>
|
||||
<div class="count">{{ '¥%0.2f'|format(today_stat.refund_amount) }}</div>
|
||||
|
@ -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 %}
|
||||
原创
|
||||
|
79
app/Http/Admin/Views/moderation/articles.volt
Normal file
79
app/Http/Admin/Views/moderation/articles.volt
Normal file
@ -0,0 +1,79 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/article') }}
|
||||
|
||||
<div class="kg-nav">
|
||||
<div class="kg-nav-left">
|
||||
<span class="layui-breadcrumb">
|
||||
<a><cite>文章审核</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="layui-table kg-table layui-form">
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文章</th>
|
||||
<th>作者</th>
|
||||
<th>来源</th>
|
||||
<th>评论</th>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in pager.items %}
|
||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||
{% set show_url = url({'for':'admin.article.show','id':item.id}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<p>标题:{{ item.title }}({{ item.id }})</p>
|
||||
<p class="meta">
|
||||
{% if item.category.id is defined %}
|
||||
<span>分类:{{ item.category.name }}</span>
|
||||
{% endif %}
|
||||
{% if item.tags %}
|
||||
<span>标签:{{ tags_info(item.tags) }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||
<p>编号:{{ item.owner.id }}</p>
|
||||
</td>
|
||||
<td>{{ source_info(item.source_type,item.source_url) }}</td>
|
||||
<td>
|
||||
{% if item.allow_comment == 1 %}
|
||||
开启
|
||||
{% else %}
|
||||
关闭
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if item.update_time > 0 %}
|
||||
{{ date('Y-m-d H:i:s',item.update_time) }}
|
||||
{% else %}
|
||||
{{ date('Y-m-d H:i:s',item.create_time) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="center">
|
||||
<a href="{{ show_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ partial('partials/pager') }}
|
||||
|
||||
{% endblock %}
|
@ -8,7 +8,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">4</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">3</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -97,6 +97,24 @@
|
||||
<td><input class="layui-input" type="text" name="event_rule[im_discuss][point]" value="{{ event_rule.im_discuss.point }}" lay-verify="required"></td>
|
||||
<td>N/A</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>发表评论</td>
|
||||
<td>
|
||||
<input type="radio" name="event_rule[comment_post][enabled]" value="1" title="是" {% if event_rule.comment_post.enabled == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="event_rule[comment_post][enabled]" value="0" title="否" {% if event_rule.comment_post.enabled == "0" %}checked="checked"{% endif %}>
|
||||
</td>
|
||||
<td><input class="layui-input" type="text" name="event_rule[comment_post][point]" value="{{ event_rule.comment_post.point }}" lay-verify="required"></td>
|
||||
<td><input class="layui-input" type="text" name="event_rule[comment_post][limit]" value="{{ event_rule.comment_post.limit }}" lay-verify="required"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>发表文章</td>
|
||||
<td>
|
||||
<input type="radio" name="event_rule[article_post][enabled]" value="1" title="是" {% if event_rule.article_post.enabled == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="event_rule[article_post][enabled]" value="0" title="否" {% if event_rule.article_post.enabled == "0" %}checked="checked"{% endif %}>
|
||||
</td>
|
||||
<td><input class="layui-input" type="text" name="event_rule[article_post][point]" value="{{ event_rule.article_post.point }}" lay-verify="required"></td>
|
||||
<td><input class="layui-input" type="text" name="event_rule[article_post][limit]" value="{{ event_rule.article_post.limit }}" lay-verify="required"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.topic.create'}) }}">
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>添加话题</legend>
|
||||
<legend>添加专题</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">标题</label>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.topic.update','id':topic.id}) }}">
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>编辑话题</legend>
|
||||
<legend>编辑专题</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">标题</label>
|
||||
|
@ -8,15 +8,15 @@
|
||||
<div class="kg-nav">
|
||||
<div class="kg-nav-left">
|
||||
<span class="layui-breadcrumb">
|
||||
<a><cite>话题管理</cite></a>
|
||||
<a><cite>专题管理</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kg-nav-right">
|
||||
<a class="layui-btn layui-btn-sm" href="{{ add_url }}">
|
||||
<i class="layui-icon layui-icon-add-1"></i>添加话题
|
||||
<i class="layui-icon layui-icon-add-1"></i>添加专题
|
||||
</a>
|
||||
<a class="layui-btn layui-btn-sm" href="{{ search_url }}">
|
||||
<i class="layui-icon layui-icon-search"></i>搜索话题
|
||||
<i class="layui-icon layui-icon-search"></i>搜索专题
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.topic.list'}) }}">
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>搜索话题</legend>
|
||||
<legend>搜索专题</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">编号</label>
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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")
|
||||
*/
|
||||
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
144
app/Http/Home/Services/Article.php
Normal file
144
app/Http/Home/Services/Article.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Home\Services;
|
||||
|
||||
use App\Http\Admin\Services\Article as ArticleService;
|
||||
use App\Library\Utils\Word as WordUtil;
|
||||
use App\Models\Article as ArticleModel;
|
||||
use App\Traits\Client as ClientTrait;
|
||||
use App\Validators\Article as ArticleValidator;
|
||||
|
||||
class Article extends ArticleService
|
||||
{
|
||||
|
||||
use ClientTrait;
|
||||
|
||||
public function createArticle()
|
||||
{
|
||||
$post = $this->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;
|
||||
}
|
||||
|
||||
}
|
125
app/Http/Home/Views/article/edit.volt
Normal file
125
app/Http/Home/Views/article/edit.volt
Normal file
@ -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' %}
|
||||
|
||||
<div class="breadcrumb">
|
||||
<span class="layui-breadcrumb">
|
||||
<a href="/">首页</a>
|
||||
<a><cite>{{ title }}</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form class="layui-form" method="POST" action="{{ action_url }}">
|
||||
<div class="layout-main clearfix">
|
||||
<div class="layout-content">
|
||||
<div class="writer-content wrap">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="title" value="{{ article.title }}" placeholder="请输入标题..." lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div id="vditor"></div>
|
||||
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ article.content }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item center">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button class="kg-back layui-btn layui-btn-primary" type="button">返回</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-sidebar">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">基本信息</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="writer-sidebar">
|
||||
<div class="layui-form-item cover-wrap">
|
||||
<label class="layui-form-label">封面</label>
|
||||
<div class="layui-input-block">
|
||||
<img id="img-cover" class="cover" src="{{ article.cover }}!cover_270">
|
||||
<input type="hidden" name="cover" value="{{ article.cover }}">
|
||||
</div>
|
||||
<button id="change-cover" class="layui-btn layui-btn-sm btn-change" type="button">更换</button>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">分类</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="category_id" lay-verify="required">
|
||||
<option value="">请选择</option>
|
||||
{% for item in categories %}
|
||||
<option value="{{ item.id }}" {% if article.category_id == item.id %}selected="selected"{% endif %}>{{ item.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">标签</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="xm-tag-ids"></div>
|
||||
<input type="hidden" name="xm_tags" value='{{ xm_tags|json_encode }}'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">来源类型</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="source_type" lay-filter="source_type" lay-verify="required">
|
||||
<option value="">请选择</option>
|
||||
{% for value,title in source_types %}
|
||||
<option value="{{ value }}" {% if article.source_type == value %}selected="selected"{% endif %}>{{ title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="source-url-block" style="{{ source_url_display }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">来源网址</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="source_url" value="{{ article.source_url }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">允许评论</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="allow_comment" value="1" title="是" {% if article.allow_comment == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="allow_comment" value="0" title="否" {% if article.allow_comment == 0 %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">仅我可见</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="private" value="1" title="是" {% if article.private == 1 %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="private" value="0" title="否" {% if article.private == 0 %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block link_css %}
|
||||
|
||||
{{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||
{{ js_include('lib/xm-select.js') }}
|
||||
{{ js_include('home/js/article.edit.js') }}
|
||||
{{ js_include('home/js/vditor.js') }}
|
||||
|
||||
{% endblock %}
|
@ -54,7 +54,7 @@
|
||||
</div>
|
||||
<div class="right">
|
||||
<span class="column">
|
||||
<span class="action action-delete" data-id="{{ item.id }}" data-url="{{ delete_url }}">删除</span>
|
||||
<span class="action action-delete" data-id="{{ comment.id }}" data-url="{{ delete_url }}">删除</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">5</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">3</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">4</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">4</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">1</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">4</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">3</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">4</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">4</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">5</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="layui-text">
|
||||
<h1>
|
||||
<span class="layui-anim layui-anim-loop">5</span>
|
||||
<span class="layui-anim layui-anim-loop layui-anim-rotate">0</span>
|
||||
<span class="layui-anim layui-anim-loop">0</span>
|
||||
<span class="layui-anim layui-anim-loop">3</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
@ -9,3 +9,15 @@
|
||||
未知
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro publish_status(type) %}
|
||||
{% if type == 1 %}
|
||||
审核中
|
||||
{% elseif type == 2 %}
|
||||
已发布
|
||||
{% elseif type == 3 %}
|
||||
未通过
|
||||
{% else %}
|
||||
未知
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
41
app/Http/Home/Views/macros/notification.volt
Normal file
41
app/Http/Home/Views/macros/notification.volt
Normal file
@ -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 %}
|
||||
<p>未知类型</p>
|
||||
{% elseif type == 147 %}
|
||||
{% set course_url = url({'for':'home.course.show','id':info.course.id}) %}
|
||||
<p>{{ sender.name }} 喜欢了你在课程 <a href="{{ course_url }}" target="_blank">{{ info.course.title }}</a> 中的咨询</p>
|
||||
<p>咨询内容:{{ info.consult.question }}</p>
|
||||
{% elseif type == 167 %}
|
||||
{% set course_url = url({'for':'home.course.show','id':info.course.id}) %}
|
||||
<p>{{ sender.name }} 喜欢了你在课程 <a href="{{ course_url }}" target="_blank">{{ info.course.title }}</a> 中的评价</p>
|
||||
<p>评价内容:{{ info.review.content }}</p>
|
||||
{% elseif type == 184 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a> 通过了审核</p>
|
||||
{% elseif type == 185 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a> 审核未通过</p>
|
||||
<p>拒绝原因:{{ info.reason }}</p>
|
||||
{% elseif type == 187 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>{{ sender.name }} 评论了你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a></p>
|
||||
<p>评论内容:{{ info.comment.content }}</p>
|
||||
{% elseif type == 188 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>{{ sender.name }} 收藏了你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a></p>
|
||||
{% elseif type == 189 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p>{{ sender.name }} 喜欢了你的文章 <a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a></p>
|
||||
{% elseif type == 506 %}
|
||||
<p>{{ sender.name }} 回复了你的评论:{{ info.comment.content }}</p>
|
||||
<p>回复内容:{{ info.reply.content }}</p>
|
||||
{% elseif type == 507 %}
|
||||
<p>{{ sender.name }} 喜欢了你的评论:{{ info.comment.content }}</p>
|
||||
{% endif %}
|
||||
|
||||
{%- endmacro %}
|
@ -43,32 +43,40 @@
|
||||
<span class="type">课程评价</span>
|
||||
{% elseif value == 8 %}
|
||||
<span class="type">微聊讨论</span>
|
||||
{% elseif value == 9 %}
|
||||
<span class="type">发布评论</span>
|
||||
{% elseif value == 10 %}
|
||||
<span class="type">发布文章</span>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro event_detail_info(history) %}
|
||||
{% set event_info = history.event_info %}
|
||||
{% if history.event_type == 1 %}
|
||||
<p class="order">{{ event_info.order.subject }}</p>
|
||||
{% elseif history.event_type == 2 %}
|
||||
{% set gift_url = url({'for':'home.point_gift.show','id':event_info.point_redeem.gift_id}) %}
|
||||
<p class="gift"><a href="{{ gift_url }}" target="_blank">{{ event_info.point_redeem.gift_name }}</a></p>
|
||||
{% elseif history.event_type == 3 %}
|
||||
{% set gift_url = url({'for':'home.point_gift.show','id':event_info.point_redeem.gift_id}) %}
|
||||
<p class="gift"><a href="{{ gift_url }}" target="_blank">{{ event_info.point_redeem.gift_name }}</a></p>
|
||||
{% elseif history.event_type == 4 %}
|
||||
{%- macro event_detail_info(type,info) %}
|
||||
{% if type == 1 %}
|
||||
<p class="order">{{ info.order.subject }}</p>
|
||||
{% elseif type == 2 %}
|
||||
{% set gift_url = url({'for':'home.point_gift.show','id':info.point_redeem.gift_id}) %}
|
||||
<p class="gift"><a href="{{ gift_url }}" target="_blank">{{ info.point_redeem.gift_name }}</a></p>
|
||||
{% elseif type == 3 %}
|
||||
{% set gift_url = url({'for':'home.point_gift.show','id':info.point_redeem.gift_id}) %}
|
||||
<p class="gift"><a href="{{ gift_url }}" target="_blank">{{ info.point_redeem.gift_name }}</a></p>
|
||||
{% elseif type == 4 %}
|
||||
<span class="none">N/A</span>
|
||||
{% elseif history.event_type == 5 %}
|
||||
{% elseif type == 5 %}
|
||||
<span class="none">N/A</span>
|
||||
{% 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}) %}
|
||||
<p class="course">课程:<a href="{{ course_url }}" target="_blank">{{ event_info.course.title }}</a></p>
|
||||
<p class="chapter">章节:<a href="{{ chapter_url }}" target="_blank">{{ event_info.chapter.title }}</a></p>
|
||||
{% elseif history.event_type == 7 %}
|
||||
{% set course_url = url({'for':'home.course.show','id':event_info.course.id}) %}
|
||||
<p class="course"><a href="{{ course_url }}" target="_blank">{{ event_info.course.title }}</a></p>
|
||||
{% 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}) %}
|
||||
<p class="course">课程:<a href="{{ course_url }}" target="_blank">{{ info.course.title }}</a></p>
|
||||
<p class="chapter">章节:<a href="{{ chapter_url }}" target="_blank">{{ info.chapter.title }}</a></p>
|
||||
{% elseif type == 7 %}
|
||||
{% set course_url = url({'for':'home.course.show','id':info.course.id}) %}
|
||||
<p class="course"><a href="{{ course_url }}" target="_blank">{{ info.course.title }}</a></p>
|
||||
{% elseif type == 8 %}
|
||||
<span class="none">N/A</span>
|
||||
{% elseif type == 9 %}
|
||||
<span class="comment">N/A</span>
|
||||
{% elseif type == 10 %}
|
||||
{% set article_url = url({'for':'home.article.show','id':info.article.id}) %}
|
||||
<p class="article"><a href="{{ article_url }}" target="_blank">{{ info.article.title }}</a></p>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
@ -39,6 +39,16 @@
|
||||
<a href="{{ url({'for':'home.im.index'}) }}" class="nav-im" target="im"><i class="layui-icon layui-icon-chat"></i> 微聊</a>
|
||||
</li>
|
||||
{% if auth_user.id > 0 %}
|
||||
<li class="layui-nav-item">
|
||||
<a href="javascript:">创建</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd><a href="javascript:">提问题</a></dd>
|
||||
<dd><a href="{{ url({'for':'home.article.add'}) }}" target="_blank">写文章</a></dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<a href="{{ url({'for':'home.uc.notifications'}) }}" target="notify"><i class="layui-icon layui-icon-notice"></i> 消息<span id="notify-dot"></span></a>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<a href="javascript:">{{ auth_user.name }}</a>
|
||||
<dl class="layui-nav-child">
|
||||
|
@ -4,12 +4,20 @@
|
||||
|
||||
{{ partial('macros/article') }}
|
||||
|
||||
{% set published_types = {'0':'全部','1':'审核中','2':'已发布','3':'未通过'} %}
|
||||
{% set published = request.get('published','trim','0') %}
|
||||
|
||||
<div class="layout-main clearfix">
|
||||
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
||||
<div class="my-content">
|
||||
<div class="wrap">
|
||||
<div class="my-nav">
|
||||
<span class="title">我的文章</span>
|
||||
{% 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}) %}
|
||||
<a class="{{ class }}" href="{{ url }}">{{ value }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if pager.total_pages > 0 %}
|
||||
<table class="layui-table">
|
||||
@ -19,6 +27,7 @@
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
@ -27,24 +36,34 @@
|
||||
<th>点赞</th>
|
||||
<th>评论</th>
|
||||
<th>收藏</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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}) %}
|
||||
<tr>
|
||||
<td>
|
||||
<p>标题:<a href="{{ article_url }}" target="_blank">{{ item.title }}</a></p>
|
||||
<p>
|
||||
标题:<a href="{{ show_url }}" target="_blank">{{ item.title }}</a>
|
||||
(<span>{{ item.create_time|time_ago }}</span>)
|
||||
</p>
|
||||
<p class="meta">
|
||||
来源:<span class="layui-badge layui-bg-gray">{{ source_type(item.source_type) }}</span>
|
||||
分类:<span class="layui-badge layui-bg-gray">{{ item.category.name }}</span>
|
||||
时间:{{ item.create_time|time_ago }}
|
||||
状态:<span class="layui-badge layui-bg-gray">{{ publish_status(item.published) }}</span>
|
||||
</p>
|
||||
</td>
|
||||
<td>{{ item.view_count }}</td>
|
||||
<td>{{ item.like_count }}</td>
|
||||
<td>{{ item.comment_count }}</td>
|
||||
<td>{{ item.favorite_count }}</td>
|
||||
<td class="center">
|
||||
<a href="{{ edit_url }}" class="layui-btn layui-btn-xs">编辑</a>
|
||||
<a href="javascript:" class="layui-btn layui-btn-xs layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
51
app/Http/Home/Views/user/console/notifications.volt
Normal file
51
app/Http/Home/Views/user/console/notifications.volt
Normal file
@ -0,0 +1,51 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ partial('macros/notification') }}
|
||||
|
||||
<div class="layout-main clearfix">
|
||||
<div class="my-sidebar">{{ partial('user/console/menu') }}</div>
|
||||
<div class="my-content">
|
||||
<div class="wrap">
|
||||
<div class="my-nav">
|
||||
<span class="title">消息提醒</span>
|
||||
</div>
|
||||
<div class="notice-list">
|
||||
{% 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}) %}
|
||||
<div class="comment-card notice-card clearfix">
|
||||
<div class="avatar">
|
||||
<a href="{{ sender_url }}" title="{{ item.sender.name }}" target="_blank">
|
||||
<img src="{{ item.sender.avatar }}!avatar_160" alt="{{ item.sender.name }}">
|
||||
</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="user">
|
||||
<a href="{{ sender_url }}" target="_blank">{{ item.sender.name }}</a>
|
||||
</div>
|
||||
<div class="content">{{ event_info(item) }}</div>
|
||||
<div class="footer">
|
||||
<div class="left">
|
||||
<div class="column">
|
||||
<span class="time" title="{{ date('Y-m-d H:i:s',item.create_time) }}">{{ item.create_time|time_ago }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ partial('partials/pager') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('home/js/user.console.js') }}
|
||||
|
||||
{% endblock %}
|
@ -32,7 +32,7 @@
|
||||
<tr>
|
||||
<td>{{ event_type_info(item.event_type) }}</td>
|
||||
<td>{{ event_point_info(item.event_point) }}</td>
|
||||
<td>{{ event_detail_info(item) }}</td>
|
||||
<td>{{ event_detail_info(item.event_type,item.event_info) }}</td>
|
||||
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
66
app/Listeners/Article.php
Normal file
66
app/Listeners/Article.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Models\Article as ArticleModel;
|
||||
use Phalcon\Events\Event as PhEvent;
|
||||
|
||||
class Article extends Listener
|
||||
{
|
||||
|
||||
public function afterCreate(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUpdate(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterDelete(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterRestore(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterApprove(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterReject(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterView(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterFavorite(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUndoFavorite(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterLike(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUndoLike(PhEvent $event, $source, ArticleModel $article)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
46
app/Listeners/Comment.php
Normal file
46
app/Listeners/Comment.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Models\Comment as CommentModel;
|
||||
use Phalcon\Events\Event as PhEvent;
|
||||
|
||||
class Comment extends Listener
|
||||
{
|
||||
|
||||
public function afterCreate(PhEvent $event, $source, CommentModel $comment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUpdate(PhEvent $event, $source, CommentModel $comment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterDelete(PhEvent $event, $source, CommentModel $comment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterRestore(PhEvent $event, $source, CommentModel $comment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterReply(PhEvent $event, $source, CommentModel $reply)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterLike(PhEvent $event, $source, CommentModel $comment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUndoLike(PhEvent $event, $source, CommentModel $comment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
56
app/Listeners/Consult.php
Normal file
56
app/Listeners/Consult.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Models\Consult as ConsultModel;
|
||||
use Phalcon\Events\Event as PhEvent;
|
||||
|
||||
class Consult extends Listener
|
||||
{
|
||||
|
||||
public function afterCreate(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUpdate(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterDelete(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterRestore(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterApprove(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterReject(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterReply(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterLike(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function afterUndoLike(PhEvent $event, $source, ConsultModel $consult)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Models\ImMessage as ImMessageModel;
|
||||
use App\Services\Logic\Point\PointHistory as PointHistoryService;
|
||||
use App\Services\Logic\Point\History\ImDiscuss as ImPointHistory;
|
||||
use Phalcon\Events\Event as PhEvent;
|
||||
|
||||
class ImMessage extends Listener
|
||||
@ -26,9 +26,9 @@ class ImMessage extends Listener
|
||||
|
||||
if ($content) return;
|
||||
|
||||
$service = new PointHistoryService();
|
||||
$service = new ImPointHistory();
|
||||
|
||||
$service->handleImDiscuss($message);
|
||||
$service->handle($message);
|
||||
|
||||
$tomorrow = strtotime($todayDate) + 86400;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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 [
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 => '未通过',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,6 +49,20 @@ class Consult extends Model
|
||||
*/
|
||||
public $replier_id = 0;
|
||||
|
||||
/**
|
||||
* 终端类型
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $client_type = 0;
|
||||
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
/**
|
||||
* 提问
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
206
app/Models/Notification.php
Normal file
206
app/Models/Notification.php
Normal file
@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class Notification extends Model
|
||||
{
|
||||
|
||||
/* -------------- 课程相关 -------------- */
|
||||
|
||||
const TYPE_COURSE_CREATED = 100;
|
||||
const TYPE_COURSE_UPDATED = 101;
|
||||
const TYPE_COURSE_DELETED = 102;
|
||||
const TYPE_COURSE_RESTORED = 103;
|
||||
const TYPE_COURSE_APPROVED = 104;
|
||||
const TYPE_COURSE_REJECTED = 105;
|
||||
const TYPE_COURSE_FEATURED = 106;
|
||||
const TYPE_COURSE_CONSULTED = 107;
|
||||
const TYPE_COURSE_FAVORITED = 108;
|
||||
const TYPE_COURSE_REVIEWED = 109;
|
||||
|
||||
/* -------------- 章节相关 -------------- */
|
||||
|
||||
const TYPE_CHAPTER_CREATED = 120;
|
||||
const TYPE_CHAPTER_UPDATED = 121;
|
||||
const TYPE_CHAPTER_DELETED = 122;
|
||||
const TYPE_CHAPTER_RESTORED = 123;
|
||||
const TYPE_CHAPTER_APPROVED = 124;
|
||||
const TYPE_CHAPTER_REJECTED = 125;
|
||||
const TYPE_CHAPTER_CONSULTED = 126;
|
||||
const TYPE_CHAPTER_COMMENTED = 127;
|
||||
const TYPE_CHAPTER_LIKED = 128;
|
||||
|
||||
/* -------------- 咨询相关 -------------- */
|
||||
|
||||
const TYPE_CONSULT_CREATED = 140;
|
||||
const TYPE_CONSULT_UPDATED = 141;
|
||||
const TYPE_CONSULT_DELETED = 142;
|
||||
const TYPE_CONSULT_RESTORED = 143;
|
||||
const TYPE_CONSULT_APPROVED = 144;
|
||||
const TYPE_CONSULT_REJECTED = 145;
|
||||
const TYPE_CONSULT_COMMENTED = 146;
|
||||
const TYPE_CONSULT_LIKED = 147;
|
||||
|
||||
/* -------------- 评价相关 -------------- */
|
||||
|
||||
const TYPE_REVIEW_CREATED = 160;
|
||||
const TYPE_REVIEW_UPDATED = 161;
|
||||
const TYPE_REVIEW_DELETED = 162;
|
||||
const TYPE_REVIEW_RESTORED = 163;
|
||||
const TYPE_REVIEW_APPROVED = 164;
|
||||
const TYPE_REVIEW_REJECTED = 165;
|
||||
const TYPE_REVIEW_COMMENTED = 166;
|
||||
const TYPE_REVIEW_LIKED = 167;
|
||||
|
||||
/* -------------- 文章相关 -------------- */
|
||||
|
||||
const TYPE_ARTICLE_CREATED = 180;
|
||||
const TYPE_ARTICLE_UPDATED = 181;
|
||||
const TYPE_ARTICLE_DELETED = 182;
|
||||
const TYPE_ARTICLE_RESTORED = 183;
|
||||
const TYPE_ARTICLE_APPROVED = 184;
|
||||
const TYPE_ARTICLE_REJECTED = 185;
|
||||
const TYPE_ARTICLE_FEATURED = 186;
|
||||
const TYPE_ARTICLE_COMMENTED = 187;
|
||||
const TYPE_ARTICLE_FAVORITED = 188;
|
||||
const TYPE_ARTICLE_LIKED = 189;
|
||||
|
||||
/* -------------- 问题相关 -------------- */
|
||||
|
||||
const TYPE_QUESTION_CREATED = 200;
|
||||
const TYPE_QUESTION_UPDATED = 201;
|
||||
const TYPE_QUESTION_DELETED = 202;
|
||||
const TYPE_QUESTION_RESTORED = 203;
|
||||
const TYPE_QUESTION_APPROVED = 204;
|
||||
const TYPE_QUESTION_REJECTED = 205;
|
||||
const TYPE_QUESTION_ANSWERED = 206;
|
||||
const TYPE_QUESTION_COMMENTED = 207;
|
||||
const TYPE_QUESTION_FAVORITED = 208;
|
||||
const TYPE_QUESTION_LIKED = 209;
|
||||
|
||||
/* -------------- 回答相关 -------------- */
|
||||
|
||||
const TYPE_ANSWER_CREATED = 220;
|
||||
const TYPE_ANSWER_UPDATED = 221;
|
||||
const TYPE_ANSWER_DELETED = 222;
|
||||
const TYPE_ANSWER_RESTORED = 223;
|
||||
const TYPE_ANSWER_APPROVED = 224;
|
||||
const TYPE_ANSWER_REJECTED = 225;
|
||||
const TYPE_ANSWER_ACCEPTED = 226;
|
||||
const TYPE_ANSWER_COMMENTED = 227;
|
||||
const TYPE_ANSWER_LIKED = 228;
|
||||
|
||||
/* -------------- 微聊相关 -------------- */
|
||||
|
||||
const TYPE_FRIEND_REQUEST = 0; // 好友请求
|
||||
const TYPE_FRIEND_ACCEPTED = 0; // 好友被接受
|
||||
const TYPE_FRIEND_REFUSED = 0; // 好友被拒绝
|
||||
const TYPE_GROUP_REQUEST = 0; // 入群请求
|
||||
const TYPE_GROUP_ACCEPTED = 0; // 入群被接受
|
||||
const TYPE_GROUP_REFUSED = 0; // 入群被拒绝
|
||||
|
||||
/* -------------- 评论相关 -------------- */
|
||||
|
||||
const TYPE_COMMENT_CREATED = 500;
|
||||
const TYPE_COMMENT_UPDATED = 501;
|
||||
const TYPE_COMMENT_DELETED = 502;
|
||||
const TYPE_COMMENT_RESTORED = 503;
|
||||
const TYPE_COMMENT_APPROVED = 504;
|
||||
const TYPE_COMMENT_REJECTED = 505;
|
||||
const TYPE_COMMENT_REPLIED = 506;
|
||||
const TYPE_COMMENT_LIKED = 507;
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id = 0;
|
||||
|
||||
/**
|
||||
* 发送方编号
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $sender_id = 0;
|
||||
|
||||
/**
|
||||
* 接收方编号
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $receiver_id = 0;
|
||||
|
||||
/**
|
||||
* 事件编号
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $event_id = 0;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $event_type = 0;
|
||||
|
||||
/**
|
||||
* 事件内容
|
||||
*
|
||||
* @var array|string
|
||||
*/
|
||||
public $event_info = [];
|
||||
|
||||
/**
|
||||
* 阅读标识
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $viewed = 0;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $create_time = 0;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $update_time = 0;
|
||||
|
||||
public function getSource(): string
|
||||
{
|
||||
return 'kg_notification';
|
||||
}
|
||||
|
||||
public function beforeCreate()
|
||||
{
|
||||
if (is_array($this->event_info) || is_object($this->event_info)) {
|
||||
$this->event_info = kg_json_encode($this->event_info);
|
||||
}
|
||||
|
||||
$this->create_time = time();
|
||||
}
|
||||
|
||||
public function beforeUpdate()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
26
app/Models/Reason.php
Normal file
26
app/Models/Reason.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class Reason
|
||||
{
|
||||
|
||||
public static function articleRejectOptions()
|
||||
{
|
||||
return [
|
||||
101 => '内容质量差',
|
||||
102 => '旧闻重提',
|
||||
103 => '内容不实',
|
||||
104 => '标题夸张',
|
||||
105 => '题文不符',
|
||||
106 => '低俗色情',
|
||||
107 => '广告软文',
|
||||
108 => '封面反感',
|
||||
109 => '归类与主题不符',
|
||||
110 => '抄袭他人作品',
|
||||
111 => '内容涉嫌违法',
|
||||
112 => '其它问题',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,20 @@ class Review extends Model
|
||||
*/
|
||||
public $owner_id = 0;
|
||||
|
||||
/**
|
||||
* 终端类型
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $client_type = 0;
|
||||
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
/**
|
||||
* 评价内容
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 . ')';
|
||||
});
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -92,4 +92,9 @@ class Comment extends Repository
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function countComments()
|
||||
{
|
||||
return (int)CommentModel::count(['conditions' => 'deleted = 0']);
|
||||
}
|
||||
|
||||
}
|
||||
|
106
app/Repos/Notification.php
Normal file
106
app/Repos/Notification.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repos;
|
||||
|
||||
use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
|
||||
use App\Models\Notification as NotificationModel;
|
||||
use Phalcon\Mvc\Model;
|
||||
use Phalcon\Mvc\Model\Resultset;
|
||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||
|
||||
class Notification extends Repository
|
||||
{
|
||||
|
||||
public function paginate($where = [], $sort = 'latest', $page = 1, $limit = 15)
|
||||
{
|
||||
$builder = $this->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]);
|
||||
}
|
||||
|
||||
}
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 ";
|
||||
|
@ -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],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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'],
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user