Files
2024-09-27 01:32:49 +08:00

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));
}
}
}