Ethan 6 жил өмнө
parent
commit
8bc175e335

+ 2 - 1
application/admin/controller/Base.php

@@ -12,7 +12,8 @@ class Base extends Controller
         }
 
         $this->assign([
-            'version' => config('version')
+            'version' => config('version'),
+            'socket' =>config('socket'),
         ]);
     }
 }

+ 89 - 0
application/admin/controller/Server.php

@@ -0,0 +1,89 @@
+<?php
+namespace app\admin\controller;
+
+class Server extends Base
+{
+    public function index()
+    {
+        // 客服信息
+        $userInfo = db('users')->where('id', cookie('l_user_id'))->find();
+
+        $this->assign([
+            'uinfo' => $userInfo,
+            'word' => db('words')->select(),
+            'groups' => db('groups')->where('status', 1)->select(),
+            'status' => db('kf_config')->where('id', 1)->find()
+        ]);
+
+        return $this->fetch();
+    }
+
+    // 获取服务用户列表
+    // 此方法是为了防止客服工作期间错误的刷新工作台,导致服务人员消失的问题
+    public function getUserList()
+    {
+        if(request()->isAjax()){
+
+            // 此处只查询过去 三个小时 内的未服务完的用户
+            $userList = db('service_log')->field('user_id id,user_name name,user_avatar avatar,user_ip ip')
+                ->where('kf_id', cookie('l_user_id'))
+                ->where('start_time', '>', time() - 3600 * 3)->where('end_time', 0)->select();
+
+            return json(['code' => 1, 'data' => $userList, 'msg' => 'ok']);
+        }
+    }
+
+    // 获取聊天记录
+    public function getChatLog()
+    {
+        if(request()->isAjax()){
+
+            $param = input('param.');
+
+            $limit = 10; // 一次显示10 条聊天记录
+            $offset = ($param['page'] - 1) * $limit;
+
+            $logs = db('chat_log')->where(function($query) use($param){
+                    $query->where('from_id', $param['uid'])->where('to_id', 'KF' . cookie('l_user_id'));
+            })->whereOr(function($query) use($param){
+                $query->where('from_id', 'KF' . cookie('l_user_id'))->where('to_id', $param['uid']);
+            })->limit($offset, $limit)->order('id', 'desc')->select();
+
+            $total =  db('chat_log')->where(function($query) use($param){
+                $query->where('from_id', $param['uid'])->where('to_id', 'KF' . cookie('l_user_id'));
+            })->whereOr(function($query) use($param){
+                $query->where('from_id', 'KF' . cookie('l_user_id'))->where('to_id', $param['uid']);
+            })->count();
+
+            foreach($logs as $key=>$vo){
+
+                $logs[$key]['type'] = 'user';
+                $logs[$key]['time_line'] = date('Y-m-d H:i:s', $vo['time_line']);
+
+                if($vo['from_id'] == 'KF' . cookie('l_user_id')){
+                    $logs[$key]['type'] = 'mine';
+                }
+            }
+
+            return json(['code' => 1, 'data' => $logs, 'msg' => intval($param['page']), 'total' => ceil($total / $limit)]);
+        }
+    }
+
+    // ip 定位
+    public function getCity()
+    {
+        $ip = input('param.ip');
+
+        $ip2region = new \Ip2Region();
+        $info = $ip2region->btreeSearch($ip);
+
+        $city = explode('|', $info['region']);
+
+        if(0 != $info['city_id']){
+            return json(['code' => 1, 'data' => $city['2'] . $city['3'] . $city['4'], 'msg' => 'ok']);
+        }else{
+
+            return json(['code' => 1, 'data' => $city['0'], 'msg' => 'ok']);
+        }
+    }
+}

+ 12 - 0
application/admin/view/menu.html

@@ -1,3 +1,15 @@
+<li class="menu">
+    <a href="#">
+        <i class="fa fa-server"></i>
+        <span class="nav-label">工作台</span>
+        <span class="fa arrow"></span>
+    </a>
+    <ul class="nav nav-second-level">
+        <li>
+            <a class="J_menuItem" href="{:url('server/index')}">我的工作台</a>
+        </li>
+    </ul>
+</li>
 <li class="menu">
     <a href="#">
         <i class="fa fa-qq"></i>

+ 150 - 0
application/admin/view/server/index.html

@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <title>whisper客服工作台</title>
+    <link rel="stylesheet" href="/static/service/js/layui/css/layui.css">
+    <link rel="stylesheet" href="/static/service/css/whisper.css">
+</head>
+<body class="layui-layout-body">
+<div class="layui-layout layui-layout-admin">
+    <div class="layui-header">
+        <div class="layui-logo" style="color: white">whisper客服【工作台】</div>
+        <ul class="layui-nav layui-layout-right">
+            <li style="margin-top: 10%">
+                <a href="javascript:;" onclick="loginOut();">
+                    <button class="layui-btn layui-bg-red">
+                        <i class="layui-icon">&#xe609;</i> 退出下班
+                    </button>
+                </a>
+            </li>
+        </ul>
+    </div>
+
+    <div class="layui-side" style="background:#f2f2f2">
+        <div class="layui-side-scroll">
+            <blockquote class="layui-elem-quote layui-bg-cyan" style="color: white">正在咨询的会员</blockquote>
+            <ul class="layui-unselect" id="user_list">
+
+            </ul>
+        </div>
+    </div>
+
+    <div class="layui-body" style="bottom:0">
+        <input type="hidden" id="active-user" data-avatar="" data-name="" data-id=""><!-- 当前对话的用户 -->
+        <div class="chat-left">
+            <div class="chat-box whisper-chat-main">
+
+            </div>
+            <div class="msg-send">
+                <div class="tools-bar">
+                    <i class="layui-icon" style="font-size: 30px;" id="face">&#xe60c;</i>
+                    <i class="layui-icon" style="font-size: 30px;" id="image">&#xe60d;</i>
+                    <i class="layui-icon" style="font-size: 30px;" id="file">&#xe61d;</i>
+                </div>
+                <div class="msg-box">
+                    <textarea class="msg-area" id="msg-area"></textarea>
+                </div>
+                <div class="send-area">
+                    <span style="margin-left:10px;color:gray">快捷键 Enter</span>
+                    <button class="layui-btn layui-btn-small layui-bg-cyan" style="float:right;margin-right:10px;height: 40px;padding: 0 15px;" id="send">
+                        <i class="layui-icon">&#xe609;</i>发送
+                    </button>
+                </div>
+            </div>
+        </div>
+
+        <div style="width:28%;height:100%;float:left;margin-left:1%">
+            <div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
+                <ul class="layui-tab-title">
+                    <li class="layui-this">访客信息</li>
+                    <li>常用语</li>
+                </ul>
+                <div class="layui-tab-content" style="height: 100px;">
+                    <div class="layui-tab-item layui-show">
+                        <div class="layui-form layui-form-pane">
+                            <div class="layui-form-item">
+                                <label class="layui-form-label">访客名</label>
+                                <div class="layui-input-block">
+                                    <input type="text" id="f-user" class="layui-input" readonly>
+                                </div>
+                            </div>
+                            <div class="layui-form-item">
+                                <label class="layui-form-label">IP</label>
+                                <div class="layui-input-block">
+                                    <input type="text" id="f-ip" class="layui-input" readonly>
+                                </div>
+                            </div>
+                            <div class="layui-form-item">
+                                <label class="layui-form-label">地区</label>
+                                <div class="layui-input-block">
+                                    <input type="text" id="f-area" class="layui-input" readonly>
+                                </div>
+                            </div>
+                            {if(1 == $status['change_status'])}
+                            <div class="layui-form-item">
+                                <label class="layui-form-label layui-bg-cyan" style="cursor: pointer;color:white" id="scroll-link">转接</label>
+                            </div>
+                            {/if}
+                        </div>
+                    </div>
+                    <div class="layui-tab-item">
+                        <div class="layui-form">
+                            <table class="layui-table">
+                                <thead>
+                                <tr>
+                                    <th>内容</th>
+                                    <th>操作</th>
+                                </tr>
+                                </thead>
+                                <tbody>
+                                {foreach name="word" item="vo"}
+                                <tr>
+                                    <td>{$vo.content}</td>
+                                    <td>
+                                        <a href="javascript:;" onclick="sendWord(this)" data-word="{$vo.content}" style="color:#009688">应用</a>
+                                    </td>
+                                </tr>
+                                {/foreach}
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 转接提示层 -->
+<div class="layui-form" id="change-box" style="display: none">
+    <div class="layui-form-item" style="margin-top: 20px">
+        <label class="layui-form-label">选择分组</label>
+        <div class="layui-input-block" style="width: 70%" >
+            <select lay-verify="required" lay-filter="group">
+                <option value=""></option>
+                {if !empty($groups)}
+                {foreach name="groups" item="vo"}
+                <option value="{$vo['id']}">{$vo['name']}</option>
+                {/foreach}
+                {/if}
+            </select>
+        </div>
+    </div>
+</div>
+<!-- 转接提示层 -->
+
+<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
+<script src="/static/service/js/layui/layui.js"></script>
+<script>
+    var uid = "{$uinfo['id']}";
+    var uname = "{$uinfo['user_name']}";
+    var avatar = "{$uinfo['user_avatar']}";
+    var group = "{$uinfo['group_id']}";
+    var socket_server = "{$socket}";
+</script>
+<script type="text/javascript" src="/static/service/js/functions.js"></script>
+<script type="text/javascript" src="/static/service/js/whisper.js"></script>
+</body>
+</html>

+ 8 - 8
application/index/controller/Common.php

@@ -11,12 +11,12 @@ class Common extends Controller
 
 
     /**
-     * 构造函数
+     * 验证apiToken
      *
      * @access public
-     * @return array JsonString
+     * @return string
      */
-    public function __construct()
+    protected function verifyApiToken()
     {
         $request     = \think\Request::instance();
         $getApiToken = input('server.HTTP_apiToken');
@@ -24,13 +24,13 @@ class Common extends Controller
         $controller  = $request->controller();
         $module      = $request->module();
         $apiToken    = md5($action.'Customer-Service'.$controller.strtotime(date('Y-m-d')).$module);
-        if ($getApiToken !== $apiToken) {
-            $code = -2;
-            $msg  = 'token错误';
-            return json(['code' => $code, 'data' => [], 'msg' => $msg]);
+        if ($getApiToken === $apiToken) {
+            return true;
+        } else {
+            return false;
         }
 
-    }//end __construct()
+    }//end verifyApiToken()
 
 
 }

+ 38 - 0
application/index/controller/Evaluate.php

@@ -0,0 +1,38 @@
+<?php
+namespace app\index\controller;
+
+/**
+ * 评价类
+ */
+class Evaluate extends Common
+{
+
+
+    /**
+     * 获取评价
+     *
+     * @access public
+     * @return array JsonString
+     */
+    public function index()
+    {
+        $tokenStatus = $this->verifyApiToken();
+        $code        = -2;
+        $msg         = '错误';
+        if ($tokenStatus === false) {
+            return json(['code' => $code, 'data' => [], 'msg' => $msg]);
+        }
+
+        try {
+            // 获取数据.
+            $robot = model('Evaluate')->getEvaluate();
+
+            return json(['code' => 200, 'data' => $robot, 'msg' => '成功']);
+        } catch (\Exception $e) {
+            return json(['code' => $code, 'data' => [], 'msg' => $msg]);
+        }//end try
+
+    }//end index()
+
+
+}

+ 7 - 2
application/index/controller/Groups.php

@@ -17,8 +17,13 @@ class Groups extends Common
      */
     public function index()
     {
-        $code = -2;
-        $msg  = '错误';
+        $tokenStatus = $this->verifyApiToken();
+        $code        = -2;
+        $msg         = '错误';
+        if ($tokenStatus === false) {
+            return json(['code' => $code, 'data' => [], 'msg' => $msg]);
+        }
+
         try {
             $groupsWhere['status'] = 1;
             // 获取符合条件数据.

+ 14 - 4
application/index/controller/Robot.php

@@ -16,8 +16,13 @@ class Robot extends Common
      */
     public function index()
     {
-        $code = -2;
-        $msg  = '错误';
+        $tokenStatus = $this->verifyApiToken();
+        $code        = -2;
+        $msg         = '错误';
+        if ($tokenStatus === false) {
+            return json(['code' => $code, 'data' => [], 'msg' => $msg]);
+        }
+
         try {
             // 获取查询条件.
             $groupsId       = input('get.groups_id');
@@ -51,8 +56,13 @@ class Robot extends Common
      */
     public function getRobotGroups()
     {
-        $code = -2;
-        $msg  = '错误';
+        $tokenStatus = $this->verifyApiToken();
+        $code        = -2;
+        $msg         = '错误';
+        if ($tokenStatus === false) {
+            return json(['code' => $code, 'data' => [], 'msg' => $msg]);
+        }
+
         try {
             $robotGroupsWhere['robotgroups_status'] = 1;
             // 获取符合条件数据.

+ 27 - 0
application/index/model/Evaluate.php

@@ -0,0 +1,27 @@
+<?php
+namespace app\index\model;
+
+use think\Model;
+
+/**
+ * 评价模型
+ */
+class Evaluate extends Model
+{
+
+
+    /**
+     * 查询评价
+     *
+     * @access public
+     * @return array 返回类型
+     */
+    public function getEvaluate()
+    {
+        $result = $this->select();
+        return $result;
+
+    }//end getEvaluate()
+
+
+}

+ 2 - 1
application/index/view/index/chat.html

@@ -29,9 +29,10 @@
         <div class="input-group" style="margin-top: 5px;width: 100%">
             <span class="input-group-btn" style="float: right;">
                 <button class="btn btn-primary" type="button" id="send">发送</button>
-                <button class="btn btn-primary" type="button" onclick="aa()">发送</button>
+                <button class="btn btn-primary" type="button" onclick="aa()">机器人</button>
             </span>
         </div>
+        <button class="btn btn-primary" type="button" onclick="bb()">凭据</button>
     </div>
 
     <div class="face-box" style="display:none" id="face-box">

+ 4 - 1
application/service/view/index/index.html

@@ -51,6 +51,9 @@
                     <button class="layui-btn layui-btn-small layui-bg-cyan" style="float:right;margin-right:10px;height: 40px;padding: 0 15px;" id="send">
                         <i class="layui-icon">&#xe609;</i>发送
                     </button>
+                    <button class="layui-btn layui-btn-small layui-bg-cyan" style="float:right;margin-right:10px;height: 40px;padding: 0 15px;" onclick="cc()">
+                        <i class="layui-icon">&#xe609;</i>关闭
+                    </button>
                 </div>
             </div>
         </div>
@@ -147,4 +150,4 @@
 <script type="text/javascript" src="/static/service/js/functions.js"></script>
 <script type="text/javascript" src="/static/service/js/whisper.js"></script>
 </body>
-</html>
+</html>

+ 23 - 6
public/static/customer/js/whisper-cli.js

@@ -14,7 +14,7 @@ var commChat = 1;
 if(config != undefined && config.socket != undefined){
 
     // 创建一个Socket实例
-    var socket = new WebSocket('ws://' + config.socket+'?apiToken=72fb0848e7bf20e052362bac4a86164c');
+    var socket = new WebSocket('ws://' + config.socket+'?apiToken=70b0a7d015ef9a5268b2bc7e3e96af24');
 
     // 加锁
     lockTextarea();
@@ -25,7 +25,7 @@ if(config != undefined && config.socket != undefined){
     socket.onopen = function(res) {
         console.log('握手成功');
         // 登录
-        var login_data = '{"type":"userInit", "uid": ' + config.uid + ', "name" : "' + config.name +
+        var login_data = '{"type":"userInit", "uid": 1, "name" : "' + config.name +
             '", "avatar" : "' + config.avatar + '", "group" : ' + config.group + '}';
         socket.send(login_data);
 
@@ -74,6 +74,15 @@ if(config != undefined && config.socket != undefined){
                 commChat = 2;
                 document.getElementById('title').innerHTML = '正在转接中...';
                 break;
+            // 评价
+            case 'evaluate':
+                if (data.data.status ==1) {
+                    alert('成功')
+                } else {
+                    alert('失败')
+                }
+                showMsg(aa);
+                break;
         }
     };
 
@@ -169,12 +178,21 @@ function showSystem(msg){
     document.getElementById('chat-list').innerHTML = _html;
 }
 
-
+//机器人
 function aa() {
     socket.send(JSON.stringify({
         type: 'toRobot',
         data: {groups_id: 1, robot_name: '范德萨1', robotgroups_id: 1,
-            from_id: config.uid, from_avatar: 333}
+            from_id: config.uid}
+    }));
+}
+//评价
+function bb() {
+    socket.send(JSON.stringify({
+        type: 'evaluate',
+        data: {
+            evaluate_id: 1, from_id: config.uid,
+        }
     }));
 }
 // 发送信息
@@ -201,8 +219,7 @@ function sendMsg(sendMsg){
     // 发送消息
     socket.send(JSON.stringify({
         type: 'chatMessage',
-        data: {to_id: kf_id, to_name: kf_name, content: msg, from_name: config.name,
-            from_id: config.uid, from_avatar: config.avatar}
+        data: {to_id: kf_id, content: msg, from_id: config.uid}
     }));
 
     // 储存我发出的信息

+ 18 - 2
public/static/service/js/whisper.js

@@ -6,7 +6,7 @@ var uinfo = {
 };
 
 // 创建一个Socket实例
-var socket = new WebSocket('ws://' + socket_server+'?apiToken=72fb0848e7bf20e052362bac4a86164c');
+var socket = new WebSocket('ws://' + socket_server+'?apiToken=70b0a7d015ef9a5268b2bc7e3e96af24');
 
 // 打开Socket
 socket.onopen = function (res) {
@@ -300,6 +300,22 @@ function sendMessage(sendMsg) {
     wordBottom();
 }
 
+// 发送消息
+function cc() {
+
+    var uid = $("#active-user").attr('data-id');
+
+    socket.send(JSON.stringify({
+        type: 'closeUser',
+        data: {to_id: uid, from_name: uinfo.username, from_id: uinfo.id}
+    }));
+
+    $("#u-" + uid).append(word);
+    $(".msg-area").val('');
+    // 滚动条自动定位到最底端
+    wordBottom();
+}
+
 // 展示客服发送来的消息
 function showUserMessage(uinfo, content) {
     if ($('#f-' + uinfo.id).length == 0) {
@@ -372,7 +388,7 @@ function changeUserTab(obj) {
 
 // 添加用户到面板
 function addUser(data) {
-
+console.log(data)
     var _html = '<li class="layui-nav-item" data-id="' + data.id + '" id="f-' + data.id +
         '" data-name="' + data.name + '" data-avatar="' + data.avatar + '" data-ip="' + data.ip + '">';
     _html += '<img src="' + data.avatar + '">';

+ 49 - 2
vendor/GatewayWorker_windows/Applications/whisper/Events.php

@@ -144,7 +144,6 @@ class Events
                 break;
             // 顾客初始化
             case 'userInit';
-
                 $userList = self::$global->userList;
                 // 如果该顾客未在内存中记录则记录
                 if(!array_key_exists($message['uid'], $userList)){
@@ -154,6 +153,9 @@ class Events
                             'id' => $message['uid'],
                             'name' => $message['name'],
                             'avatar' => $message['avatar'],
+                            'website' => $_SESSION['origin'],//$_SERVER['HTTP_ORIGIN'],
+                            'browse' => Gateway::browse_info(),
+                            'system' => Gateway::get_os(),
                             'ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
                             'group' => $message['group'],
                             'client_id' => $client_id
@@ -320,6 +322,17 @@ class Events
             case 'toRobot':
                 self::toRobot($client_id, $message);
                 break;
+            // 评价.
+            case 'evaluate':
+                self::evaluate($client_id, $message);
+                break;
+            // 关闭会话.
+            case 'closeUser':
+                $client = Gateway::getClientIdByUid($message['data']['to_id']);
+                if(!empty($client)){
+                    self::onClose($client['0']);
+                }
+                break;
         }
     }
 
@@ -342,7 +355,7 @@ class Events
                     $isServiceUserOut = true;
 
                     // 根据client id 去更新这个会员离线的一些信息
-                    self::$db->query("update `ws_service_log` set `end_time` = " . time() . " where `client_id`= '" . $client_id . "'");
+                    self::$db->query("update `ws_service_log` set `end_time` = " . time() . " , `status` = '2' where `client_id`= '" . $client_id . "'");
 
                     // 从会员的内存表中检索出该会员的信息,并更新内存
                     $oldSimple = $simpleList = self::$global->uidSimpleList;
@@ -618,6 +631,10 @@ class Events
                 'kf_id' => intval(ltrim($res['data']['0'], 'KF')),
                 'start_time' => time(),
                 'group_id' => $group,
+                'website' => $res['data']['3']['website'],
+                'system' => $res['data']['3']['system'],
+                'browse' => $res['data']['3']['browse'],
+                'status' => 1,
                 'end_time' => 0
             ];
 
@@ -825,4 +842,34 @@ class Events
         ];
         Gateway::sendToClient($client_id, json_encode($chat_message));
     }
+
+    /**
+     * 评价
+     * @param $client_id 服务ID
+     * @param $message 数据
+     */
+    private static function evaluate($client_id, $message)
+    {
+        // 查询问题.
+        $evaluate_id = $message['data']['evaluate_id'];
+        $result = self::$db->query("UPDATE `ws_service_log` SET `evaluate_id` = '" . $evaluate_id . "' WHERE `client_id`='" . $client_id . "'");
+        if ($result) {
+            $chat_message = [
+                'message_type' => 'evaluate',
+                'data' => [
+                    'status' => 1,
+                    'time' => date('H:i'),
+                ]
+            ];
+        } else {
+            $chat_message = [
+                'message_type' => 'evaluate',
+                'data' => [
+                    'status' => 2,
+                    'time' => date('H:i'),
+                ]
+            ];
+        }
+        Gateway::sendToClient($client_id, json_encode($chat_message));
+    }
 }

+ 4 - 0
vendor/GatewayWorker_windows/Applications/whisper/start_gateway.php

@@ -45,6 +45,10 @@ $gateway->onConnect = function($connection)
     $connection->onWebSocketConnect = function($connection , $http_header)
     {
         $apiToken = md5('Customer-Service'.strtotime(date('Y-m-d')).$_SERVER['HTTP_ORIGIN']);
+        //echo $apiToken."/".$_GET['apiToken'];
+        $_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
+        $_SESSION['origin'] = $_SERVER['HTTP_ORIGIN'];
+
         if ($_GET['apiToken'] !== $apiToken) {
             $connection->close();
         }

+ 60 - 8
vendor/GatewayWorker_windows/vendor/workerman/gateway-worker-for-win/src/Lib/Gateway.php

@@ -53,7 +53,7 @@ class Gateway
      * @var bool
      */
     public static $persistentConnection = true;
-    
+
     /**
      * 向所有客户端连接(或者 client_id_array 指定的客户端连接)广播消息
      *
@@ -170,7 +170,7 @@ class Gateway
     {
         return (int)self::getClientIdByUid($uid);
     }
-    
+
     /**
      * 判断某个客户端连接是否在线
      *
@@ -261,7 +261,7 @@ class Gateway
     {
         return self::getAllClientSessions($group);
     }
-    
+
     /**
      * 获取所有连接数
      *
@@ -319,10 +319,10 @@ class Gateway
         }
         return $client_list;
     }
-    
+
     /**
      * 生成验证包,用于验证此客户端的合法性
-     * 
+     *
      * @return string
      */
     protected static function generateAuthBuffer()
@@ -595,7 +595,7 @@ class Gateway
         }
         return self::setSocketSession($client_id, Context::sessionEncode($session));
     }
-    
+
     /**
      * 更新 session,实际上是与老的session合并
      *
@@ -610,12 +610,12 @@ class Gateway
         }
         return self::sendCmdAndMessageToClient($client_id, GatewayProtocol::CMD_UPDATE_SESSION, '', Context::sessionEncode($session));
     }
-    
+
     /**
      * 获取某个client_id的session
      *
      * @param int   $client_id
-     * @return mixed false表示出错、null表示用户不存在、array表示具体的session信息 
+     * @return mixed false表示出错、null表示用户不存在、array表示具体的session信息
      */
     public static function getSession($client_id)
     {
@@ -857,6 +857,58 @@ class Gateway
         }
         return $addresses_cache;
     }
+
+    /**
+     * 获得访客操作系统
+     * @return string
+     */
+    public static function get_os(){
+        if (!empty($_SESSION['userAgent'])) {
+            $os = $_SESSION['userAgent'];
+            if (preg_match('/win/i', $os)) {
+                $os = 'Windows';
+            } else if (preg_match('/mac/i', $os)) {
+                $os = 'MAC';
+            } else if (preg_match('/linux/i', $os)) {
+                $os = 'Linux';
+            } else if (preg_match('/unix/i', $os)) {
+                $os = 'Unix';
+            } else if (preg_match('/bsd/i', $os)) {
+                $os = 'BSD';
+            } else {
+                $os = 'Other';
+            }
+            return $os;
+        } else {
+            return 'unknow';
+        }
+    }
+
+    /**
+     * 获得访问者浏览器
+     * @return string
+     */
+    public static function browse_info(){
+        if (!empty($_SESSION['userAgent'])) {
+            $br = $_SESSION['userAgent'];
+            if (preg_match('/MSIE/i', $br)) {
+                $br = 'MSIE';
+            } else if (preg_match('/Firefox/i', $br)) {
+                $br = 'Firefox';
+            } else if (preg_match('/Chrome/i', $br)) {
+                $br = 'Chrome';
+            } else if (preg_match('/Safari/i', $br)) {
+                $br = 'Safari';
+            } else if (preg_match('/Opera/i', $br)) {
+                $br = 'Opera';
+            } else {
+                $br = 'Other';
+            }
+            return $br;
+        } else {
+            return 'unknow';
+        }
+    }
 }
 
 if (!class_exists('\Protocols\GatewayProtocol')) {