1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-26 20:52:44 +08:00

Merge branch 'koogua/v1.5.6' into demo

This commit is contained in:
koogua 2022-08-08 17:13:16 +08:00
commit 95f179eb57
799 changed files with 19267 additions and 27222 deletions

1
.gitignore vendored
View File

@ -5,7 +5,6 @@
/config/xs.course.ini /config/xs.course.ini
/config/xs.article.ini /config/xs.article.ini
/config/xs.question.ini /config/xs.question.ini
/config/xs.group.ini
/config/xs.user.ini /config/xs.user.ini
/config/alipay/*.crt /config/alipay/*.crt
/config/wxpay/*.pem /config/wxpay/*.pem

View File

@ -1,3 +1,9 @@
### [v1.5.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.6)(2022-08-08)
- 增加应用内命令行migrations
- 移除群组和微聊模块
- kindeditor替换vditor - markdown转html
### [v1.5.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.5)(2022-07-27) ### [v1.5.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.5)(2022-07-27)
- 修正获分类查询条件 - 修正获分类查询条件

View File

@ -1,51 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\User as UserRepo;
class ImFriendUserList extends Builder
{
public function handleFriends(array $relations)
{
$users = $this->getFriends($relations);
foreach ($relations as $key => $value) {
$relations[$key]['friend'] = $users[$value['friend_id']] ?? new \stdClass();
}
return $relations;
}
public function getFriends(array $relations)
{
$ids = kg_array_column($relations, 'friend_id');
$userRepo = new UserRepo();
$columns = [
'id', 'name', 'avatar', 'title', 'about', 'vip',
'gender', 'area', 'active_time',
];
$users = $userRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,86 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class ImGroupList extends Builder
{
public function handleGroups(array $groups)
{
$baseUrl = kg_cos_url();
foreach ($groups as $key => $group) {
$groups[$key]['avatar'] = $baseUrl . $group['avatar'];
}
return $groups;
}
public function handleCourses(array $groups)
{
$courses = $this->getCourses($groups);
foreach ($groups as $key => $group) {
$groups[$key]['course'] = $courses[$group['course_id']] ?? new \stdClass();
}
return $groups;
}
public function handleUsers(array $groups)
{
$users = $this->getUsers($groups);
foreach ($groups as $key => $group) {
$groups[$key]['owner'] = $users[$group['owner_id']] ?? new \stdClass();
}
return $groups;
}
public function getCourses(array $groups)
{
$ids = kg_array_column($groups, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($courses->toArray() as $course) {
$result[$course['id']] = $course;
}
return $result;
}
public function getUsers(array $groups)
{
$ids = kg_array_column($groups, 'owner_id');
$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;
}
}

View File

@ -1,105 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\User as UserRepo;
class ImGroupUserList extends Builder
{
public function handleGroups(array $relations)
{
$groups = $this->getGroups($relations);
foreach ($relations as $key => $value) {
$relations[$key]['group'] = $groups[$value['group_id']] ?? new \stdClass();
}
return $relations;
}
public function handleUsers(array $relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
}
return $relations;
}
public function getUsers(array $relations)
{
$ids = kg_array_column($relations, 'user_id');
$userRepo = new UserRepo();
$columns = ['id', 'name', 'avatar', 'title', 'about', 'vip', 'gender', 'area'];
$users = $userRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
public function getGroups(array $relations)
{
$ids = kg_array_column($relations, 'group_id');
$groupRepo = new ImGroupRepo();
$columns = ['id', 'type', 'name', 'avatar', 'about', 'owner_id', 'user_count', 'msg_count'];
$groups = $groupRepo->findByIds($ids, $columns);
$users = $this->getGroupOwners($groups->toArray());
$baseUrl = kg_cos_url();
$result = [];
foreach ($groups->toArray() as $group) {
$group['avatar'] = $baseUrl . $group['avatar'];
$group['owner'] = $users[$group['owner_id']] ?? new \stdClass();
unset($group['owner_id']);
$result[$group['id']] = $group;
}
return $result;
}
protected function getGroupOwners(array $groups)
{
$ids = kg_array_column($groups, 'owner_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name']);
$result = [];
if ($users->count() > 0) {
foreach ($users->toArray() as $user) {
$result[$user['id']] = $user;
}
}
return $result;
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\User as UserRepo;
class ImMessageList extends Builder
{
public function handleSenders(array $messages)
{
$users = $this->getSenders($messages);
foreach ($messages as $key => $message) {
$messages[$key]['sender'] = $users[$message['sender_id']] ?? new \stdClass();
}
return $messages;
}
public function getSenders(array $messages)
{
$ids = kg_array_column($messages, 'sender_id');
$userRepo = new UserRepo();
$users = $userRepo->findShallowUserByIds($ids);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,89 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\ImMessage;
use App\Models\ImMessage as ImMessageModel;
use App\Models\User as UserModel;
use App\Repos\ImGroup as ImGroupRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImActiveGroupList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_active_group_list';
}
public function getContent($id = null)
{
$groups = $this->findGroups();
if (empty($groups)) return [];
$result = [];
foreach ($groups as $group) {
$result[] = [
'id' => $group->id,
'type' => $group->type,
'name' => $group->name,
'avatar' => $group->avatar,
'about' => $group->about,
'user_count' => $group->user_count,
'msg_count' => $group->msg_count,
];
}
return $result;
}
/**
* @param int $days
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findGroups($days = 7, $limit = 12)
{
$result = [];
$startTime = strtotime("-{$days} days");
$endTime = time();
$rows = ImMessageModel::query()
->columns(['receiver_id', 'total_count' => 'count(receiver_id)'])
->groupBy('receiver_id')
->orderBy('total_count DESC')
->where('receiver_type = :type:', ['type' => ImMessageModel::TYPE_GROUP])
->betweenWhere('create_time', $startTime, $endTime)
->andWhere('deleted = 0')
->limit($limit)
->execute();
if ($rows->count() > 0) {
$ids = kg_array_column($rows->toArray(), 'receiver_id');
$groupRepo = new ImGroupRepo();
$result = $groupRepo->findByIds($ids);
}
return $result;
}
}

View File

@ -1,85 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\ImMessage as ImMessageModel;
use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImActiveUserList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_active_user_list';
}
public function getContent($id = null)
{
$users = $this->findUsers($id);
if (empty($users)) return [];
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $days
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findUsers($days = 7, $limit = 12)
{
$result = [];
$startTime = strtotime("-{$days} days");
$endTime = time();
$rows = ImMessageModel::query()
->columns(['sender_id', 'total_count' => 'count(sender_id)'])
->groupBy('sender_id')
->orderBy('total_count DESC')
->betweenWhere('create_time', $startTime, $endTime)
->limit($limit)
->execute();
if ($rows->count() > 0) {
$ids = kg_array_column($rows->toArray(), 'sender_id');
$userRepo = new UserRepo();
$result = $userRepo->findByIds($ids);
}
return $result;
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Repos\ImGroup as ImGroupRepo;
class ImGroup extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "im_group:{$id}";
}
public function getContent($id = null)
{
$groupRepo = new ImGroupRepo();
$group = $groupRepo->findById($id);
return $group ?: null;
}
}

View File

@ -1,88 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\ImMessage as ImMessageModel;
use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImGroupActiveUserList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "im_group_active_user_list:{$id}";
}
public function getContent($id = null)
{
$users = $this->findUsers($id);
if (empty($users)) return [];
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $groupId
* @param int $days
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findUsers($groupId, $days = 7, $limit = 5)
{
$result = [];
$startTime = strtotime("-{$days} days");
$endTime = time();
$rows = ImMessageModel::query()
->columns(['sender_id', 'total_count' => 'count(sender_id)'])
->groupBy('sender_id')
->orderBy('total_count DESC')
->where('receiver_id = :group_id:', ['group_id' => $groupId])
->andWhere('receiver_type = :type:', ['type' => ImMessageModel::TYPE_GROUP])
->betweenWhere('create_time', $startTime, $endTime)
->limit($limit)
->execute();
if ($rows->count() > 0) {
$ids = kg_array_column($rows->toArray(), 'sender_id');
$userRepo = new UserRepo();
$result = $userRepo->findByIds($ids);
}
return $result;
}
}

View File

@ -1,79 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\ImGroup as ImGroupModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImNewGroupList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_new_group_list';
}
public function getContent($id = null)
{
$limit = 12;
$groups = $this->findGroups($limit);
if ($groups->count() == 0) {
return [];
}
return $this->handleContent($groups);
}
/**
* @param ImGroupModel[] $groups
* @return array
*/
protected function handleContent($groups)
{
$result = [];
foreach ($groups as $group) {
$result[] = [
'id' => $group->id,
'type' => $group->type,
'name' => $group->name,
'avatar' => $group->avatar,
'about' => $group->about,
'user_count' => $group->user_count,
'msg_count' => $group->msg_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|ImGroupModel[]
*/
public function findGroups($limit = 12)
{
return ImGroupModel::query()
->where('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -1,77 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\User as UserModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImNewUserList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_new_user_list';
}
public function getContent($id = null)
{
$limit = 12;
$users = $this->findUsers($limit);
if ($users->count() == 0) {
return [];
}
return $this->handleContent($users);
}
/**
* @param UserModel[] $users
* @return array
*/
protected function handleContent($users)
{
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
public function findUsers($limit = 12)
{
return UserModel::query()
->where('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\ImGroup as ImGroupModel;
class MaxImGroupId extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'max_im_group_id';
}
public function getContent($id = null)
{
$group = ImGroupModel::findFirst(['order' => 'id DESC']);
return $group->id ?? 0;
}
}

View File

@ -12,7 +12,6 @@ use App\Repos\Article as ArticleRepo;
use App\Repos\Comment as CommentRepo; use App\Repos\Comment as CommentRepo;
use App\Repos\Consult as ConsultRepo; use App\Repos\Consult as ConsultRepo;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Repos\ImGroup as GroupRepo;
use App\Repos\Package as PackageRepo; use App\Repos\Package as PackageRepo;
use App\Repos\Question as QuestionRepo; use App\Repos\Question as QuestionRepo;
use App\Repos\Review as ReviewRepo; use App\Repos\Review as ReviewRepo;
@ -42,7 +41,6 @@ class SiteGlobalStat extends Cache
$answerRepo = new AnswerRepo(); $answerRepo = new AnswerRepo();
$commentRepo = new CommentRepo(); $commentRepo = new CommentRepo();
$consultRepo = new ConsultRepo(); $consultRepo = new ConsultRepo();
$groupRepo = new GroupRepo();
$packageRepo = new PackageRepo(); $packageRepo = new PackageRepo();
$reviewRepo = new ReviewRepo(); $reviewRepo = new ReviewRepo();
$topicRepo = new TopicRepo(); $topicRepo = new TopicRepo();
@ -55,7 +53,6 @@ class SiteGlobalStat extends Cache
'answer_count' => $answerRepo->countAnswers(), 'answer_count' => $answerRepo->countAnswers(),
'comment_count' => $commentRepo->countComments(), 'comment_count' => $commentRepo->countComments(),
'consult_count' => $consultRepo->countConsults(), 'consult_count' => $consultRepo->countConsults(),
'group_count' => $groupRepo->countGroups(),
'vip_count' => $userRepo->countVipUsers(), 'vip_count' => $userRepo->countVipUsers(),
'package_count' => $packageRepo->countPackages(), 'package_count' => $packageRepo->countPackages(),
'review_count' => $reviewRepo->countReviews(), 'review_count' => $reviewRepo->countReviews(),

View File

@ -0,0 +1,13 @@
<?php
/**
* @copyright Copyright (c) 2022 深圳市酷瓜软件有限公司
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @link https://www.koogua.com
*/
namespace App\Console\Migrations;
abstract class Migration
{
abstract public function run();
}

View File

@ -0,0 +1,162 @@
<?php
/**
* @copyright Copyright (c) 2022 深圳市酷瓜软件有限公司
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @link https://www.koogua.com
*/
namespace App\Console\Migrations;
use App\Models\Answer as AnswerModel;
use App\Models\Article as ArticleModel;
use App\Models\Course as CourseModel;
use App\Models\Help as HelpModel;
use App\Models\Page as PageModel;
use App\Models\PointGift as PointGiftModel;
use App\Models\Question as QuestionModel;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use Phalcon\Mvc\Model\Resultset;
class V202207291145 extends Migration
{
/**
* @var GithubFlavoredMarkdownConverter
*/
protected $markdownConverter;
public function run()
{
$this->initMarkdownConverter();
$this->courseMarkdownToHtml();
$this->articleMarkdownToHtml();
$this->questionMarkdownToHtml();
$this->answerMarkdownToHtml();
$this->pageMarkdownToHtml();
$this->helpMarkdownToHtml();
$this->pointGiftMarkdownToHtml();
}
protected function initMarkdownConverter()
{
$this->markdownConverter = new GithubFlavoredMarkdownConverter([
'html_input' => 'escape',
'allow_unsafe_links' => false,
]);
}
protected function articleMarkdownToHtml()
{
/**
* @var $articles Resultset|ArticleModel[]
*/
$articles = ArticleModel::query()->execute();
if ($articles->count() == 0) return;
foreach ($articles as $article) {
$content = $this->markdownConverter->convertToHtml($article->content);
$article->content = $content;
$article->update();
}
}
protected function courseMarkdownToHtml()
{
/**
* @var $courses Resultset|CourseModel[]
*/
$courses = CourseModel::query()->execute();
if ($courses->count() == 0) return;
foreach ($courses as $course) {
$details = $this->markdownConverter->convertToHtml($course->details);
$course->details = $details;
$course->update();
}
}
protected function questionMarkdownToHtml()
{
/**
* @var $questions Resultset|QuestionModel[]
*/
$questions = QuestionModel::query()->execute();
if ($questions->count() == 0) return;
foreach ($questions as $question) {
$content = $this->markdownConverter->convertToHtml($question->content);
$question->content = $content;
$question->update();
}
}
protected function answerMarkdownToHtml()
{
/**
* @var $answers Resultset|AnswerModel[]
*/
$answers = AnswerModel::query()->execute();
if ($answers->count() == 0) return;
foreach ($answers as $answer) {
$content = $this->markdownConverter->convertToHtml($answer->content);
$answer->content = $content;
$answer->update();
}
}
protected function pageMarkdownToHtml()
{
/**
* @var $pages Resultset|PageModel[]
*/
$pages = PageModel::query()->execute();
if ($pages->count() == 0) return;
foreach ($pages as $page) {
$content = $this->markdownConverter->convertToHtml($page->content);
$page->content = $content;
$page->update();
}
}
protected function helpMarkdownToHtml()
{
/**
* @var $helps Resultset|HelpModel[]
*/
$helps = HelpModel::query()->execute();
if ($helps->count() == 0) return;
foreach ($helps as $help) {
$content = $this->markdownConverter->convertToHtml($help->content);
$help->content = $content;
$help->update();
}
}
protected function pointGiftMarkdownToHtml()
{
/**
* @var $gifts Resultset|PointGiftModel[]
*/
$gifts = PointGiftModel::query()
->where('type = :type:', ['type' => PointGiftModel::TYPE_GOODS])
->execute();
if ($gifts->count() == 0) return;
foreach ($gifts as $gift) {
$details = $this->markdownConverter->convertToHtml($gift->details);
$gift->details = $details;
$gift->update();
}
}
}

View File

@ -1,131 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Console\Tasks;
use App\Models\ImGroup as GroupModel;
use App\Services\Search\GroupDocument;
use App\Services\Search\GroupSearcher;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class GroupIndexTask extends Task
{
/**
* 搜索测试
*
* @command: php console.php group_index search {query}
* @param array $params
* @throws \XSException
*/
public function searchAction($params)
{
$query = $params[0] ?? null;
if (!$query) {
exit('please special a query word' . PHP_EOL);
}
$result = $this->searchGroups($query);
var_export($result);
}
/**
* 清空索引
*
* @command: php console.php group_index clean
*/
public function cleanAction()
{
$this->cleanGroupIndex();
}
/**
* 重建索引
*
* @command: php console.php group_index rebuild
*/
public function rebuildAction()
{
$this->rebuildGroupIndex();
}
/**
* 清空索引
*/
protected function cleanGroupIndex()
{
$handler = new GroupSearcher();
$index = $handler->getXS()->getIndex();
echo '------ start clean group index ------' . PHP_EOL;
$index->clean();
echo '------ end clean group index ------' . PHP_EOL;
}
/**
* 重建索引
*/
protected function rebuildGroupIndex()
{
$groups = $this->findGroups();
if ($groups->count() == 0) return;
$handler = new GroupSearcher();
$documenter = new GroupDocument();
$index = $handler->getXS()->getIndex();
echo '------ start rebuild group index ------' . PHP_EOL;
$index->beginRebuild();
foreach ($groups as $group) {
$document = $documenter->setDocument($group);
$index->add($document);
}
$index->endRebuild();
echo '------ end rebuild group index ------' . PHP_EOL;
}
/**
* 搜索课程
*
* @param string $query
* @return array
* @throws \XSException
*/
protected function searchGroups($query)
{
$handler = new GroupSearcher();
return $handler->search($query);
}
/**
* 查找课程
*
* @return ResultsetInterface|Resultset|GroupModel[]
*/
protected function findGroups()
{
return GroupModel::query()
->where('published = 1')
->andWhere('deleted = 0')
->execute();
}
}

View File

@ -11,9 +11,7 @@ use App\Models\Order as OrderModel;
use App\Models\Refund as RefundModel; use App\Models\Refund as RefundModel;
use App\Models\Task as TaskModel; use App\Models\Task as TaskModel;
use App\Models\Trade as TradeModel; use App\Models\Trade as TradeModel;
use App\Repos\Course as CourseRepo;
use App\Repos\CourseUser as CourseUserRepo; use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Repos\Order as OrderRepo; use App\Repos\Order as OrderRepo;
use App\Repos\Refund as RefundRepo; use App\Repos\Refund as RefundRepo;
use App\Repos\Trade as TradeRepo; use App\Repos\Trade as TradeRepo;
@ -178,18 +176,6 @@ class RefundTask extends Task
throw new \RuntimeException('Delete Course User Failed'); throw new \RuntimeException('Delete Course User Failed');
} }
} }
$courseRepo = new CourseRepo();
$group = $courseRepo->findImGroup($order->item_id);
$groupUserRepo = new ImGroupUserRepo();
$groupUser = $groupUserRepo->findGroupUser($group->id, $order->owner_id);
if ($groupUser) {
if ($groupUser->delete() === false) {
throw new \RuntimeException('Delete Group User Failed');
}
}
} }
/** /**
@ -200,8 +186,6 @@ class RefundTask extends Task
protected function handlePackageOrderRefund(OrderModel $order) protected function handlePackageOrderRefund(OrderModel $order)
{ {
$courseUserRepo = new CourseUserRepo(); $courseUserRepo = new CourseUserRepo();
$groupUserRepo = new ImGroupUserRepo();
$courseRepo = new CourseRepo();
$itemInfo = $order->item_info; $itemInfo = $order->item_info;
@ -215,15 +199,6 @@ class RefundTask extends Task
throw new \RuntimeException('Delete Course User Failed'); throw new \RuntimeException('Delete Course User Failed');
} }
} }
$group = $courseRepo->findImGroup($course['id']);
$groupUser = $groupUserRepo->findGroupUser($group->id, $order->owner_id);
if ($groupUser) {
if ($groupUser->delete() === false) {
throw new \RuntimeException('Delete Group User Failed');
}
}
} }
} }

View File

@ -11,7 +11,6 @@ use App\Library\Sitemap;
use App\Models\Article as ArticleModel; use App\Models\Article as ArticleModel;
use App\Models\Course as CourseModel; use App\Models\Course as CourseModel;
use App\Models\Help as HelpModel; use App\Models\Help as HelpModel;
use App\Models\ImGroup as ImGroupModel;
use App\Models\Page as PageModel; use App\Models\Page as PageModel;
use App\Models\Question as QuestionModel; use App\Models\Question as QuestionModel;
use App\Models\Topic as TopicModel; use App\Models\Topic as TopicModel;
@ -46,7 +45,6 @@ class SitemapTask extends Task
$this->addQuestions(); $this->addQuestions();
$this->addTeachers(); $this->addTeachers();
$this->addTopics(); $this->addTopics();
$this->addImGroups();
$this->addHelps(); $this->addHelps();
$this->addPages(); $this->addPages();
$this->addOthers(); $this->addOthers();
@ -155,21 +153,6 @@ class SitemapTask extends Task
} }
} }
protected function addImGroups()
{
/**
* @var Resultset|ImGroupModel[] $groups
*/
$groups = ImGroupModel::query()->where('published = 1')->execute();
if ($groups->count() == 0) return;
foreach ($groups as $group) {
$loc = sprintf('%s/im/group/%s', $this->siteUrl, $group->id);
$this->sitemap->addItem($loc, 0.6);
}
}
protected function addPages() protected function addPages()
{ {
/** /**
@ -203,7 +186,6 @@ class SitemapTask extends Task
protected function addOthers() protected function addOthers()
{ {
$this->sitemap->addItem("{$this->siteUrl}/course/list", 0.6); $this->sitemap->addItem("{$this->siteUrl}/course/list", 0.6);
$this->sitemap->addItem("{$this->siteUrl}/im/group/list", 0.6);
$this->sitemap->addItem("{$this->siteUrl}/teacher/list", 0.6); $this->sitemap->addItem("{$this->siteUrl}/teacher/list", 0.6);
$this->sitemap->addItem("{$this->siteUrl}/vip", 0.6); $this->sitemap->addItem("{$this->siteUrl}/vip", 0.6);
$this->sitemap->addItem("{$this->siteUrl}/help", 0.6); $this->sitemap->addItem("{$this->siteUrl}/help", 0.6);

View File

@ -1,65 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Console\Tasks;
use App\Repos\ImGroup as GroupRepo;
use App\Services\Search\GroupDocument;
use App\Services\Search\GroupSearcher;
use App\Services\Sync\GroupIndex as GroupIndexSync;
class SyncGroupIndexTask extends Task
{
public function mainAction()
{
$redis = $this->getRedis();
$key = $this->getSyncKey();
$groupIds = $redis->sRandMember($key, 1000);
if (!$groupIds) return;
$groupRepo = new GroupRepo();
$groups = $groupRepo->findByIds($groupIds);
if ($groups->count() == 0) return;
$document = new GroupDocument();
$handler = new GroupSearcher();
$index = $handler->getXS()->getIndex();
$index->openBuffer();
foreach ($groups as $group) {
$doc = $document->setDocument($group);
if ($group->published == 1) {
$index->update($doc);
} else {
$index->del($group->id);
}
}
$index->closeBuffer();
$redis->sRem($key, ...$groupIds);
}
protected function getSyncKey()
{
$sync = new GroupIndexSync();
return $sync->getSyncKey();
}
}

View File

@ -7,16 +7,20 @@
namespace App\Console\Tasks; namespace App\Console\Tasks;
use App\Caches\AppInfo as AppInfoCache;
use App\Caches\NavTreeList as NavTreeListCache; use App\Caches\NavTreeList as NavTreeListCache;
use App\Caches\Setting as SettingCache; use App\Caches\Setting as SettingCache;
use App\Caches\AppInfo as AppInfoCache; use App\Models\MigrationTask as MigrationTaskModel;
use App\Models\Setting as SettingModel; use App\Models\Setting as SettingModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class UpgradeTask extends Task class UpgradeTask extends Task
{ {
public function mainAction() public function mainAction()
{ {
$this->migrateAction();
$this->resetAppInfoAction(); $this->resetAppInfoAction();
$this->resetSettingAction(); $this->resetSettingAction();
$this->resetAnnotationAction(); $this->resetAnnotationAction();
@ -25,6 +29,51 @@ class UpgradeTask extends Task
$this->resetNavAction(); $this->resetNavAction();
} }
/**
* 执行迁移
*
* @command: php console.php upgrade migrate
*/
public function migrateAction()
{
$tasks = $this->findMigrationTasks();
$versionList = [];
if ($tasks->count() > 0) {
$versionList = kg_array_column($tasks->toArray(), 'version');
}
$files = scandir(app_path('Console/Migrations'));
foreach ($files as $file) {
if (preg_match('/^V[0-9]+\.php$/', $file)) {
$version = substr($file, 0, -4);
if (!in_array($version, $versionList)) {
$startTime = time();
$className = "\App\Console\Migrations\\{$version}";
$obj = new $className();
$obj->run();
$endTime = time();
$task = new MigrationTaskModel();
$task->version = $version;
$task->start_time = $startTime;
$task->end_time = $endTime;
$task->create();
echo "------ console migration {$version} ok ------" . PHP_EOL;
}
}
}
}
/** /**
* 重置应用信息 * 重置应用信息
* *
@ -144,6 +193,14 @@ class UpgradeTask extends Task
echo '------ end reset navigation ------' . PHP_EOL; echo '------ end reset navigation ------' . PHP_EOL;
} }
/**
* @return ResultsetInterface|Resultset|MigrationTaskModel[]
*/
protected function findMigrationTasks()
{
return MigrationTaskModel::query()->execute();
}
protected function handlePhKeys($keys) protected function handlePhKeys($keys)
{ {
return array_map(function ($key) { return array_map(function ($key) {

View File

@ -1,155 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\ImGroup as ImGroupService;
/**
* @RoutePrefix("/admin/im/group")
*/
class ImGroupController extends Controller
{
/**
* @Get("/{id:[0-9]+}/users", name="admin.im_group.users")
*/
public function usersAction($id)
{
$service = new ImGroupService();
$group = $service->getGroup($id);
$pager = $service->getGroupUsers($id);
$this->view->setVar('group', $group);
$this->view->setVar('pager', $pager);
}
/**
* @Get("/list", name="admin.im_group.list")
*/
public function listAction()
{
$groupService = new ImGroupService();
$pager = $groupService->getGroups();
$this->view->setVar('pager', $pager);
}
/**
* @Get("/search", name="admin.im_group.search")
*/
public function searchAction()
{
$groupService = new ImGroupService();
$types = $groupService->getGroupTypes();
$this->view->setVar('types', $types);
}
/**
* @Get("/add", name="admin.im_group.add")
*/
public function addAction()
{
}
/**
* @Get("/{id:[0-9]+}/edit", name="admin.im_group.edit")
*/
public function editAction($id)
{
$groupService = new ImGroupService();
$group = $groupService->getGroup($id);
$this->view->setVar('group', $group);
}
/**
* @Post("/create", name="admin.im_group.create")
*/
public function createAction()
{
$groupService = new ImGroupService();
$group = $groupService->createGroup();
$location = $this->url->get([
'for' => 'admin.im_group.edit',
'id' => $group->id,
]);
$content = [
'location' => $location,
'msg' => '创建群组成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/update", name="admin.im_group.update")
*/
public function updateAction($id)
{
$groupService = new ImGroupService();
$groupService->updateGroup($id);
$location = $this->url->get(['for' => 'admin.im_group.list']);
$content = [
'location' => $location,
'msg' => '更新群组成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/delete", name="admin.im_group.delete")
*/
public function deleteAction($id)
{
$groupService = new ImGroupService();
$groupService->deleteGroup($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除群组成功',
];
return $this->jsonSuccess($content);
}
/**
* @Post("/{id:[0-9]+}/restore", name="admin.im_group.restore")
*/
public function restoreAction($id)
{
$groupService = new ImGroupService();
$groupService->restoreGroup($id);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '还原群组成功',
];
return $this->jsonSuccess($content);
}
}

View File

@ -1,37 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Admin\Controllers;
use App\Http\Admin\Services\ImGroupUser as ImGroupUserService;
/**
* @RoutePrefix("/admin/im/group/user")
*/
class ImGroupUserController extends Controller
{
/**
* @Post("/delete", name="admin.im_group_user.delete")
*/
public function deleteAction()
{
$groupService = new ImGroupUserService();
$groupService->deleteGroupUser();
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除成员成功',
];
return $this->jsonSuccess($content);
}
}

View File

@ -301,33 +301,6 @@ class SettingController extends Controller
} }
} }
/**
* @Route("/im", name="admin.setting.im")
*/
public function imAction()
{
$settingService = new SettingService();
if ($this->request->isPost()) {
$section = $this->request->getPost('section', 'string');
$data = $this->request->getPost();
$settingService->updateSettings($section, $data);
return $this->jsonSuccess(['msg' => '更新配置成功']);
} else {
$main = $settingService->getSettings('im.main');
$cs = $settingService->getSettings('im.cs');
$this->view->setVar('main', $main);
$this->view->setVar('cs', $cs);
}
}
/** /**
* @Route("/oauth", name="admin.setting.oauth") * @Route("/oauth", name="admin.setting.oauth")
*/ */

View File

@ -1111,55 +1111,6 @@ class AuthNode extends Service
] ]
], ],
], ],
[
'id' => '4-3',
'title' => '群组管理',
'type' => 'menu',
'children' => [
[
'id' => '4-3-1',
'title' => '群组列表',
'type' => 'menu',
'route' => 'admin.im_group.list',
],
[
'id' => '4-3-2',
'title' => '搜索群组',
'type' => 'menu',
'route' => 'admin.im_group.search',
],
[
'id' => '4-3-3',
'title' => '添加群组',
'type' => 'menu',
'route' => 'admin.im_group.add',
],
[
'id' => '4-3-4',
'title' => '编辑群组',
'type' => 'button',
'route' => 'admin.im_group.edit',
],
[
'id' => '4-3-5',
'title' => '删除群组',
'type' => 'button',
'route' => 'admin.im_group.delete',
],
[
'id' => '4-3-6',
'title' => '群员列表',
'type' => 'button',
'route' => 'admin.im_group.users',
],
[
'id' => '4-3-7',
'title' => '删除群员',
'type' => 'button',
'route' => 'admin.im_group_user.delete',
],
],
],
[ [
'id' => '4-4', 'id' => '4-4',
'title' => '操作记录', 'title' => '操作记录',
@ -1293,12 +1244,6 @@ class AuthNode extends Service
'title' => '其它配置', 'title' => '其它配置',
'type' => 'menu', 'type' => 'menu',
'children' => [ 'children' => [
[
'id' => '5-4-1',
'title' => '即时通讯',
'type' => 'menu',
'route' => 'admin.setting.im',
],
[ [
'id' => '5-4-2', 'id' => '5-4-2',
'title' => '微信公众号', 'title' => '微信公众号',

View File

@ -18,14 +18,12 @@ use App\Models\Course as CourseModel;
use App\Models\CourseCategory as CourseCategoryModel; use App\Models\CourseCategory as CourseCategoryModel;
use App\Models\CourseRelated as CourseRelatedModel; use App\Models\CourseRelated as CourseRelatedModel;
use App\Models\CourseUser as CourseUserModel; use App\Models\CourseUser as CourseUserModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Repos\Category as CategoryRepo; use App\Repos\Category as CategoryRepo;
use App\Repos\Chapter as ChapterRepo; use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Repos\CourseCategory as CourseCategoryRepo; use App\Repos\CourseCategory as CourseCategoryRepo;
use App\Repos\CourseRelated as CourseRelatedRepo; use App\Repos\CourseRelated as CourseRelatedRepo;
use App\Repos\CourseUser as CourseUserRepo; use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
use App\Services\Sync\CourseIndex as CourseIndexSync; use App\Services\Sync\CourseIndex as CourseIndexSync;
use App\Validators\Course as CourseValidator; use App\Validators\Course as CourseValidator;
@ -212,38 +210,28 @@ class Course extends Service
$course->update($data); $course->update($data);
$this->updateImGroup($course);
return $course; return $course;
} }
public function deleteCourse($id) public function deleteCourse($id)
{ {
$course = $this->findOrFail($id); $course = $this->findOrFail($id);
$course->deleted = 1; $course->deleted = 1;
$course->update(); $course->update();
$groupRepo = new ImGroupRepo();
$group = $groupRepo->findByCourseId($course->id);
$group->deleted = 1;
$group->update();
return $course; return $course;
} }
public function restoreCourse($id) public function restoreCourse($id)
{ {
$course = $this->findOrFail($id); $course = $this->findOrFail($id);
$course->deleted = 0; $course->deleted = 0;
$course->update(); $course->update();
$groupRepo = new ImGroupRepo();
$group = $groupRepo->findByCourseId($course->id);
$group->deleted = 0;
$group->update();
return $course; return $course;
} }
@ -590,36 +578,6 @@ class Course extends Service
$cache->rebuild($course->id); $cache->rebuild($course->id);
} }
protected function updateImGroup(CourseModel $course)
{
$groupRepo = new ImGroupRepo();
$group = $groupRepo->findByCourseId($course->id);
$data = [];
if ($course->title != $group->name) {
$data['name'] = $course->title;
}
if ($course->published != $group->published) {
$data['published'] = $course->published;
}
if ($course->teacher_id > 0 && $group->owner_id == 0) {
$groupUser = new ImGroupUserModel();
$groupUser->group_id = $group->id;
$groupUser->user_id = $course->teacher_id;
$groupUser->create();
$data['owner_id'] = $course->teacher_id;
$data['user_count'] = $group->user_count + 1;
}
$group->update($data);
}
protected function handleCourses($pager) protected function handleCourses($pager)
{ {
if ($pager->total_items > 0) { if ($pager->total_items > 0) {

View File

@ -1,226 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Admin\Services;
use App\Builders\ImGroupList as ImGroupListBuilder;
use App\Builders\ImGroupUserList as ImGroupUserListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Models\ImUser as ImUserModel;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Validators\ImGroup as ImGroupValidator;
use App\Validators\ImGroupUser as ImGroupUserValidator;
class ImGroup extends Service
{
public function getGroupTypes()
{
return ImGroupModel::types();
}
public function getGroups()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['deleted'] = $params['deleted'] ?? 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$groupRepo = new ImGroupRepo();
$pager = $groupRepo->paginate($params, $sort, $page, $limit);
return $this->handleGroups($pager);
}
public function getGroup($id)
{
return $this->findOrFail($id);
}
public function createGroup()
{
$post = $this->request->getPost();
$validator = new ImGroupValidator();
$data = [];
$data['name'] = $validator->checkName($post['name']);
$data['about'] = $validator->checkAbout($post['about']);
$data['type'] = $validator->checkType($post['type']);
$group = new ImGroupModel();
$group->create($data);
return $group;
}
public function updateGroup($id)
{
$group = $this->findOrFail($id);
$post = $this->request->getPost();
$validator = new ImGroupValidator();
$data = [];
if (isset($post['name'])) {
$data['name'] = $validator->checkName($post['name']);
}
if (isset($post['about'])) {
$data['about'] = $validator->checkAbout($post['about']);
}
if (isset($post['avatar'])) {
$data['avatar'] = $validator->checkAvatar($post['avatar']);
}
if (isset($post['published'])) {
$data['published'] = $validator->checkPublishStatus($post['published']);
}
if (isset($post['owner_id'])) {
$validator = new ImGroupUserValidator();
$user = $validator->checkUser($post['owner_id']);
$data['owner_id'] = $user->id;
$this->handleGroupOwner($group, $user);
}
$group->update($data);
return $group;
}
public function deleteGroup($id)
{
$group = $this->findOrFail($id);
$group->deleted = 1;
$group->update();
return $group;
}
public function restoreGroup($id)
{
$group = $this->findOrFail($id);
$group->deleted = 0;
$group->update();
return $group;
}
public function getGroupUsers($id)
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['group_id'] = $id;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$groupUserRepo = new ImGroupUserRepo();
$pager = $groupUserRepo->paginate($params, $sort, $page, $limit);
return $this->handleGroupUsers($pager);
}
protected function handleGroupUsers($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new ImGroupUserListBuilder();
$stepA = $pager->items->toArray();
$stepB = $builder->handleUsers($stepA);
$pager->items = $stepB;
return $pager;
}
protected function handleGroupOwner(ImGroupModel $group, ImUserModel $user)
{
$repo = new ImGroupUserRepo();
$groupUser = $repo->findGroupUser($group->id, $user->id);
if ($groupUser) return;
$groupUser = new ImGroupUserModel();
$groupUser->group_id = $group->id;
$groupUser->user_id = $user->id;
$groupUser->create();
$this->incrGroupUserCount($group);
$this->incrUserGroupCount($user);
}
protected function incrGroupUserCount(ImGroupModel $group)
{
$group->user_count += 1;
$group->update();
}
protected function incrUserGroupCount(ImUserModel $user)
{
$user->group_count += 1;
$user->update();
}
protected function handleGroups($pager)
{
if ($pager->total_items > 0) {
$builder = new ImGroupListBuilder();
$items = $pager->items->toArray();
$pipeA = $builder->handleGroups($items);
$pipeB = $builder->handleUsers($pipeA);
$pipeC = $builder->objects($pipeB);
$pager->items = $pipeC;
}
return $pager;
}
protected function findOrFail($id)
{
$validator = new ImGroupValidator();
return $validator->checkGroup($id);
}
}

View File

@ -1,60 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Admin\Services;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImUser as ImUserModel;
use App\Validators\ImGroupUser as ImGroupUserValidator;
class ImGroupUser extends Service
{
public function deleteGroupUser()
{
$groupId = $this->request->getQuery('group_id', 'int', 0);
$userId = $this->request->getQuery('user_id', 'int', 0);
$validator = new ImGroupUserValidator();
$group = $validator->checkGroup($groupId);
$user = $validator->checkUser($userId);
$validator->checkIfAllowDelete($groupId, $userId);
$groupUser = $this->findOrFail($groupId, $userId);
$groupUser->delete();
$this->decrGroupUserCount($group);
$this->decrUserGroupCount($user);
}
protected function decrGroupUserCount(ImGroupModel $group)
{
if ($group->user_count > 0) {
$group->user_count -= 1;
$group->update();
}
}
protected function decrUserGroupCount(ImUserModel $user)
{
if ($user->group_count > 0) {
$user->group_count -= 1;
$user->update();
}
}
protected function findOrFail($groupId, $userId)
{
$validator = new ImGroupUserValidator();
return $validator->checkGroupUser($groupId, $userId);
}
}

View File

@ -10,13 +10,9 @@ namespace App\Http\Admin\Services;
use App\Builders\CourseUserList as CourseUserListBuilder; use App\Builders\CourseUserList as CourseUserListBuilder;
use App\Builders\LearningList as LearningListBuilder; use App\Builders\LearningList as LearningListBuilder;
use App\Library\Paginator\Query as PagerQuery; use App\Library\Paginator\Query as PagerQuery;
use App\Models\Course as CourseModel;
use App\Models\CourseUser as CourseUserModel; use App\Models\CourseUser as CourseUserModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Models\User as UserModel;
use App\Repos\Course as CourseRepo; use App\Repos\Course as CourseRepo;
use App\Repos\CourseUser as CourseUserRepo; use App\Repos\CourseUser as CourseUserRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Repos\Learning as LearningRepo; use App\Repos\Learning as LearningRepo;
use App\Repos\User as UserRepo; use App\Repos\User as UserRepo;
use App\Validators\CourseUser as CourseUserValidator; use App\Validators\CourseUser as CourseUserValidator;
@ -159,8 +155,6 @@ class Student extends Service
$user->course_count += 1; $user->course_count += 1;
$user->update(); $user->update();
$this->handleImGroupUser($course, $user);
return $courseUser; return $courseUser;
} }
@ -183,35 +177,6 @@ class Student extends Service
return $relation; return $relation;
} }
protected function handleImGroupUser(CourseModel $course, UserModel $user)
{
$courseRepo = new CourseRepo();
$imGroup = $courseRepo->findImGroup($course->id);
$userRepo = new UserRepo();
$imUser = $userRepo->findImUser($user->id);
$imGroupUserRepo = new ImGroupUserRepo();
$imGroupUser = $imGroupUserRepo->findGroupUser($imGroup->id, $user->id);
if ($imGroupUser) return;
$imGroupUser = new ImGroupUserModel();
$imGroupUser->group_id = $imGroup->id;
$imGroupUser->user_id = $imUser->id;
$imGroupUser->create();
$imUser->group_count += 1;
$imUser->update();
$imGroup->user_count += 1;
$imGroup->update();
}
protected function findOrFail($id) protected function findOrFail($id)
{ {
$validator = new CourseUserValidator(); $validator = new CourseUserValidator();

View File

@ -13,8 +13,7 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block" style="margin:0;"> <div class="layui-input-block" style="margin:0;">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea"></textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
</div> </div>
</div> </div>
<div class="layui-input-block kg-center" style="margin:0;"> <div class="layui-input-block kg-center" style="margin:0;">
@ -27,15 +26,10 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -13,8 +13,7 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block" style="margin:0;"> <div class="layui-input-block" style="margin:0;">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ answer.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ answer.content }}</textarea>
</div> </div>
</div> </div>
<div class="layui-input-block kg-center" style="margin:0;"> <div class="layui-input-block kg-center" style="margin:0;">
@ -26,15 +25,10 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -14,7 +14,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ answer.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ answer.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i:s',answer.create_time) }}</span> <span>{{ date('Y-m-d H:i:s',answer.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ answer.content }}</div> <div class="content ke-content">{{ answer.content }}</div>
</div> </div>
<fieldset class="layui-elem-field layui-field-title"> <fieldset class="layui-elem-field layui-field-title">
@ -57,7 +57,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -25,7 +25,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ answer.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ answer.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',answer.create_time) }}</span> <span>{{ date('Y-m-d H:i',answer.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ answer.content }}</div> <div class="content ke-content">{{ answer.content }}</div>
</div> </div>
</div> </div>
<div class="layui-tab-item"> <div class="layui-tab-item">
@ -86,7 +86,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -23,17 +23,12 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }}
{{ js_include('lib/xm-select.js') }} {{ js_include('lib/xm-select.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -1,8 +1,7 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.update','id':article.id}) }}"> <form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.update','id':article.id}) }}">
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block" style="margin:0;"> <div class="layui-input-block" style="margin:0;">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ article.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ article.content }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">

View File

@ -17,7 +17,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ article.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ article.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i:s',article.create_time) }}</span> <span>{{ date('Y-m-d H:i:s',article.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ article.content }}</div> <div class="content ke-content">{{ article.content }}</div>
{% if article.tags %} {% if article.tags %}
<div class="tags"> <div class="tags">
{% for item in article.tags %} {% for item in article.tags %}
@ -70,7 +70,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -26,7 +26,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ article.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ article.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',article.create_time) }}</span> <span>{{ date('Y-m-d H:i',article.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ article.content }}</div> <div class="content ke-content">{{ article.content }}</div>
{% if article.tags %} {% if article.tags %}
<div class="tags"> <div class="tags">
{% for item in article.tags %} {% for item in article.tags %}
@ -99,7 +99,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -47,20 +47,13 @@
{% endblock %} {% endblock %}
{% block link_css %}
{% if chapter.model == 3 %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endif %}
{% endblock %}
{% block include_js %} {% block include_js %}
{% if chapter.model == 3 %} {% if chapter.model == 3 %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% elseif chapter.model == 1 %} {% elseif chapter.model == 1 %}

View File

@ -1,7 +1,6 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.chapter.content','id':chapter.id}) }}"> <form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.chapter.content','id':chapter.id}) }}">
<div class="layui-form-item"> <div class="layui-form-item">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ read.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ read.content }}</textarea>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label"></label> <label class="layui-form-label"></label>

View File

@ -15,7 +15,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ comment.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ comment.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i:s',comment.create_time) }}</span> <span>{{ date('Y-m-d H:i:s',comment.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ comment.content }}</div> <div class="content ke-content">{{ comment.content }}</div>
</div> </div>
<fieldset class="layui-elem-field layui-field-title"> <fieldset class="layui-elem-field layui-field-title">
@ -56,7 +56,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -24,7 +24,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ comment.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ comment.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',comment.create_time) }}</span> <span>{{ date('Y-m-d H:i',comment.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ comment.content }}</div> <div class="content ke-content">{{ comment.content }}</div>
</div> </div>
</div> </div>
<div class="layui-tab-item"> <div class="layui-tab-item">
@ -85,7 +85,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -39,18 +39,13 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }}
{{ js_include('lib/xm-select.js') }} {{ js_include('lib/xm-select.js') }}
{{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{{ js_include('admin/js/cover.upload.js') }} {{ js_include('admin/js/cover.upload.js') }}
{{ js_include('admin/js/vditor.js') }}
{% endblock %} {% endblock %}

View File

@ -2,8 +2,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">详情</label> <label class="layui-form-label">详情</label>
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="details" class="layui-hide" id="editor-textarea">{{ course.details }}</textarea>
<textarea name="details" class="layui-hide" id="vditor-textarea">{{ course.details }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">

View File

@ -26,8 +26,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">内容</label> <label class="layui-form-label">内容</label>
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea"></textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
@ -47,15 +46,10 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -26,8 +26,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">内容</label> <label class="layui-form-label">内容</label>
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ help.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ help.content }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
@ -54,15 +53,10 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -1,38 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.im_group.create'}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>添加群组</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" name="about"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">类型</label>
<div class="layui-input-block">
<input type="radio" name="type" value="1" title="课程" disabled="disabled">
<input type="radio" name="type" value="2" title="聊天" checked="checked">
<input type="radio" name="type" value="3" title="员工">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
{% endblock %}

View File

@ -1,59 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.im_group.update','id':group.id}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>编辑群组</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">头像</label>
<div class="layui-input-inline" style="width: 110px;">
<img id="img-avatar" class="kg-avatar" src="{{ group.avatar }}">
<input type="hidden" name="avatar" value="{{ group.avatar }}">
</div>
<div class="layui-input-inline" style="padding-top:35px;">
<button id="change-avatar" class="layui-btn layui-btn-sm" type="button">更换</button>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" value="{{ group.name }}" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" name="about">{{ group.about }}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">群主编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="owner_id" value="{{ group.owner_id }}" lay-verify="number">
</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="是" {% if group.published == 1 %}checked="checked"{% endif %}>
<input type="radio" name="published" value="0" title="否" {% if group.published == 0 %}checked="checked"{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
{% endblock %}
{% block include_js %}
{{ js_include('admin/js/avatar.upload.js') }}
{% endblock %}

View File

@ -1,100 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
{{ partial('macros/group') }}
{% set add_url = url({'for':'admin.im_group.add'}) %}
{% set search_url = url({'for':'admin.im_group.search'}) %}
<div class="kg-nav">
<div class="kg-nav-left">
<span class="layui-breadcrumb">
<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>添加群组
</a>
<a class="layui-btn layui-btn-sm" href="{{ search_url }}">
<i class="layui-icon layui-icon-search"></i>搜索群组
</a>
</div>
</div>
<table class="kg-table layui-table layui-form">
<colgroup>
<col>
<col>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>头像</th>
<th>名称</th>
<th>群主</th>
<th>成员</th>
<th>发布</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set group_url = url({'for':'home.im_group.show','id':item.id}) %}
{% set edit_url = url({'for':'admin.im_group.edit','id':item.id}) %}
{% set update_url = url({'for':'admin.im_group.update','id':item.id}) %}
{% set delete_url = url({'for':'admin.im_group.delete','id':item.id}) %}
{% set restore_url = url({'for':'admin.im_group.restore','id':item.id}) %}
{% set users_url = url({'for':'admin.im_group.users','id':item.id}) %}
<tr>
<td class="center">
<img class="avatar-sm" src="{{ item.avatar }}!avatar_160" alt="{{ item.name }}">
</td>
<td>
<p>
名称:<a href="{{ edit_url }}">{{ item.name }}</a>{{ item.id }}
</p>
<p class="meta">
<span>类型:{{ type_info(item.type) }}</span>
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
</p>
</td>
<td>
{% if item.owner.id is defined %}
<p>昵称:<a href="{{ url({'for':'home.user.show','id':item.owner.id}) }}" target="_blank">{{ item.owner.name }}</a></p>
<p>编号:{{ item.owner.id }}</p>
{% else %}
N/A
{% endif %}
</td>
<td><a href="{{ users_url }}" class="layui-badge layui-bg-green">{{ item.user_count }}</a></td>
<td><input type="checkbox" name="published" value="1" lay-filter="published" lay-skin="switch" lay-text="是|否" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}></td>
<td class="center">
<div class="kg-dropdown">
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
<ul>
{% if item.published == 1 %}
<li><a href="{{ group_url }}" target="_blank">浏览</a></li>
{% endif %}
<li><a href="{{ users_url }}">成员</a></li>
<li><a href="{{ edit_url }}">编辑</a></li>
{% if item.deleted == 0 %}
<li><a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除</a></li>
{% else %}
<li><a href="javascript:" class="kg-restore" data-url="{{ restore_url }}">还原</a></li>
{% endif %}
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endblock %}

View File

@ -1,64 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.im_group.list'}) }}">
<fieldset class="layui-elem-field layui-field-title">
<legend>搜索群组</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">群组编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="id" placeholder="群组编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">群组名称</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" placeholder="群组名称模糊匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">群主编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="user_id" placeholder="群主编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">课程编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="course_id" placeholder="课程编号精确匹配">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">类型</label>
<div class="layui-input-block">
{% for value,title in types %}
<input type="radio" name="type" value="{{ value }}" title="{{ title }}">
{% endfor %}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发布</label>
<div class="layui-input-block">
<input type="radio" name="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">
<input type="radio" name="deleted" value="1" title="是">
<input type="radio" name="deleted" value="0" title="否">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
</div>
</div>
</form>
{% endblock %}

View File

@ -1,70 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
{{ partial('macros/user') }}
{% set back_url = url({'for':'admin.im_group.list'}) %}
<div class="kg-nav">
<div class="kg-nav-left">
<span class="layui-breadcrumb">
<a class="kg-back" href="{{ back_url }}"><i class="layui-icon layui-icon-return"></i>返回</a>
<a href="{{ back_url }}"><cite>群组列表</cite></a>
<a><cite>{{ group.name }}</cite></a>
<a><cite>成员管理</cite></a>
</span>
</div>
</div>
<table class="kg-table layui-table">
<colgroup>
<col width="10%">
<col>
<col>
<col>
<col>
<col>
<col width="10%">
</colgroup>
<thead>
<tr>
<th>用户头像</th>
<th>用户名称</th>
<th>所在地区</th>
<th>用户性别</th>
<th>成员角色</th>
<th>加入时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in pager.items %}
{% set user_url = url({'for':'home.user.show','id':item.user.id}) %}
{% set delete_url = url({'for':'admin.im_group_user.delete'},{'group_id':item.group_id,'user_id':item.user_id}) %}
{% set is_owner = item.user.id == group.owner_id ? 1 : 0 %}
{% set role_type = is_owner == 1 ? '群主' : '成员' %}
<tr>
<td class="center">
<img class="avatar-sm" src="{{ item.user.avatar }}!avatar_160" alt="{{ item.user.name }}">
</td>
<td><a href="{{ user_url }}" title="{{ item.user.about }}" target="_blank">{{ item.user.name }}</a>{{ item.user.id }}</td>
<td>{{ item.user.area }}</td>
<td>{{ gender_info(item.user.gender) }}</td>
<td>{{ role_type }}</td>
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
<td class="center">
{% if is_owner == 0 %}
<button class="layui-btn layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</button>
{% else %}
<button class="layui-btn layui-btn-disabled">删除</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endblock %}

View File

@ -32,12 +32,6 @@
<div class="count">{{ global_stat.topic_count }}</div> <div class="count">{{ global_stat.topic_count }}</div>
</div> </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="layui-col-md2">
<div class="kg-stat-card"> <div class="kg-stat-card">
<div class="name">评价数</div> <div class="name">评价数</div>

View File

@ -15,8 +15,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">内容</label> <label class="layui-form-label">内容</label>
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea"></textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
@ -30,15 +29,10 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -21,8 +21,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">内容</label> <label class="layui-form-label">内容</label>
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ page.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ page.content }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
@ -43,15 +42,10 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -14,20 +14,13 @@
{% endblock %} {% endblock %}
{% block link_css %}
{% if gift.type == 2 %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endif %}
{% endblock %}
{% block include_js %} {% block include_js %}
{% if gift.type == 2 %} {% if gift.type == 2 %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{{ js_include('admin/js/cover.upload.js') }} {{ js_include('admin/js/cover.upload.js') }}
{{ js_include('admin/js/vditor.js') }}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -27,8 +27,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">商品详情</label> <label class="layui-form-label">商品详情</label>
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="details" class="layui-hide" id="editor-textarea">{{ gift.details }}</textarea>
<textarea name="details" class="layui-hide" id="vditor-textarea">{{ gift.details }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">

View File

@ -23,17 +23,12 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }}
{{ js_include('lib/xm-select.js') }} {{ js_include('lib/xm-select.js') }}
{{ js_include('admin/js/vditor.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('admin/js/content.editor.js') }}
{% endblock %} {% endblock %}

View File

@ -1,8 +1,7 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.question.update','id':question.id}) }}"> <form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.question.update','id':question.id}) }}">
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block" style="margin:0;"> <div class="layui-input-block" style="margin:0;">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ question.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ question.content }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">

View File

@ -14,7 +14,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ question.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ question.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i:s',question.create_time) }}</span> <span>{{ date('Y-m-d H:i:s',question.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ question.content }}</div> <div class="content ke-content">{{ question.content }}</div>
{% if question.tags %} {% if question.tags %}
<div class="tags"> <div class="tags">
{% for item in question.tags %} {% for item in question.tags %}
@ -62,7 +62,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -25,7 +25,7 @@
<span><a href="{{ owner_url }}" target="_blank">{{ question.owner.name }}</a></span> <span><a href="{{ owner_url }}" target="_blank">{{ question.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',question.create_time) }}</span> <span>{{ date('Y-m-d H:i',question.create_time) }}</span>
</div> </div>
<div class="content markdown-body">{{ question.content }}</div> <div class="content ke-content">{{ question.content }}</div>
{% if question.tags %} {% if question.tags %}
<div class="tags"> <div class="tags">
{% for item in question.tags %} {% for item in question.tags %}
@ -93,7 +93,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -1,16 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
<div class="layui-tab layui-tab-brief">
<ul class="layui-tab-title kg-tab-title">
<li class="layui-this">微聊设置</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
{{ partial('setting/im_main') }}
</div>
</div>
</div>
{% endblock %}

View File

@ -1,57 +0,0 @@
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.im'}) }}">
<div class="layui-form-item">
<label class="layui-form-label">开启服务</label>
<div class="layui-input-block">
<input type="radio" name="enabled" value="1" title="是" {% if main.enabled == 1 %}checked="checked"{% endif %}>
<input type="radio" name="enabled" value="0" title="否" {% if main.enabled == 0 %}checked="checked"{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">应用名称</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="title" value="{{ main.title }}" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">消息最大长度</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="msg_max_length" value="{{ main.msg_max_length }}" lay-verify="number">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">开启图片上传</label>
<div class="layui-input-block">
<input type="radio" name="upload_img_enabled" value="1" title="是" {% if main.upload_img_enabled == "1" %}checked="checked"{% endif %}>
<input type="radio" name="upload_img_enabled" value="0" title="否" {% if main.upload_img_enabled == "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="upload_file_enabled" value="1" title="是" {% if main.upload_file_enabled == "1" %}checked="checked"{% endif %}>
<input type="radio" name="upload_file_enabled" value="0" title="否" {% if main.upload_file_enabled == "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="tool_audio_enabled" value="1" title="是" {% if main.tool_audio_enabled == "1" %}checked="checked"{% endif %}>
<input type="radio" name="tool_audio_enabled" value="0" title="否" {% if main.tool_audio_enabled == "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="tool_video_enabled" value="1" title="是" {% if main.tool_video_enabled == "1" %}checked="checked"{% endif %}>
<input type="radio" name="tool_video_enabled" value="0" title="否" {% if main.tool_video_enabled == "0" %}checked="checked"{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
<input type="hidden" name="section" value="im.main">
</div>
</div>
</form>

View File

@ -88,15 +88,6 @@
<td><input class="layui-input" type="text" name="event_rule[chapter_study][point]" value="{{ event_rule.chapter_study.point }}" lay-verify="required"></td> <td><input class="layui-input" type="text" name="event_rule[chapter_study][point]" value="{{ event_rule.chapter_study.point }}" lay-verify="required"></td>
<td>N/A</td> <td>N/A</td>
</tr> </tr>
<tr>
<td>微聊讨论</td>
<td>
<input type="radio" name="event_rule[im_discuss][enabled]" value="1" title="是" {% if event_rule.im_discuss.enabled == "1" %}checked="checked"{% endif %}>
<input type="radio" name="event_rule[im_discuss][enabled]" value="0" title="否" {% if event_rule.im_discuss.enabled == "0" %}checked="checked"{% endif %}>
</td>
<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> <tr>
<td>发表评论</td> <td>发表评论</td>
<td> <td>

View File

@ -1,30 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Api\Controllers;
use App\Services\Logic\Im\FriendQuit as FriendQuitService;
/**
* @RoutePrefix("/api/im/friend")
*/
class ImFriendController extends Controller
{
/**
* @Post("/{id:[0-9]+}/quit", name="api.im_friend.quit")
*/
public function quitAction($id)
{
$service = new FriendQuitService();
$service->handle($id);
return $this->jsonSuccess();
}
}

View File

@ -1,77 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Api\Controllers;
use App\Services\Logic\Im\GroupInfo as GroupInfoService;
use App\Services\Logic\Im\GroupList as GroupListService;
use App\Services\Logic\Im\GroupQuit as GroupQuitService;
use App\Services\Logic\Im\GroupUserList as GroupUserListService;
/**
* @RoutePrefix("/api/im/group")
*/
class ImGroupController extends Controller
{
/**
* @Get("/list", name="api.im_group.list")
*/
public function listAction()
{
$service = new GroupListService();
$pager = $service->handle();
return $this->jsonPaginate($pager);
}
/**
* @Get("/{id:[0-9]+}/info", name="api.im_group.info")
*/
public function infoAction($id)
{
$service = new GroupInfoService();
$group = $service->handle($id);
if ($group['deleted'] == 1) {
$this->notFound();
}
if ($group['published'] == 0) {
$this->notFound();
}
return $this->jsonSuccess(['group' => $group]);
}
/**
* @Get("/{id:[0-9]+}/users", name="api.im_group.users")
*/
public function usersAction($id)
{
$service = new GroupUserListService();
$pager = $service->handle($id);
return $this->jsonPaginate($pager);
}
/**
* @Post("/{id:[0-9]+}/quit", name="api.im_group.quit")
*/
public function quitAction($id)
{
$service = new GroupQuitService();
$service->handle($id);
return $this->jsonSuccess();
}
}

View File

@ -37,20 +37,4 @@ class UploadController extends Controller
return $this->jsonSuccess(['data' => $data]); return $this->jsonSuccess(['data' => $data]);
} }
/**
* @Post("/im/img", name="api.upload.im_img")
*/
public function uploadImImageAction()
{
}
/**
* @Post("/im/file", name="api.upload.im_file")
*/
public function uploadImFileAction()
{
}
} }

View File

@ -13,8 +13,6 @@ use App\Services\Logic\User\Console\ArticleList as ArticleListService;
use App\Services\Logic\User\Console\ConsultList as ConsultListService; use App\Services\Logic\User\Console\ConsultList as ConsultListService;
use App\Services\Logic\User\Console\CourseList as CourseListService; use App\Services\Logic\User\Console\CourseList as CourseListService;
use App\Services\Logic\User\Console\FavoriteList as FavoriteListService; 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\NotificationList as NotificationListService;
use App\Services\Logic\User\Console\NotificationRead as NotificationReadService; use App\Services\Logic\User\Console\NotificationRead as NotificationReadService;
use App\Services\Logic\User\Console\NotifyStats as NotifyStatsService; use App\Services\Logic\User\Console\NotifyStats as NotifyStatsService;
@ -164,30 +162,6 @@ class UserConsoleController extends Controller
return $this->jsonPaginate($pager); return $this->jsonPaginate($pager);
} }
/**
* @Get("/friends", name="api.uc.friends")
*/
public function friendsAction()
{
$service = new FriendListService();
$pager = $service->handle();
return $this->jsonPaginate($pager);
}
/**
* @Get("/groups", name="api.uc.groups")
*/
public function groupsAction()
{
$service = new GroupListService();
$pager = $service->handle();
return $this->jsonPaginate($pager);
}
/** /**
* @Get("/notifications", name="api.uc.notifications") * @Get("/notifications", name="api.uc.notifications")
*/ */

View File

@ -10,8 +10,6 @@ namespace App\Http\Api\Controllers;
use App\Services\Logic\User\AnswerList as AnswerListService; use App\Services\Logic\User\AnswerList as AnswerListService;
use App\Services\Logic\User\ArticleList as ArticleListService; use App\Services\Logic\User\ArticleList as ArticleListService;
use App\Services\Logic\User\CourseList as CourseListService; use App\Services\Logic\User\CourseList as CourseListService;
use App\Services\Logic\User\FriendList as FriendListService;
use App\Services\Logic\User\GroupList as GroupListService;
use App\Services\Logic\User\QuestionList as QuestionListService; use App\Services\Logic\User\QuestionList as QuestionListService;
use App\Services\Logic\User\UserInfo as UserInfoService; use App\Services\Logic\User\UserInfo as UserInfoService;
@ -85,28 +83,4 @@ class UserController extends Controller
return $this->jsonPaginate($pager); return $this->jsonPaginate($pager);
} }
/**
* @Get("/{id:[0-9]+}/friends", name="api.user.friends")
*/
public function friendsAction($id)
{
$service = new FriendListService();
$pager = $service->handle($id);
return $this->jsonPaginate($pager);
}
/**
* @Get("/{id:[0-9]+}/groups", name="api.user.groups")
*/
public function groupsAction($id)
{
$service = new GroupListService();
$pager = $service->handle($id);
return $this->jsonPaginate($pager);
}
} }

View File

@ -48,7 +48,7 @@ class Controller extends \Phalcon\Mvc\Controller
/** /**
* @var array * @var array
*/ */
protected $imInfo; protected $websocketInfo;
/** /**
* @var UserModel * @var UserModel
@ -89,7 +89,7 @@ class Controller extends \Phalcon\Mvc\Controller
$this->navs = $this->getNavs(); $this->navs = $this->getNavs();
$this->appInfo = $this->getAppInfo(); $this->appInfo = $this->getAppInfo();
$this->contactInfo = $this->getContactInfo(); $this->contactInfo = $this->getContactInfo();
$this->imInfo = $this->getImInfo(); $this->websocketInfo = $this->getWebsocketInfo();
$this->seo->setTitle($this->siteInfo['title']); $this->seo->setTitle($this->siteInfo['title']);
@ -99,7 +99,7 @@ class Controller extends \Phalcon\Mvc\Controller
$this->view->setVar('app_info', $this->appInfo); $this->view->setVar('app_info', $this->appInfo);
$this->view->setVar('site_info', $this->siteInfo); $this->view->setVar('site_info', $this->siteInfo);
$this->view->setVar('contact_info', $this->contactInfo); $this->view->setVar('contact_info', $this->contactInfo);
$this->view->setVar('im_info', $this->imInfo); $this->view->setVar('websocket_info', $this->websocketInfo);
} }
protected function getAuthUser($cache = false) protected function getAuthUser($cache = false)
@ -139,7 +139,7 @@ class Controller extends \Phalcon\Mvc\Controller
return new AppInfo(); return new AppInfo();
} }
protected function getImInfo() protected function getWebsocketInfo()
{ {
$websocket = $this->getConfig()->get('websocket'); $websocket = $this->getConfig()->get('websocket');
@ -153,11 +153,7 @@ class Controller extends \Phalcon\Mvc\Controller
$websocket->connect_url = sprintf('ws://%s', $websocket->connect_address); $websocket->connect_url = sprintf('ws://%s', $websocket->connect_address);
} }
return [ return $websocket;
'main' => $this->getSettings('im.main'),
'cs' => $this->getSettings('im.cs'),
'ws' => $websocket,
];
} }
protected function getConfig() protected function getConfig()

View File

@ -1,357 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Controllers;
use App\Http\Home\Services\Im as ImService;
use Phalcon\Mvc\View;
/**
* @RoutePrefix("/im")
*/
class ImController extends Controller
{
public function initialize()
{
parent::initialize();
if ($this->authUser->id == 0) {
return $this->response->redirect(['for' => 'home.account.login']);
}
}
/**
* @Get("/", name="home.im.index")
*/
public function indexAction()
{
$this->seo->prependTitle('微聊');
$service = new ImService();
$activeGroups = $service->getActiveGroups();
$activeUsers = $service->getActiveUsers();
$newGroups = $service->getNewGroups();
$newUsers = $service->getNewUsers();
$this->view->setVar('active_users', $activeUsers);
$this->view->setVar('active_groups', $activeGroups);
$this->view->setVar('new_groups', $newGroups);
$this->view->setVar('new_users', $newUsers);
}
/**
* @Get("/cs", name="home.im.cs")
*/
public function csAction()
{
$service = new ImService();
$csUser = $service->getCsUser();
$this->view->setVar('cs_user', $csUser);
}
/**
* @Get("/init", name="home.im.init")
*/
public function initAction()
{
$service = new ImService();
$data = $service->getInitInfo();
return $this->jsonSuccess(['data' => $data]);
}
/**
* @Get("/group/users", name="home.im.group_users")
*/
public function groupUsersAction()
{
$service = new ImService();
$list = $service->getGroupUsers();
return $this->jsonSuccess(['data' => ['list' => $list]]);
}
/**
* @Get("/msgbox", name="home.im.msgbox")
*/
public function msgboxAction()
{
$service = new ImService();
$pager = $service->getNotices();
$this->view->setVar('pager', $pager);
}
/**
* @Get("/chatlog", name="home.im.chatlog")
*/
public function chatlogAction()
{
$service = new ImService();
$pager = $service->getChatMessages();
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->setVar('pager', $pager);
}
/**
* @Get("/friend/msg/unread", name="home.im.unread_friend_msg")
*/
public function unreadFriendMessageAction()
{
$service = new ImService();
$id = $this->request->getQuery('id', 'int');
$service->pullUnreadFriendMessages($id);
return $this->jsonSuccess();
}
/**
* @Get("/notice/unread", name="home.im.unread_notice")
*/
public function unreadNoticeAction()
{
$service = new ImService();
$count = $service->countUnreadNotices();
return $this->jsonSuccess(['count' => $count]);
}
/**
* @Get("/notice", name="home.im.notice")
*/
public function noticeAction()
{
$service = new ImService();
$pager = $service->getNotices();
$this->view->setVar('pager', $pager);
}
/**
* @Get("/notice/read", name="home.im.read_notice")
*/
public function readNoticeAction()
{
$service = new ImService();
$service->readNotices();
return $this->jsonSuccess();
}
/**
* @Get("/friend/status", name="home.im.friend_status")
*/
public function friendStatusAction()
{
$service = new ImService();
$status = $service->getFriendStatus();
return $this->jsonSuccess(['status' => $status]);
}
/**
* @Get("/chat/history", name="home.im.chat_history")
*/
public function chatHistoryAction()
{
$service = new ImService();
$pager = $service->getChatMessages();
return $this->jsonPaginate($pager);
}
/**
* @Post("/user/bind", name="home.im.bind_user")
*/
public function bindUserAction()
{
$service = new ImService();
$service->bindUser();
return $this->jsonSuccess();
}
/**
* @Post("/msg/chat/send", name="home.im.send_chat_msg")
*/
public function sendChatMessageAction()
{
$from = $this->request->getPost('from', 'string');
$to = $this->request->getPost('to', 'string');
$service = new ImService();
$service->sendChatMessage($from, $to);
return $this->jsonSuccess();
}
/**
* @Post("/msg/cs/send", name="home.im.send_cs_msg")
*/
public function sendCustomMessageAction()
{
$from = $this->request->getPost('from', 'string');
$to = $this->request->getPost('to', 'string');
$service = new ImService();
$service->sendCustomMessage($from, $to);
return $this->jsonSuccess();
}
/**
* @Post("/status/update", name="home.im.update_status")
*/
public function updateStatusAction()
{
$service = new ImService();
$service->updateStatus();
return $this->jsonSuccess();
}
/**
* @Post("/sign/update", name="home.home.im.update_sign")
*/
public function updateSignatureAction()
{
$service = new ImService();
$service->updateSignature();
return $this->jsonSuccess();
}
/**
* @Post("/skin/update", name="home.home.im.update_skin")
*/
public function updateSKinAction()
{
$service = new ImService();
$service->updateSkin();
return $this->jsonSuccess();
}
/**
* @Post("/friend/apply", name="home.im.apply_friend")
*/
public function applyFriendAction()
{
$service = new ImService();
$service->applyFriend();
return $this->jsonSuccess(['msg' => '发送申请成功']);
}
/**
* @Post("/friend/accept", name="home.im.accept_friend")
*/
public function acceptFriendAction()
{
$service = new ImService();
$service->acceptFriend();
return $this->jsonSuccess();
}
/**
* @Post("/friend/refuse", name="home.im.refuse_friend")
*/
public function refuseFriendAction()
{
$service = new ImService();
$service->refuseFriend();
return $this->jsonSuccess();
}
/**
* @Post("/group/apply", name="home.im.apply_group")
*/
public function applyGroupAction()
{
$service = new ImService();
$service->applyGroup();
return $this->jsonSuccess(['msg' => '发送申请成功']);
}
/**
* @Post("/group/accept", name="home.home.im.accept_group")
*/
public function acceptGroupAction()
{
$service = new ImService();
$service->acceptGroup();
return $this->jsonSuccess();
}
/**
* @Post("/group/refuse", name="home.home.im.refuse_group")
*/
public function refuseGroupAction()
{
$service = new ImService();
$service->refuseGroup();
return $this->jsonSuccess();
}
/**
* @Post("/friend/{id:[0-9]+}/quit", name="home.im.quit_friend")
*/
public function quitFriendAction($id)
{
$service = new ImService();
$service->quitFriend($id);
return $this->jsonSuccess(['msg' => '解除好友成功']);
}
/**
* @Post("/group/{id:[0-9]+}/quit", name="home.im.quit_group")
*/
public function quitGroupAction($id)
{
$service = new ImService();
$service->quitGroup($id);
return $this->jsonSuccess(['msg' => '退出群组成功']);
}
}

View File

@ -1,153 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Controllers;
use App\Http\Home\Services\FullH5Url as FullH5UrlService;
use App\Http\Home\Services\ImGroup as ImGroupService;
use Phalcon\Mvc\View;
/**
* @RoutePrefix("/im/group")
*/
class ImGroupController extends Controller
{
/**
* @Get("/list", name="home.im_group.list")
*/
public function listAction()
{
$service = new FullH5UrlService();
if ($service->isMobileBrowser() && $service->h5Enabled()) {
$location = $service->getImGroupListUrl();
return $this->response->redirect($location);
}
$this->seo->prependTitle('群组');
$this->view->pick('im/group/list');
}
/**
* @Get("/pager", name="home.im_group.pager")
*/
public function pagerAction()
{
$service = new ImGroupService();
$pager = $service->getGroups();
$pager->target = 'group-list';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('im/group/pager');
$this->view->setVar('pager', $pager);
}
/**
* @Get("/{id:[0-9]+}", name="home.im_group.show")
*/
public function showAction($id)
{
$service = new FullH5UrlService();
if ($service->isMobileBrowser() && $service->h5Enabled()) {
$location = $service->getImGroupIndexUrl($id);
return $this->response->redirect($location);
}
$service = new ImGroupService();
$group = $service->getGroup($id);
if ($group['deleted'] == 1) {
$this->notFound();
}
if ($group['published'] == 0) {
$this->notFound();
}
$this->seo->prependTitle(['群组', $group['name']]);
$this->view->pick('im/group/show');
$this->view->setVar('group', $group);
}
/**
* @Get("/{id:[0-9]+}/users", name="home.im_group.users")
*/
public function usersAction($id)
{
$service = new ImGroupService();
$pager = $service->getGroupUsers($id);
$pager->target = 'user-list';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('im/group/users');
$this->view->setVar('pager', $pager);
}
/**
* @Get("/{id:[0-9]+}/edit", name="home.im_group.edit")
*/
public function editAction($id)
{
$service = new ImGroupService();
$group = $service->getGroup($id);
$this->view->pick('im/group/edit');
$this->view->setVar('group', $group);
}
/**
* @Get("/{id:[0-9]+}/users/active", name="home.im_group.active_users")
*/
public function activeUsersAction($id)
{
$service = new ImGroupService();
$users = $service->getActiveGroupUsers($id);
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('im/group/active_users');
$this->view->setVar('users', $users);
}
/**
* @Get("/{id:[0-9]+}/users/manage", name="home.im_group.manage_users")
*/
public function manageUsersAction($id)
{
$service = new ImGroupService();
$group = $service->getGroup($id);
$pager = $service->getGroupUsers($id);
$this->view->pick('im/group/manage_users');
$this->view->setVar('group', $group);
$this->view->setVar('pager', $pager);
}
/**
* @Post("/{id:[0-9]+}/update", name="home.im_group.update")
*/
public function updateAction($id)
{
$service = new ImGroupService();
$service->updateGroup($id);
return $this->jsonSuccess(['msg' => '更新群组成功']);
}
}

View File

@ -1,37 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Controllers;
use App\Http\Home\Services\ImGroupUser as ImGroupUserService;
/**
* @RoutePrefix("/im/group/user")
*/
class ImGroupUserController extends Controller
{
/**
* @Post("/delete", name="home.im_group_user.delete")
*/
public function deleteAction()
{
$groupService = new ImGroupUserService();
$groupService->deleteGroupUser();
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,
'msg' => '删除成员成功',
];
return $this->jsonSuccess($content);
}
}

View File

@ -95,20 +95,4 @@ class UploadController extends Controller
return $this->jsonSuccess(['data' => $data]); return $this->jsonSuccess(['data' => $data]);
} }
/**
* @Post("/im/img", name="home.upload.im_img")
*/
public function uploadImImageAction()
{
}
/**
* @Post("/im/file", name="home.upload.im_file")
*/
public function uploadImFileAction()
{
}
} }

View File

@ -78,11 +78,6 @@ class FullH5Url extends Service
return sprintf('%s/discovery/index', $this->baseUrl); return sprintf('%s/discovery/index', $this->baseUrl);
} }
public function getImGroupListUrl()
{
return sprintf('%s/discovery/index', $this->baseUrl);
}
public function getPointGiftListUrl() public function getPointGiftListUrl()
{ {
return sprintf('%s/point/gift/list', $this->baseUrl); return sprintf('%s/point/gift/list', $this->baseUrl);
@ -145,11 +140,6 @@ class FullH5Url extends Service
return sprintf('%s/teacher/index?id=%s', $this->baseUrl, $id); return sprintf('%s/teacher/index?id=%s', $this->baseUrl, $id);
} }
public function getImGroupIndexUrl($id)
{
return sprintf('%s/im/group/index?id=%s', $this->baseUrl, $id);
}
public function getPointGiftInfoUrl($id) public function getPointGiftInfoUrl($id)
{ {
return sprintf('%s/point/gift/info?id=%s', $this->baseUrl, $id); return sprintf('%s/point/gift/info?id=%s', $this->baseUrl, $id);

View File

@ -64,13 +64,6 @@ class FullWebUrl extends Service
return $this->baseUrl . $route; return $this->baseUrl . $route;
} }
public function getImGroupShowUrl($id)
{
$route = $this->url->get(['for' => 'home.im_group.show', 'id' => $id]);
return $this->baseUrl . $route;
}
public function getPointGiftShowUrl($id) public function getPointGiftShowUrl($id)
{ {
$route = $this->url->get(['for' => 'home.point_gift.show', 'id' => $id]); $route = $this->url->get(['for' => 'home.point_gift.show', 'id' => $id]);

View File

@ -1,354 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Models\ImUser as ImUserModel;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Validators\ImGroup as ImGroupValidator;
use App\Validators\ImUser as ImUserValidator;
use GatewayClient\Gateway;
class Im extends Service
{
use ImCsTrait;
use ImFriendTrait;
use ImGroupTrait;
use ImMessageTrait;
use ImNoticeTrait;
use ImStatTrait;
public function getInitInfo()
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$mine = [
'id' => $user->id,
'username' => $user->name,
'avatar' => $user->avatar,
'sign' => $user->sign,
'status' => $user->status,
];
$friend = $this->handleFriendList($user);
$group = $this->handleGroupList($user);
return [
'mine' => $mine,
'friend' => $friend,
'group' => $group,
];
}
public function getGroupUsers()
{
$id = $this->request->getQuery('id', 'int');
$validator = new ImGroupValidator();
$group = $validator->checkGroup($id);
Gateway::$registerAddress = $this->getRegisterAddress();
$userIds = Gateway::getUidListByGroup($this->getGroupName($group->id));
if (count($userIds) == 0) {
return [];
}
$userRepo = new ImUserRepo();
$users = $userRepo->findByIds($userIds);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[] = [
'id' => $user['id'],
'username' => $user['name'],
'avatar' => $user['avatar'],
'sign' => $user['sign'],
];
}
return $result;
}
public function getFriendStatus()
{
$id = $this->request->getQuery('id', 'int');
$validator = new ImUserValidator();
$friend = $validator->checkUser($id);
/**
* 对方设置隐身,不返回真实情况
*/
if ($friend->status == 'hide') {
return 'unknown';
}
Gateway::$registerAddress = $this->getRegisterAddress();
return Gateway::isUidOnline($friend->id) ? 'online' : 'offline';
}
public function bindUser()
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$clientId = $this->request->getPost('client_id', 'string');
Gateway::$registerAddress = $this->getRegisterAddress();
Gateway::bindUid($clientId, $user->id);
$userRepo = new ImUserRepo();
$chatGroups = $userRepo->findGroups($user->id);
if ($chatGroups->count() > 0) {
foreach ($chatGroups as $group) {
Gateway::joinGroup($clientId, $this->getGroupName($group->id));
}
}
$this->pushOnlineTips($user);
}
public function updateStatus()
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$status = $this->request->getPost('status', 'string');
$validator = new ImUserValidator();
$validator->checkSign($status);
$user->update(['status' => $status]);
$this->pushOnlineTips($user);
}
public function updateSignature()
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$sign = $this->request->getPost('sign', 'string');
$validator = new ImUserValidator();
$sign = $validator->checkSign($sign);
$user->update(['sign' => $sign]);
return $user;
}
public function updateSkin()
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$skin = $this->request->getPost('skin', 'string');
$validator = new ImUserValidator();
$skin = $validator->checkSkin($skin);
$user->update(['skin' => $skin]);
return $user;
}
protected function pushOnlineTips(ImUserModel $user)
{
/**
* 隐身状态不推送消息
*/
if ($user->status == 'hide') {
return;
}
$onlinePushTime = $this->persistent->online_push_time;
/**
* 避免频繁推送消息
*/
if ($onlinePushTime && time() - $onlinePushTime < 600) {
return;
}
$this->persistent->online_push_time = time();
$userRepo = new ImUserRepo();
$friendUsers = $userRepo->findFriendUsers($user->id);
if ($friendUsers->count() == 0) {
return;
}
Gateway::$registerAddress = $this->getRegisterAddress();
foreach ($friendUsers as $friendUser) {
if (Gateway::isUidOnline($friendUser->friend_id)) {
$content = kg_json_encode([
'type' => 'show_online_tips',
'friend' => [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
],
]);
Gateway::sendToUid($friendUser->friend_id, $content);
}
}
}
protected function handleFriendList(ImUserModel $user)
{
$userRepo = new ImUserRepo();
$friendGroups = $userRepo->findFriendGroups($user->id);
$friendUsers = $userRepo->findFriendUsers($user->id);
$items = [];
$items[] = [
'id' => 0,
'groupname' => '我的好友',
'list' => [],
];
if ($friendGroups->count() > 0) {
foreach ($friendGroups as $group) {
$items[] = [
'id' => $group->id,
'groupname' => $group->name,
'online' => 0,
'list' => [],
];
}
}
if ($friendUsers->count() == 0) {
return $items;
}
$ids = kg_array_column($friendUsers->toArray(), 'friend_id');
$users = $userRepo->findByIds($ids);
$baseUrl = kg_cos_url();
$mapping = [];
/**
* 用户可以设置状态为 ['online', 'hide']
* 列表在线状态识别为 ['online', 'offline']
*/
foreach ($users->toArray() as $user) {
$status = in_array($user['status'], ['online', 'offline']) ? $user['status'] : 'offline';
$user['avatar'] = $baseUrl . $user['avatar'];
$mapping[$user['id']] = [
'id' => $user['id'],
'username' => $user['name'],
'avatar' => $user['avatar'],
'sign' => $user['sign'],
'status' => $status,
];
}
foreach ($items as $key => $item) {
foreach ($friendUsers as $friendUser) {
$friend = $mapping[$friendUser->friend_id];
if ($item['id'] == $friendUser->group_id) {
$friend['msg_count'] = $friendUser->msg_count;
$items[$key]['list'][] = $friend;
} else {
$items[0]['list'][] = $friend;
}
}
}
return $items;
}
protected function handleGroupList(ImUserModel $user)
{
$userRepo = new ImUserRepo();
$groupUsers = $userRepo->findGroupUsers($user->id);
if ($groupUsers->count() == 0) {
return [];
}
$groupRepo = new ImGroupRepo();
$ids = kg_array_column($groupUsers->toArray(), 'group_id');
$groups = $groupRepo->findByIds($ids);
$baseUrl = kg_cos_url();
$mapping = [];
foreach ($groups->toArray() as $group) {
$mapping[$group['id']] = [
'id' => $group['id'],
'groupname' => $group['name'],
'avatar' => $baseUrl . $group['avatar'],
];
}
$result = [];
foreach ($groupUsers as $groupUser) {
$result[] = $mapping[$groupUser->group_id];
}
return $result;
}
protected function getImUser($id)
{
$repo = new ImUserRepo();
return $repo->findById($id);
}
protected function getGroupName($id)
{
return "group_{$id}";
}
protected function getRegisterAddress()
{
$config = $this->getConfig();
return $config->path('websocket.register_address');
}
}

View File

@ -1,60 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Services\Service as AppService;
use GatewayClient\Gateway;
trait ImCsTrait
{
public function getCsUser()
{
$csUserIds = [];
$onlineUserIds = [];
$appService = new AppService();
$csInfo = $appService->getSettings('im.cs');
Gateway::$registerAddress = $this->getRegisterAddress();
if (!empty($csInfo['user1_id'])) {
$csUserIds[] = $csInfo['user1_id'];
if (Gateway::isUidOnline($csInfo['user1_id'])) {
$onlineUserIds[] = $csInfo['user1_id'];
}
}
if (!empty($csInfo['user2_id'])) {
$csUserIds[] = $csInfo['user2_id'];
if (Gateway::isUidOnline($csInfo['user2_id'])) {
$onlineUserIds[] = $csInfo['user2_id'];
}
}
if (!empty($csInfo['user3_id'])) {
$csUserIds[] = $csInfo['user3_id'];
if (Gateway::isUidOnline($csInfo['user3_id'])) {
$onlineUserIds[] = $csInfo['user3_id'];
}
}
if (count($onlineUserIds) > 0) {
$key = array_rand($onlineUserIds);
$userId = $onlineUserIds[$key];
} else {
$key = array_rand($csUserIds);
$userId = $csUserIds[$key];
}
return $this->getImUser($userId);
}
}

View File

@ -1,313 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Models\ImFriendGroup as ImFriendGroupModel;
use App\Models\ImFriendUser as ImFriendUserModel;
use App\Models\ImNotice as ImNoticeModel;
use App\Models\ImUser as ImUserModel;
use App\Repos\ImFriendUser as ImFriendUserRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Validators\ImFriendUser as ImFriendUserValidator;
use App\Validators\ImNotice as ImNoticeValidator;
use GatewayClient\Gateway;
use Phalcon\Di;
use Phalcon\Http\Request;
Trait ImFriendTrait
{
public function applyFriend()
{
/**
* @var Request $request
*/
$request = Di::getDefault()->get('request');
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$post = $request->getPost();
$validator = new ImFriendUserValidator();
$friend = $validator->checkFriend($post['friend_id']);
$group = $validator->checkGroup($post['group_id']);
$remark = $validator->checkRemark($post['remark']);
$validator->checkIfSelfApply($user->id, $friend->id);
$validator->checkIfJoined($user->id, $friend->id);
$this->handleApplyFriendNotice($user, $friend, $group, $remark);
}
public function acceptFriend()
{
/**
* @var Request $request
*/
$request = Di::getDefault()->get('request');
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$noticeId = $request->getPost('notice_id');
$groupId = $request->getPost('group_id');
$validator = new ImFriendUserValidator();
$group = $validator->checkGroup($groupId);
$validator = new ImNoticeValidator();
$notice = $validator->checkNotice($noticeId);
if ($notice->item_type != ImNoticeModel::TYPE_FRIEND_REQUEST) {
return;
}
$sender = $this->getImUser($notice->sender_id);
$friendUserRepo = new ImFriendUserRepo();
$friendUser = $friendUserRepo->findFriendUser($user->id, $sender->id);
if (!$friendUser) {
$friendUserModel = new ImFriendUserModel();
$friendUserModel->create([
'user_id' => $user->id,
'friend_id' => $sender->id,
'group_id' => $group->id,
]);
$this->incrUserFriendCount($user);
}
$friendUser = $friendUserRepo->findFriendUser($sender->id, $user->id);
$groupId = $notice->item_info['group']['id'] ?: 0;
if (!$friendUser) {
$friendUserModel = new ImFriendUserModel();
$friendUserModel->create([
'user_id' => $sender->id,
'friend_id' => $user->id,
'group_id' => $groupId,
]);
$this->incrUserFriendCount($sender);
}
$itemInfo = $notice->item_info;
$itemInfo['status'] = ImNoticeModel::REQUEST_ACCEPTED;
$notice->update(['item_info' => $itemInfo]);
$this->handleAcceptFriendNotice($user, $sender, $notice);
}
public function refuseFriend()
{
/**
* @var Request $request
*/
$request = Di::getDefault()->get('request');
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$noticeId = $request->getPost('notice_id');
$validator = new ImNoticeValidator();
$notice = $validator->checkNotice($noticeId);
if ($notice->item_type != ImNoticeModel::TYPE_FRIEND_REQUEST) {
return;
}
$itemInfo = $notice->item_info;
$itemInfo['status'] = ImNoticeModel::REQUEST_REFUSED;
$notice->update(['item_info' => $itemInfo]);
$sender = $this->getImUser($notice->sender_id);
$this->handleRefuseFriendNotice($user, $sender);
}
public function quitFriend($id)
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$validator = new ImFriendUserValidator();
$friend = $validator->checkFriend($id);
$friendUser = $validator->checkFriendUser($user->id, $friend->id);
$friendUser->delete();
$this->decrUserFriendCount($user);
}
protected function handleApplyFriendNotice(ImUserModel $sender, ImUserModel $receiver, ImFriendGroupModel $group, $remark)
{
$userRepo = new ImUserRepo();
$itemType = ImNoticeModel::TYPE_FRIEND_REQUEST;
$notice = $userRepo->findNotice($receiver->id, $itemType);
if ($notice) {
$expired = time() - $notice->create_time > 7 * 86400;
$pending = $notice->item_info['status'] == ImNoticeModel::REQUEST_PENDING;
if (!$expired && $pending) {
return;
}
}
$noticeModel = new ImNoticeModel();
$noticeModel->sender_id = $sender->id;
$noticeModel->receiver_id = $receiver->id;
$noticeModel->item_type = ImNoticeModel::TYPE_FRIEND_REQUEST;
$noticeModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
],
'group' => [
'id' => $group->id,
'name' => $group->name,
],
'remark' => $remark,
'status' => ImNoticeModel::REQUEST_PENDING,
];
$noticeModel->create();
Gateway::$registerAddress = $this->getRegisterAddress();
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
$content = kg_json_encode(['type' => 'refresh_msg_box']);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleAcceptFriendNotice(ImUserModel $sender, ImUserModel $receiver, ImNoticeModel $applyNotice)
{
$noticeModel = new ImNoticeModel();
$noticeModel->sender_id = $sender->id;
$noticeModel->receiver_id = $receiver->id;
$noticeModel->item_type = ImNoticeModel::TYPE_FRIEND_ACCEPTED;
$noticeModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
]
];
$noticeModel->create();
Gateway::$registerAddress = $this->getRegisterAddress();
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
/**
* 上层操作更新了item_info类型发生了变化故重新获取
*/
$applyNotice->afterFetch();
/**
* @var array $itemInfo
*/
$itemInfo = $applyNotice->item_info;
$content = kg_json_encode([
'type' => 'friend_accepted',
'friend' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
],
'group' => [
'id' => $itemInfo['group']['id'],
'name' => $itemInfo['group']['name'],
],
]);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleRefuseFriendNotice(ImUserModel $sender, ImUserModel $receiver)
{
$noticeModel = new ImNoticeModel();
$noticeModel->sender_id = $sender->id;
$noticeModel->receiver_id = $receiver->id;
$noticeModel->item_type = ImNoticeModel::TYPE_FRIEND_REFUSED;
$noticeModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
]
];
$noticeModel->create();
Gateway::$registerAddress = $this->getRegisterAddress();
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
$content = kg_json_encode(['type' => 'refresh_msg_box']);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function incrUserFriendCount(ImUserModel $user)
{
$user->friend_count += 1;
$user->update();
}
protected function decrUserFriendCount(ImUserModel $user)
{
if ($user->friend_count > 0) {
$user->friend_count -= 1;
$user->update();
}
}
}

View File

@ -1,205 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Builders\ImGroupList as ImGroupListBuilder;
use App\Builders\ImGroupUserList as ImGroupUserListBuilder;
use App\Caches\ImGroupActiveUserList as ImGroupActiveUserListCache;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\ImGroup as ImGroupModel;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Repos\User as UserRepo;
use App\Validators\ImGroup as ImGroupValidator;
class ImGroup extends Service
{
public function getGroups()
{
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['published'] = 1;
$params['deleted'] = 0;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$groupRepo = new ImGroupRepo();
$pager = $groupRepo->paginate($params, $sort, $page, $limit);
return $this->handleGroups($pager);
}
public function getGroup($id)
{
$validator = new ImGroupValidator();
$group = $validator->checkGroup($id);
$owner = $this->handleGroupOwner($group);
return [
'id' => $group->id,
'type' => $group->type,
'name' => $group->name,
'avatar' => $group->avatar,
'about' => $group->about,
'published' => $group->published,
'deleted' => $group->deleted,
'user_count' => $group->user_count,
'msg_count' => $group->msg_count,
'create_time' => $group->create_time,
'update_time' => $group->update_time,
'owner' => $owner,
];
}
public function getGroupUsers($id)
{
$validator = new ImGroupValidator();
$group = $validator->checkGroup($id);
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['group_id'] = $group->id;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$repo = new ImGroupUserRepo();
$pager = $repo->paginate($params, $sort, $page, $limit);
return $this->handleGroupUsers($pager);
}
public function getActiveGroupUsers($id)
{
$cache = new ImGroupActiveUserListCache();
$result = $cache->get($id);
return $result ?: [];
}
public function updateGroup($id)
{
$post = $this->request->getPost();
$user = $this->getLoginUser();
$validator = new ImGroupValidator();
$group = $validator->checkGroup($id);
$validator->checkOwner($user->id, $group->owner_id);
$data = [];
/**
* 课程群组不允许改名
*/
if (!empty($post['name']) && $group->type == ImGroupModel::TYPE_CHAT) {
$data['name'] = $validator->checkName($post['name']);
}
if (!empty($post['about'])) {
$data['about'] = $validator->checkAbout($post['about']);
}
if (!empty($post['avatar'])) {
$data['avatar'] = $validator->checkAvatar($post['avatar']);
}
$group->update($data);
return $group;
}
protected function handleGroupOwner(ImGroupModel $group)
{
if ($group->owner_id == 0) return new \stdClass();
$userRepo = new UserRepo();
$owner = $userRepo->findById($group->owner_id);
return [
'id' => $owner->id,
'name' => $owner->name,
'avatar' => $owner->avatar,
'title' => $owner->title,
'about' => $owner->about,
'vip' => $owner->vip,
];
}
protected function handleGroupUsers($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new ImGroupUserListBuilder();
$stepA = $pager->items->toArray();
$stepB = $builder->handleUsers($stepA);
$pager->items = $stepB;
return $pager;
}
protected function handleGroups($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$builder = new ImGroupListBuilder();
$groups = $pager->items->toArray();
$users = $builder->getUsers($groups);
$baseUrl = kg_cos_url();
$items = [];
foreach ($groups as $group) {
$group['avatar'] = $baseUrl . $group['avatar'];
$group['owner'] = $users[$group['owner_id']] ?? new \stdClass();
$items[] = [
'id' => $group['id'],
'type' => $group['type'],
'name' => $group['name'],
'avatar' => $group['avatar'],
'about' => $group['about'],
'user_count' => $group['user_count'],
'msg_count' => $group['msg_count'],
'owner' => $group['owner'],
];
}
$pager->items = $items;
return $pager;
}
}

View File

@ -1,342 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Models\ImNotice as ImNoticeModel;
use App\Models\ImUser as ImUserModel;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Validators\ImGroup as ImGroupValidator;
use App\Validators\ImGroupUser as ImGroupUserValidator;
use App\Validators\ImNotice as ImNoticeValidator;
use GatewayClient\Gateway;
use Phalcon\Di;
use Phalcon\Http\Request;
Trait ImGroupTrait
{
public function applyGroup()
{
/**
* @var Request $request
*/
$request = Di::getDefault()->get('request');
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$validator = new ImGroupUserValidator();
$post = $request->getPost();
$group = $validator->checkGroup($post['group_id']);
$remark = $validator->checkRemark($post['remark']);
$validator->checkIfJoined($group->id, $user->id);
$validator->checkIfAllowJoin($group->id, $user->id);
$this->handleApplyGroupNotice($user, $group, $remark);
}
public function acceptGroup()
{
/**
* @var Request $request
*/
$request = Di::getDefault()->get('request');
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$noticeId = $request->getPost('notice_id', 'int');
$validator = new ImNoticeValidator();
$notice = $validator->checkNotice($noticeId);
if ($notice->item_type != ImNoticeModel::TYPE_GROUP_REQUEST) return;
$groupId = $notice->item_info['group']['id'] ?: 0;
$validator = new ImGroupValidator();
$group = $validator->checkGroup($groupId);
$validator->checkOwner($user->id, $group->owner_id);
$applicant = $this->getImUser($notice->sender_id);
$groupUserRepo = new ImGroupUserRepo();
$groupUser = $groupUserRepo->findGroupUser($group->id, $applicant->id);
if (!$groupUser) {
$groupUserModel = new ImGroupUserModel();
$groupUserModel->create([
'group_id' => $group->id,
'user_id' => $applicant->id,
]);
$this->incrGroupUserCount($group);
$this->incrUserGroupCount($applicant);
}
$itemInfo = $notice->item_info;
$itemInfo['status'] = ImNoticeModel::REQUEST_ACCEPTED;
$notice->update(['item_info' => $itemInfo]);
$this->handleAcceptGroupNotice($user, $applicant, $group);
$this->handleNewGroupUserNotice($applicant, $group);
}
public function refuseGroup()
{
/**
* @var Request $request
*/
$request = Di::getDefault()->get('request');
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$noticeId = $request->getPost('notice_id', 'int');
$validator = new ImNoticeValidator();
$notice = $validator->checkNotice($noticeId);
if ($notice->item_type != ImNoticeModel::TYPE_GROUP_REQUEST) return;
$groupId = $notice->item_info['group']['id'] ?: 0;
$validator = new ImGroupValidator();
$group = $validator->checkGroup($groupId);
$validator->checkOwner($user->id, $group->owner_id);
$itemInfo = $notice->item_info;
$itemInfo['status'] = ImNoticeModel::REQUEST_REFUSED;
$notice->update(['item_info' => $itemInfo]);
$sender = $this->getImUser($notice->sender_id);
$this->handleRefuseGroupNotice($user, $sender);
}
public function quitGroup($id)
{
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$validator = new ImGroupUserValidator();
$group = $validator->checkGroup($id);
$groupUser = $validator->checkGroupUser($group->id, $user->id);
$validator->checkIfAllowDelete($group->id, $user->id);
$groupUser->delete();
$this->decrGroupUserCount($group);
$this->decrUserGroupCount($user);
}
protected function handleApplyGroupNotice(ImUserModel $sender, ImGroupModel $group, $remark)
{
$userRepo = new ImUserRepo();
$receiver = $userRepo->findById($group->owner_id);
$itemType = ImNoticeModel::TYPE_GROUP_REQUEST;
$notice = $userRepo->findNotice($receiver->id, $itemType);
if ($notice) {
$expired = time() - $notice->create_time > 7 * 86400;
$pending = $notice->item_info['status'] == ImNoticeModel::REQUEST_PENDING;
if (!$expired && $pending) {
return;
}
}
$noticeModel = new ImNoticeModel();
$noticeModel->sender_id = $sender->id;
$noticeModel->receiver_id = $receiver->id;
$noticeModel->item_type = ImNoticeModel::TYPE_GROUP_REQUEST;
$noticeModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
],
'group' => [
'id' => $group->id,
'name' => $group->name,
],
'remark' => $remark,
'status' => ImNoticeModel::REQUEST_PENDING,
];
$noticeModel->create();
Gateway::$registerAddress = $this->getRegisterAddress();
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
$content = kg_json_encode(['type' => 'refresh_msg_box']);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleAcceptGroupNotice(ImUserModel $sender, ImUserModel $receiver, ImGroupModel $group)
{
$noticeModel = new ImNoticeModel();
$noticeModel->sender_id = $sender->id;
$noticeModel->receiver_id = $receiver->id;
$noticeModel->item_type = ImNoticeModel::TYPE_GROUP_ACCEPTED;
$noticeModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
]
];
$noticeModel->create();
Gateway::$registerAddress = $this->getRegisterAddress();
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
$content = kg_json_encode([
'type' => 'group_accepted',
'group' => [
'id' => $group->id,
'name' => $group->name,
'avatar' => $group->avatar,
],
]);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleRefuseGroupNotice(ImUserModel $sender, ImUserModel $receiver)
{
$noticeModel = new ImNoticeModel();
$noticeModel->sender_id = $sender->id;
$noticeModel->receiver_id = $receiver->id;
$noticeModel->item_type = ImNoticeModel::TYPE_GROUP_REFUSED;
$noticeModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
]
];
$noticeModel->create();
Gateway::$registerAddress = $this->getRegisterAddress();
if (Gateway::isUidOnline($receiver->id)) {
$content = kg_json_encode(['type' => 'refresh_msg_box']);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleNewGroupUserNotice(ImUserModel $newUser, ImGroupModel $group)
{
$groupRepo = new ImGroupRepo();
$users = $groupRepo->findUsers($group->id);
if ($users->count() == 0) return;
Gateway::$registerAddress = $this->getRegisterAddress();
foreach ($users as $user) {
$content = kg_json_encode([
'type' => 'new_group_user',
'user' => [
'id' => $newUser->id,
'name' => $newUser->name,
'avatar' => $newUser->avatar,
],
'group' => [
'id' => $group->id,
'name' => $group->name,
'avatar' => $group->avatar,
],
]);
if (Gateway::isUidOnline($user->id)) {
Gateway::sendToUid($user->id, $content);
}
}
}
protected function incrUserGroupCount(ImUserModel $user)
{
$user->group_count += 1;
$user->update();
}
protected function decrUserGroupCount(ImUserModel $user)
{
if ($user->group_count > 0) {
$user->group_count -= 1;
$user->update();
}
}
protected function incrGroupUserCount(ImGroupModel $group)
{
$group->user_count += 1;
$group->update();
}
protected function decrGroupUserCount(ImGroupModel $group)
{
if ($group->user_count > 0) {
$group->user_count -= 1;
$group->update();
}
}
}

View File

@ -1,63 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImUser as ImUserModel;
use App\Validators\ImGroupUser as ImGroupUserValidator;
class ImGroupUser extends Service
{
public function deleteGroupUser()
{
$groupId = $this->request->getQuery('group_id', 'int', 0);
$userId = $this->request->getQuery('user_id', 'int', 0);
$validator = new ImGroupUserValidator();
$group = $validator->checkGroup($groupId);
$user = $validator->checkUser($userId);
$loginUser = $this->getLoginUser();
$validator->checkOwner($loginUser->id, $group->owner_id);
$validator->checkIfAllowDelete($groupId, $userId);
$groupUser = $this->findOrFail($groupId, $userId);
$groupUser->delete();
$this->decrGroupUserCount($group);
$this->decrUserGroupCount($user);
}
protected function decrGroupUserCount(ImGroupModel $group)
{
if ($group->user_count > 0) {
$group->user_count -= 1;
$group->update();
}
}
protected function decrUserGroupCount(ImUserModel $user)
{
if ($user->group_count > 0) {
$user->group_count -= 1;
$user->update();
}
}
protected function findOrFail($groupId, $userId)
{
$validator = new ImGroupUserValidator();
return $validator->checkGroupUser($groupId, $userId);
}
}

View File

@ -1,356 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Builders\ImMessageList as ImMessageListBuilder;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\ImFriendUser as ImFriendUserModel;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Models\ImMessage as ImMessageModel;
use App\Repos\ImFriendUser as ImFriendUserRepo;
use App\Repos\ImMessage as ImMessageRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Services\Logic\Notice\DingTalk\CustomService as CustomServiceNotice;
use App\Validators\ImFriendUser as ImFriendUserValidator;
use App\Validators\ImGroup as ImGroupValidator;
use App\Validators\ImGroupUser as ImGroupUserValidator;
use App\Validators\ImMessage as ImMessageValidator;
use App\Validators\ImUser as ImUserValidator;
use GatewayClient\Gateway;
/**
* layim中普通聊天和自定义聊天中接收方用户名使用的字段不一样也够坑爹的
* 普通聊天username,自定义聊天name
*/
trait ImMessageTrait
{
public function getChatMessages()
{
$user = $this->getLoginUser();
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$validator = new ImMessageValidator();
$validator->checkType($params['type']);
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
if ($params['type'] == 'friend') {
$chatId = ImMessageModel::getChatId($user->id, $params['id']);
$where = ['chat_id' => $chatId];
$messageRepo = new ImMessageRepo();
$pager = $messageRepo->paginate($where, $sort, $page, $limit);
return $this->handleChatMessagePager($pager);
} elseif ($params['type'] == 'group') {
$where = [
'receiver_type' => ImMessageModel::TYPE_GROUP,
'receiver_id' => $params['id'],
];
$messageRepo = new ImMessageRepo();
$pager = $messageRepo->paginate($where, $sort, $page, $limit);
return $this->handleChatMessagePager($pager);
}
}
public function sendChatMessage($from, $to)
{
$validator = new ImMessageValidator();
$validator->checkIfSelfChat($from['id'], $to['id']);
$from['content'] = $validator->checkContent($from['content']);
$message = [
'username' => $from['username'],
'avatar' => $from['avatar'],
'content' => $from['content'],
'fromid' => $from['id'],
'id' => $from['id'],
'type' => $to['type'],
'timestamp' => 1000 * time(),
'mine' => false,
];
if ($to['type'] == 'group') {
$message['id'] = $to['id'];
}
$content = kg_json_encode([
'type' => 'show_chat_msg',
'message' => $message,
]);
Gateway::$registerAddress = $this->getRegisterAddress();
$imMessage = new ImMessageModel();
if ($to['type'] == 'friend') {
$validator = new ImFriendUserValidator();
$relation = $validator->checkFriendUser($to['id'], $from['id']);
$online = Gateway::isUidOnline($to['id']);
$imMessage = new ImMessageModel();
$imMessage->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'receiver_type' => ImMessageModel::TYPE_FRIEND,
'content' => $from['content'],
'viewed' => $online ? 1 : 0,
]);
$this->updateFriendUserChatTime($relation);
if ($online) {
Gateway::sendToUid($to['id'], $content);
} else {
$this->incrFriendUserMsgCount($relation);
}
} elseif ($to['type'] == 'group') {
$user = $this->getLoginUser();
$validator = new ImGroupValidator();
$group = $validator->checkGroup($to['id']);
$validator = new ImGroupUserValidator();
$relation = $validator->checkGroupUser($group->id, $user->id);
$imMessage = new ImMessageModel();
$imMessage->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'receiver_type' => ImMessageModel::TYPE_GROUP,
'content' => $from['content'],
]);
$this->updateGroupUserChatTime($relation);
$this->incrGroupMsgCount($group);
$excludeClientId = null;
/**
* 不推送自己在群组中发的消息
*/
if ($user->id == $from['id']) {
$excludeClientId = Gateway::getClientIdByUid($user->id);
}
$groupName = $this->getGroupName($group->id);
Gateway::sendToGroup($groupName, $content, $excludeClientId);
}
$this->eventsManager->fire('ImMessage:afterCreate', $this, $imMessage);
return $imMessage;
}
public function sendCustomMessage($from, $to)
{
$validator = new ImMessageValidator();
$validator->checkIfSelfChat($from['id'], $to['id']);
$sender = $this->getImUser($from['id']);
$receiver = $this->getImUser($to['id']);
$friendUserRepo = new ImFriendUserRepo();
$friendUser = $friendUserRepo->findFriendUser($sender->id, $receiver->id);
if (!$friendUser) {
$friendUserModel = new ImFriendUserModel();
$friendUserModel->create([
'user_id' => $sender->id,
'friend_id' => $receiver->id,
]);
$this->incrUserFriendCount($sender);
}
$friendUser = $friendUserRepo->findFriendUser($receiver->id, $sender->id);
if (!$friendUser) {
$friendUserModel = new ImFriendUserModel();
$friendUserModel->create([
'user_id' => $receiver->id,
'friend_id' => $sender->id,
]);
$this->incrUserFriendCount($receiver);
}
/**
* 统一普通聊天和自定义聊天的用户名字段
*/
$to['username'] = $to['name'];
unset($to['name']);
$message = $this->sendChatMessage($from, $to);
$this->handleCustomServiceNotice($message);
return $message;
}
public function pullUnreadFriendMessages($id)
{
$user = $this->getLoginUser();
$validator = new ImUserValidator();
$friend = $validator->checkUser($id);
$userRepo = new ImUserRepo();
$messages = $userRepo->findUnreadFriendMessages($friend->id, $user->id);
if ($messages->count() == 0) {
return;
}
Gateway::$registerAddress = $this->getRegisterAddress();
foreach ($messages as $message) {
$message->update(['viewed' => 1]);
$content = kg_json_encode([
'type' => 'show_chat_msg',
'message' => [
'username' => $friend->name,
'avatar' => $friend->avatar,
'id' => $friend->id,
'fromid' => $friend->id,
'content' => $message->content,
'timestamp' => 1000 * $message->create_time,
'type' => 'friend',
'mine' => false,
],
]);
Gateway::sendToUid($user->id, $content);
}
$repo = new ImFriendUserRepo();
$friendUser = $repo->findFriendUser($user->id, $friend->id);
$friendUser->update(['msg_count' => 0]);
}
protected function handleChatMessagePager($pager)
{
if ($pager->total_items == 0) {
return $pager;
}
$messages = $pager->items->toArray();
$builder = new ImMessageListBuilder();
$senders = $builder->getSenders($messages);
$items = [];
foreach ($messages as $message) {
$sender = $senders[$message['sender_id']] ?? new \stdClass();
$items[] = [
'id' => $message['id'],
'content' => $message['content'],
'timestamp' => $message['create_time'] * 1000,
'user' => $sender,
];
}
$pager->items = $items;
return $pager;
}
protected function updateFriendUserChatTime(ImFriendUserModel $hisFriendUser)
{
/**
* 用于联系人排序,近期有联系的排上面
*/
if (time() - $hisFriendUser->update_time > 15 * 60) {
$hisFriendUser->update(['update_time' => time()]);
$repo = new ImFriendUserRepo();
$myFriendUser = $repo->findFriendUser($hisFriendUser->friend_id, $hisFriendUser->user_id);
$myFriendUser->update(['update_time' => time()]);
}
}
protected function updateGroupUserChatTime(ImGroupUserModel $groupUser)
{
/**
* 用于联系人排序,近期有联系的排上面
*/
if (time() - $groupUser->update_time > 15 * 60) {
$groupUser->update_time = time();
$groupUser->update();
}
}
protected function incrFriendUserMsgCount(ImFriendUserModel $friendUser)
{
$friendUser->msg_count += 1;
$friendUser->update();
}
protected function incrGroupMsgCount(ImGroupModel $group)
{
$group->msg_count += 1;
$group->update();
}
protected function handleCustomServiceNotice(ImMessageModel $message)
{
$notice = new CustomServiceNotice();
$notice->createTask($message);
}
}

View File

@ -1,61 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Library\Paginator\Query as PagerQuery;
use App\Repos\ImNotice as ImNoticeRepo;
use App\Repos\ImUser as ImUserRepo;
trait ImNoticeTrait
{
public function countUnreadNotices()
{
$user = $this->getLoginUser();
$userRepo = new ImUserRepo();
return $userRepo->countUnreadNotices($user->id);
}
public function getNotices()
{
$user = $this->getLoginUser();
$pagerQuery = new PagerQuery();
$params = $pagerQuery->getParams();
$params['receiver_id'] = $user->id;
$sort = $pagerQuery->getSort();
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$noticeRepo = new ImNoticeRepo();
return $noticeRepo->paginate($params, $sort, $page, $limit);
}
public function readNotices()
{
$user = $this->getLoginUser();
$userRepo = new ImUserRepo();
$messages = $userRepo->findUnreadNotices($user->id);
if ($messages->count() > 0) {
foreach ($messages as $message) {
$message->viewed = 1;
$message->update();
}
}
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Http\Home\Services;
use App\Caches\ImActiveGroupList;
use App\Caches\ImActiveUserList;
use App\Caches\ImNewGroupList;
use App\Caches\ImNewUserList;
trait ImStatTrait
{
public function getActiveGroups()
{
$cache = new ImActiveGroupList();
return $cache->get();
}
public function getActiveUsers()
{
$cache = new ImActiveUserList();
return $cache->get();
}
public function getNewGroups()
{
$cache = new ImNewGroupList();
return $cache->get();
}
public function getNewUsers()
{
$cache = new ImNewUserList();
return $cache->get();
}
}

View File

@ -19,8 +19,7 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea"></textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
</div> </div>
</div> </div>
<div class="layui-form-item center"> <div class="layui-form-item center">
@ -39,16 +38,11 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('home/js/content.editor.js') }}
{{ js_include('home/js/answer.edit.js') }} {{ js_include('home/js/answer.edit.js') }}
{{ js_include('home/js/vditor.js') }}
{% endblock %} {% endblock %}

View File

@ -19,8 +19,7 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ answer.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ answer.content }}</textarea>
</div> </div>
</div> </div>
<div class="layui-form-item center"> <div class="layui-form-item center">
@ -34,16 +33,11 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }} {{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('home/js/content.editor.js') }}
{{ js_include('home/js/answer.edit.js') }} {{ js_include('home/js/answer.edit.js') }}
{{ js_include('home/js/vditor.js') }}
{% endblock %} {% endblock %}

View File

@ -26,8 +26,7 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-block"> <div class="layui-input-block">
<div id="vditor"></div> <textarea name="content" class="layui-hide" id="editor-textarea">{{ article.content }}</textarea>
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ article.content }}</textarea>
</div> </div>
</div> </div>
</div> </div>
@ -74,17 +73,12 @@
{% endblock %} {% endblock %}
{% block link_css %}
{{ css_link('lib/vditor/dist/index.css') }}
{% endblock %}
{% block include_js %} {% block include_js %}
{{ js_include('lib/vditor/dist/index.min.js') }}
{{ js_include('lib/xm-select.js') }} {{ js_include('lib/xm-select.js') }}
{{ js_include('lib/kindeditor/kindeditor.min.js') }}
{{ js_include('lib/kindeditor/lang/zh-CN.js') }}
{{ js_include('home/js/content.editor.js') }}
{{ js_include('home/js/article.edit.js') }} {{ js_include('home/js/article.edit.js') }}
{{ js_include('home/js/vditor.js') }}
{% endblock %} {% endblock %}

View File

@ -61,7 +61,7 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="content markdown-body">{{ article.content }}</div> <div class="content ke-content">{{ article.content }}</div>
{% if article.tags %} {% if article.tags %}
<div class="tags"> <div class="tags">
{% for item in article.tags %} {% for item in article.tags %}
@ -111,7 +111,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -23,7 +23,7 @@
<div class="layout-content"> <div class="layout-content">
<div class="article-info wrap"> <div class="article-info wrap">
<div class="title">{{ chapter.title }}</div> <div class="title">{{ chapter.title }}</div>
<div class="content markdown-body"> <div class="content ke-content">
{{ chapter.content }} {{ chapter.content }}
</div> </div>
</div> </div>
@ -57,7 +57,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -48,7 +48,7 @@
</ul> </ul>
<div class="layui-tab-content"> <div class="layui-tab-content">
<div class="layui-tab-item layui-show"> <div class="layui-tab-item layui-show">
<div class="course-details markdown-body">{{ course.details }}</div> <div class="course-details ke-content">{{ course.details }}</div>
</div> </div>
<div class="layui-tab-item"> <div class="layui-tab-item">
{{ partial('course/show_catalog') }} {{ partial('course/show_catalog') }}
@ -107,7 +107,7 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -15,7 +15,7 @@
<div class="layout-main clearfix"> <div class="layout-main clearfix">
<div class="layout-content"> <div class="layout-content">
<div class="page-info wrap"> <div class="page-info wrap">
<div class="content markdown-body">{{ help.content }}</div> <div class="content ke-content">{{ help.content }}</div>
</div> </div>
</div> </div>
<div class="layout-sidebar"> <div class="layout-sidebar">
@ -38,6 +38,6 @@
{% block link_css %} {% block link_css %}
{{ css_link('home/css/markdown.css') }} {{ css_link('home/css/content.css') }}
{% endblock %} {% endblock %}

View File

@ -1,104 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>聊天记录</title>
{{ css_link('lib/layui/css/layui.css') }}
{{ css_link('lib/layui/extends/layim/assets/layim.css') }}
<style>
body .layim-chat-main {
height: auto;
}
#LAY_page {
margin-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="layim-chat-main">
<ul id="LAY_view"></ul>
</div>
<div id="LAY_page" data-count="{{ pager.total_items }}"></div>
<textarea title="消息模版" id="LAY_tpl" style="display:none;">
<%# layui.each(d.data, function(index, item) {
if (item.user.id == parent.layui.layim.cache().mine.id) { %>
<li class="layim-chat-mine"><div class="layim-chat-user"><img src="<% item.user.avatar %>"><cite><i><% parent.layui.data.date(item.timestamp) %></i><% item.user.name %></cite></div><div class="layim-chat-text"><% parent.layui.layim.content(item.content) %></div></li>
<%# } else { %>
<li><div class="layim-chat-user"><img src="<% item.user.avatar %>"><cite><% item.user.name %><i><% parent.layui.data.date(item.timestamp) %></i></cite></div><div class="layim-chat-text"><% parent.layui.layim.content(item.content) %></div></li>
<%# }
}); %>
</textarea>
{{ js_include('lib/layui/layui.js') }}
<script>
layui.use(['jquery', 'laytpl', 'laypage'], function () {
var $ = layui.jquery;
var laypage = layui.laypage;
var laytpl = layui.laytpl;
laytpl.config({
open: '<%',
close: '%>'
});
var $target = $('#LAY_view');
var $page = $('#LAY_page');
var $tpl = $('#LAY_tpl');
var count = $page.data('count');
var limit = 15;
var params = {
id: layui.url().search.id,
type: layui.url().search.type,
limit: limit,
sort: 'oldest',
page: 1
};
/**
* 加载第一页数据
*/
loadPageHtml($target, params);
/**
* 两页以上才显示分页
*/
if (count > limit) {
laypage.render({
elem: $page.attr('id'),
limit: limit,
count: count,
layout: ['page', 'count'],
jump: function (obj, first) {
if (!first) {
params.page = obj.curr;
loadPageHtml($target, params);
}
}
});
}
function loadPageHtml(target, params) {
$.get('/im/chat/history', params, function (res) {
var html = laytpl($tpl.val()).render({
data: res.pager.items
});
target.html(html);
});
}
});
</script>
</body>
</html>

View File

@ -1,18 +0,0 @@
{% extends 'templates/layer.volt' %}
{% block content %}
<div class="layui-hide">
<input type="hidden" name="cs_user.id" value="{{ cs_user.id }}">
<input type="hidden" name="cs_user.name" value="{{ cs_user.name }}">
<input type="hidden" name="cs_user.avatar" value="{{ cs_user.avatar }}">
<input type="hidden" name="cs_user.welcome" value="欢迎到访在线客服,有什么可以帮助您的?">
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/im.cs.js') }}
{% endblock %}

View File

@ -1,21 +0,0 @@
{% if users %}
<div class="layui-card">
<div class="layui-card-header">活跃成员</div>
<div class="layui-card-body">
{% for user in users %}
{% set user_url = url({'for':'home.user.show','id':user.id}) %}
<div class="sidebar-user-card clearfix">
<div class="avatar">
<img src="{{ user.avatar }}" alt="{{ user.name }}">
</div>
<div class="info">
<div class="name layui-elip">
<a href="{{ user_url }}" title="{{ user.about }}" target="_blank">{{ user.name }}</a>
</div>
<div class="title layui-elip">{{ user.title|default('暂露头角') }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}

View File

@ -1,46 +0,0 @@
{% extends 'templates/layer.volt' %}
{% block content %}
{% set update_url = url({'for':'home.im_group.update','id':group.id}) %}
{% set name_readonly = group.type == 1 ? 'readonly="readonly"' : '' %}
<form class="layui-form" method="post" action="{{ update_url }}">
<div class="layui-form-item">
<label class="layui-form-label">头像</label>
<div class="layui-input-inline" style="width: 110px;">
<img id="img-avatar" class="my-avatar" src="{{ group.avatar }}">
<input type="hidden" name="avatar" value="{{ group.avatar }}">
</div>
<div class="layui-input-inline" style="padding-top:35px;">
<button id="change-avatar" class="layui-btn layui-btn-sm" type="button">更换</button>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="name" value="{{ group.name }}" {{ name_readonly }} lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" name="about" lay-verify="required">{{ group.about }}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
<button class="layui-btn layui-btn-primary" type="reset">重置</button>
</div>
</div>
</form>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/upload.avatar.js') }}
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
{% set pager_url = url({'for':'home.im_group.pager'}) %}
<div class="layui-breadcrumb breadcrumb">
<a href="/">首页</a>
<a><cite>群组</cite></a>
</div>
<div id="group-list" data-url="{{ pager_url }}"></div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/im.group.list.js') }}
{{ js_include('home/js/im.apply.js') }}
{% endblock %}

View File

@ -1,54 +0,0 @@
{% extends 'templates/layer.volt' %}
{% block content %}
{{ partial('macros/user') }}
<table class="layui-table mt0">
<colgroup>
<col>
<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 user_url = url({'for':'home.user.show','id':item.user.id}) %}
{% set delete_url = url({'for':'home.im_group_user.delete'},{'group_id':group.id,'user_id':item.user.id}) %}
<tr>
<td class="center">
<img class="avatar-sm" src="{{ item.user.avatar }}!avatar_160" alt="{{ item.user.name }}">
</td>
<td><a href="{{ user_url }}" title="{{ item.user.about }}" target="_blank">{{ item.user.name }}</a>{{ item.user.id }}</td>
<td>{{ item.user.area }}</td>
<td>{{ gender_info(item.user.gender) }}</td>
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
<td class="center">
{% if item.user.id != group.owner.id %}
<button class="layui-btn layui-btn-sm layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</button>
{% else %}
<button class="layui-btn layui-btn-sm layui-btn-disabled">删除</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ partial('partials/pager') }}
{% endblock %}

View File

@ -1,32 +0,0 @@
{{ partial('macros/group') }}
{% if pager.total_pages > 0 %}
<div class="group-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
{% set group_url = url({'for':'home.im_group.show','id':item.id}) %}
<div class="layui-col-md3">
<div class="user-card">
<span class="type layui-badge layui-bg-green">{{ type_info(item.type) }}</span>
<div class="avatar">
<a href="{{ group_url }}" title="{{ item.about }}" target="_blank">
<img src="{{ item.avatar }}!avatar_160" alt="{{ item.name }}">
</a>
</div>
<div class="name layui-elip">
<a href="{{ group_url }}" title="{{ item.name }}" target="_blank">{{ item.name }}</a>
</div>
<div class="meta layui-elip">
<span>成员:{{ item.user_count }}</span>
<span>讨论:{{ item.msg_count }}</span>
</div>
<div class="action">
<span class="layui-btn apply-group" data-id="{{ item.id }}" data-name="{{ item.name }}" data-avatar="{{ item.avatar }}">加入群组</span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -1,50 +0,0 @@
{% extends 'templates/main.volt' %}
{% block content %}
{% set users_url = url({'for':'home.im_group.users','id':group.id}) %}
{% set active_users_url = url({'for':'home.im_group.active_users','id':group.id}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
<a><cite>群组</cite></a>
<a><cite>{{ group.name }}</cite></a>
</span>
</div>
<div class="layout-main clearfix">
<div class="layout-content">
<div class="layui-card">
<div class="layui-card-header">小组介绍</div>
<div class="layui-card-body group-about">{{ group.about|default('这个家伙真懒,什么都没有留下') }}</div>
</div>
<div class="layui-card">
<div class="layui-card-header">小组成员</div>
<div class="layui-card-body">
<div id="user-list" data-url="{{ users_url }}"></div>
</div>
</div>
<br>
</div>
<div class="layout-sidebar">
{% if group.owner.id is defined %}
<div class="sidebar">
{{ partial('im/group/show_owner') }}
</div>
{% endif %}
<div class="sidebar wrap">
<button class="layui-btn layui-btn-fluid apply-group" data-id="{{ group.id }}" data-name="{{ group.name }}" data-avatar="{{ group.avatar }}">加入群组</button>
</div>
<div class="sidebar" id="active-user-list" data-url="{{ active_users_url }}"></div>
</div>
</div>
{% endblock %}
{% block include_js %}
{{ js_include('home/js/im.group.show.js') }}
{{ js_include('home/js/im.apply.js') }}
{% endblock %}

View File

@ -1,18 +0,0 @@
{% set owner_url = url({'for':'home.user.show','id':group.owner.id}) %}
<div class="layui-card">
<div class="layui-card-header">小组组长</div>
<div class="layui-card-body">
<div class="sidebar-user-card clearfix">
<div class="avatar">
<img src="{{ group.owner.avatar }}" alt="{{ group.owner.name }}">
</div>
<div class="info">
<div class="name layui-elip">
<a href="{{ owner_url }}" title="{{ group.owner.about }}" target="_blank">{{ group.owner.name }}</a>
</div>
<div class="title layui-elip">{{ group.owner.title|default('暂露头角') }}</div>
</div>
</div>
</div>
</div>

View File

@ -1,26 +0,0 @@
<div class="user-list group-user-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
{% set user_url = url({'for':'home.user.show','id':item.user.id}) %}
{% set avatar_class = item.user.vip == 1 ? 'avatar vip' : 'avatar' %}
<div class="layui-col-md3">
<div class="user-card">
<div class="{{ avatar_class }}">
<a href="{{ user_url }}" title="{{ item.user.about }}" target="_blank">
<img src="{{ item.user.avatar }}" alt="{{ item.user.name }}">
</a>
</div>
<div class="name layui-elip">
<a href="{{ user_url }}" title="{{ item.user.about }}" target="_blank">{{ item.user.name }}</a>
</div>
<div class="title layui-elip">{{ item.user.title|default('暂露头角') }}</div>
<div class="action">
<span class="layui-btn apply-friend" data-id="{{ item.user.id }}" data-name="{{ item.user.name }}" data-avatar="{{ item.user.avatar }}">添加好友</span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
<br>

Some files were not shown because too many files have changed in this diff Show More