kuaifan 5 anni fa
parent
commit
03d3f13777

+ 20 - 0
app/Events/WorkerStartEvent.php

@@ -0,0 +1,20 @@
+<?php
+namespace App\Events;
+
+use DB;
+use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface;
+use Swoole\Http\Server;
+
+class WorkerStartEvent implements WorkerStartInterface
+{
+
+    public function __construct()
+    {
+    }
+
+    public function handle(Server $server, $workerId)
+    {
+        // TODO: Implement handle() method.
+        DB::table('ws')->delete();
+    }
+}

+ 53 - 2
app/Module/Chat.php

@@ -15,14 +15,18 @@ class Chat
      * 打开对话(创建对话)
      * @param string $username      发送者用户名
      * @param string $receive       接受者用户名
+     * @param bool $forceRefresh    是否强制刷新缓存
      * @return mixed
      */
-    public static function openDialog($username, $receive)
+    public static function openDialog($username, $receive, $forceRefresh = false)
     {
         if (!$username || !$receive) {
             return Base::retError('参数错误!');
         }
         $cacheKey = $username . "@" . $receive;
+        if ($forceRefresh === true) {
+            Cache::forget($cacheKey);
+        }
         $result = Cache::remember($cacheKey, now()->addMinutes(10), function() use ($receive, $username) {
             $row = Base::DBC2A(DB::table('chat_dialog')->where([
                 'user1' => $username,
@@ -125,12 +129,15 @@ class Chat
                 $lastText = '[未知类型]';
                 break;
         }
+        $field = ($dialog['recField'] == 1 ? 'unread1' : 'unread2');
+        $unread = intval(DB::table('chat_dialog')->where('id', $dialog['id'])->value($field));
         if ($lastText) {
             $upArray = [];
             if ($username != $receive) {
                 $upArray = Base::DBUP([
-                    ($dialog['recField'] == 1 ? 'unread1' : 'unread2') => 1,
+                    $field => 1,
                 ]);
+                $unread += 1;
             }
             $upArray['lasttext'] = $lastText;
             $upArray['lastdate'] = $indate;
@@ -147,7 +154,51 @@ class Chat
         }
         $inArray['id'] = DB::table('chat_msg')->insertGetId($inArray);
         $inArray['message'] = $message;
+        $inArray['unread'] = $unread;
         //
         return Base::retSuccess('success', $inArray);
     }
+
+    /**
+     * 格式化信息(来自接收)
+     * @param $data
+     * @return array
+     */
+    public static function formatMsgReceive($data) {
+        return self::formatMsgData(Base::json2array($data));
+    }
+
+    /**
+     * 格式化信息(用于发送)
+     * @param $array
+     * @return string
+     */
+    public static function formatMsgSend($array) {
+        return Base::array2json(self::formatMsgData($array));
+    }
+
+    /**
+     * 格式化信息
+     * @param array $array
+     * @return array
+     */
+    public static function formatMsgData($array = []) {
+        if (!is_array($array)) {
+            $array = [];
+        }
+        //messageType来自客户端(前端->后端):refresh/unread/read/roger/user/team
+        //messageType来自服务端(后端->前端):error/open/kick/user/back/unread
+        if (!isset($array['messageType'])) $array['messageType'] = '';  //消息类型
+        if (!isset($array['messageId'])) $array['messageId'] = '';      //消息ID(用于back给客户端)
+        if (!isset($array['contentId'])) $array['contentId'] = 0;       //消息数据ID(用于roger给服务端)
+        if (!isset($array['channel'])) $array['channel'] = '';          //渠道(用于多端登录)
+        if (!isset($array['username'])) $array['username'] = '';        //发送者
+        if (!isset($array['target'])) $array['target'] = null;          //接受者
+        if (!isset($array['body'])) $array['body'] = [];                //正文内容
+        if (!isset($array['time'])) $array['time'] = time();            //时间
+        //
+        $array['contentId'] = intval($array['contentId']);
+        if (!is_array($array['body']) || empty($array['body'])) $array['body'] = ['_' => time()];
+        return $array;
+    }
 }

+ 186 - 178
app/Services/WebSocketService.php

@@ -2,12 +2,14 @@
 
 namespace App\Services;
 
-use App\Model\DBCache;
 use App\Module\Base;
 use App\Module\Chat;
 use App\Module\Users;
+use App\Tasks\ChromeExtendTask;
+use App\Tasks\PushTask;
 use Cache;
 use DB;
+use Hhxsv5\LaravelS\Swoole\Task\Task;
 use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
 use Swoole\Http\Request;
 use Swoole\WebSocket\Frame;
@@ -38,22 +40,22 @@ class WebSocketService implements WebSocketHandlerInterface
         $_A = [
             '__static_langdata' => [],
         ];
-        //
-        $to = $request->fd;
+        //判断参数
+        $fd = $request->fd;
         if (!isset($request->get['token'])) {
-            $server->push($to, $this->formatMsgSend([
+            $server->push($fd, Chat::formatMsgSend([
                 'messageType' => 'error',
-                'type' => 'user',
-                'content' => [
+                'body' => [
                     'error' => '参数错误'
                 ],
             ]));
-            $server->close($to);
-            $this->forgetUser($to);
+            $server->close($fd);
+            $this->deleteUser($fd);
             return;
         }
-        //
+        //判断token
         $token = $request->get['token'];
+        $channel = $request->get['channel'] ?: '';
         $cacheKey = "ws::token:" . md5($token);
         $username = Cache::remember($cacheKey, now()->addSeconds(1), function () use ($token) {
             list($id, $username, $encrypt, $timestamp) = explode("@", base64_decode($token) . "@@@@");
@@ -66,57 +68,66 @@ class WebSocketService implements WebSocketHandlerInterface
         });
         if (empty($username)) {
             Cache::forget($cacheKey);
-            $server->push($to, $this->formatMsgSend([
+            $server->push($fd, Chat::formatMsgSend([
                 'messageType' => 'error',
-                'type' => 'user',
-                'content' => [
+                'channel' => $channel,
+                'body' => [
                     'error' => '会员不存在',
                 ],
             ]));
-            $server->close($to);
-            $this->forgetUser($to);
+            $server->close($fd);
+            $this->deleteUser($fd);
             return;
         }
-        //
-        $wsid = $this->name2fd($username);
-        if ($wsid > 0) {
-            $server->push($wsid, $this->formatMsgSend([
-                'messageType' => 'forced',
-                'type' => 'user',
-                'content' => [
-                    'ip' => Base::getIp(),
-                    'time' => time(),
-                    'new_wsid' => $to,
-                    'old_wsid' => $wsid,
-                ],
-            ]));
-        }
-        $this->saveUser($to, $username);
-        $server->push($to, $this->formatMsgSend([
+        //踢下线
+        /*if ($channel != 'chromeExtend') {
+            $userLists = $this->getUser('', $channel, $username);
+            foreach ($userLists AS $user) {
+                $server->push($user['fd'], Chat::formatMsgSend([
+                    'messageType' => 'kick',
+                    'channel' => $channel,
+                    'body' => [
+                        'ip' => Base::getIp(),
+                        'time' => time(),
+                        'newfd' => $fd,
+                    ],
+                ]));
+                $this->deleteUser($user['fd']);
+            }
+        }*/
+        //保存用户、发送open事件
+        $this->saveUser($fd, $channel, $username);
+        $server->push($fd, Chat::formatMsgSend([
             'messageType' => 'open',
-            'type' => 'user',
-            'content' => [
-                'swid' => $to,
+            'channel' => $channel,
+            'body' => [
+                'fd' => $fd,
             ],
         ]));
-        //
+        //发送最后一条未发送的信息
         $lastMsg = Base::DBC2A(DB::table('chat_msg')->where('receive', $username)->orderByDesc('indate')->first());
         if ($lastMsg && $lastMsg['roger'] === 0) {
-            $content = Base::string2array($lastMsg['message']);
-            $content['resend'] = 1;
-            $content['id'] = $lastMsg['id'];
-            $content['username'] = $lastMsg['username'];
-            $content['userimg'] = Users::userimg($lastMsg['username']);
-            $content['indate'] = $lastMsg['indate'];
-            $server->push($to, $this->formatMsgSend([
-                'messageType' => 'send',
-                'contentId' => $lastMsg['id'],
-                'type' => 'user',
-                'sender' => $lastMsg['username'],
-                'target' => $lastMsg['receive'],
-                'content' => $content,
-                'time' => $lastMsg['indate'],
-            ]));
+            $dialog = Chat::openDialog($lastMsg['username'], $lastMsg['receive']);
+            if (!Base::isError($dialog)) {
+                $dialog = $dialog['data'];
+                $unread = intval(DB::table('chat_dialog')->where('id', $dialog['id'])->value(($dialog['recField'] == 1 ? 'unread1' : 'unread2')));
+                $body = Base::string2array($lastMsg['message']);
+                $body['id'] = $lastMsg['id'];
+                $body['resend'] = 1;
+                $body['unread'] = $unread;
+                $body['username'] = $lastMsg['username'];
+                $body['userimg'] = Users::userimg($lastMsg['username']);
+                $body['indate'] = $lastMsg['indate'];
+                $server->push($fd, Chat::formatMsgSend([
+                    'messageType' => 'user',
+                    'contentId' => $lastMsg['id'],
+                    'channel' => $channel,
+                    'username' => $lastMsg['username'],
+                    'target' => $lastMsg['receive'],
+                    'body' => $body,
+                    'time' => $lastMsg['indate'],
+                ]));
+            }
         }
     }
 
@@ -132,30 +143,37 @@ class WebSocketService implements WebSocketHandlerInterface
             '__static_langdata' => [],
         ];
         //
-        $data = $this->formatMsgReceive($frame->data);
-        $feedback = [
+        $data = Chat::formatMsgReceive($frame->data);
+        $back = [
             'status' => 1,
             'message' => '',
         ];
-        switch ($data['type']) {
+        //
+        switch ($data['messageType']) {
             /**
              * 刷新
              */
             case 'refresh':
-                DB::table('users')->where('username', $this->fd2name($frame->fd))->update(['wsdate' => time()]);
+                DB::table('ws')->where([
+                    'fd' => $frame->fd,
+                    'channel' => $data['channel'],
+                ])->update(['update' => time()]);
                 break;
 
             /**
-             * 未读消息
+             * 未读消息数
              */
             case 'unread':
-                $from = $this->fd2name($frame->fd);
-                if ($from) {
-                    $num = intval(DB::table('chat_dialog')->where('user1', $from)->sum('unread1'));
-                    $num+= intval(DB::table('chat_dialog')->where('user2', $from)->sum('unread2'));
-                    $feedback['message'] = $num;
+                $username = DB::table('ws')->where([
+                    'fd' => $frame->fd,
+                    'channel' => $data['channel'],
+                ])->value('username');
+                if ($username) {
+                    $num = intval(DB::table('chat_dialog')->where('user1', $username)->sum('unread1'));
+                    $num+= intval(DB::table('chat_dialog')->where('user2', $username)->sum('unread2'));
+                    $back['message'] = $num;
                 } else {
-                    $feedback['message'] = 0;
+                    $back['message'] = 0;
                 }
                 break;
 
@@ -163,21 +181,24 @@ class WebSocketService implements WebSocketHandlerInterface
              * 已读会员消息
              */
             case 'read':
-                $to = $this->name2fd($data['target']);
-                if ($to) {
-                    $dialog = Chat::openDialog($this->fd2name($frame->fd), $data['target']);
-                    if (!Base::isError($dialog)) {
-                        $dialog = $dialog['data'];
-                        $upArray = [];
-                        if ($dialog['user1'] == $dialog['user2']) {
-                            $upArray['unread1'] = 0;
-                            $upArray['unread2'] = 0;
-                        } else {
-                            $upArray[($dialog['recField'] == 1 ? 'unread2' : 'unread1')] = 0;
-                        }
-                        DB::table('chat_dialog')->where('id', $dialog['id'])->update($upArray);
+                $username = DB::table('ws')->where([
+                    'fd' => $frame->fd,
+                    'channel' => $data['channel'],
+                ])->value('username');
+                $dialog = Chat::openDialog($username, $data['target']);
+                if (!Base::isError($dialog)) {
+                    $dialog = $dialog['data'];
+                    $upArray = [];
+                    if ($dialog['user1'] == $dialog['user2']) {
+                        $upArray['unread1'] = 0;
+                        $upArray['unread2'] = 0;
+                    } else {
+                        $upArray[($dialog['recField'] == 1 ? 'unread2' : 'unread1')] = 0;
                     }
+                    DB::table('chat_dialog')->where('id', $dialog['id'])->update($upArray);
                 }
+                $chromeExtendTask = new ChromeExtendTask($username);
+                Task::deliver($chromeExtendTask);
                 break;
 
             /**
@@ -185,9 +206,13 @@ class WebSocketService implements WebSocketHandlerInterface
              */
             case 'roger':
                 if ($data['contentId'] > 0) {
+                    $username = DB::table('ws')->where([
+                        'fd' => $frame->fd,
+                        'channel' => $data['channel'],
+                    ])->value('username');
                     DB::table('chat_msg')->where([
                         'id' => $data['contentId'],
-                        'receive' => $this->fd2name($frame->fd),
+                        'receive' => $username,
                     ])->update([
                         'roger' => 1,
                     ]);
@@ -198,21 +223,32 @@ class WebSocketService implements WebSocketHandlerInterface
              * 发给用户
              */
             case 'user':
-                $to = $this->name2fd($data['target']);
-                $res = Chat::saveMessage($this->fd2name($frame->fd), $data['target'], $data['content']);
+                $username = DB::table('ws')->where([
+                    'fd' => $frame->fd,
+                    'channel' => $data['channel'],
+                ])->value('username');
+                $res = Chat::saveMessage($username, $data['target'], $data['body']);
                 if (Base::isError($res)) {
-                    $feedback = [
+                    $back = [
                         'status' => 0,
                         'message' => $res['msg'],
                     ];
                 } else {
-                    $contentId = $res['data']['id'];
-                    $data['contentId'] = $contentId;
-                    $data['content']['id'] = $contentId;
-                    $feedback['message'] = $contentId;
-                    if ($to) {
-                        $server->push($to, $this->formatMsgSend($data));
+                    $resData = $res['data'];
+                    $back['message'] = $resData['id'];
+                    $data['contentId'] = $resData['id'];
+                    $data['body']['id'] = $resData['id'];
+                    $data['body']['unread'] = $resData['unread'];
+                    //
+                    $pushLists = [];
+                    foreach ($this->getUserOfName($data['target']) AS $item) {
+                        $pushLists[] = [
+                            'fd' => $item['fd'],
+                            'msg' => $data
+                        ];
                     }
+                    $pushTask = new PushTask($pushLists);
+                    Task::deliver($pushTask);
                 }
                 break;
 
@@ -220,27 +256,40 @@ class WebSocketService implements WebSocketHandlerInterface
              * 发给整个团队
              */
             case 'team':
-                if (Base::val($data['content'], 'type') === 'taskA') {
-                    $taskId = intval(Base::val($data['content'], 'taskDetail.id'));
+                if (Base::val($data['body'], 'type') === 'taskA') {
+                    $taskId = intval(Base::val($data['body'], 'taskDetail.id'));
                     if ($taskId > 0) {
                         $userLists = $this->getTaskUsers($taskId);
                     } else {
                         $userLists = $this->getTeamUsers();
                     }
+                    //
+                    $pushLists = [];
                     foreach ($userLists as $user) {
+                        $data['messageType'] = 'user';
                         $data['target'] = $user['username'];
-                        $server->push($user['wsid'], $this->formatMsgSend($data));
+                        $pushLists[] = [
+                            'fd' => $user['fd'],
+                            'msg' => $data
+                        ];
                     }
+                    $pushTask = new PushTask($pushLists);
+                    Task::deliver($pushTask);
                 }
                 break;
         }
         if ($data['messageId']) {
-            $server->push($frame->fd, $this->formatMsgSend([
-                'messageType' => 'feedback',
-                'messageId' => $data['messageId'],
-                'type' => 'user',
-                'content' => $feedback,
-            ]));
+            $pushLists = [];
+            $pushLists[] = [
+                'fd' => $frame->fd,
+                'msg' => [
+                    'messageType' => 'back',
+                    'messageId' => $data['messageId'],
+                    'body' => $back,
+                ]
+            ];
+            $pushTask = new PushTask($pushLists);
+            Task::deliver($pushTask);
         }
     }
 
@@ -252,7 +301,7 @@ class WebSocketService implements WebSocketHandlerInterface
      */
     public function onClose(Server $server, $fd, $reactorId)
     {
-        $this->forgetUser($fd);
+        $this->deleteUser($fd);
     }
 
     /** ****************************************************************************** */
@@ -260,83 +309,64 @@ class WebSocketService implements WebSocketHandlerInterface
     /** ****************************************************************************** */
 
     /**
-     * 格式化信息(来自接收)
-     * @param $data
-     * @return array
-     */
-    private function formatMsgReceive($data) {
-        return $this->formatMsgData(Base::json2array($data));
-    }
-
-    /**
-     * 格式化信息(用于发送)
-     * @param $array
-     * @return string
+     * 保存用户
+     * @param $fd
+     * @param $channel
+     * @param $username
      */
-    private function formatMsgSend($array) {
-        return Base::array2json($this->formatMsgData($array));
-    }
+    private function saveUser($fd, $channel, $username)
+    {
+        try {
+            DB::transaction(function () use ($username, $channel, $fd) {
+                $this->deleteUser($fd);
+                DB::table('ws')->updateOrInsert([
+                    'key' => md5($fd . '@' . $channel . '@' . $username)
+                ], [
+                    'fd' => $fd,
+                    'username' => $username,
+                    'channel' => $channel,
+                    'update' => time()
+                ]);
+            });
+        } catch (\Throwable $e) {
 
-    /**
-     * 格式化信息
-     * @param array $array
-     * @return array
-     */
-    private function formatMsgData($array = []) {
-        if (!is_array($array)) {
-            $array = [];
         }
-        if (!isset($array['messageType'])) $array['messageType'] = '';
-        if (!isset($array['messageId'])) $array['messageId'] = '';
-        if (!isset($array['contentId'])) $array['contentId'] = 0;
-        if (!isset($array['type'])) $array['type'] = '';
-        if (!isset($array['sender'])) $array['sender'] = null;
-        if (!isset($array['target'])) $array['target'] = null;
-        if (!isset($array['content'])) $array['content'] = [];
-        if (!isset($array['time'])) $array['time'] = time();
-        if (!is_array($array['content'])) $array['content'] = [];
-        $array['contentId'] = intval($array['contentId']);
-        return $array;
     }
 
     /**
-     * 保存用户wsid
+     * 清除用户
      * @param $fd
-     * @param $username
      */
-    private function saveUser($fd, $username)
+    private function deleteUser($fd)
     {
-        $this->forgetUser($fd);
-        $this->forgetName($username);
-        DB::table('users')->where('username', $username)->update(['wsid' => $fd, 'wsdate' => time()]);
+        DB::table('ws')->where('fd', $fd)->delete();
     }
 
     /**
-     * 清除用户wsid
-     * @param $fd
+     * 获取用户
+     * @param string $fd
+     * @param string $channel
+     * @param string $username
+     * @return array
      */
-    private function forgetUser($fd)
+    private function getUser($fd  = '', $channel = '', $username = '')
     {
-        $this->forgetFd($fd);
-        DB::table('users')->where('wsid', $fd)->update(['wsid' => 0]);
+        $array = [];
+        if ($fd) $array['fd'] = $fd;
+        if ($channel) $array['channel'] = $channel;
+        if ($username) $array['username'] = $username;
+        if (empty($array)) {
+            return [];
+        }
+        return Base::DBC2A(DB::table('ws')->select(['fd', 'username', 'channel'])->where($array)->get());
     }
 
-    /**
-     * 根据wsid清除缓存
-     * @param $fd
-     */
-    private function forgetFd($fd) {
-        Cache::forget('ws::name:' . $this->fd2name($fd));
-        Cache::forget('ws::fd:' . $fd);
+    private function getUserOfFd($fd, $channel = '') {
+        return $this->getUser($fd, $channel);
     }
 
-    /**
-     * 根据username清除缓存
-     * @param $username
-     */
-    private function forgetName($username) {
-        Cache::forget('ws::fd:' . $this->name2fd($username));
-        Cache::forget('ws::name:' . $username);
+    private function getUserOfName($username, $channel = '') {
+        return $this->getUser('', $channel, $username);
     }
 
     /**
@@ -345,9 +375,8 @@ class WebSocketService implements WebSocketHandlerInterface
      */
     private function getTeamUsers()
     {
-        return Base::DBC2A(DB::table('users')->select(['wsid', 'username'])->where([
-            ['wsid', '>', 0],
-            ['wsdate', '>', time() - 600],
+        return Base::DBC2A(DB::table('ws')->select(['fd', 'username', 'channel'])->where([
+            ['update', '>', time() - 600],
         ])->get());
     }
 
@@ -376,29 +405,8 @@ class WebSocketService implements WebSocketHandlerInterface
             }
         }
         //
-        return Base::DBC2A(DB::table('users')->select(['wsid', 'username'])->where([
-            ['wsid', '>', 0],
-            ['wsdate', '>', time() - 600],
+        return Base::DBC2A(DB::table('ws')->select(['fd', 'username', 'channel'])->where([
+            ['update', '>', time() - 600],
         ])->whereIn('username', array_values(array_unique($userArray)))->get());
     }
-
-    /**
-     * wsid获取用户名
-     * @param $fd
-     * @return mixed
-     */
-    private function fd2name($fd)
-    {
-        return DBCache::table('users')->cacheKeyname('ws::fd:' . $fd)->select(['username'])->where('wsid', $fd)->value('username');
-    }
-
-    /**
-     * 用户名获取wsid
-     * @param $username
-     * @return mixed
-     */
-    private function name2fd($username)
-    {
-        return DBCache::table('users')->cacheKeyname('ws::name:' . $username)->select(['wsid'])->where('username', $username)->value('wsid');
-    }
 }

+ 45 - 0
app/Tasks/ChromeExtendTask.php

@@ -0,0 +1,45 @@
+<?php
+namespace App\Tasks;
+
+use App\Module\Base;
+use App\Module\Chat;
+use DB;
+use Hhxsv5\LaravelS\Swoole\Task\Task;
+
+class ChromeExtendTask extends Task
+{
+    private $username;
+
+    /**
+     * ChromeExtendTask constructor.
+     * @param $username
+     */
+    public function __construct($username)
+    {
+        $this->username = $username;
+    }
+
+    public function handle()
+    {
+        $lists = Base::DBC2A(DB::table('ws')->select(['fd', 'username', 'channel'])->where([
+            'username' => $this->username,
+            'channel' => 'chromeExtend',
+        ])->where([
+            ['update', '>', time() - 600],
+        ])->get());
+        if (count($lists) > 0) {
+            $unread = intval(DB::table('chat_dialog')->where('user1', $this->username)->sum('unread1'));
+            $unread+= intval(DB::table('chat_dialog')->where('user2', $this->username)->sum('unread2'));
+            //
+            $swoole = app('swoole');
+            foreach ($lists AS $item) {
+                $swoole->push($item['fd'], Chat::formatMsgSend([
+                    'messageType' => 'unread',
+                    'body' => [
+                        'unread' => $unread
+                    ],
+                ]));
+            }
+        }
+    }
+}

+ 27 - 0
app/Tasks/PushTask.php

@@ -0,0 +1,27 @@
+<?php
+namespace App\Tasks;
+
+use App\Module\Chat;
+use Hhxsv5\LaravelS\Swoole\Task\Task;
+
+class PushTask extends Task
+{
+    private $lists;
+
+    /**
+     * PushTask constructor.
+     * @param array $lists      [fd, msg]
+     */
+    public function __construct($lists)
+    {
+        $this->lists = $lists;
+    }
+
+    public function handle()
+    {
+        $swoole = app('swoole');
+        foreach ($this->lists AS $item) {
+            $swoole->push($item['fd'], Chat::formatMsgSend($item['msg']));
+        }
+    }
+}

+ 4 - 2
config/laravels.php

@@ -18,7 +18,9 @@ return [
         'excluded_dirs' => [],
         'log'           => true,
     ],
-    'event_handlers'           => [],
+    'event_handlers'           => [
+        'WorkerStart' => \App\Events\WorkerStartEvent::class,
+    ],
     'websocket'                => [
         'enable' => true,
         'handler' => \App\Services\WebSocketService::class,
@@ -59,7 +61,7 @@ return [
         'dispatch_mode'      => 2,
         'reactor_num'        => env('LARAVELS_REACTOR_NUM', function_exists('swoole_cpu_num') ? swoole_cpu_num() * 2 : 4),
         'worker_num'         => env('LARAVELS_WORKER_NUM', function_exists('swoole_cpu_num') ? swoole_cpu_num() * 2 : 8),
-        //'task_worker_num'    => env('LARAVELS_TASK_WORKER_NUM', function_exists('swoole_cpu_num') ? swoole_cpu_num() * 2 : 8),
+        'task_worker_num'    => env('LARAVELS_TASK_WORKER_NUM', function_exists('swoole_cpu_num') ? swoole_cpu_num() * 2 : 8),
         'task_ipc_mode'      => 1,
         'task_max_request'   => env('LARAVELS_TASK_MAX_REQUEST', 8000),
         'task_tmpdir'        => @is_writable('/dev/shm/') ? '/dev/shm' : '/tmp',

+ 58 - 3
resources/assets/js/common.js

@@ -519,12 +519,15 @@
          * @returns {*}
          */
         jsonParse(str, defaultVal) {
-            if (str !== null && typeof str === "object") {
+            if (str === null) {
+                return defaultVal ? defaultVal : {};
+            }
+            if (typeof str === "object") {
                 return str;
             }
-            try{
+            try {
                 return JSON.parse(str);
-            }catch (e) {
+            } catch (e) {
                 return defaultVal ? defaultVal : {};
             }
         },
@@ -716,6 +719,58 @@
         },
 
         /**
+         * 删除地址中的参数
+         * @param url
+         * @param parameter
+         * @returns {string|*}
+         */
+        removeURLParameter(url, parameter) {
+            if (parameter instanceof Array) {
+                parameter.forEach((key) => {
+                    url = $A.removeURLParameter(url, key)
+                });
+                return url;
+            }
+            var urlparts = url.split('?');
+            if (urlparts.length >= 2) {
+                //参数名前缀
+                var prefix = encodeURIComponent(parameter) + '=';
+                var pars = urlparts[1].split(/[&;]/g);
+
+                //循环查找匹配参数
+                for (var i = pars.length; i-- > 0;) {
+                    if (pars[i].lastIndexOf(prefix, 0) !== -1) {
+                        //存在则删除
+                        pars.splice(i, 1);
+                    }
+                }
+
+                return urlparts[0] + (pars.length > 0 ? '?' + pars.join('&') : '');
+            }
+            return url;
+        },
+
+        /**
+         * 连接加上参数
+         * @param url
+         * @param params
+         * @returns {*}
+         */
+        urlAddParams(url, params) {
+            if (typeof params === "object" && params !== null) {
+                url+= "";
+                url+= url.indexOf("?") === -1 ? '?' : '';
+                for (var key in params) {
+                    if (!params.hasOwnProperty(key)) {
+                        continue;
+                    }
+                    url+= '&' + key + '=' + params[key];
+                }
+            }
+            return url.replace("?&", "?");
+        },
+
+        /**
          * 链接字符串
          * @param value 第一个参数为连接符
          * @returns {string}

+ 48 - 33
resources/assets/js/main/App.vue

@@ -21,6 +21,16 @@
             }
         },
         mounted() {
+            let token = $A.hashParameter("token");
+            if ($A.count(token) > 10) {
+                $.setToken(token);
+                $A.getUserInfo(true);
+                let path = $A.removeURLParameter(window.location.href,'token');
+                if ($A.strExists(path, "#")) {
+                    this.goForward({path: $A.getMiddle(path, '#')}, true);
+                }
+            }
+            //
             this.sessionStorage('/', 1);
             //
             let hash = window.location.hash;
@@ -149,47 +159,52 @@
                 });
             },
 
-            handleWebSocket(force) {
+            handleWebSocket() {
                 if ($A.getToken() === false) {
-                    $A.WS.close();
+                    $A.WSOB.close();
                 } else {
-                    $A.WS.setOnMsgListener("app", (msgDetail) => {
-                        if (msgDetail.sender == $A.getUserName()) {
+                    $A.WSOB.setOnMsgListener("app", (msgDetail) => {
+                        if (msgDetail.username == $A.getUserName()) {
                             return;
                         }
-                        if (msgDetail.messageType == 'forced') {
-                            $A.token("");
-                            $A.storage("userInfo", {});
-                            $A.triggerUserInfoListener({});
-                            //
-                            let content = $A.jsonParse(msgDetail.content)
-                            let id = 'inip_' + Math.round(Math.random() * 10000);
-                            let ip = content.ip;
-                            let ip2 = ip.substring(0, ip.lastIndexOf('.')) + '.*';
-                            this.$Modal.warning({
-                                title: this.$L("系统提示"),
-                                content: this.$L('您的帐号在其他地方(%)登录,您被迫退出,如果这不是您本人的操作,请注意帐号安全!', '<span id="' + id + '">' + ip2 + '</span>'),
-                                onOk: () => {
-                                    this.goForward({path: '/'}, true);
+                        switch (msgDetail.messageType) {
+                            case 'open':
+                                window.localStorage.setItem("__::WookTeam:config", $A.jsonStringify(msgDetail.config));
+                                break;
+                            case 'close':
+                                window.localStorage.setItem("__::WookTeam:config", $A.jsonStringify({}));
+                                break;
+                            case 'user':
+                                if (msgDetail.body.type == 'taskA') {
+                                    $A.triggerTaskInfoListener(msgDetail.body.act, msgDetail.body.taskDetail, false);
                                 }
-                            });
-                            this.$nextTick(() => {
-                                $A.getIpInfo(ip, (res) => {
-                                    if (res.ret === 1) {
-                                        $A("span#" + id).text(res.data.textSmall);
-                                        $A("span#" + id).attr("title", ip2);
+                                break;
+                            case 'kick':
+                                $A.token("");
+                                $A.storage("userInfo", {});
+                                $A.triggerUserInfoListener({});
+                                //
+                                let id = 'inip_' + Math.round(Math.random() * 10000);
+                                let ip = msgDetail.body.ip;
+                                let ip2 = ip.substring(0, ip.lastIndexOf('.')) + '.*';
+                                this.$Modal.warning({
+                                    title: this.$L("系统提示"),
+                                    content: this.$L('您的帐号在其他地方(%)登录,您被迫退出,如果这不是您本人的操作,请注意帐号安全!', '<span id="' + id + '">' + ip2 + '</span>'),
+                                    onOk: () => {
+                                        this.goForward({path: '/'}, true);
                                     }
                                 });
-                            });
-                            return;
-                        } else if (msgDetail.messageType != 'send') {
-                            return;
-                        }
-                        let content = $A.jsonParse(msgDetail.content)
-                        if (content.type == 'taskA') {
-                            $A.triggerTaskInfoListener(content.act, content.taskDetail, false);
+                                this.$nextTick(() => {
+                                    $A.getIpInfo(ip, (res) => {
+                                        if (res.ret === 1) {
+                                            $A("span#" + id).text(res.data.textSmall);
+                                            $A("span#" + id).attr("title", ip2);
+                                        }
+                                    });
+                                });
+                                break;
                         }
-                    }).connection(force);
+                    });
                 }
             }
         }

+ 2 - 0
resources/assets/js/main/app.js

@@ -73,3 +73,5 @@ const app = new Vue({
 
 $A.app = app;
 
+window.localStorage.setItem("__::WookTeam:check", "success")
+

+ 2 - 3
resources/assets/js/main/components/UserView.vue

@@ -48,9 +48,6 @@
             username() {
                 this.getUserData(0, 300);
             },
-            nickname(val) {
-                this.$emit("on-result", val);
-            }
         },
         methods: {
             popperShow() {
@@ -76,6 +73,7 @@
                 if (localData[this.username].success === true) {
                     this.nickname = localData[this.username].data.nickname;
                     this.profession = localData[this.username].data.profession;
+                    this.$emit("on-result", localData[this.username].data);
                     if (localData[this.username].update + cacheTime > Math.round(new Date().getTime() / 1000)) {
                         return;
                     }
@@ -108,6 +106,7 @@
                             localData[this.username].update = 0;
                             localData[this.username].data = {};
                         }
+                        this.$emit("on-result", localData[this.username].data);
                         window.localStorage[keyName] = $A.jsonStringify(localData);
                         window.__userViewNetworking = false;
                     }

+ 10 - 2
resources/assets/js/main/components/WHeader.vue

@@ -28,7 +28,7 @@
                 </Dropdown>
                 <div class="right-info" @click="chatDrawerShow=true">
                     <Icon class="right-mticon" type="md-notifications" size="24"/>
-                    <em v-if="chatUnreadTotal > 0" class="right-info-num">{{chatUnreadTotal}}</em>
+                    <em v-if="chatUnreadTotal > 0" class="right-info-num">{{chatUnreadTotal > 99 ? '99+' : chatUnreadTotal}}</em>
                 </div>
                 <Dropdown class="right-info" trigger="click" @on-click="setLanguage" transfer>
                     <div>
@@ -228,7 +228,7 @@
                     .right-info-num {
                         position: absolute;
                         top: 2px;
-                        right: -20%;
+                        left: 12px;
                         height: auto;
                         line-height: normal;
                         color: #ffffff;
@@ -238,6 +238,7 @@
                         padding: 1px 5px;
                         font-size: 12px;
                         transform: scale(0.9);
+                        z-index: 1;
                     }
                 }
             }
@@ -378,12 +379,19 @@
             resCall();
             //
             this.tabActive = this.$route.meta.tabActive;
+            //
+            if ($A.hashParameter("open") === 'chat' && $A.getToken() !== false) {
+                this.chatDrawerShow = true;
+            }
         },
         watch: {
             '$route' () {
                 this.tabActive = this.$route.meta.tabActive;
                 this.systemDrawerShow = false;
                 this.userDrawerShow = false;
+                if ($A.hashParameter("open") === 'chat' && $A.getToken() !== false) {
+                    this.chatDrawerShow = true;
+                }
             }
         },
         methods: {

+ 83 - 78
resources/assets/js/main/components/chat/Index.vue

@@ -8,7 +8,7 @@
             </li>
             <li :class="{active:chatTap=='dialog'}" @click="chatTap='dialog'">
                 <Icon type="md-text" />
-                <em v-if="unreadTotal > 0" class="chat-num">{{unreadTotal}}</em>
+                <em v-if="unreadTotal > 0" class="chat-num">{{unreadTotal > 99 ? '99+' : unreadTotal}}</em>
             </li>
             <li :class="{active:chatTap=='team'}" @click="chatTap='team'">
                 <Icon type="md-person" />
@@ -29,7 +29,7 @@
                         <img :src="dialog.userimg">
                         <div class="user-msg-box">
                             <div class="user-msg-title">
-                                <span><user-view :username="dialog.username" placement="right" @on-result="(n)=>{dialog['nickname']=n}"/></span>
+                                <span><user-view :username="dialog.username" placement="right" @on-result="userViewResult(dialog, $event)"/></span>
                                 <em>{{formatCDate(dialog.lastdate)}}</em>
                             </div>
                             <div class="user-msg-text">{{dialog.lasttext}}</div>
@@ -54,7 +54,7 @@
                         <ul>
                             <li v-for="(item, index) in teamListsS(lists)" :key="index" @click="openDialog(item, true)">
                                 <img :src="item.userimg">
-                                <div class="team-username"><user-view :username="item.username" placement="right" @on-result="(n)=>{item['nickname']=n}"/></div>
+                                <div class="team-username"><user-view :username="item.username" placement="right" @on-result="userViewResult(item, $event)"/></div>
                             </li>
                         </ul>
                     </li>
@@ -657,7 +657,7 @@
                 if ($A.getToken() === false) {
                     return;
                 }
-                $A.WS.sendTo('unread', (res) => {
+                $A.WSOB.sendTo('unread', (res) => {
                     if (res.status === 1) {
                         this.unreadTotal = $A.runNum(res.message);
                     } else {
@@ -675,77 +675,76 @@
             }, false);
             resCall();
             //
-            $A.WS.setOnMsgListener("chat/index", (msgDetail) => {
-                if (msgDetail.sender == $A.getUserName()) {
+            $A.WSOB.setOnMsgListener("chat/index", (msgDetail) => {
+                if (msgDetail.username == $A.getUserName()) {
                     return;
                 }
-                if (msgDetail.messageType == 'open') {
-                    if (this.openWindow) {
-                        this.getDialogLists();
-                        this.getDialogMessage();
-                    } else {
-                        this.openAlready = false;
-                        this.dialogTarget = {};
-                    }
-                    return;
-                }
-                if (msgDetail.messageType != 'send') {
-                    return;
-                }
-                //
-                let content = $A.jsonParse(msgDetail.content);
-                if (['taskA'].indexOf(content.type) !== -1) {
-                    return;
-                }
-                let lasttext = $A.WS.getMsgDesc(content);
-                let plusUnread = msgDetail.sender != this.dialogTarget.username || !this.openWindow;
-                this.addDialog({
-                    username: content.username,
-                    userimg: content.userimg,
-                    lasttext: lasttext,
-                    lastdate: content.indate
-                }, plusUnread && content.resend !== 1);
-                if (msgDetail.sender == this.dialogTarget.username) {
-                    this.addMessageData(content, true);
-                }
-                if (!plusUnread) {
-                    $A.WS.sendTo('read', content.username);
-                }
-                if (!this.openWindow) {
-                    this.$Notice.close('chat-notice');
-                    this.$Notice.open({
-                        name: 'chat-notice',
-                        duration: 0,
-                        render: h => {
-                            return h('div', {
-                                class: 'chat-notice-box',
-                                on: {
-                                    click: () => {
-                                        this.$Notice.close('chat-notice');
-                                        this.$emit("on-open-notice", content.username);
-                                        this.clickDialog(content.username);
-                                    }
+                switch (msgDetail.messageType) {
+                    case 'open':
+                        if (this.openWindow) {
+                            this.getDialogLists();
+                            this.getDialogMessage();
+                        } else {
+                            this.openAlready = false;
+                            this.dialogTarget = {};
+                        }
+                        break;
+                    case 'user':
+                        let body = msgDetail.body;
+                        if (['taskA'].indexOf(body.type) !== -1) {
+                            return;
+                        }
+                        let lasttext = $A.WSOB.getMsgDesc(body);
+                        this.unreadTotal += 1;
+                        this.addDialog({
+                            username: body.username,
+                            userimg: body.userimg,
+                            lasttext: lasttext,
+                            lastdate: body.indate,
+                            unread: body.unread
+                        });
+                        if (msgDetail.username == this.dialogTarget.username) {
+                            this.addMessageData(body, true);
+                        }
+                        if (!this.openWindow) {
+                            this.$Notice.close('chat-notice');
+                            this.$Notice.open({
+                                name: 'chat-notice',
+                                duration: 0,
+                                render: h => {
+                                    return h('div', {
+                                        class: 'chat-notice-box',
+                                        on: {
+                                            click: () => {
+                                                this.$Notice.close('chat-notice');
+                                                this.$emit("on-open-notice", body.username);
+                                                this.clickDialog(body.username);
+                                            }
+                                        }
+                                    }, [
+                                        h('img', {class: 'chat-notice-userimg', attrs: {src: body.userimg}}),
+                                        h('div', {class: 'ivu-notice-with-desc'}, [
+                                            h('div', {class: 'ivu-notice-title'}, [
+                                                h('UserView', {props: {username: body.username}})
+                                            ]),
+                                            h('div', {class: 'ivu-notice-desc'}, lasttext)
+                                        ])
+                                    ])
                                 }
-                            }, [
-                                h('img', { class: 'chat-notice-userimg', attrs: { src: content.userimg } }),
-                                h('div', { class: 'ivu-notice-with-desc' }, [
-                                    h('div', { class: 'ivu-notice-title' }, [
-                                        h('UserView', { props: { username: content.username } })
-                                    ]),
-                                    h('div', { class: 'ivu-notice-desc' }, lasttext)
-                                ])
-                            ])
+                            });
                         }
-                    });
+                        break;
                 }
             });
-            $A.WS.setOnSpecialListener("chat/index", (content) => {
+            $A.WSOB.setOnSpecialListener("chat/index", (simpleMsg) => {
                 this.addDialog({
-                    username: content.username,
-                    userimg: content.userimg,
-                    lasttext: $A.WS.getMsgDesc(content),
-                    lastdate: content.indate
+                    username: simpleMsg.target,
+                    lasttext: $A.WSOB.getMsgDesc(simpleMsg.body),
+                    lastdate: simpleMsg.body.indate
                 });
+                if (simpleMsg.target == this.dialogTarget.username) {
+                    this.addMessageData(simpleMsg.body, true);
+                }
             });
         },
 
@@ -764,7 +763,7 @@
 
             openWindow(val) {
                 if (val) {
-                    $A.WS.connection();
+                    $A.WSOB.connection();
                     if (!this.openAlready) {
                         this.openAlready = true;
                         this.getDialogLists();
@@ -978,20 +977,21 @@
                 });
             },
 
-            addDialog(data, plusUnread = false) {
+            addDialog(data) {
                 if (!data.username) {
                     return;
                 }
                 let lists = this.dialogLists.filter((item) => {return item.username == data.username});
+                let unread = 0;
                 if (lists.length > 0) {
-                    data.unread = $A.runNum(lists[0].unread);
+                    if (typeof data.userimg === "undefined") {
+                        data.userimg = lists[0].userimg;
+                    }
+                    unread = $A.runNum(lists[0].unread);
                     this.dialogLists = this.dialogLists.filter((item) => {return item.username != data.username});
-                } else {
-                    data.unread = 0;
                 }
-                if (plusUnread) {
-                    data.unread += 1;
-                    this.unreadTotal += 1;
+                if (typeof data.unread === "undefined") {
+                    data.unread = unread;
                 }
                 this.dialogLists.unshift(data);
             },
@@ -1008,7 +1008,7 @@
                 if (typeof user.unread === "number" && user.unread > 0) {
                     this.unreadTotal -= user.unread;
                     this.$set(user, 'unread', 0);
-                    $A.WS.sendTo('read', user.username);
+                    $A.WSOB.sendTo('read', user.username);
                 }
             },
 
@@ -1114,7 +1114,7 @@
                             indate: Math.round(new Date().getTime() / 1000),
                             url: item.url
                         };
-                        $A.WS.sendTo('user', this.dialogTarget.username, data, (res) => {
+                        $A.WSOB.sendTo('user', this.dialogTarget.username, data, (res) => {
                             this.$set(data, res.status === 1 ? 'id' : 'error', res.message)
                         });
                         //
@@ -1174,7 +1174,7 @@
                         indate: Math.round(new Date().getTime() / 1000),
                         text: text
                     };
-                    $A.WS.sendTo('user', this.dialogTarget.username, data, (res) => {
+                    $A.WSOB.sendTo('user', this.dialogTarget.username, data, (res) => {
                         this.$set(data, res.status === 1 ? 'id' : 'error', res.message)
                     });
                     //
@@ -1189,6 +1189,11 @@
                     this.messageText = "";
                 });
             },
+
+            userViewResult(user, data) {
+                this.$set(user, 'nickname', data.nickname);
+                this.$set(user, 'userimg', data.userimg);
+            },
         }
     }
 </script>

+ 2 - 2
resources/assets/js/main/components/report/add.vue

@@ -12,7 +12,7 @@
             </div>
             <t-editor class="add-edit" v-model="dataDetail.content" height="100%"></t-editor>
             <div class="add-input">
-                <user-input v-model="dataDetail.ccuser" :placeholder="$L('输入关键词搜索')" multiple><span slot="prepend">{{$L('抄送人')}}</span></user-input>
+                <user-input v-model="dataDetail.ccuser" :nousername="$A.getUserName()" :placeholder="$L('输入关键词搜索')" multiple><span slot="prepend">{{$L('抄送人')}}</span></user-input>
             </div>
             <div class="add-footer">
                 <Button :loading="loadIng > 0" type="primary" @click="handleSubmit" style="margin-right:6px">{{$L('保存')}}</Button>
@@ -177,7 +177,7 @@
                                 };
                                 this.dataDetail.ccuserArray.forEach((username) => {
                                     if (username != msgData.username) {
-                                        $A.WS.sendTo('user', username, msgData, 'special');
+                                        $A.WSOB.sendTo('user', username, msgData, 'special');
                                     }
                                 });
                             }

+ 1 - 1
resources/assets/js/main/components/report/my.vue

@@ -290,7 +290,7 @@
                                 };
                                 res.data.ccuserArray.forEach((username) => {
                                     if (username != msgData.username) {
-                                        $A.WS.sendTo('user', username, msgData, 'special');
+                                        $A.WSOB.sendTo('user', username, msgData, 'special');
                                     }
                                 });
                             }

+ 318 - 116
resources/assets/js/main/main.js

@@ -270,7 +270,7 @@ import '../../sass/main.scss';
                 }
             }
             if (sendToWS === true) {
-                $A.WS.sendTo('team', {
+                $A.WSOB.sendTo('team', {
                     type: "taskA",
                     act: act,
                     taskDetail: taskDetail
@@ -303,7 +303,7 @@ import '../../sass/main.scss';
                             };
                             res.data.follower.forEach((username) => {
                                 if (username != msgData.username) {
-                                    $A.WS.sendTo('user', username, msgData, 'special');
+                                    $A.WSOB.sendTo('user', username, msgData, 'special');
                                 }
                             });
                         });
@@ -319,137 +319,243 @@ import '../../sass/main.scss';
      * =============================================================================
      */
     $.extend({
-        WS: {
-            __instance: null,
-            __connected: false,
-            __callbackid: {},
-            __openNum: 0,
-            __autoNum: 0,
-            __autoLine(timeout) {
-                let tempNum = this.__autoNum;
-                setTimeout(() => {
-                    if (tempNum === this.__autoNum) {
-                        this.__autoNum++
-                        if ($A.getToken() === false) {
-                            this.__log("[WS] No token");
-                            this.__autoLine(timeout + 5);
+        /**
+         * @param config {username, url, token, channel, logCallback}
+         */
+        WTWS: function (config) {
+            this.__instance = null;
+            this.__connected = false;
+            this.__callbackid = {};
+            this.__openNum = 0;
+            this.__autoNum = 0;
+
+            this.__autoLine = function (timeout) {
+                var tempNum = this.__autoNum;
+                var thas = this;
+                setTimeout(function () {
+                    if (tempNum === thas.__autoNum) {
+                        thas.__autoNum++
+                        if (!thas.__config.token) {
+                            thas.__log("[WS] No token");
+                            thas.__autoLine(timeout + 5);
                         } else {
-                            this.sendTo('refresh', (res) => {
-                                this.__log("[WS] Connection " + (res.status ? 'success' : 'error'));
-                                this.__autoLine(timeout + 5);
+                            thas.sendTo('refresh', function (res) {
+                                thas.__log("[WS] Connection " + (res.status ? 'success' : 'error'));
+                                thas.__autoLine(timeout + 5);
                             });
                         }
                     }
                 }, Math.min(timeout, 30) * 1000);
-            },
-            __log(text) {
-                //console.log(text);
-            },
+            }
+            this.__log = function (text, event) {
+                typeof this.__config.logCallback === "function" && this.__config.logCallback(text, event);
+            }
+            this.__lExists = function (string, find, lower) {
+                string += "";
+                find += "";
+                if (lower !== true) {
+                    string = string.toLowerCase();
+                    find = find.toLowerCase();
+                }
+                return (string.substring(0, find.length) === find);
+            }
+            this.__rNum = function (str, fixed) {
+                var _s = Number(str);
+                if (_s + "" === "NaN") {
+                    _s = 0;
+                }
+                if (/^[0-9]*[1-9][0-9]*$/.test(fixed)) {
+                    _s = _s.toFixed(fixed);
+                    var rs = _s.indexOf('.');
+                    if (rs < 0) {
+                        _s += ".";
+                        for (var i = 0; i < fixed; i++) {
+                            _s += "0";
+                        }
+                    }
+                }
+                return _s;
+            }
+            this.__jParse = function (str, defaultVal) {
+                if (str === null) {
+                    return defaultVal ? defaultVal : {};
+                }
+                if (typeof str === "object") {
+                    return str;
+                }
+                try {
+                    return JSON.parse(str);
+                } catch (e) {
+                    return defaultVal ? defaultVal : {};
+                }
+            }
+            this.__randString = function (len) {
+                len = len || 32;
+                var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678oOLl9gqVvUuI1';
+                var maxPos = $chars.length;
+                var pwd = '';
+                for (var i = 0; i < len; i++) {
+                    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
+                }
+                return pwd;
+            }
+            this.__urlParams = function(url, params) {
+                if (typeof params === "object" && params !== null) {
+                    url+= "";
+                    url+= url.indexOf("?") === -1 ? '?' : '';
+                    for (var key in params) {
+                        if (!params.hasOwnProperty(key)) {
+                            continue;
+                        }
+                        url+= '&' + key + '=' + params[key];
+                    }
+                }
+                return url.replace("?&", "?");
+            }
+            this.__isArr = function (obj){
+                return Object.prototype.toString.call(obj)=='[object Array]';
+            }
+
+            /**
+             * 设置参数
+             * @param config
+             */
+            this.config = function (config) {
+                if (typeof config !== "object" || config === null) {
+                    config = {};
+                }
+                config.username = config.username || '';
+                config.url = config.url || '';
+                config.token = config.token || '';
+                config.channel = config.channel || '';
+                config.logCallback = config.logCallback || null;
+                this.__config = config;
+                return this;
+            }
 
             /**
              * 连接
+             * @param force
              */
-            connection(force = false) {
-                let url = $A.getObject(window.webSocketConfig, 'URL');
-                url += ($A.strExists(url, "?") ? "&" : "?") + "token=" + $A.getToken();
-                if (!$A.leftExists(url, "ws://") && !$A.leftExists(url, "wss://")) {
+            this.connection = function (force) {
+                if (!this.__lExists(this.__config.url, "ws://") && !this.__lExists(this.__config.url, "wss://")) {
                     this.__log("[WS] No connection address");
-                    return;
+                    return this;
                 }
 
-                if ($A.getToken() === false) {
+                if (!this.__config.token) {
                     this.__log("[WS] No connected token");
-                    return;
+                    return this;
                 }
 
                 if (this.__instance !== null && force !== true) {
                     this.__log("[WS] Connection exists");
-                    return;
+                    return this;
                 }
 
+                var thas = this;
+
                 // 初始化客户端套接字并建立连接
-                this.__instance = new WebSocket(url);
+                this.__instance = new WebSocket(this.__urlParams(this.__config.url, {
+                    token: this.__config.token,
+                    channel: this.__config.channel
+                }));
 
                 // 连接建立时触发
-                this.__instance.onopen = (event) => {
-                    this.__log("[WS] Connection opened");
+                this.__instance.onopen = function (event) {
+                    thas.__log("[WS] Connection opened", event);
                 }
 
                 // 接收到服务端推送时执行
-                this.__instance.onmessage = (event) => {
-                    let msgDetail = $A.jsonParse(event.data);
+                this.__instance.onmessage = function (event) {
+                    var msgDetail = thas.__jParse(event.data);
                     if (msgDetail.messageType === 'open') {
-                        this.__log("[WS] Connection connected");
-                        msgDetail.openNum = this.__openNum;
-                        this.__openNum++;
-                        this.__connected = true;
-                        this.__autoLine(30);
-                    } else if (msgDetail.messageType === 'feedback') {
-                        typeof this.__callbackid[msgDetail.messageId] === "function" && this.__callbackid[msgDetail.messageId](msgDetail.content);
-                        delete this.__callbackid[msgDetail.messageId];
+                        thas.__log("[WS] Connection connected");
+                        msgDetail.openNum = thas.__openNum;
+                        msgDetail.config = thas.__config;
+                        thas.__openNum++;
+                        thas.__connected = true;
+                        thas.__autoLine(30);
+                    } else if (msgDetail.messageType === 'back') {
+                        typeof thas.__callbackid[msgDetail.messageId] === "function" && thas.__callbackid[msgDetail.messageId](msgDetail.body);
+                        delete thas.__callbackid[msgDetail.messageId];
                         return;
                     }
-                    if ($A.runNum(msgDetail.contentId) > 0) {
-                        $A.WS.sendTo('roger', msgDetail.contentId);
+                    if (thas.__rNum(msgDetail.contentId) > 0) {
+                        thas.sendTo('roger', msgDetail.contentId);
                     }
-                    this.triggerMsgListener(msgDetail);
+                    thas.triggerMsgListener(msgDetail);
                 };
 
                 // 连接关闭时触发
-                this.__instance.onclose = (event) => {
-                    this.__log("[WS] Connection closed");
-                    this.__connected = false;
-                    this.__instance = null;
-                    this.__autoLine(5);
+                this.__instance.onclose = function (event) {
+                    thas.__log("[WS] Connection closed", event);
+                    thas.__connected = false;
+                    thas.__instance = null;
+                    thas.__autoLine(5);
                 }
 
                 // 连接出错
-                this.__instance.onerror = (event) => {
-                    this.__log("[WS] Connection error");
-                    this.__connected = false;
-                    this.__instance = null;
-                    this.__autoLine(5);
+                this.__instance.onerror = function (event) {
+                    thas.__log("[WS] Connection error", event);
+                    thas.__connected = false;
+                    thas.__instance = null;
+                    thas.__autoLine(5);
                 }
 
                 return this;
-            },
+            }
 
             /**
              * 添加消息监听
              * @param listenerName
+             * @param listenerType
              * @param callback
              */
-            setOnMsgListener(listenerName, callback) {
+            this.setOnMsgListener = function (listenerName, listenerType, callback) {
                 if (typeof listenerName != "string") {
-                    return;
+                    return this;
+                }
+                if (typeof listenerType === "function") {
+                    callback = listenerType;
+                    listenerType = [];
+                }
+                if (!this.__isArr(listenerType)) {
+                    listenerType = [listenerType];
                 }
                 if (typeof callback === "function") {
                     this.__msgListenerObject[listenerName] = {
                         callback: callback,
+                        listenerType: listenerType,
                     }
                 }
                 return this;
-            },
-            triggerMsgListener(msgDetail) {
-                let key, item;
+            }
+            this.triggerMsgListener = function (msgDetail) {
+                var key, item;
                 for (key in this.__msgListenerObject) {
-                    if (!this.__msgListenerObject.hasOwnProperty(key)) continue;
+                    if (!this.__msgListenerObject.hasOwnProperty(key)) {
+                        continue;
+                    }
                     item = this.__msgListenerObject[key];
+                    if (item.listenerType.length > 0 &&  item.listenerType.indexOf(msgDetail.messageType) === -1) {
+                        continue;
+                    }
                     if (typeof item.callback === "function") {
                         item.callback(msgDetail);
                     }
                 }
-            },
-            __msgListenerObject: {},
+            }
+            this.__msgListenerObject = {}
 
             /**
              * 添加特殊监听
              * @param listenerName
              * @param callback
              */
-            setOnSpecialListener(listenerName, callback) {
+            this.setOnSpecialListener = function (listenerName, callback) {
                 if (typeof listenerName != "string") {
-                    return;
+                    return this;
                 }
                 if (typeof callback === "function") {
                     this.__specialListenerObject[listenerName] = {
@@ -457,113 +563,209 @@ import '../../sass/main.scss';
                     }
                 }
                 return this;
-            },
-            triggerSpecialListener(content) {
-                let key, item;
+            }
+            this.triggerSpecialListener = function (simpleMsg) {
+                var key, item;
                 for (key in this.__specialListenerObject) {
-                    if (!this.__specialListenerObject.hasOwnProperty(key)) continue;
+                    if (!this.__specialListenerObject.hasOwnProperty(key)) {
+                        continue;
+                    }
                     item = this.__specialListenerObject[key];
                     if (typeof item.callback === "function") {
-                        item.callback(content);
+                        item.callback(simpleMsg);
                     }
                 }
-            },
-            __specialListenerObject: {},
+            }
+            this.__specialListenerObject = {}
 
             /**
              * 发送消息
-             * @param type      会话类型
+             * @param messageType       会话类型
              * - refresh: 刷新
              * - unread: 未读信息总数量
              * - read: 已读会员信息
              * - roger: 收到信息回执
              * - user: 指定target
              * - team: 团队会员
-             * @param target    发送目标
-             * @param content   发送内容(对象或数组)
-             * @param callback  发送回调
+             * @param target            发送目标
+             * @param body              发送内容(对象或数组)
+             * @param callback          发送回调
              * @param againNum
              */
-            sendTo(type, target, content, callback, againNum = 0) {
-                if (typeof target === "object" && typeof content === "undefined") {
-                    content = target;
+            this.sendTo = function (messageType, target, body, callback, againNum = 0) {
+                if (typeof target === "object" && typeof body === "undefined") {
+                    body = target;
                     target = null;
                 }
                 if (typeof target === "function") {
-                    content = target;
+                    body = target;
                     target = null;
                 }
-                if (typeof content === "function") {
-                    callback = content;
-                    content = null;
+                if (typeof body === "function") {
+                    callback = body;
+                    body = null;
+                }
+                if (body === null || typeof body !== "object") {
+                    body = {};
                 }
                 //
-                if (this.__instance === null) {
-                    if (againNum < 10 && type != 'team') {
-                        setTimeout(() => {
-                            this.sendTo(type, target, content, callback, $A.runNum(againNum) + 1)
+                var thas = this;
+                if (this.__instance === null || this.__connected === false) {
+                    if (againNum < 10 && messageType != 'team') {
+                        setTimeout(function () {
+                            thas.sendTo(messageType, target, body, callback, thas.__rNum(againNum) + 1)
                         }, 600);
                         if (againNum === 0) {
                             this.connection();
                         }
                     } else {
-                        this.__log("[WS] Service not connected");
-                        typeof callback === "function" && callback({status: 0, message: '服务未连接'});
+                        if (this.__instance === null) {
+                            this.__log("[WS] Service not connected");
+                            typeof callback === "function" && callback({status: 0, message: '服务未连接'});
+                        } else {
+                            this.__log("[WS] Failed connection");
+                            typeof callback === "function" && callback({status: 0, message: '未连接成功'});
+                        }
                     }
-                    return;
+                    return this;
                 }
-                if (this.__connected === false) {
-                    this.__log("[WS] Failed connection");
-                    typeof callback === "function" && callback({status: 0, message: '未连接成功'});
-                    return;
-                }
-                if (['refresh', 'unread', 'read', 'roger', 'user', 'team'].indexOf(type) === -1) {
-                    this.__log("[WS] Wrong message type: " + type);
-                    typeof callback === "function" && callback({status: 0, message: '错误的消息类型: ' + type});
-                    return;
+                if (['refresh', 'unread', 'read', 'roger', 'user', 'team'].indexOf(messageType) === -1) {
+                    this.__log("[WS] Wrong message messageType: " + messageType);
+                    typeof callback === "function" && callback({status: 0, message: '错误的消息类型: ' + messageType});
+                    return this;
                 }
                 //
-                let contentId = 0;
-                if (type === 'roger') {
+                var contentId = 0;
+                if (messageType === 'roger') {
                     contentId = target;
                     target = null;
                 }
-                let messageId = '';
+                var messageId = '';
                 if (typeof callback === "string" && callback === 'special') {
-                    callback = (res) => {
-                        res.status === 1 && this.triggerSpecialListener(content);
+                    callback = function (res) {
+                        res.status === 1 && thas.triggerSpecialListener({
+                            target: target,
+                            body: body,
+                        });
                     }
                 }
                 if (typeof callback === "function") {
-                    messageId = $A.randomString(16);
+                    messageId = this.__randString(16);
                     this.__callbackid[messageId] = callback;
                 }
                 this.__instance.send(JSON.stringify({
-                    messageType: 'send',
+                    messageType: messageType,
                     messageId: messageId,
                     contentId: contentId,
-                    type: type,
-                    sender: $A.getUserName(),
+                    channel: this.__config.channel,
+                    username: this.__config.username,
                     target: target,
-                    content: content,
+                    body: body,
                     time: Math.round(new Date().getTime() / 1000),
                 }));
                 return this;
-            },
+            }
 
             /**
              * 关闭连接
              */
-            close() {
+            this.close = function () {
                 if (this.__instance === null) {
                     this.__log("[WS] Service not connected");
-                    return;
+                    return this;
                 }
                 if (this.__connected === false) {
                     this.__log("[WS] Failed connection");
-                    return;
+                    return this;
                 }
                 this.__instance.close();
+                return this;
+            }
+
+            return this.config(config);
+        },
+
+        WSOB: {
+            instance: null,
+            isClose: false,
+
+            /**
+             * 初始化
+             */
+            initialize() {
+                let url = $A.getObject(window.webSocketConfig, 'URL');
+                if (!url) {
+                    url = window.location.origin;
+                    url = url.replace("https://", "wss://");
+                    url = url.replace("http://", "ws://");
+                    url+= "/ws";
+                }
+                let config = {
+                    username: $A.getUserName(),
+                    url: url,
+                    token: $A.getToken(),
+                    channel: 'web'
+                };
+                if (this.instance === null) {
+                    this.instance = new $A.WTWS(config);
+                    this.instance.connection()
+                } else if (this.isClose) {
+                    this.isClose = false
+                    this.instance.config(config);
+                    this.instance.connection();
+                }
+            },
+
+            /**
+             * 主动连接
+             */
+            connection() {
+                this.initialize();
+                this.instance.connection();
+            },
+
+            /**
+             * 监听消息
+             * @param listenerName
+             * @param listenerType
+             * @param callback
+             */
+            setOnMsgListener(listenerName, listenerType, callback) {
+                this.initialize();
+                this.instance.setOnMsgListener(listenerName, listenerType, callback);
+            },
+
+            /**
+             * 添加特殊监听
+             * @param listenerName
+             * @param callback
+             */
+            setOnSpecialListener(listenerName, callback) {
+                this.initialize();
+                this.instance.setOnSpecialListener(listenerName, callback);
+            },
+
+            /**
+             * 发送消息
+             * @param messageType
+             * @param target
+             * @param body
+             * @param callback
+             */
+            sendTo(messageType, target, body, callback) {
+                this.initialize();
+                this.instance.sendTo(messageType, target, body, callback);
+            },
+
+            /**
+             * 关闭连接
+             */
+            close() {
+                if (this.instance === null) {
+                    return;
+                }
+                this.isClose = true
+                this.instance.config(null).close();
             },
 
             /**