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

完善在线客服,聊天机器人,拆分im为多个trait

This commit is contained in:
xiaochong0302 2020-08-18 20:04:20 +08:00
parent e40e3d9faf
commit c9f286582a
19 changed files with 735 additions and 381 deletions

View File

@ -2,41 +2,23 @@
{% block content %} {% block content %}
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.im'}) }}"> <div class="layui-tab layui-tab-brief">
<fieldset class="layui-elem-field layui-field-title"> <ul class="layui-tab-title kg-tab-title">
<legend>微聊配置</legend> <li class="layui-this">基本信息</li>
</fieldset> <li>在线客服</li>
<div class="layui-form-item"> <li>聊天机器人</li>
<label class="layui-form-label">应用名称</label> </ul>
<div class="layui-input-block"> <div class="layui-tab-content">
<input class="layui-input" type="text" name="title" value="{{ im.title }}" layui-verify="required"> <div class="layui-tab-item layui-show">
{{ partial('setting/im_basic') }}
</div>
<div class="layui-tab-item">
{{ partial('setting/im_cs') }}
</div>
<div class="layui-tab-item">
{{ partial('setting/im_robot') }}
</div> </div>
</div> </div>
<div class="layui-form-item"> </div>
<label class="layui-form-label">客服1用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user1_id" value="{{ im.cs_user1_id }}" layui-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服2用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user2_id" value="{{ im.cs_user2_id }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服3用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user3_id" value="{{ im.cs_user3_id }}">
</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 %} {% endblock %}

View File

@ -0,0 +1,15 @@
<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 class="layui-input" type="text" name="title" value="{{ im.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>

View File

@ -0,0 +1,34 @@
<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="cs_enabled" value="1" title="是" lay-filter="status" {% if im.cs_enabled == 1 %}checked{% endif %}>
<input type="radio" name="cs_enabled" value="0" title="否" lay-filter="status" {% if im.cs_enabled == 0 %}checked{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服1用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user1_id" value="{{ im.cs_user1_id }}" layui-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服2用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user2_id" value="{{ im.cs_user2_id }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服3用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user3_id" value="{{ im.cs_user3_id }}">
</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>

View File

@ -0,0 +1,34 @@
<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="robot_enabled" value="1" title="是" lay-filter="status" {% if im.robot_enabled == 1 %}checked{% endif %}>
<input type="radio" name="robot_enabled" value="0" title="否" lay-filter="status" {% if im.robot_enabled == 0 %}checked{% endif %}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服1用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user1_id" value="{{ im.cs_user1_id }}" layui-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服2用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user2_id" value="{{ im.cs_user2_id }}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">客服3用户编号</label>
<div class="layui-input-block">
<input class="layui-input" type="text" name="cs_user3_id" value="{{ im.cs_user3_id }}">
</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>

View File

@ -35,6 +35,11 @@ class Controller extends \Phalcon\Mvc\Controller
*/ */
protected $siteInfo; protected $siteInfo;
/**
* @var array
*/
protected $imInfo;
/** /**
* @var UserModel * @var UserModel
*/ */
@ -63,17 +68,18 @@ class Controller extends \Phalcon\Mvc\Controller
{ {
$this->seo = $this->getSeo(); $this->seo = $this->getSeo();
$this->navs = $this->getNavs(); $this->navs = $this->getNavs();
$this->appInfo = $this->getAppInfo();
$this->authUser = $this->getAuthUser(); $this->authUser = $this->getAuthUser();
$this->appInfo = $this->getAppInfo();
$this->imInfo = $this->getImInfo();
$this->seo->setTitle($this->siteInfo['title']); $this->seo->setTitle($this->siteInfo['title']);
$this->view->setVar('seo', $this->seo); $this->view->setVar('seo', $this->seo);
$this->view->setVar('navs', $this->navs); $this->view->setVar('navs', $this->navs);
$this->view->setVar('auth_user', $this->authUser);
$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('auth_user', $this->authUser); $this->view->setVar('im_info', $this->imInfo);
$this->view->setVar('socket_url', $this->getSocketUrl());
} }
protected function getAuthUser() protected function getAuthUser()
@ -95,14 +101,14 @@ class Controller extends \Phalcon\Mvc\Controller
{ {
$cache = new NavCache(); $cache = new NavCache();
return $cache->get() ?: []; return $cache->get();
} }
protected function getSiteInfo() protected function getSiteInfo()
{ {
$cache = new SettingCache(); $cache = new SettingCache();
return $cache->get('site') ?: []; return $cache->get('site');
} }
protected function getAppInfo() protected function getAppInfo()
@ -110,11 +116,24 @@ class Controller extends \Phalcon\Mvc\Controller
return new AppInfo(); return new AppInfo();
} }
protected function getSocketUrl() protected function getImInfo()
{ {
$config = $this->getDI()->get('config'); $cache = new SettingCache();
return $config->websocket->url; $im = $cache->get('im');
return [
'title' => $im['title'],
'cs' => [
'enabled' => $im['cs_enabled'],
],
'robot' => [
'enabled' => $im['robot_enabled'],
],
'websocket' => [
'url' => $this->config->websocket->url,
],
];
} }
protected function checkSiteStatus() protected function checkSiteStatus()

View File

@ -14,12 +14,21 @@ class ImController extends Controller
use ResponseTrait; use ResponseTrait;
public function initialize()
{
parent::initialize();
if ($this->authUser->id == 0) {
return $this->response->redirect(['for' => 'web.account.login']);
}
}
/** /**
* @Get("/", name="web.im.index") * @Get("/", name="web.im.index")
*/ */
public function indexAction() public function indexAction()
{ {
$this->seo->prependTitle('微聊');
} }
/** /**
@ -27,9 +36,13 @@ class ImController extends Controller
*/ */
public function csAction() public function csAction()
{ {
/** $this->seo->prependTitle('在线客服');
* @todo 在线客服
*/ $service = new ImService();
$csUser = $service->getCsUser();
$this->view->setVar('cs_user', $csUser);
} }
/** /**
@ -37,9 +50,13 @@ class ImController extends Controller
*/ */
public function robotAction() public function robotAction()
{ {
/** $this->seo->prependTitle('智能聊天');
* @todo 聊天机器人
*/ $service = new ImService();
$csUser = $service->getRobotUser();
$this->view->setVar('cs_user', $csUser);
} }
/** /**
@ -49,7 +66,7 @@ class ImController extends Controller
{ {
$service = new ImService(); $service = new ImService();
$data = $service->init(); $data = $service->getInitInfo();
return $this->jsonSuccess(['data' => $data]); return $this->jsonSuccess(['data' => $data]);
} }
@ -178,13 +195,25 @@ class ImController extends Controller
} }
/** /**
* @Post("/msg/send", name="web.im.send_msg") * @Post("/msg/chat/send", name="web.im.send_chat_msg")
*/ */
public function sendMessageAction() public function sendChatMessageAction()
{ {
$service = new ImService(); $service = new ImService();
$service->sendMessage(); $service->sendChatMessage();
return $this->jsonSuccess();
}
/**
* @Post("/msg/cs/send", name="web.im.send_cs_msg")
*/
public function sendCsMessageAction()
{
$service = new ImService();
$service->sendCsMessage();
return $this->jsonSuccess(); return $this->jsonSuccess();
} }

View File

@ -2,19 +2,11 @@
namespace App\Http\Web\Services; namespace App\Http\Web\Services;
use App\Builders\ImMessageList as ImMessageListBuilder; use App\Caches\Setting as SettingCache;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\ImMessage as ImMessageModel;
use App\Models\ImUser as ImUserModel; use App\Models\ImUser as ImUserModel;
use App\Repos\ImFriendUser as ImFriendUserRepo;
use App\Repos\ImGroup as ImGroupRepo; use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImMessage as ImMessageRepo;
use App\Repos\ImNotice as ImNoticeRepo;
use App\Repos\ImUser as ImUserRepo; use App\Repos\ImUser as ImUserRepo;
use App\Validators\ImFriendUser as ImFriendUserValidator;
use App\Validators\ImGroup as ImGroupValidator; 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 App\Validators\ImUser as ImUserValidator;
use GatewayClient\Gateway; use GatewayClient\Gateway;
@ -23,8 +15,64 @@ class Im extends Service
use ImFriendTrait; use ImFriendTrait;
use ImGroupTrait; use ImGroupTrait;
use ImMessageTrait;
use ImNoticeTrait;
public function init() public function getRobotUser()
{
$imUser = new ImUserModel();
$imUser->id = -10000;
$imUser->name = '聊天机器人';
$imUser->avatar = kg_ci_avatar_img_url(null);
return $imUser;
}
public function getCsUser()
{
$userIds = [];
$onlineUserIds = [];
$cache = new SettingCache();
$imInfo = $cache->get('im');
Gateway::$registerAddress = $this->getRegisterAddress();
if (!empty($imInfo['cs_user1_id'])) {
$userIds[] = $imInfo['cs_user1_id'];
if (Gateway::isUidOnline($imInfo['cs_user1_id'])) {
$onlineUserIds[] = $imInfo['cs_user1_id'];
}
}
if (!empty($imInfo['cs_user2_id'])) {
$userIds[] = $imInfo['cs_user2_id'];
if (Gateway::isUidOnline($imInfo['cs_user2_id'])) {
$onlineUserIds[] = $imInfo['cs_user2_id'];
}
}
if (!empty($imInfo['cs_user3_id'])) {
$userIds[] = $imInfo['cs_user3_id'];
if (Gateway::isUidOnline($imInfo['cs_user3_id'])) {
$onlineUserIds[] = $imInfo['cs_user3_id'];
}
}
if (count($onlineUserIds) > 0) {
$key = array_rand($onlineUserIds);
$userId = $onlineUserIds[$key];
} else {
$key = array_rand($userIds);
$userId = $userIds[$key];
}
return $this->getImUser($userId);
}
public function getInitInfo()
{ {
$loginUser = $this->getLoginUser(); $loginUser = $this->getLoginUser();
@ -81,125 +129,6 @@ class Im extends Service
return $result; return $result;
} }
public function pullUnreadFriendMessages()
{
$user = $this->getLoginUser();
$id = $this->request->getQuery('id');
$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]);
}
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 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'] == ImMessageModel::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'] == ImMessageModel::TYPE_GROUP) {
$where = [
'receiver_type' => $params['type'],
'receiver_id' => $params['id'],
];
$messageRepo = new ImMessageRepo();
$pager = $messageRepo->paginate($where, $sort, $page, $limit);
return $this->handleChatMessagePager($pager);
}
}
public function getFriendStatus() public function getFriendStatus()
{ {
$id = $this->request->getQuery('id'); $id = $this->request->getQuery('id');
@ -245,115 +174,6 @@ class Im extends Service
$this->pushOnlineTips($user); $this->pushOnlineTips($user);
} }
public function sendMessage()
{
$user = $this->getLoginUser();
$from = $this->request->getPost('from');
$to = $this->request->getPost('to');
$validator = new ImMessageValidator();
$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 = json_encode([
'type' => 'show_chat_msg',
'message' => $message,
]);
Gateway::$registerAddress = $this->getRegisterAddress();
if ($to['type'] == ImMessageModel::TYPE_FRIEND) {
$validator = new ImFriendUserValidator();
$relation = $validator->checkFriendUser($to['id'], $user->id);
$online = Gateway::isUidOnline($to['id']);
$messageModel = new ImMessageModel();
$messageModel->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'chat_type' => $to['type'],
'content' => $from['content'],
'viewed' => $online ? 1 : 0,
]);
if ($online) {
Gateway::sendToUid($to['id'], $content);
} else {
$this->incrFriendUserMsgCount($relation);
}
} elseif ($to['type'] == ImMessageModel::TYPE_GROUP) {
$validator = new ImGroupValidator();
$group = $validator->checkGroup($to['id']);
$validator = new ImGroupUserValidator();
$validator->checkGroupUser($group->id, $user->id);
$messageModel = new ImMessageModel();
$messageModel->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'chat_type' => $to['type'],
'content' => $from['content'],
]);
$this->incrGroupMessageCount($group);
$excludeClientId = null;
/**
* 不推送自己在群组中发的消息
*/
if ($user->id == $from['id']) {
$excludeClientId = Gateway::getClientIdByUid($user->id);
}
$groupName = $this->getGroupName($to['id']);
Gateway::sendToGroup($groupName, $content, $excludeClientId);
}
}
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();
}
}
}
public function updateStatus() public function updateStatus()
{ {
$loginUser = $this->getLoginUser(); $loginUser = $this->getLoginUser();
@ -543,35 +363,6 @@ class Im extends Service
return $result; return $result;
} }
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 getImUser($id) protected function getImUser($id)
{ {
$repo = new ImUserRepo(); $repo = new ImUserRepo();

View File

@ -0,0 +1,331 @@
<?php
namespace App\Http\Web\Services;
use App\Builders\ImMessageList as ImMessageListBuilder;
use App\Library\Paginator\Query as PagerQuery;
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\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 pullUnreadFriendMessages()
{
$user = $this->getLoginUser();
$id = $this->request->getQuery('id');
$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]);
}
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'] == ImMessageModel::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'] == ImMessageModel::TYPE_GROUP) {
$where = [
'receiver_type' => $params['type'],
'receiver_id' => $params['id'],
];
$messageRepo = new ImMessageRepo();
$pager = $messageRepo->paginate($where, $sort, $page, $limit);
return $this->handleChatMessagePager($pager);
}
}
public function sendChatMessage()
{
$user = $this->getLoginUser();
$from = $this->request->getPost('from');
$to = $this->request->getPost('to');
$validator = new ImMessageValidator();
$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();
if ($to['type'] == ImMessageModel::TYPE_FRIEND) {
$validator = new ImFriendUserValidator();
$relation = $validator->checkFriendUser($to['id'], $user->id);
$online = Gateway::isUidOnline($to['id']);
$messageModel = new ImMessageModel();
$messageModel->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'receiver_type' => $to['type'],
'content' => $from['content'],
'viewed' => $online ? 1 : 0,
]);
if ($online) {
Gateway::sendToUid($to['id'], $content);
} else {
$this->incrFriendUserMsgCount($relation);
}
} elseif ($to['type'] == ImMessageModel::TYPE_GROUP) {
$validator = new ImGroupValidator();
$group = $validator->checkGroup($to['id']);
$validator = new ImGroupUserValidator();
$validator->checkGroupUser($group->id, $user->id);
$messageModel = new ImMessageModel();
$messageModel->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'receiver_type' => $to['type'],
'content' => $from['content'],
]);
$this->incrGroupMessageCount($group);
$excludeClientId = null;
/**
* 不推送自己在群组中发的消息
*/
if ($user->id == $from['id']) {
$excludeClientId = Gateway::getClientIdByUid($user->id);
}
$groupName = $this->getGroupName($to['id']);
Gateway::sendToGroup($groupName, $content, $excludeClientId);
}
}
public function sendCsMessage()
{
$from = $this->request->getPost('from');
$to = $this->request->getPost('to');
$validator = new ImMessageValidator();
$from['content'] = $validator->checkContent($from['content']);
if ($to['id'] > 0) {
$this->sendCsUserMessage($from, $to);
} else {
$this->sendCsRobotMessage($from, $to);
}
}
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;
}
/**
* 向客服发送消息,建立临时好友关系
*
* @param array $from
* @param array $to
*/
protected function sendCsUserMessage($from, $to)
{
$message = [
'username' => $from['username'],
'avatar' => $from['avatar'],
'content' => $from['content'],
'fromid' => $from['id'],
'id' => $from['id'],
'type' => $to['type'],
'timestamp' => 1000 * time(),
'mine' => false,
];
$content = kg_json_encode([
'type' => 'show_cs_msg',
'message' => $message,
]);
Gateway::$registerAddress = $this->getRegisterAddress();
$online = Gateway::isUidOnline($to['id']);
$messageModel = new ImMessageModel();
$messageModel->create([
'sender_id' => $from['id'],
'receiver_id' => $to['id'],
'receiver_type' => $to['type'],
'content' => $from['content'],
'viewed' => $online ? 1 : 0,
]);
if ($online) {
Gateway::sendToUid($to['id'], $content);
}
}
/**
* 向机器人发送消息,机器人自动应答
*
* @param array $from
* @param array $to
*/
protected function sendCsRobotMessage($from, $to)
{
/**
* @todo 从腾讯平台获取应答内容
*/
$content = '不知道你在说什么...';
$message = [
'username' => $to['name'],
'avatar' => $to['avatar'],
'content' => $content,
'fromid' => $to['id'],
'id' => $to['id'],
'type' => $to['type'],
'timestamp' => 1000 * time(),
'mine' => false,
];
$content = kg_json_encode([
'type' => 'show_cs_msg',
'message' => $message,
]);
Gateway::$registerAddress = $this->getRegisterAddress();
Gateway::sendToUid($from['id'], $content);
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Http\Web\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

@ -0,0 +1,25 @@
{% extends 'templates/main.volt' %}
{% block content %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
<a><cite>在线客服</cite></a>
</span>
</div>
<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('web/js/im.cs.js') }}
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends 'templates/main.volt' %}
{% block content %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
<a><cite>智能聊天</cite></a>
</span>
</div>
<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('web/js/im.cs.js') }}
{% endblock %}

View File

@ -1,19 +1,23 @@
<script> <script>
window.koogua = { window.user = {
user: { id: '{{ auth_user.id }}',
id: '{{ auth_user.id }}', name: '{{ auth_user.name }}',
name: '{{ auth_user.name }}', avatar: '{{ auth_user.avatar }}',
avatar: '{{ auth_user.avatar }}', locked: '{{ auth_user.locked }}',
locked: '{{ auth_user.locked }}', vip: '{{ auth_user.vip }}'
vip: '{{ auth_user.vip }}' };
window.im = {
title: '{{ im_info.title }}',
cs: {
enabled: '{{ im_info.cs.enabled }}'
}, },
im: { robot: {
title: '', enabled: '{{ im_info.robot.enabled }}'
cs_user1_id: '', },
cs_user2_id: '', websocket: {
cs_user3_id: '', url: '{{ im_info.websocket.url }}'
socket_url: ''
} }
}; };

View File

@ -16,7 +16,7 @@ layui.define(['jquery', 'layer'], function (exports) {
}; };
helper.checkLogin = function (callback) { helper.checkLogin = function (callback) {
if (window.koogua.user.id === '0') { if (window.user.id === '0') {
layer.msg('继续操作前请先登录', {icon: 2, anim: 6}); layer.msg('继续操作前请先登录', {icon: 2, anim: 6});
return false; return false;
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,19 @@
layui.use(['util'], function () { layui.use(['jquery', 'util'], function () {
var $ = layui.jquery;
var util = layui.util; var util = layui.util;
var csEnabled = $('input[name="im.cs.enabled"]').val();
var robotEnabled = $('input[name="im.robot.enabled"]').val();
util.fixbar({ util.fixbar({
bar1: true, bar1: csEnabled === '1' ? '&#xe626;' : false,
bar2: robotEnabled === '1' ? '&#xe684;' : false,
click: function (type) { click: function (type) {
if (type === 'bar1') { if (type === 'bar1') {
alert('点击了bar1'); window.open('/im/cs', 'cs');
} else if (type === 'bar2') {
window.open('/im/robot', 'robot');
} }
} }
}); });

View File

@ -2,7 +2,21 @@ layui.use(['jquery', 'layim'], function () {
var $ = layui.jquery; var $ = layui.jquery;
var layim = layui.layim; var layim = layui.layim;
var socket = new WebSocket(window.koogua.im.socket_url);
var me = {
id: window.user.id,
name: window.user.name,
avatar: window.user.avatar
};
var csUser = {
id: $('input[name="cs_user.id"]').val(),
name: $('input[name="cs_user.name"]').val(),
avatar: $('input[name="cs_user.avatar"]').val(),
welcome: $('input[name="cs_user.welcome"]').val()
};
var socket = new WebSocket(window.im.websocket.url);
socket.onopen = function () { socket.onopen = function () {
console.log('socket connect success'); console.log('socket connect success');
@ -23,35 +37,37 @@ layui.use(['jquery', 'layim'], function () {
socket.send('pong...'); socket.send('pong...');
} else if (data.type === 'bind_user') { } else if (data.type === 'bind_user') {
bindUser(data); bindUser(data);
} else if (data.type === 'show_chat_msg') { } else if (data.type === 'show_cs_msg') {
showChatMessage(data); showCsMessage(data);
} }
}; };
layim.config({ layim.config({
brief: true,
init: { init: {
mine: { mine: {
id: "100000123", id: me.id,
username: "访客", username: me.name,
avatar: "//res.layui.com/images/fly/avatar/00.jpg", avatar: me.avatar,
status: "online", status: 'online',
} }
}, },
brief: true,
maxLength: 1000, maxLength: 1000,
}); });
layim.chat({ layim.chat({
id: csUser.id,
name: csUser.name,
avatar: csUser.avatar,
type: 'friend', type: 'friend',
id: 1111111,
name: '在线客服一',
avatar: '//tp1.sinaimg.cn/5619439268/180/40030060651/1',
}); });
layim.on('sendMessage', function (res) { layim.on('sendMessage', function (res) {
sendChatMessage(res); sendCsMessage(res);
}); });
showWelcomeMessage(csUser);
function bindUser(res) { function bindUser(res) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
@ -60,16 +76,28 @@ layui.use(['jquery', 'layim'], function () {
}); });
} }
function sendChatMessage(res) { function sendCsMessage(res) {
console.log(res);
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: '/im/msg/send', url: '/im/msg/cs/send',
data: {from: res.mine, to: res.to} data: {from: res.mine, to: res.to}
}); });
} }
function showChatMessage(res) { function showCsMessage(res) {
layim.getMessage(res.message); layim.getMessage(res.message);
} }
function showWelcomeMessage(csUser) {
layim.getMessage({
id: csUser.id,
username: csUser.name,
avatar: csUser.avatar,
content: csUser.welcome,
timestamp: new Date().getTime(),
type: 'friend',
});
}
}); });

View File

@ -2,7 +2,7 @@ layui.use(['jquery', 'layim'], function () {
var $ = layui.jquery; var $ = layui.jquery;
var layim = layui.layim; var layim = layui.layim;
var socket = new WebSocket(window.koogua.im.socket_url); var socket = new WebSocket(window.im.websocket.url);
socket.onopen = function () { socket.onopen = function () {
console.log('socket connect success'); console.log('socket connect success');
@ -43,7 +43,7 @@ layui.use(['jquery', 'layim'], function () {
}; };
layim.config({ layim.config({
title: '菜鸟驿站', title: window.im.title,
init: { init: {
url: '/im/init' url: '/im/init'
}, },
@ -114,7 +114,7 @@ layui.use(['jquery', 'layim'], function () {
function sendChatMessage(res) { function sendChatMessage(res) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: '/im/msg/send', url: '/im/msg/chat/send',
data: {from: res.mine, to: res.to} data: {from: res.mine, to: res.to}
}); });
} }

View File

@ -1,4 +0,0 @@
layui.use(['jquery', 'layim'], function () {
});