Events.php<?php
use \GatewayWorker\Lib\Gateway;
use \GatewayWorker\Lib\Db;
if(!isset($_SESSION)){
session_start();
}
/**
&
Events.php
<?php
use \GatewayWorker\Lib\Gateway;
use \GatewayWorker\Lib\Db;
if(!isset($_SESSION)){
session_start();
}
/**
* 主逻辑
* 主要是处理 onConnect onMessage onClose 三个方法
* onConnect 和 onClose 如果不需要可以不用实现并删除
*/
class Events
{
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $data) {
$message = json_decode($data, true);
$message_type = strtolower($message['type']);
switch($message_type) {
case 'init':
if(isset($message['id'])){
// 用户id 用户名 个性签名 头像等信息
$id = $message['id'];
$username = $message['username'];
$sign = $message['sign'];
$avatar = $message['avatar'];
// 将client_id与uid绑定,通过Gateway::sendToUid给这个uid发消息(在send_message.php中有调用)
$_SESSION['laychat']['id'] = $id;
Gateway::bindUid($client_id, $id);
}
// 通知当前客户端初始化
$init_message = array(
'message_type' => 'init',
'client_id' => $client_id,
);
$_SESSION['laychat']['client_id'] = $client_id;
Gateway::sendToClient($client_id, json_encode($init_message));
if(isset($message['id'])){
Gateway::updateSession($client_id, $message);
// 获取用户所在群组id
$db = Db::instance('laychat');
$group_id_array = $db->select('gid')->from('group_members')->where('uid=:uid')->bindValue('uid',$id)->column();
if($group_id_array){
foreach($group_id_array as $gid){
// 让当前客户端加入群组
Gateway::joinGroup($client_id, $gid);
}
}
// 通知所有客户端当前用户上线
$online_message = array(
'message_type' => 'online',
'id' => $id
);
Gateway::sendToAll(json_encode($online_message));
}
return;
case 'hide':
$status_message = array(
'message_type' => $message_type,
'id' => $message['uid'],
);
Gateway::sendToAll(json_encode($status_message));
return;
case 'online':
$status_message = array(
'message_type' => $message_type,
'id' => $message['uid'],
);
$_SESSION['online'] = $message_type;
Gateway::sendToAll(json_encode($status_message));
return;
case 'ping':
return;
case 'logout':
if(isset($message['id']) && !Gateway::isUidOnline($message['id'])){
$logout_message = array(
'message_type' => 'logout',
'id' => $message['id']
);
Gateway::sendToAll(json_encode($logout_message));
Db::instance('laychat')->update('user')->cols(array('logout_timestamp'=>time(), 'status'=>'offline'))->where('uid = :uid')->bindValue('uid',$message['id'])->query();
}
return;
case 'chatMessage':
// 聊天数据
$data = $message['data'];
// 发送者,也就是当前登录用户的id
$from = $data['mine']['id'];
// 发送给谁
$to = $data['to']['id'];
// 类型,私聊(friend)还是群组(group)
$type = $data['to']['type'];
// 消息格式
$message_data = array(
'username' => $data['mine']['username'],
'avatar' => $data['mine']['avatar'],
'id' => $type === 'friend' ? $from : $to,
'type' => $type,
'content' => htmlspecialchars($data['mine']['content']),
'timestamp'=> time()*1000,
'from_id' => $from,
);
$new_message = array(
'message_type' => 'chatMessage',
'data' => $message_data
);
// 根据类型走不同的接口发送
switch($message['data']['to']['type']){
// 私聊
case 'friend':
Gateway::sendToUid($message['data']['to']['id'], json_encode($new_message));
break;
// 群聊
case 'group':
Gateway::sendToGroup($message['data']['to']['id'], json_encode($new_message), $client_id);
break;
}
//自己
//Gateway::sendToCurrentClient(json_encode($new_message));
//自己
//Gateway::sendToClient($client_id, json_encode($new_message));
//私聊
//Gateway::sendToUid($message['data']['to']['id'], json_encode($new_message));
//群聊
//Gateway::sendToGroup($message['data']['to']['id'], json_encode($new_message), $client_id);
//所有
//Gateway::sendToAll(json_encode($new_message));
return;
default:
echo "unknown message $data";
//Gateway::sendToClient($client_id, $data);
}
}
/**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose() {
// 有可能多页面登录,没有全部下线
if(isset($_SESSION['laychat']['id']) && !Gateway::isUidOnline($_SESSION['laychat']['id'])){
$logout_message = array(
'message_type' => 'logout',
'id' => $_SESSION['laychat']['id']
);
Gateway::sendToAll(json_encode($logout_message));
Db::instance('laychat')->update('user')->cols(array('logout_timestamp'=>time(), 'status'=>'offline'))->where('uid = :uid')->bindValue('uid',$_SESSION['laychat']['id'])->query();
}
}
}start_businessworker.php
<?php
use \Workerman\Worker;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../vendor/autoload.php';
// bussinessWorker 进程
$worker = new BusinessWorker();
// worker名称
$worker->name = 'artimax_laychat';
// bussinessWorker进程数量
$worker->count = 4;
// 服务注册地址
$worker->registerAddress = __host__.':1238';
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START')) {
Worker::runAll();
}start_gateway.php
<?php
use \Workerman\Worker;
use \GatewayWorker\Gateway;
// 自动加载类
require_once __DIR__ . '/../vendor/autoload.php';
// gateway 进程,这里使用Text协议,可以用telnet测试
$gateway = new Gateway("websocket://".__host__.":2346");
// gateway名称,status方便查看
$gateway->name = 'artimax_laychat';
// gateway进程数
$gateway->count = 1;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = __host__;
// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = __host__.':1238';
// 心跳间隔
$gateway->pingInterval = 12;
// 心跳数据
//$gateway->pingData = '{"message_type":"ping"}';
// 25秒内客户端不发来任何消息则认为客户端下线
$gateway->pingNotResponseLimit = 1;
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START')) {
Worker::runAll();
}start_register.php
<?php
use \Workerman\Worker;
use \GatewayWorker\Register;
// 自动加载类
require_once __DIR__ . '/../vendor/autoload.php';
$register = new Register('text://'.__host__.':1238');
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START')) {
Worker::runAll();
}javascript.js
/*
// +----------------------------------------------------------------------
// | Chat函数库
// +----------------------------------------------------------------------
// | Copyright (c) 2016-2017 http://www.nswlp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.nswlp.cn )
// +----------------------------------------------------------------------
// | Author: lwj <nswlp@nswlp.cn>
// +----------------------------------------------------------------------
*/
var WEB_SOCKET_SWF_LOCATION = SITE_URL + "/ymchat/static/swf/WebSocketMain.swf";
var WEB_SOCKET_DEBUG = true;
var WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;
var imgerror = SITE_URL + '/ymchat/static/imgs/onerror.jpg';
var requrl = SITE_URL + '/ymchat.php?app=ymchat/';
var laychat = {
appName : 'YaMeichat',
initUrl : requrl + 'default&act=init',
sendMessageUrl : requrl + 'default&act=sendmessage',
membersUrl : requrl + 'haystack&act=members&',
uploadImageUrl : requrl + 'haystack&act=uploadimg&',
uploadFileUrl : requrl + 'haystack&act=uploadfile&',
chatLogUrl : requrl + 'haystack&act=chatlog&',
bindUrl : requrl + 'default&act=bind',
updateSignUrl : requrl + 'haystack&act=updatesign&',
msgBoxUrl : requrl + 'haystack&act=msgbox&',
sysmsgbox : requrl,
agreeUrl : requrl + 'haystack&act=agree&',
getNoticeUrl : requrl + 'haystack&act=getnotice&',
ligature : requrl + 'default&act=connected',
address : (_con.is_ssl ? 'wss://' : 'ws://') + (_con.__host__ || document.domain) +':' + _con._wsport_,
isMobile : false,
setMin : true,
enableAudio : false,
enableVideo : false,
enableGroup : false,
enableFriend : false,
initSkins : '24.jpg',
voice : 'default.mp3',
findUrl : false,
inited : 0,
socket : null,
jq : null,
/*系统分组tab*/
issystemtab : true,
/*开启在线状态*/
islinestate : false,
/*开启签名*/
issign : false,
/*开启关于*/
copyright : true,
/*开启皮肤*/
skins : false,
/*开启底部消息中心*/
sysmsg : true,
/*开启搜索*/
searchs : false,
/*客服是否可查看聊天记录*/
brief : false,
notice : _con.notice, // 是否开启桌面消息提醒,默认false
//skin : ['aaa.jpg'], //新增皮肤
//right : '5px', // 主面板相对浏览器右侧距离
//minRight : '5px', // 聊天面板最小化时相对浏览器右侧距离
/*特效*/
effects : false,
/*自动匹配回复*/
automatching : true,
/*end*/
open : function() {
if (this.inited) return;
if(this.isIE6or7()) return;
if(!this.jq) this.jq = $;
this.connectWorkerman();
this.inited = 1;
},
isIE6or7 : function(){
var b = document.createElement('b');
b.innerHTML = '<!--[if IE 5]><i></i><![endif]--><!--[if IE 6]><i></i><![endif]--><!--[if IE 7]><i></i><![endif]-->';
return b.getElementsByTagName('i').length === 1;
},
connectWorkerman : function() {
laychat.socket = new WebSocket(laychat.address);
laychat.socket.onopen = function(){
var login_data = '{"type":"init","id":"'+ _con.uinfo.id + '","username":"'+ _con.uinfo.username + '","avatar":"'+ _con.uinfo.avatar + '","sign":"'+ _con.uinfo.sign + '"}';
//laychat.socket.send(JSON.stringify({type: 'init',id:_con.uinfo.id}));
laychat.socket.send(login_data);
};
laychat.socket.onmessage = function(e){
var msg = JSON.parse(e.data);
// 如果layim还没有初始化就收到消息则忽略(init消息除外)
if(!msg.message_type || (msg.message_type != 'init' && !layui.layim)) return;
switch(msg.message_type) {
// 初始化im
case 'init':
/*ajax请求,绑定client_id和uid*/
$.ajax({
url: laychat.bindUrl,
type: 'post',
data: {client_id: msg.client_id},
dataType: 'json',
success: function(bindJson){
if(bindJson.code == 0){
$.ajax({
url: laychat.initUrl,
type: 'post',
data: {},
dataType: 'json',
success: function(initData){
if(initData.code != 0){
console.log('初始化数据失败:' + initData.msg);
}else{
laychat.initIM(bindJson, initData.data);
}
},
error: function(xhr, ajaxOptions, thrownError){
console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
}else{
console.log('绑定客户端失败:' + bindJson.msg);
}
},
error: function(xhr, ajaxOptions, thrownError){
console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
return;
// 添加一个用户到好友列表
case 'addList':
if($('#layim-friend'+msg.data.id).length == 0 && layui.layim.cache() && layui.layim.cache().id != msg.data.id){
return layui.layim.addList && layui.layim.addList(msg.data);
}
if (msg.data.type == 'friend') {
layui.layim.setFriendStatus && layui.layim.setFriendStatus(msg.data.id, 'online');
}
return;
// 收到一个消息
case 'chatMessage':
msg.data.content = String(msg.data.content);
if(msg.data.type == 'group') {
if(msg.data.from_id != layui.layim.cache().mine.id){
layui.layim.getMessage(msg.data);
}
}else if(layui.layim.cache().mine.id != msg.data.id){
layui.layim.getMessage(msg.data);
}
if(laychat.effects){
autoword.getword(msg.data.content);
}
lastspeech(msg.data.content,msg.data.from_id);
if(msg.data.type == 'kefu'){
/*重置超时 - 按正常道理接收不应该触发重置超时*-/
time_out();
/*重置超时自动断开*/
break_out();
layui.layim.setChatStatus('');
}
return;
case 'msgbox':
layui.layim.msgbox && layui.layim.msgbox(msg.count);
return;
case 'oppo':
layui.layim.setChatStatus(msg.msg_data);
layui.layim.setFriendStatus && layui.layim.setFriendStatus(msg.id, msg.status);
return;
// 退出
case 'logout':
//layui.layim.setChatStatus('<span style="color:#FF5722;">对方不在线</span>');
return layui.layim.setFriendStatus && layui.layim.setFriendStatus(msg.id, 'offline');
// 隐身
case 'hide':
//layui.layim.setChatStatus('<span style="color:#FF5722;">对方不在线</span>');
return layui.layim.setFriendStatus && layui.layim.setFriendStatus(msg.id, 'offline');
// 上线
case 'online':
//layui.layim.setChatStatus('<span>在线</span>');
return layui.layim.setFriendStatus && layui.layim.setFriendStatus(msg.id, 'online');
// 人工客服
case 'ligature':
if(_con.ligature){
$.ajax({
url: laychat.ligature,
type: 'post',
data: {data:{kefu:_con.ligature,laun:msg.data,to:_con.uinfo}},
dataType: 'json',
success: function(lgt){},
error: function(xhr, ajaxOptions, thrownError){
console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
}else{
/*接收客服信息*/
_con.ligature = msg.data;
/*给客服提示已接上*/
var kdata = {system:true,mine:_con.uinfo,to:_con.ligature};
kdata.mine.content = sprintf(_con.d_replay.userhrough,_con.uinfo.username);
kdata.mine.mine = true;
kdata.to.type = 'service';
$.post(laychat.sendMessageUrl, {data: kdata}, function(data){
if(data.code != 0) {
ymservice.getms(_con.d_replay.userfailure);
_con.ligature = null;
}else{
/*提示已接上*/
ymservice.getms(sprintf(_con.d_replay.yetthrough,_con.ligature.username),true);
/*开始超时自动断开*/
if(_con.d_replay.break_out) break_out();
/*接上客服就不要默认的超时了*/
keeptimes({
key : 'time_out',
num : 0,
bac : null,
feed : null,
clear : true
});
/*end*/
}
}, 'json');
}
return;
// 订单消息
case 'ordermsg':
// 自动弹出
$('[type-id="sysmsgbox"]').click();
return;
// 物流消息
case 'logistics':
// 自动弹出
$('[type-id="logistics"]').click();
return;
// 系统消息
case 'systemmsg':
// 自动弹出
$('[type-id="systemmsg"]').click();
return;
// 全部消息
case 'allmsg':
// 自动弹出
$('[type-id="systemmsg"]').click();
return;
case 'beiyong':
return;
}
}
laychat.socket.onclose = laychat.connectWorkerman;
},
sendHeartbeat : function() {
if(this.socket && this.socket.readyState == 1) {
this.socket.send(JSON.stringify({type :'ping'}));
}
},
/*关闭事件*/
unload : function(){
if(this.socket){
this.socket.send(JSON.stringify({type :'logout',id:_con.uinfo.id}));
//layui.layer.msg(_con.uinfo.username + ' 下线了', {time: 7000});
}
},
initIM : function(msg_data, init_data){
var unread_msg_tips = function(msg_data){
// 离线交易消息
if(msg_data.msgcentre){
var clicks = false;
$.each(msg_data.msgcentre,function(i,v){
if(parseInt(v)){
if(i == 'systemmsg') layui.layim.msgbox('new');
else $('[type-id="+i+"]').addClass('msgnotify');
clicks = true;
}
});
if(clicks) $('.layui-layim-min').click();
}
// 离线消息
for(var key in msg_data.unread_message){
layui.layim.getMessage(JSON.parse(msg_data.unread_message[key]));
}
if (msg_data.unread_notice_count) {
// 设置消息盒子未读计数
layui.layim.msgbox && layui.layim.msgbox(msg_data.unread_notice_count);
}
return;
};
// layim已经初始化了,则只提示未读消息
if(this.inited == 2) {
return unread_msg_tips(msg_data);
}
this.inited = 2;
// 心跳数据,用来保持长链接,避免socket链接长时间不通讯被路由节点防火墙关闭
setInterval('laychat.sendHeartbeat()', 12000);
var module = laychat.isMobile ? ['mobile','layedit'] : ['layim','layedit'];
layui.use(module, function(layim){
_con.layedit_s = layui.layedit;
_con.ymlayim = layim;
_con.ymmsg = layui.layer;
if (laychat.isMobile) {
var mobile = layui.mobile
, layim = mobile.layim
, layer = mobile.layer;
layui.layim = layim;
layui.layer = layer;
}
//layim建立就绪
layim.on('ready', function(res){
// 离线消息
return unread_msg_tips(msg_data);
});
//监听发送消息
layim.on('sendMessage', function(data){
var sendata = data;
if(_con.ligature){
var kdata = {mine:_con.uinfo,to:_con.ligature};
kdata.mine.content = data.mine.content;
kdata.mine.mine = true;
kdata.to.type = 'service';
sendata = kdata;
}
$.ajax({
url: laychat.sendMessageUrl,
type: 'post',
data: {data: sendata},
dataType: 'json',
success: function(sendData){
if(sendData.code != 0){
layui.layer.msg(sendData.msg, {time: 7000});
}
},
error: function(xhr, ajaxOptions, thrownError){
console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
if(laychat.effects){
autoword.sendword(data.mine.content);
}
lastspeech(data.mine.content,data.to.id);
if(data.to.type == 'kefu'){
/*重置超时*/
time_out();
/*重置超时自动断开*/
break_out();
if(!_con.ligature){
layim.setChatStatus('<span style="color:#FF5722;">对方正在输入......</span>');
}
}
});
//监听在线状态的切换事件
layim.on('online', function(data){
laychat.socket.send(JSON.stringify({type: data,uid:layui.layim.cache().mine.id}));
});
//更改个性签名
layim.on('sign', function(value){
$.post(laychat.updateSignUrl, {sign: value} , function(data){
if(data.code != 0) {
layui.layer.msg(data.msg, {time: 7000});
}
}, 'json');
});
//监听自定义工具栏点击,以添加代码为例
layim.on('tool(code)', function(insert){
layui.layer.prompt({
title: '插入代码 - 工具栏扩展示例'
,formType: 2
,shade: 0
},
function(text, index){
layui.layer.close(index);
insert('[pre class=layui-code]' + text + '[/pre]'); //将内容插入到编辑器
});
});
layim.on('close', function(value){
alert(999999);
});
//富文本
//layedit.build('ftextarea');
layui.layim.config({
//初始化接口
init: init_data
//查看群员接口
,members: {
url: laychat.membersUrl
}
// 上传图片
,uploadImage: {
url: laychat.uploadImageUrl
}
// 上传文件
,uploadFile: {
url: laychat.uploadFileUrl
}
//扩展工具栏
/*,tool: [
{
alias : 'code'
,title: '代码'
,icon : ''
}
]*/
,initSkin: laychat.initSkins
,isgroup: laychat.enableGroup
,isfriend: laychat.enableFriend
,msgbox: laychat.msgBoxUrl ? laychat.msgBoxUrl+(laychat.msgBoxUrl.indexOf('?') == -1 ? '?' : '&')+'getNoticeUrl='+encodeURI(laychat.getNoticeUrl)+'&agreeUrl='+encodeURI(laychat.agreeUrl) : false
//聊天记录地址
,chatLog: laychat.chatLogUrl
,find: laychat.findUrl
,issigns: laychat.issign
,copyright: laychat.copyright
,skins: laychat.skins
,sysmsg: laychat.sysmsg
,sysmsgbox: laychat.sysmsgbox
,issystemtab: laychat.issystemtab
,islinestate: laychat.islinestate
,title: _con.uinfo.username ? _con.uinfo.username : laychat.appName
,min: laychat.setMin
,searchs: laychat.searchs
,isAudio: laychat.enableAudio
,isVideo: laychat.enableVideo
,voice: laychat.voice
,_brief_: laychat.brief
});
});
}
};
if(_con.ymopen) laychat.open();评论列表
发表评论
热评文章
相关阅读