481 lines
23 KiB
PHP
481 lines
23 KiB
PHP
<?php
|
|
|
|
namespace app\http\service;
|
|
|
|
use app\facade\AppConstant;
|
|
use app\facade\RedisCache;
|
|
use think\captcha\Captcha;
|
|
use think\Db;
|
|
use think\facade\Cache;
|
|
use think\facade\Config;
|
|
use think\facade\Log;
|
|
|
|
class ChatMessageService extends CommonService
|
|
{
|
|
protected $fd=0;
|
|
protected $message_data=[];
|
|
protected $server=null;
|
|
protected $fd_store='';
|
|
|
|
public function __construct($server,$frame)
|
|
{
|
|
$this->server=$server;
|
|
$this->fd=$frame->fd;
|
|
$this->message_data = json_decode($frame->data,true);
|
|
if ($this->message_data['type']!='ping') {
|
|
Log::info('收到消息数据:'.$frame->data);
|
|
}
|
|
}
|
|
public function deal() {
|
|
switch ($this->message_data['type']) {
|
|
case 'ping' ://心跳检测
|
|
$message_type='ping';
|
|
$push_data='心跳检测';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
//保持会话
|
|
$push_data=[];
|
|
$session_id='';
|
|
$need_check=false;
|
|
$fd_store=RedisCache::hget('CHAT_CS_FD_MAP',$this->fd);
|
|
if ($fd_store) {
|
|
$cs_info_data = json_decode(RedisCache::hget('CHAT_CS_INFO',$fd_store),true);
|
|
$session_id=$cs_info_data['session_id'];
|
|
$need_check=true;
|
|
$check_field='admin_user_id';
|
|
} else {
|
|
$fd_store=RedisCache::hget('CHAT_USER_FD_MAP',$this->fd);
|
|
if ($fd_store) {
|
|
$user_info_data = json_decode(RedisCache::hget('CHAT_USER_INFO',$fd_store),true);
|
|
$session_id=$user_info_data['session_id'];
|
|
if (!checkGuestFlag($fd_store)) {
|
|
$need_check=true;
|
|
$check_field='user_id';
|
|
}
|
|
} else {
|
|
$fd_store=RedisCache::hget('CHAT_ADMIN_FD_MAP',$this->fd);
|
|
if ($fd_store) {
|
|
$admin_info_data = json_decode(RedisCache::hget('CHAT_ADMIN_INFO',$fd_store),true);
|
|
$session_id=$admin_info_data['session_id'];
|
|
$need_check=true;
|
|
$check_field='admin_user_id';
|
|
}
|
|
}
|
|
}
|
|
if ($session_id!='' && $need_check) {
|
|
//查看会话是否存在
|
|
$session_redis=RedisCache::store('session_store');
|
|
//Log::info('ping当前会话ID'.$session_id);
|
|
$session_data=$this->unserializePhp($session_redis->get($session_id));
|
|
//Log::info('ping当前会话信息'.json_encode($session_data));
|
|
if ($session_data) {
|
|
if (isset($session_data[$check_field])) {
|
|
$session_redis->expire($session_id,Config::get('session.expire'));
|
|
} else {
|
|
$message_type='initError';
|
|
$push_data['content']='您已退出登录';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
} else {
|
|
$message_type='initError';
|
|
$push_data['content']='您已退出登录';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
//切换缓存
|
|
RedisCache::store('default');
|
|
}
|
|
break;
|
|
case 'openService' ://客服专有,打开对外服务(进入在线客服队列)
|
|
if ($this->csInit()) { $this->openService(); }
|
|
break;
|
|
case 'getUserChat' ://客服专有,从用户等待队列中获得一个用户对话
|
|
if ($this->csInit()) { $this->getUserChat(); }
|
|
break;
|
|
case 'closeService' ://客服专有,关闭对外服务(离开在线客服队列,但目前正在对话中的,仍保持通讯)
|
|
if ($this->csInit()) { $this->closeService(); }
|
|
break;
|
|
case 'closeUserChat' ://客服专有,关闭和指定用户的对话
|
|
if ($this->csInit()) { $this->closeUserChat(); }
|
|
break;
|
|
case 'chatCSMessage' ://客服专有
|
|
if ($this->csInit()) { $this->chatCSMessage(); }
|
|
break;
|
|
case 'getChatCSMessage' ://客服专有
|
|
if ($this->csInit()) { $this->getChatCSMessage(); }
|
|
break;
|
|
case 'switchToOther' ://客服专有
|
|
//转接给其他客服
|
|
if ($this->csInit()) { $this->switchToOther(); }
|
|
break;
|
|
case 'chatMessage' ://用户专有
|
|
if ($this->userInit()) { $this->chatMessage(); }
|
|
break;
|
|
case 'getChatMessage' ://用户专有
|
|
if ($this->userInit()) { $this->getChatMessage(); }
|
|
break;
|
|
case 'reConnect' ://用户专有
|
|
//重新进入分配和等待客服队列
|
|
if ($this->userInit()) { $this->reConnect(); }
|
|
break;
|
|
default:
|
|
break;
|
|
/*$result['message_type']='error';
|
|
$result['data']=['错误消息类型'];*/
|
|
}
|
|
}
|
|
|
|
protected function csInit() {
|
|
//客服初始化
|
|
//读出fd所对应的客服
|
|
$fd_store=RedisCache::hget('CHAT_CS_FD_MAP',$this->fd);
|
|
if ($fd_store) {
|
|
$this->fd_store=$fd_store;
|
|
return true;
|
|
} else {
|
|
$message_type='initError';
|
|
$push_data['content']='没有对应工作台信息';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function userInit() {
|
|
//用户初始化
|
|
//读出fd所对应的用户
|
|
$fd_store=RedisCache::hget('CHAT_USER_FD_MAP',$this->fd);
|
|
if ($fd_store) {
|
|
$this->fd_store=$fd_store;
|
|
return true;
|
|
} else {
|
|
$message_type='initError';
|
|
$push_data['content']='没有建立通话';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function openService() {
|
|
//打开服务,增加到在线客服队列
|
|
//查看队列中是否已经存在
|
|
$online_cs_list = RedisCache::lrange("CUSTOMER_ONLINE_QUEUE");
|
|
if (!in_array($this->fd_store,$online_cs_list)) {
|
|
RedisCache::lpush("CUSTOMER_ONLINE_QUEUE",$this->fd_store);
|
|
}
|
|
/*$message_type='openService';
|
|
$push_data['content']='打开对外服务成功';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));*/
|
|
//不再自动分配
|
|
}
|
|
|
|
protected function closeService() {
|
|
//关闭对外服务,从在线客服队列中移除
|
|
RedisCache::lrem('CUSTOMER_ONLINE_QUEUE',$this->fd_store,0);
|
|
/*$result['message_type']='closeService';
|
|
$result['data']=['关闭对外服务成功'];*/
|
|
}
|
|
|
|
protected function chatCSMessage() {
|
|
//客服消息
|
|
$offline_flag=true;
|
|
//先看双方有没有建立连接
|
|
$user_store_id=$this->message_data['data']['to_id'];
|
|
$link_check = RedisCache::llen('CHAT_DATA_'.$user_store_id.'_'.$this->fd_store);
|
|
if ($link_check>0) {
|
|
//双方在对话中
|
|
//读出用户对应的fd
|
|
$fd_store=RedisCache::hget('CHAT_USER_FD',$user_store_id);
|
|
if ($fd_store) {
|
|
$now_time=date('Y-m-d H:i:s');
|
|
$message_type='chatMessage';
|
|
$cs_info_data = json_decode(RedisCache::hget('CHAT_CS_INFO',$this->fd_store),true);
|
|
$push_data=["cs_info"=>$cs_info_data,"time"=>$now_time,"content"=>$this->message_data['data']['content']];
|
|
$this->server->push($fd_store,myResult($message_type,$push_data));
|
|
//增加到对话记录中
|
|
RedisCache::lpush('CHAT_DATA_'.$user_store_id.'_'.$this->fd_store,json_encode(['time'=>$now_time,"content"=>$this->message_data['data']['content'],"from_id"=>$this->fd_store,"to_id"=>$user_store_id]));
|
|
$offline_flag=false;
|
|
}
|
|
/*else {
|
|
$message_type='error';
|
|
$push_data['content']='消息发送失败,用户已离线';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='消息发送失败,双方未建立连接';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));*/
|
|
}
|
|
|
|
//离线消息
|
|
if ($offline_flag) {
|
|
if (!checkGuestFlag($user_store_id)) {
|
|
//离线留言
|
|
//创建离线留言
|
|
RedisCache::lpush('OFFLINE_CHAT_DATA_'.$user_store_id.'_'.$this->fd_store,json_encode(['time'=>date('m-d H:i:s'),"content"=>$this->message_data['data']['content'],"from_id"=>$this->fd_store,"to_id"=>$user_store_id]));
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='不能对游客进行离线留言';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getChatCSMessage() {
|
|
//Log::info('客服获取聊天记录'.$this->fd);
|
|
//获得历史聊天记录
|
|
$push_data['to_id']=$user_store_id=$this->message_data['data']['to_id'];
|
|
$push_data['from_times']=$from_times=$this->message_data['data']['from_times'];
|
|
$push_data['times']=$times=$this->message_data['data']['times'];
|
|
//user_id或user_session=$user_store_id
|
|
if (checkGuestFlag($user_store_id)) {
|
|
$user_where['user_session']=$user_store_id;
|
|
} else {
|
|
$user_where['user_id'] = $user_store_id;
|
|
}
|
|
$cs_where['admin_user_id']=[$this->fd_store,0];
|
|
$order['id']='desc';
|
|
//$chat_cs_message_count=Db::name('chat_message')->where(function ($query) use($user_where) { $query->whereOr($user_where); })->where($cs_where)->count();
|
|
$chat_cs_message_count=Db::name('chat_message')->where($user_where)->where($cs_where)->count();
|
|
//$chat_cs_message=Db::name('chat_message')->field('admin_user_id,offline_flag,create_time,message')->where(function ($query) use($user_where) { $query->whereOr($user_where); })->where($cs_where)->order($order)->limit($from_times,$times)->select();
|
|
$chat_cs_message=Db::name('chat_message')->field('admin_user_id,offline_flag,create_time,message')->where($user_where)->where($cs_where)->order($order)->limit($from_times,$times)->select();
|
|
foreach ($chat_cs_message as $k=>$v) {
|
|
$chat_cs_message_list=json_decode($v['message'],true);
|
|
$chat_cs_message_result=[];
|
|
foreach ($chat_cs_message_list as $value) {
|
|
$chat_cs_message_result[]=json_decode($value,true);
|
|
|
|
}
|
|
//$chat_cs_message[$k]['message']=array_reverse($chat_cs_message_result);
|
|
$chat_cs_message[$k]['message']=$chat_cs_message_result;
|
|
}
|
|
|
|
$message_type='getChatCSMessage';
|
|
$push_data['message_count']=$chat_cs_message_count;
|
|
$push_data['chat_message']=$chat_cs_message;
|
|
//Log::info('客服获取聊天记录结果'.myResult($message_type,$push_data));
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
|
|
protected function closeUserChat($message_type='disconnect') {
|
|
//客服关闭和对方对话
|
|
$user_store_id=$this->message_data['data']['to_id'];
|
|
//先看双方有没有建立连接
|
|
$link_check = RedisCache::llen('CHAT_DATA_'.$user_store_id.'_'.$this->fd_store);
|
|
if ($link_check>0) {
|
|
$this->noticeCSLeave($this->server,$user_store_id,$this->fd_store,$message_type);
|
|
//存储并删除对话
|
|
$add_data['create_time']=date('Y-m-d H:i:s');
|
|
$add_data['admin_user_id']=$this->fd_store;
|
|
if (checkGuestFlag($user_store_id)) {
|
|
$add_data['user_id']=0;
|
|
$add_data['user_session']=$user_store_id;
|
|
} else {
|
|
$add_data['user_id']=$user_store_id;
|
|
$add_data['user_session']='';
|
|
}
|
|
$add_data['message']=json_encode(RedisCache::lrange('CHAT_DATA_'.$user_store_id.'_'.$this->fd_store));
|
|
$add_data['offline_flag']=0;
|
|
Db::name('chat_message')->insert($add_data);
|
|
|
|
RedisCache::batch_del(['CHAT_DATA_'.$user_store_id.'_'.$this->fd_store]);
|
|
}
|
|
|
|
//看看有没有离线消息
|
|
$link_check = RedisCache::llen('OFFLINE_CHAT_DATA_'.$user_store_id.'_'.$this->fd_store);
|
|
if ($link_check>0) {
|
|
//存储并删除对话
|
|
$add_data['create_time']=date('Y-m-d H:i:s');
|
|
$add_data['admin_user_id']=$this->fd_store;
|
|
if (checkGuestFlag($user_store_id)) {
|
|
$add_data['user_id']=0;
|
|
$add_data['user_session']=$user_store_id;
|
|
} else {
|
|
$add_data['user_id']=$user_store_id;
|
|
$add_data['user_session']='';
|
|
}
|
|
$add_data['message']=json_encode(RedisCache::lrange('OFFLINE_CHAT_DATA_'.$user_store_id.'_'.$this->fd_store));
|
|
$add_data['offline_flag']=1;
|
|
$add_data['read_flag']=0;
|
|
Db::name('chat_message')->insert($add_data);
|
|
|
|
RedisCache::batch_del(['OFFLINE_CHAT_DATA_'.$user_store_id.'_'.$this->fd_store]);
|
|
}
|
|
}
|
|
|
|
protected function switchToOther() {
|
|
//转接其他客服
|
|
//先看双方有没有建立连接
|
|
Log::info('CHAT_DATA_'.$this->message_data['data']['to_id'].'_'.$this->fd_store);
|
|
$link_check = RedisCache::llen('CHAT_DATA_'.$this->message_data['data']['to_id'].'_'.$this->fd_store);
|
|
if ($link_check>0) {
|
|
//双方在对话中
|
|
//读出用户对应的fd
|
|
$fd_store=RedisCache::hget('CHAT_USER_FD',$this->message_data['data']['to_id']);
|
|
if ($fd_store) {
|
|
//看看转向的客服有没有建立连接
|
|
Log::info('转接的目标客服id:'.$this->message_data['data']['to_cs_id']);
|
|
$to_cs_fd=RedisCache::hget('CHAT_CS_FD',$this->message_data['data']['to_cs_id']);
|
|
Log::info('转接的目标客服fd:'.$to_cs_fd);
|
|
if ($to_cs_fd) {
|
|
$this->closeUserChat('switchToOther');
|
|
$this->createChatRelation($this->server,$fd_store,$this->message_data['data']['to_id'],$to_cs_fd,$this->message_data['data']['to_cs_id']);
|
|
$message_type='switchToOther';
|
|
$push_data['content']='转接成功';
|
|
$push_data['user_id']=$this->message_data['data']['to_id'];
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='转接失败,目标客服未开启工作台';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='转接失败,用户已离线';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='转接失败,双方未建立连接';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
}
|
|
|
|
protected function chatMessage() {
|
|
//用户消息
|
|
//先看双方有没有建立连接
|
|
//Log::info('CHAT_DATA_'.$this->fd_store.'_'.$this->message_data['data']['to_id']);
|
|
//增加留言功能
|
|
if ($this->message_data['data']['to_id']>0) {
|
|
$offline_flag=true;
|
|
$link_check = RedisCache::llen('CHAT_DATA_'.$this->fd_store.'_'.$this->message_data['data']['to_id']);
|
|
if ($link_check>0) {
|
|
//双方在对话中
|
|
//读出用户对应的fd
|
|
$fd_store=RedisCache::hget('CHAT_CS_FD',$this->message_data['data']['to_id']);
|
|
if ($fd_store) {
|
|
$now_time=date('Y-m-d H:i:s');
|
|
$message_type='chatMessage';
|
|
$user_info_data = json_decode(RedisCache::hget('CHAT_USER_INFO',$this->fd_store),true);
|
|
$push_data=["user_info"=>$user_info_data,"time"=>$now_time,"content"=>$this->message_data['data']['content']];
|
|
$this->server->push($fd_store,myResult($message_type,$push_data));
|
|
//增加到对话记录中
|
|
RedisCache::lpush('CHAT_DATA_'.$this->fd_store.'_'.$this->message_data['data']['to_id'],json_encode(['time'=>$now_time,"content"=>$this->message_data['data']['content'],"from_id"=>$this->fd_store,"to_id"=>$this->message_data['data']['to_id']]));
|
|
$offline_flag=false;
|
|
}
|
|
/*else {
|
|
$message_type='error';
|
|
$push_data['content']='消息发送失败,客服已离线';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='消息发送失败,双方未建立连接';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));*/
|
|
}
|
|
//离线消息
|
|
if ($offline_flag) {
|
|
if (!checkGuestFlag($this->fd_store)) {
|
|
//离线留言
|
|
//创建离线留言
|
|
RedisCache::lpush('OFFLINE_CHAT_DATA_'.$this->fd_store.'_'.$this->message_data['data']['to_id'],json_encode(['time'=>date('Y-m-d H:i:s'),"content"=>$this->message_data['data']['content'],"from_id"=>$this->fd_store,"to_id"=>$this->message_data['data']['to_id']]));
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='游客不能进行留言,请先登录';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
}
|
|
} else {
|
|
if (!checkGuestFlag($this->fd_store)) {
|
|
//离线留言
|
|
//创建离线留言
|
|
RedisCache::lpush('OFFLINE_CHAT_DATA_' . $this->fd_store . '_0', json_encode(['time' => date('Y-m-d H:i:s'), "content" => $this->message_data['data']['content'], "from_id" => $this->fd_store, "to_id" => 0]));
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='游客不能进行留言,请先登录';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getChatMessage() {
|
|
//获得历史聊天记录
|
|
$push_data['to_id']=$cs_store_id=$this->message_data['data']['to_id'];
|
|
$push_data['from_times']=$from_times=$this->message_data['data']['from_times'];
|
|
$push_data['times']=$times=$this->message_data['data']['times'];
|
|
if (checkGuestFlag($this->fd_store)) {
|
|
$user_where['user_session']=$this->fd_store;
|
|
} else {
|
|
$user_where['user_id'] = $this->fd_store;
|
|
}
|
|
$user_where['read_flag'] = 1;
|
|
//$user_where['user_id']=$this->fd_store;
|
|
//$user_where['user_session']=$this->fd_store;
|
|
$cs_where['admin_user_id']=[$cs_store_id,0];
|
|
$order['id']='desc';
|
|
//$chat_message_count=Db::name('chat_message')->where(function ($query) use($user_where) { $query->whereOr($user_where); })->where($cs_where)->count();
|
|
//$chat_message=Db::name('chat_message')->field('admin_user_id,offline_flag,create_time,message')->where(function ($query) use($user_where) { $query->whereOr($user_where); })->where($cs_where)->order($order)->limit($from_times,$times)->select();
|
|
$chat_message_count=Db::name('chat_message')->where($user_where)->where($cs_where)->count();
|
|
$chat_message=Db::name('chat_message')->field('admin_user_id,offline_flag,create_time,message')->where($user_where)->where($cs_where)->order($order)->limit($from_times,$times)->select();
|
|
|
|
foreach ($chat_message as $k=>$v) {
|
|
$chat_message_list=json_decode($v['message'],true);
|
|
$chat_message_result=[];
|
|
foreach ($chat_message_list as $value) {
|
|
$chat_message_result[]=json_decode($value,true);
|
|
|
|
}
|
|
//$chat_message[$k]['message']=array_reverse($chat_message_result);
|
|
$chat_message[$k]['message']=$chat_message_result;
|
|
}
|
|
|
|
$message_type='getChatMessage';
|
|
$push_data['message_count']=$chat_message_count;
|
|
$push_data['chat_message']=$chat_message;
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
|
|
protected function reConnect() {
|
|
$user_info_data = json_decode(RedisCache::hget('CHAT_USER_INFO',$this->fd_store),true);
|
|
$this->assignCS($this->server, $this->fd, $this->fd_store,$user_info_data['guest_flag']);
|
|
}
|
|
|
|
protected function getUserChat() {
|
|
$assign_flag=false;
|
|
//从等待队列中获得一个用户对话,应优先客服专属的用户
|
|
$online_user_list = RedisCache::lrange("CHAT_USER_QUEUE");
|
|
if (count($online_user_list)>0) {
|
|
//查询有没有专属用户
|
|
$sp_user_list=Db::name('users')->where(['user_id'=>$online_user_list,'custom_service_id'=>$this->fd_store])->column('user_id');
|
|
if ($sp_user_list!=null) {
|
|
//取第一个专属用户
|
|
foreach ($online_user_list as $v) {
|
|
if (in_array($v,$sp_user_list)) {
|
|
//从队列删除
|
|
RedisCache::lrem('CHAT_USER_QUEUE',$v,0);
|
|
//广播通知
|
|
$this->pushUserQueueNum($this->server);
|
|
//可建立通话关系
|
|
$user_id=$v;
|
|
$assign_flag=true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
//从队列中取一个用户
|
|
$user_id=RedisCache::rpop("CHAT_USER_QUEUE");
|
|
//广播通知
|
|
$this->pushUserQueueNum($this->server);
|
|
$assign_flag=true;
|
|
}
|
|
}
|
|
|
|
if ($assign_flag) {
|
|
//获得用户的fd
|
|
$user_fd=RedisCache::hget('CHAT_USER_FD',$user_id);;
|
|
//建立对话关系
|
|
$this->createChatRelation($this->server,$user_fd,$user_id,$this->fd,$this->fd_store);
|
|
} else {
|
|
$message_type='error';
|
|
$push_data['content']='暂无用户需要服务';
|
|
$this->server->push($this->fd,myResult($message_type,$push_data));
|
|
}
|
|
}
|
|
} |