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

270 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\http\service;
use app\facade\RedisCache;
use think\Db;
use think\facade\Cache;
use think\facade\Config;
use think\facade\Log;
class ChatService extends CommonService
{
protected $session_id='';
protected $session_data=[];
protected $fd=0;
protected $ip='';
protected $store_id=0;
protected $guest_flag=true;
protected $user_type='user';
protected $init_flag=true; //客服专用
protected $construct_flag=true; //初始化是否成功
protected $server=null;
public function __construct($server,$request)
{
$this->server=$server;
$this->fd=$request->fd;
//取IP地址后续用于区域判别和记录
$this->ip=$request->server['remote_addr'];
//取会话数据
if (isset($request->cookie[Config::get("access.SESSION_DEFAULT_NAME")])) {
$this->session_id=$request->cookie[Config::get("access.SESSION_DEFAULT_NAME")];
$this->session_data=$this->unserializePhp(Cache::store('session_store')->get($this->session_id));
//切换缓存
Cache::store('default');
} else {
$this->construct_flag=false;
Log::info('没有会话ID的数据'.json_encode($request));
}
if ($this->construct_flag) {
//用户类型
if (strpos($request->header['origin'], 'admin')) {
$this->user_type = 'cs_admin';
}
if ($this->user_type == 'user') {
//缺省游客信息
$user_info_data['guest_flag'] = true;
$user_info_data['user_name'] = '游客';
$user_info_data['area_name'] = '-';
$this->session_data['user_id'] = $this->session_data['user_id'] ?? 0;
//存储用户fd对应表
if ($this->session_data['user_id'] == 0) {
$this->store_id = $this->session_id;
} else {
$this->store_id = $this->session_data['user_id'];
//取用户信息
$user_info_result = Db::name("users")
->field('phone as user_name,area_id,buyer_status,custom_service_id')
->where(['user_id' => $this->store_id])
->find();
if ($user_info_result != null) {
$user_info_data = $user_info_result;
if ($user_info_result['buyer_status'] != 0) {
//取得用户的采购商名称
$user_info_data['buyer_name'] = Db::name('buyer')->where(['buyer_user_id' => $this->store_id])->value('name');
}
if ($user_info_result['custom_service_id'] != 0) {
//取得用户的专属客服信息
$user_info_data['sp_cs_info'] = Db::name('admin_user')->field('mobile,nickname,cs_code,cs_status')->where(['admin_user_id' => $user_info_result['custom_service_id']])->find();
}
if ($user_info_result['area_id'] != 0) {
$user_info_data['area_name'] = $this->getIDArea($user_info_result['area_id']);
} else {
$user_info_data['area_name'] = '-';
}
$user_info_data['guest_flag'] = false;
$this->guest_flag = false;
}
}
$user_info_data['ip_addr'] = $this->ip;
$user_info_data['ip_area'] = $this->getIPArea($this->ip);
$old_fd = RedisCache::hget('CHAT_USER_FD', $this->store_id);
if ($old_fd != $this->fd) {
if ($old_fd) {
//如果存在fd说明用户已经有一个窗口建立了连接需要先关闭原来的连接
$server->disconnect($old_fd, 1000, '重复开启对话');
$chat_deal = new ChatCloseService($server, $old_fd);
$chat_deal->onClose();
}
RedisCache::hset('CHAT_USER_FD', $this->store_id, $this->fd);
//存储fd和用户信息对应缓存
RedisCache::hset('CHAT_USER_FD_MAP', $this->fd, $this->store_id);
//存储用户信息
//增加用户ID以便通讯使用
$user_info_data['user_store'] = $this->store_id;
//存储session_id以便ping时保持会话
$user_info_data['session_id']=$this->session_id;
RedisCache::hset('CHAT_USER_INFO', $this->store_id, json_encode($user_info_data));
}
//相等什么都不做,实际的可能性也不存在
} else {
//客服
$this->session_data['admin_user_id'] = $this->session_data['admin_user_id'] ?? 0;
//存储客服fd对应表
if ($this->session_data['admin_user_id'] == 0) {
$this->init_flag = false;
} else {
$this->store_id = $this->session_data['admin_user_id'];
//判断是客服还是普通工作人员
$admin_info_data = Db::name("admin_user")
->field('admin_user_id as cs_store,mobile,cs_code,nickname,cs_status')
->where(['admin_user_id' => $this->store_id])
->find();
if ($admin_info_data==null) {
$this->init_flag = false;
} else {
if ($admin_info_data['cs_status']==1) {
//客服人员
$old_fd = RedisCache::hget('CHAT_CS_FD', $this->store_id);
if ($old_fd != $this->fd) {
if ($old_fd) {
//如果存在fd说明客服已经有一个窗口建立了连接需要先关闭原来的连接
$server->disconnect($old_fd, 1000, '重复开启对话');
$chat_deal = new ChatCloseService($server, $old_fd);
$chat_deal->onClose();
}
//存储session_id以便ping时保持会话
$admin_info_data['session_id'] = $this->session_id;
RedisCache::hset('CHAT_CS_FD', $this->store_id, $this->fd);
RedisCache::hset('CHAT_CS_FD_MAP', $this->fd, $this->store_id);
//存储客服信息
RedisCache::hset('CHAT_CS_INFO', $this->store_id, json_encode($admin_info_data));
}
//相等什么都不做,实际的可能性也不存在
} else {
//普通工作人员
$this->user_type = 'normal_admin';
$old_fd = RedisCache::hget('CHAT_ADMIN_FD', $this->store_id);
if ($old_fd != $this->fd) {
if ($old_fd) {
//如果存在fd说明工作人员已经有一个窗口建立了连接需要先关闭原来的连接
$server->disconnect($old_fd, 1000, '重复开启对话');
$chat_deal = new ChatCloseService($server, $old_fd);
$chat_deal->onClose();
}
//存储session_id以便ping时保持会话
$admin_info_data['session_id'] = $this->session_id;
RedisCache::hset('CHAT_ADMIN_FD', $this->store_id, $this->fd);
RedisCache::hset('CHAT_ADMIN_FD_MAP', $this->fd, $this->store_id);
//存储客服信息
RedisCache::hset('CHAT_ADMIN_INFO', $this->store_id, json_encode($admin_info_data));
}
//相等什么都不做,实际的可能性也不存在
}
}
}
}
}
}
public function onOpen() {
if ($this->construct_flag) {
if ($this->user_type=='user') {
$this->onUserOpen();
} else {
$this->onCSOpen();
}
} else {
$message_type='initError';
$push_data['content']='初始化连接异常';
$this->server->push($this->fd,myResult($message_type,$push_data));
}
}
public function onUserOpen() {
//连接成功通知
$result['message_type']='success';
$result['data']='在线客服已连接';
//返回连接成功的消息
$this->server->push($this->fd,myResult($result['message_type'],$result['data']));
//离线消息部分
if (!$this->guest_flag) {
$cs_id_list=[];
$offline_chat_message_list=[];
//是注册用户,则需要处理离线消息部分
//先取出所有的keys
$offline_chat_list=RedisCache::keys('OFFLINE_CHAT_DATA_'.$this->store_id.'_*');
foreach ($offline_chat_list as $v) {
//取出用户本次离线消息的记录
$offline_chat_message_list=array_merge($offline_chat_message_list,RedisCache::lrange($v));
//取出客服目前的存储id
$cs_id_list[]=substr($v,strlen('OFFLINE_CHAT_DATA_'.$this->store_id.'_'));
}
//数据库中未读离线消息处理
$user_where['user_id'] = $this->store_id;
//$user_where['offline_flag'] = 0;//由于目前未读消息只有可能是离线消息,暂不增加离线字段判断
$user_where['read_flag'] = 0;
$chat_message=Db::name('chat_message')->field('admin_user_id,offline_flag,message')->where($user_where)->select();
//把数据置为已读
$update_data['read_flag'] = 1;
Db::name('chat_message')->where($user_where)->update($update_data);
foreach ($chat_message as $k=>$v) {
$chat_message_list=json_decode($v['message'],true);
$offline_chat_message_list=array_merge($offline_chat_message_list,$chat_message_list);
//取出客服目前的存储id
$cs_id_list[]=$v['admin_user_id'];
}
//取出客服信息
$cs_info_data = Db::name("admin_user")
->where(['admin_user_id' => $cs_id_list])
->column('admin_user_id as cs_store,mobile,cs_code,nickname','admin_user_id');
//组装离线消息信息
$offline_chat_message_array=[];
foreach ($offline_chat_message_list as $k=>$v) {
$tmp_message=json_decode($v,true);
$offline_chat_message_array[$tmp_message['time'].$k]=$tmp_message;
}
ksort($offline_chat_message_array);
foreach ($offline_chat_message_array as $v) {
$message_type='chatMessage';
$push_data=["cs_info"=>$cs_info_data[$v['from_id']],"time"=>$v['time'],"content"=>$v['content'],"offline"=>1];
$this->server->push($this->fd,myResult($message_type,$push_data));
}
}
//分配客服
$this->assignCS($this->server,$this->fd,$this->store_id,$this->guest_flag);
}
public function onCSOpen() {
if (!$this->init_flag) {
$message_type='initError';
$push_data['content']='请先登录管理系统';
} else {
//暂时没有其他事情,由客服手动决定是否开始接受客户请求
$message_type='success';
$push_data['content']='在线办公已连接';
}
$this->server->push($this->fd,myResult($message_type,$push_data));
if ($this->user_type == 'cs_admin') {
if ($message_type=='success') {
Log::info('发送等待用户数');
//发送等待用户数
$this->pushUserQueueNum($this->server,$this->fd);
}
}
}
}