diff --git a/env.config.js b/env.config.js index 597f9f7..3e547a6 100644 --- a/env.config.js +++ b/env.config.js @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2022-05-05 14:40:00 * @LastEditors: ch - * @LastEditTime: 2022-06-02 15:09:46 + * @LastEditTime: 2022-06-09 11:25:44 * @Description: 根据git分支生成对应环境的环境变量 * 开发时如果环境变量换了,可以不用重启服务,直接运行node env.config.js即可 */ @@ -13,8 +13,8 @@ const envConfig = { dev : { baseUrl: 'https://k8s-horse-gateway.mashibing.cn', staticUrl : 'https://k8s-shop-app.mashibing.cn', - imUrl : 'ws://192.168.10.94:8090' - // imUrl : 'wss://k8s-horse-gateway.mashibing.cn' + // imUrl : 'ws://192.168.10.94:8090' + imUrl : 'wss://k8s-horse-gateway.mashibing.cn' }, test : { baseUrl: 'https://k8s-horse-gateway.mashibing.cn', diff --git a/src/common/api/im.js b/src/common/api/im.js index c509808..60668d6 100644 --- a/src/common/api/im.js +++ b/src/common/api/im.js @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2022-05-27 17:44:36 * @LastEditors: ch - * @LastEditTime: 2022-06-02 14:45:51 + * @LastEditTime: 2022-06-09 11:34:36 * @Description: file content */ import {ToAsyncAwait, MsbRequestTk} from '@/common/utils'; @@ -11,4 +11,6 @@ const BASE_URL = '/mall/im'; /** * 获取soket登录秘钥 */ -export const ApiGetSoketTicket = () => ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/ticket`)); +export const ApiGetSoketTicket = () => ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/ticket`, { + ticketType: 'CONNECT_TICKET' +})); diff --git a/src/common/plugins/msbIm.js b/src/common/plugins/msbIm.js index 996a0cd..9ec96bc 100644 --- a/src/common/plugins/msbIm.js +++ b/src/common/plugins/msbIm.js @@ -2,406 +2,439 @@ * @Author: ch * @Date: 2022-05-18 14:54:47 * @LastEditors: ch - * @LastEditTime: 2022-06-07 19:28:14 + * @LastEditTime: 2022-06-09 11:38:48 * @Description: file content */ import { CreateUUID, FormatDate, ToAsyncAwait } from "@/common/utils"; import './potoReq'; import './protoRsp' -const connect = Symbol('connect'), - send = Symbol('send'), - onResponse = Symbol('onResponse'), - onMessage = Symbol('onMessage'), - updateData = Symbol('updateData') - ; +const connect = Symbol('connect'); +const send = Symbol('send'); +const onMessage = Symbol('onMessage'); const fromatPotoReq = (traceId, traceType, content) => { - let messageModel = new proto.ReqModel(); - messageModel.setTraceid(traceId); - messageModel.setTracetype(traceType); - content && messageModel.setContent(JSON.stringify(content)); - return messageModel.serializeBinary(); -}, -fromatPotoRsp = (data)=>{ - const res = proto.RspModel.deserializeBinary(new Uint8Array(data)); - let ctx = res.getContent(); - ctx = ctx ? JSON.parse(ctx) : {}; - if (ctx.payload) { - ctx.payload = JSON.parse(ctx.payload) - } - return { - content: ctx, - traceId: res.getTraceid(), - traceType: res.getTracetype(), - code: res.getCode(), - message : res.getMessage() - } -} -export default class MsbIm { - option = { - ioKey : 'traceId', - reconnect: true, - logout : false - } - socket = null; - isOpen = false; - queue = {}; - interceptors = { - dataChangeBefore: null, - dataChangeAfter: null, - onLogout : null, - onMessage: null - - }; - sessionData = []; - curSessionId = null; - - constructor(option) { - this.option = { - ...this.option, - ...option - } - } - /** - * 创建连接返回一个Promise 创建成功并成功打开连接算连接成功 - * @param {*} option - */ - - [connect](option) { - return new Promise((resolve, reject) => { - this.socket = uni.connectSocket({ - ...option, - fail(e){ - reject(e); - } - }); - this.socket.onOpen(() => { - this.socket.onMessage(async (res) => { - console.log(res.data,'apppppppp'); - const result = fromatPotoRsp(res.data); - - // if (result.content?.payload) { - // result.content.payload = JSON.parse(result.content.payload); - // } - // 401主动退出 - if (result.code === 401) { - this.logout(); - return false; - } - this.interceptors.onMessage && this.interceptors.onMessage(result); - // 处理服务端主动推送的消息 - this[onMessage](result); - - // 如果再消息堆里有此消息回调,则执行回调,并删除 - const cbk = this.queue[result[this.option.ioKey]]; - if (cbk) { - cbk(result.code !== 200 ? {error:result} : {result:result}); - delete this.queue[result[this.option.ioKey]]; - } - }) - resolve(this.socket); - }); - this.socket.onClose(() => { - if (this.option.reconnect && !this.option.logout) { - this[connect](); - } - this.option.logout = false; - }); - }); - } - /** - * 向服务端发送消息||请求,返回一个Promise对象,收到ioKey对应的消息会算一个同步完成 - * @param {*} data - */ - [send](data) { - return new Promise((resolve, reject) => { - this.queue[data[this.option.ioKey]] = ({result, error}) => { - if (result) { - resolve(result); - } else { - reject(error); - } - }; - - this.socket.send({ - data : fromatPotoReq(data.traceId, data.traceType, data.content), - fail(e) { - reject({error : e}); - } - }); - }) - } - /** - * 服务端推送消息,只处理服务端主动推送的消息 - * @param {*} data - */ - [onMessage](data) { - // 判断非服务端主动推送消息不做处理 - if (data[this.option.ioKey] || data.code !== 200) { - return false; - } - let ctx = data.content; - let historyData = [...this.sessionData], - newData = []; - const hisIndex = historyData.findIndex(i => i.id === ctx.sessionId); - if(hisIndex >= 0){ - // 存在会话往现有会话增加一条消息,并修改最后一条消息为当前消息 - const curHisData = historyData[hisIndex]; - curHisData.messageList.push(ctx); - curHisData.lastMessage = ctx; - // 不在当前会话窗口则向会话消息加1条未读 - if(ctx.sessionId !== this.curSessionId){ - curHisData.unreadCount++; - } - newData = historyData; - }else{ - // 会话列表不存在,则创建一个会话 - newData = [...historyData, { - fromAvatar : ctx.fromAvatar, - fromId : ctx.fromId, - fromNickname : ctx.fromNickname, - id : ctx.id, - lastMessage : ctx, - messageList : [ctx], - unreadCount : 1 - }] - } - this.setSessionData(newData) - - } - init (config) { - return new Promise((resolve, reject) => { - const heart = () => { - setTimeout(async () => { - await this[send]({ - traceId: CreateUUID(), - traceType: 0, - content: { text: "ping" } - }).catch((e)=>{}); - heart(); - },1000) - } - this[connect]({ - ...config, - }).then(() => { - heart(); - resolve(this); - }).catch((e)=>{}); - }) - - } - logout() { - this.option.logout = true; - this.socket.close(); - this.interceptors.onLogout && this.interceptors.onLogout(); - } - /** - * 设置数据 - */ - setSessionData(data) { - this.interceptors.dataChangeBefore && this.interceptors.dataChangeBefore(data, this.sessionData); - this.sessionData = data; - this.interceptors.dataChangeAfter && this.interceptors.dataChangeAfter(this.sessionData); - } - /** - * 设置当前聊天窗口 - * Data为Session数据 - * @param {*} data - */ - setCurSessionId(id) { - this.curSessionId = id; - } - /** - * 获取会话列表 - * @param {*} params - */ - async getSessionList(params) { - const par = { - traceId: CreateUUID(), - traceType: 1, - ...params - }; - let {error, result} = await ToAsyncAwait(this[send](par)); - if (error) { - return Promise.reject(error); - } - const { content } = result; + let messageModel = new proto.ReqModel(); + messageModel.setTraceid(traceId); + messageModel.setTracetype(traceType); + content && messageModel.setContent(JSON.stringify(content)); + return messageModel.serializeBinary(); + }, + fromatPotoRsp = (data) => { + const res = proto.RspModel.deserializeBinary(new Uint8Array(data)); + let ctx = res.getContent(); + ctx = ctx ? JSON.parse(ctx) : {}; + if (ctx.payload) { + ctx.payload = JSON.parse(ctx.payload); + } + return { + content: ctx, + traceId: res.getTraceid(), + traceType: res.getTracetype(), + code: res.getCode(), + message: res.getMessage(), + }; + }; +class MsbIm { + defaultOption = { + ioKey: 'traceId', + reconnect: true, + }; + socket = null; + isOpen = false; + queue = {}; + interceptors = { + dataChangeBefore: null, + dataChangeAfter: null, + onClose: null, + onMessage: null, + }; + sessionData = []; + curSessionId = null; + constructor(option) { + this.option = { + ...this.defaultOption, + ...option, + }; + } + + /** + * 创建连接返回一个Promise 创建成功并成功打开连接算连接成功 + * @param {*} option + */ + [connect](option) { + return new Promise((resolve, reject) => { + const open = () => { + console.log('[im] open'); + this.isOpen = true; + resolve(this.socket); + }; + const message = async (res) => { + const result = fromatPotoRsp(res.data); + this.interceptors.onMessage && this.interceptors.onMessage(result); + // 处理服务端主动推送的消息 + this[onMessage](result); + + // 如果再消息堆里有此消息回调,则执行回调,并删除 + const cbk = this.queue[result[this.option.ioKey]]; + + if (cbk) { + cbk(result.code !== 200 ? { error: result } : { result: result }); + delete this.queue[result[this.option.ioKey]]; + } + }; + const close = () => { + console.log('[im] close'); + this.interceptors.onClose && this.interceptors.onClose(); + }; + if (uni) { + this.socket = uni.connectSocket({ + ...this.option, + fail(e) { + reject(e); + }, + }); + this.socket.onOpen(() => { + open(); + this.socket.onMessage((res) => { + message(res); + }); + }); + this.socket.onClose(() => { + close(); + }); + } else if (WebSocket) { + try { + this.socket = new WebSocket(this.option.url); + this.socket.binaryType = 'arraybuffer'; + this.socket.onopen = () => { + open(); + }; + this.socket.onmessage = (res) => { + message(res); + }; + this.socket.onclose = () => { + close(); + }; + } catch (e) { + reject(e); + } + } + }); + } + /** + * 向服务端发送消息||请求,返回一个Promise对象,收到ioKey对应的消息会算一个同步完成 + * @param {*} data + */ + [send](data) { + return new Promise((resolve, reject) => { + if (!this.isOpen) { + return reject('连接未打开'); + } + this.queue[data[this.option.ioKey]] = ({ result, error }) => { + if (result) { + resolve(result); + } else { + reject(error); + } + }; - console.log('获取会话列表', par, result); - - content.sessionVOS.forEach(item => { - if (item.lastMessage) { - item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {}); - } - let historyData = this.sessionData; - let hisIndex = historyData.findIndex(i => i.id === item.id); - if(hisIndex >= 0){ - historyData[hisIndex].lastMessage = item.lastMessage; - historyData[hisIndex].unreadCount++; - this.setSessionData(historyData) - }else{ - item.messageList = []; - const newData = [...historyData, item] - this.setSessionData(newData); - } - }); - return Promise.resolve(result); - } - /** - * 获取会话的历史消息记录 - * @param {*} params - */ - async getHistoryMsg() { - const curSessionIdx = this.sessionData.findIndex(i => i.id === this.curSessionId); - const curSession = this.sessionData[curSessionIdx]; - const msgList = curSession.messageList || []; - const par = { - traceId: CreateUUID(), - traceType: 23, - content: { - sessionId: this.curSessionId, - topMessageId: msgList.length ? msgList[0].id : null - } - }; - const {error, result} = await ToAsyncAwait(this[send](par)); - if (error) { - return Promise.reject(error); - } - const { content } = result; - console.log('获取会话历史消息',par, result); - if (content.length) { - let newData = this.sessionData; - content.forEach(item => { - item.payload = JSON.parse(item.payload) - }) - newData[curSessionIdx].messageList = content.concat(newData[curSessionIdx].messageList); - this.setSessionData(newData); - } - return Promise.resolve(result); - - } - /** - * 会话已读 - * @param {*} params - */ - async setRead(params) { - const par = { - traceId : CreateUUID(), - traceType : "6", - ...params - } - const {error, result } = await this[send](par); + const par = fromatPotoReq(data.traceId, data.traceType, data.content); + if (uni) { + this.socket.send({ + data: par, + fail(e) { + reject({ error: e }); + }, + }); + } else if (WebSocket) { + this.socket.send(par); + } + }); + } + /** + * 服务端推送消息,只处理服务端主动推送的消息 + * @param {*} data + */ + [onMessage](data) { + // 判断非服务端主动推送消息不做处理 + if (data[this.option.ioKey] || data.code !== 200) { + return false; + } + let ctx = data.content; + let historyData = [...this.sessionData], + newData = []; + const hisIndex = historyData.findIndex((i) => i.id === ctx.sessionId); + if (hisIndex >= 0) { + // 存在会话往现有会话增加一条消息,并修改最后一条消息为当前消息 + const curHisData = historyData[hisIndex]; + curHisData.messageList.push(ctx); + curHisData.lastMessage = ctx; + // 不在当前会话窗口则向会话消息加1条未读 + if (ctx.sessionId !== this.curSessionId) { + curHisData.unreadCount++; + } + newData = historyData; + } else { + // 会话列表不存在,则创建一个会话 + newData = [ + ...historyData, + { + fromAvatar: ctx.fromAvatar, + fromId: ctx.fromId, + fromNickname: ctx.fromNickname, + id: ctx.id, + lastMessage: ctx, + messageList: [ctx], + unreadCount: 1, + }, + ]; + } + this.setSessionData(newData); + } + init(config) { + return new Promise((resolve, reject) => { + const heart = () => { + // 要优化 心跳没回复需要重连 + setTimeout(async () => { + if (this.isOpen) { + await this[send]({ + traceId: CreateUUID(), + traceType: 0, + content: { text: 'ping' }, + }); + } + heart(); + }, 1000); + }; + this.option = { + ...this.option, + ...config, + }; + this[connect]() + .then((res) => { + resolve(res); + heart(); + }) + .catch((e) => { + console.log('eeeee', e); + }); + }); + } + /** + * 设置数据 + */ + setSessionData(data) { + let newData = JSON.parse(JSON.stringify(data)); + this.interceptors.dataChangeBefore && this.interceptors.dataChangeBefore(newData, this.sessionData); + this.sessionData = newData; + this.interceptors.dataChangeAfter && this.interceptors.dataChangeAfter(this.sessionData); + } + /** + * 设置当前聊天窗口 + * Data为Session数据 + * @param {*} data + */ + setCurSessionId(id) { + this.curSessionId = id; + } + /** + * 获取会话列表 + * @param {*} params + */ + async getSessionList(params) { + const par = { + traceId: CreateUUID(), + traceType: 1, + ...params, + }; - console.log('会话已读', par, result); - - let newData = this.sessionData.map(item => { - if (item.id == params.content.sessionId) { - item.unreadCount = 0; - } - return item; - }); - - this.setSessionData(newData); - } - /** - * 发送消息 - * @param {*} params - */ - async sendMsg(params) { - const index = this.sessionData.findIndex(i => i.id === this.curSessionId) - let curSession = this.sessionData[index]; - // 临时消息体 - let par = { - ...params, - traceId: CreateUUID(), - traceType: 20, - } - let msgCtx = { - ...params.content, - ...par, - fromId: params.fromId, - createTimeStamp : (new Date()).getTime(), - sendStatus : 'loading' - } - // 点发送,立即把消息加入消息列表,标记为发送中状态 - curSession.messageList.push(msgCtx); - // 超过时间未返回视为发送失败 - this.timerStatus(msgCtx); - const { error, result } = await ToAsyncAwait(this[send](par)); + console.log('[im] 获取会话列表--start', par); + let { error, result } = await ToAsyncAwait(this[send](par)); + console.log('[im] 获取会话列表--end', result, error); + if (error) { + return Promise.reject(error); + } + const { content } = result; - console.log('发送消息', par, result); - // 接到通知,标记消息是否发送成功 - for (let i = curSession.messageList.length; i--;) { - const item = curSession.messageList[i]; - if (item[this.option.ioKey] === par[this.option.ioKey]) { - curSession.messageList[i].sendStatus = msgCtx.sendStatus = error ? 'fail' : 'success'; - break; - } - } - let newData = [...this.sessionData]; - newData[index] = curSession; - this.setSessionData(newData); - if (error) { - return Promise.reject(error); - } + content.sessionVOS.forEach((item) => { + if (item.lastMessage) { + item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {}); + } + let historyData = this.sessionData; + let hisIndex = historyData.findIndex((i) => i.id === item.id); + if (hisIndex >= 0) { + historyData[hisIndex].lastMessage = item.lastMessage; + historyData[hisIndex].unreadCount++; + this.setSessionData(historyData); + } else { + item.messageList = []; + const newData = [...historyData, item]; + this.setSessionData(newData); + } + }); + return Promise.resolve(result); + } + /** + * 获取会话的历史消息记录 + * @param {*} params + */ + async getHistoryMsg() { + const curSessionIdx = this.sessionData.findIndex((i) => i.id === this.curSessionId); + const curSession = this.sessionData[curSessionIdx]; + const msgList = curSession.messageList || []; + const par = { + traceId: CreateUUID(), + traceType: 2, + content: { + sessionId: this.curSessionId, + topMessageId: msgList.length ? msgList[0].id : null, + }, + }; + console.log('[im] 获取会话历史消息--start', par); + const { error, result } = await ToAsyncAwait(this[send](par)); + console.log('[im] 获取会话历史消息--end', result, error); + if (error) { + return Promise.reject(error); + } + const { content } = result; + if (content.length) { + let newData = this.sessionData; + content.forEach((item) => { + item.payload = JSON.parse(item.payload); + }); + newData[curSessionIdx].messageList = content.concat(newData[curSessionIdx].messageList); + this.setSessionData(newData); + } + return Promise.resolve(result); + } + /** + * 会话已读 + * @param {*} params + */ + async setRead(params) { + const par = { + traceId: CreateUUID(), + traceType: '6', + ...params, + }; + console.log('[im] 会话已读--start', par); + const { error, result } = await this[send](par); + console.log('[im] 会话已读--end', result, error); - return Promise.resolve(result); - } - /** - * 发送失败时,重新发送 - * @param {*} params - */ - async resend(params) { - params.sendStatus = 'loading'; - this.timerStatus(params) - const { error, result } = await ToAsyncAwait(this[send]({ - traceId: params.traceId, - traceType: params.traceType, - content : params.content - })); - params.createTimeStamp = result.createTimeStamp; - if (error) { - params.sendStatus = 'fail'; - return Promise.reject(error); - } - params.sendStatus = 'success'; - return Promise.resolve(result); - } - timerStatus(msg) { - - setTimeout(() => { - if (msg.sendStatus === 'loading') { - msg.sendStatus = 'fail'; - delete this.queue[msg.traceId]; - } - }, 3000); - } - /** - * 主动创建会话 - * @param {*} params - */ - async createSession (params){ - const { result, error } = await ToAsyncAwait(this[send]({ - traceId : CreateUUID(), - traceType : 21, - ...params - })); - if (error) { - return Promise.reject(error); - } - const { content } = result; - let historyData = this.sessionData; - let curSession = historyData.find(i => i.id === content.id); - if (!curSession) { - curSession = { - ...content, - unreadCount: 0, - messageList : [] - } - const newData = [...historyData, curSession]; - this.setSessionData(newData); - } - return Promise.resolve(result); - } - + let newData = this.sessionData.map((item) => { + if (item.id == params.content.sessionId) { + item.unreadCount = 0; + } + return item; + }); -} \ No newline at end of file + this.setSessionData(newData); + } + /** + * 发送消息 + * @param {*} params + */ + async sendMsg(params) { + const index = this.sessionData.findIndex((i) => i.id === this.curSessionId); + let curSession = this.sessionData[index]; + // 临时消息体 + let par = { + ...params, + traceId: CreateUUID(), + traceType: 3, + }; + let msgCtx = { + ...params.content, + ...par, + createTimeStamp: new Date().getTime(), + sendStatus: 'loading', + }; + // 点发送,立即把消息加入消息列表,标记为发送中状态 + curSession.messageList.push(msgCtx); + // 超过时间未返回视为发送失败 + this.timerStatus(msgCtx); + + console.log('[im] 发送消息--start', par); + const { error, result } = await ToAsyncAwait(this[send](par)); + console.log('[im] 发送消息--end', result, error); + // 接到通知,标记消息是否发送成功 + for (let i = curSession.messageList.length; i--; ) { + const item = curSession.messageList[i]; + if (item[this.option.ioKey] === par[this.option.ioKey]) { + curSession.messageList[i].sendStatus = msgCtx.sendStatus = error ? 'fail' : 'success'; + break; + } + } + let newData = [...this.sessionData]; + newData[index] = curSession; + this.setSessionData(newData); + if (error) { + return Promise.reject(error); + } + + return Promise.resolve(result); + } + /** + * 发送失败时,重新发送 + * @param {*} params + */ + async resend(params) { + params.sendStatus = 'loading'; + this.timerStatus(params); + + console.log('[im] 重新发送消息--start', params); + const { error, result } = await ToAsyncAwait( + this[send]({ + traceId: params.traceId, + traceType: params.traceType, + content: params.content, + }) + ); + console.log('[im] 重新发送消息--end', result, error); + params.createTimeStamp = result.createTimeStamp; + if (error) { + params.sendStatus = 'fail'; + return Promise.reject(error); + } + params.sendStatus = 'success'; + return Promise.resolve(result); + } + timerStatus(msg) { + setTimeout(() => { + if (msg.sendStatus === 'loading') { + msg.sendStatus = 'fail'; + delete this.queue[msg.traceId]; + } + }, 3000); + } + /** + * 主动创建会话 + * @param {*} params + */ + async createSession(params) { + const par = { + traceId: CreateUUID(), + traceType: 9, + ...params, + }; + console.log('[im] 主动创建会话--start', par); + const { result, error } = await ToAsyncAwait(this[send](par)); + console.log('[im] 主动创建会话--end', result, error); + if (error) { + return Promise.reject(error); + } + const { content } = result; + let historyData = this.sessionData; + let curSession = historyData.find((i) => i.id === content.id); + if (!curSession) { + curSession = { + ...content, + unreadCount: 0, + messageList: [], + }; + const newData = [...historyData, curSession]; + this.setSessionData(newData); + } + return Promise.resolve(result); + } +} +export default MsbIm; \ No newline at end of file diff --git a/src/common/utils/im.js b/src/common/utils/im.js index 13abf99..b9df58c 100644 --- a/src/common/utils/im.js +++ b/src/common/utils/im.js @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2022-05-20 11:00:07 * @LastEditors: ch - * @LastEditTime: 2022-06-09 10:00:15 + * @LastEditTime: 2022-06-09 11:32:37 * @Description: file content */ diff --git a/src/main.js b/src/main.js index 94c3ca4..0ccadfa 100644 --- a/src/main.js +++ b/src/main.js @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2021-07-26 23:22:16 * @LastEditors: ch - * @LastEditTime: 2022-06-02 16:11:56 + * @LastEditTime: 2022-06-09 11:43:04 * @Description: file content */ import Vue from 'vue'; diff --git a/src/pages/account/message/chat/index.vue b/src/pages/account/message/chat/index.vue index 1f3c4a0..2d8f7a6 100644 --- a/src/pages/account/message/chat/index.vue +++ b/src/pages/account/message/chat/index.vue @@ -2,7 +2,7 @@ * @Author: ch * @Date: 2022-03-26 14:32:03 * @LastEditors: ch - * @LastEditTime: 2022-06-02 17:33:13 + * @LastEditTime: 2022-06-09 11:41:50 * @Description: file content -->