###### tags: `mynah-admin`、`mynah-client-web`、`chatroom` # mynah-admin ## 後台 ### 群組觀念 | Name | TypeCode | 說明 | | -------- | -------- | -------- | | 商號 | company | 公司 | | 站點 | site | 商號下面的站點 | | 聊天群 | group | 站點下面的聊天群組 | ==(聊天室流程都會圍繞這三個元素,是一個樹狀結構,root 通常是商號)== ### 角色分類 | Name | TypeCode | 說明 | | -------- | -------- | -------- | | 客服人員 | service | 待補上 | | 訪客 | guest | 待補上 | | 機器人 | bot | 待補上 | ### 開發 ``` git clone repoUrl cd repoName npm i npm run dev ``` ### 路由權限驗證 ```javascript= path: 'list', name: 'CsrList', component: () =>import('@/views/setting/csr/list.vue'), meta: { title: '客服列表', icon: 'list-of-three-elements-on-black-background', noCache: true, id: 'MPCustomerList' // super 為不驗證 } ``` ### 流程 ```flow st=>start: Start e=>end: End get_site=>operation: 取得站點列表 get_group=>operation: 取得各站群組 /cs/<site>/group connect=>operation: ws連線 /cs/ws click_chat=>operation: 點擊聊天室 get_members=>operation: 取得成員 /cs/<group>/member get_history=>operation: 取得歷史訊息 /cs/<group>/history show_new_msg=>operation: 從ws接收並顯示新訊息 disconnect=>operation: ws斷線 ------------------ st->get_site get_site->connect connect->get_group get_group->click_chat click_chat->get_members get_members->get_history get_history->show_new_msg show_new_msg->disconnect disconnect(left)->connect ``` ### 數據結構 > **chat** : 存放所有的聊天室資料 ```javascript chat: { // 開啟大頭貼 noAvatarMode: window.localStorage.getItem('noAvatarMode') === 'true', // 送出訊息模式 sendMode: window.localStorage.getItem('sendMode') || 'auto', // 會話板多視窗或單視窗 singleMode: window.localStorage.getItem('chatWindowMode') === 'true', alertAudio: { // 新訊息提示音 normal: setAlertNormalAudio(), // 來電音效 ring: setAlertRingAudio() }, // 當前選中站點 activeSite: '', // 判斷站點資訊是否被初始化,用來防止 refresh 時送出兩次 getChatSiteInfo request isSitesInit: false, // 存放所有站點資料 chatSiteData: {...}, // 存放所有聊天室資訊 chatMessagePool: {...}, // 撥接清單 ringList: {...}, // 傳送已讀請求的倒數 readTimer: null } ``` --- > **chatSiteData** : 存放所有站點資料 > **siteInfo** : 站點資訊 ```javascript // 格式是用站點 ID 的 Map chatSiteData: { siteId1: siteData, siteId2: siteData, other site ... } // 站點資訊格式 siteData: { code: siteId, // 站點ID logo: url, // 站點圖標 name: siteName, // 站點名稱 activeChatKey: groupId, // 當前選中聊天室群組ID lastMsgHint: 1590544156538, // 站點最後訊息時間 readHint: 1590544156538, // 站點最後讀取時間 } ``` --- > **chatMessagePool** : 存放所有聊天室資訊 ```javascript // 格式 chatMessagePool => 站點ID => 群組ID => 群組資訊 chatMessagePool: { siteId1: { groupId1: groupData, groupId2: groupData, groupId3: groupData, ... } } // 群組資訊格式 groupData: { // 是否結束聊天 isEnd: false, // 錯誤判斷:通常用在歷史訊息取得失敗 isError: false, // 聊天室ID groupId: group.id, // 聊天室圖案 groupAvatar: group.avatar_url !== '' ? group.avatar_url : guestDefaultAvatar, // 聊天室名稱 groupName: group.name, // 聊天室成員清單 groupMember: {...}, // 創建時間 createTime: new Date(group.created_at).getTime(), // 未讀訊息計算,會先使用 server 回傳的數量,當前群組被 actived 的話不會被計算 count: group.unread || 0, // 新訊息計算: 主要只用在當前群組被 actived 做 count++,到某的數量後發送已讀取的請求 readPollingCount: 0, // 其他使用者正在打字的訊息提醒 typing: '', // 草稿區:會使用 localStorage 做暫存 msgInput: window.localStorage.getItem(`draft_${group.site}_${group.id}`) || '', uploadList: {...}, // 最後一則訊息內容:使用在右邊群組 sidebar 的顯示資料 lastMsgContent: { type: group.last_msg.msg_type || 'text', value: group.last_msg.text || group.last_msg.url || '对话已开启', timestamp: group.last_msg.timestamp || moment(group.created_at).valueOf() }, lastRead: now, // 聊天訊息池 messages: {...}, // 是否有拿過歷史訊息,有拿過之後的更新都是靠 ws 的回傳 hasHistory: false } ``` --- > **groupMember** : 聊天室成員清單,位置是放在 `chatMessagePool` 裡面 ```javascript // 格式 groupMember => 成員ID => 成員資訊 groupMember: { memberId1: memberInfo, memberId2: memberInfo, memberId3: memberInfo } // 成員資訊格式 memberInfo: { id: user.id, status: 1, // 0 是下線, 1 是上線 nickname: user.nickname, username: user.username, avatar_url: user.avatar_url, type: user.type // guest: 訪客 || bot: 機器人 || service: 客服人員 } ``` --- > **messages** : 聊天訊息池,位置是放在 `chatMessagePool` 裡面 ```javascript // 格式 messages => 訊息ID => 訊息資訊 messages: { messageId1: meesageInfo, messageId2: meesageInfo, messageId3: meesageInfo } meesageInfo: { // 發送訊息者的 id authorId: "GU44ynzLicpA9", // 客戶端自己產生的 id,在沒有 server id 時使用 cid: "69f55670-1a64-11eb-9809-ffb19b1556aa", // server 端的 id id: "budovk7jc49dirmi8d10", // 判斷自己傳送的訊息有沒有成功 isUpdated: true, timestamp: 1604030416565, // 目前總類有: text | event | image type: "image", // 圖片的連結 url: "http://demo/CM44ZcnwRaphL/hahaha.png", // 文字訊息的值 value: 'hahaha' } ``` > **ringList** : 撥接清單 ```javascript // 格式 ringList => 站點ID => 群組ID => 撥接資訊 ringList: { siteId1: { groupId1: ringInfo, groupId2: ringInfo, groupId3: ringInfo }, siteId2: {...} } // 撥接資訊 ringInfo: { isRinging: true, isReceived: false, groupId: group.id, groupAvatar: group.avatar_url, groupName: group.name, authorId: chatUser.id } ``` --- ## 前台 ### 群組觀念 只有一個聊天群組,通常就一個訪客對應一個客服人員。 ### 角色分類 1. 訪客 2. 第三方登入會員(產線) ### 開發 ``` git clone repoUrl cd repoName git submodule init //安裝submodule git submodule update --recursive //更新submodule npm i npm run dev ``` ### 使用 > - 前台聊天室:/chatroom?company=**company**&site=**id** (*參數請查看 /api/siteinfo*) ![](https://i.imgur.com/bUHoBIP.png) ### 更新模式 > 1. Websocket更新數據模式 (store.stat.chat.chatUpdateMode) > 2. Pooling更新數據模式 ### 重連 > 重連機制有兩個情境 > 每一次重新連線都會確認是否要填詢前表單以確認對話狀態(有可能被客服主動掛斷) > 如果被掛斷視同重新對話與重新創群組因此需要重新填寫表單 > 1. websocketUrl不只一個時 > 會嘗試連線每一個wsUrl後, 皆失敗後進行pooling模式 > 2. websocketUrl只有一個時 > 會嘗試連線三次wsUrl, 失敗後進行pooling模式 ### 流程 ```flow cond1=>condition: 是否要填寫詢前表單 cond2=>condition: 是否要填寫評價表單 opChatEnd=>operation: 聊天結束 opReview=>operation: 填評價表單 opGroupEnd=>operation: 對話結束 end=>end: 填詢前表單 op2=>operation: 聊天開始 cond1(yes)->end->op2->opChatEnd->cond2(yes)->opReview->opGroupEnd cond1(yes)->end->op2->opChatEnd->cond2(no)->opGroupEnd cond1(no)->op2 ``` ### 數據結構 > 因前台只會有一個聊天群組, 因此群組資訊分開存取 ``` javascript // store.stat.chat.chatGroupInfo chatGroupInfo: { "isComplete": true, // 聊天資料是否取得完成的狀態 "isEnd": false, // 對話是否結束 "connectMessage": { // 連線時展示的url 先是連線中, 再來就是客服接入中 "shouldShow": true, // 要不要展示連線狀態訊息區 "isConnecting": false, // 連線是否已經建立的狀態 "hint": "正在等待客服接入", // 提示 title "message": "推荐收藏 https://3355100.com 拉斯维加斯线 路检测中心" }, "groupId": "CM42NcgvEXyjm", // 對話群組id "lastRead": 1600152111244, // 最後讀的時間 "chatMate": [ // 對話群組成員 { "id": "BO3SXyVTvmLPs", // 成員id "type": "bot", // 成員類別 service, guest, bot "username": "bot", // 成員名稱 "nickname": "客服人员", // 成員暱稱 "status": 0, // 成員是否在對話群中 "reject": false, // 沒用... "company": "FM3SDuUDSiGX3", // 商號id 沒用... "site": [ "WS3SXyGDtjQmq" // 站點id 沒用... ], "avatar_url": "http://366cdn.com/mynah/avatar/cs_avatar.png" // 成員頭像 }, { "id": "CS3SSB1tjHrv3", "type": "service", "username": "csr_aqua002", "nickname": "阿克雅002", "status": 1, "reject": false, "company": "FM3SDuUDSiGX3", "site": null, "avatar_url": "http://squirrel-dev.paradise-soft.com.tw/5b0c90b21.png" } ] } // store.state.chat.messagePool messagePool: { "auto_btg5ve44fgj8fr1voidg": { "authorId": "BO3SXyVTvmLPs", // 訊息作者id "timestamp": 1600151480283, // 訊息時間 (server time) "isUpdated": true, // 訊息是否從server side 通知更新 "id": "auto_btg5ve44fgj8fr1voidg", // server產生的 msg_id "cid": "", // client產生的 msg_id "type": "text", msg_type "uploadError": false, // msg_type為image時會上傳是否失敗的狀態 "uploadProgress": 100, // msg_type為image上傳進度條 "url": "", // msg_type為image時 訊息資料為url "value": "今天中午吃拉麵~" // msg_type為text 使用text, image為url }, "auto_btg5ve44fgj8fr1voie0": { "authorId": "BO3SXyVTvmLPs", "timestamp": 1600151480285, "isUpdated": true, "id": "auto_btg5ve44fgj8fr1voie0", "cid": "", "type": "text", "uploadError": false, "uploadProgress": 100, "url": "", "value": "第二則信息" }, "auto_btg5ve44fgj8fr1voieg": { "authorId": "BO3SXyVTvmLPs", "timestamp": 1600151480287, "isUpdated": true, "id": "auto_btg5ve44fgj8fr1voieg", "cid": "", "type": "text", "uploadError": false, "uploadProgress": 100, "url": "", "value": "B哥好帥" }, "18b5eeb0-f71d-11ea-9c76-ed588078d85a": { "authorId": "GU3ZsHw286TBf", "timestamp": 1600151493346, "isUpdated": true, "id": "btg5vhc4fgj8fr1voif0", "cid": "18b5eeb0-f71d-11ea-9c76-ed588078d85a", "type": "text", "uploadError": false, "uploadProgress": 100, "value": "123" } } ``` --- ## Components ### 前台(Client) #### ChatContent 1. 聊天訊息的容器 2. 處理訊息與訊息間狀態展示 3. 處理連線提示狀態展示 #### ChatBubble 1. 聊天訊息組件 2. 處理聊天訊息parser和聊天訊息各種功能 #### ChatFooter 1. 訊息發送用組件 2. 處理發送邏輯和input ### 後台(Admin) #### board ![](https://i.imgur.com/V7Covao.png) 1. 處理站點訊息通知 2. 處理選中聊天站點群 #### chatroom ![](https://i.imgur.com/8WRYi60.png) 1. 處理聊天室列表 2. 處理響鈴列表 #### chatArea ![](https://i.imgur.com/yx3SSgi.png) 1. 處理聊天室發送訊息 2. 處理聊天室輔助功能展示(drawer, tool) #### chatPool ![](https://i.imgur.com/d9gxzH6.png) 1. 處理訊息列表相關功能 #### chatMsg ![](https://i.imgur.com/m52dFM3.png) 1. 處理訊息內部功能