useIM.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. import { ref, onMounted, onUnmounted, watch } from 'vue';
  2. import { getConversationSync, getMessageSync, getChatKey, setUnread, deleteConversation } from '@/api/common'
  3. import { Base64 } from 'js-base64'
  4. import { useUserStore } from '@/store/user'
  5. import { isEnterprise } from '@/utils/auth'
  6. import { useIMStore } from '@/store/im'
  7. // 配置悟空IM
  8. import {
  9. MessageText,
  10. Channel,
  11. WKSDK,
  12. ChannelTypePerson,
  13. // Conversation,
  14. MessageContent,
  15. // Message, StreamItem, ChannelTypeGroup, MessageStatus, SyncOptions, MessageExtra, MessageContent
  16. } from "wukongimjssdk"
  17. // 默认招呼语
  18. export const defaultText = '您好,关注到您发布该职位信息,请问有机会与您进一步沟通吗?'
  19. // 企业默认招呼语
  20. export const defaultTextEnt = '您好,我们正在寻找充满激情、勇于挑战的您,快来和我聊一聊吧~'
  21. const { ObjectContent } = initRegister(101)
  22. const { ObjectContent: ObjectContent2 } = initRegister(102)
  23. const { ObjectContent: ObjectContent3 } = initRegister(103)
  24. const { ObjectContent: ObjectContent4 } = initRegister(104)
  25. const { ObjectContent: ObjectContent5 } = initRegister(105) // 发送简历
  26. const contentType = {
  27. 101: ObjectContent,
  28. 102: ObjectContent2,
  29. 103: ObjectContent3,
  30. 104: ObjectContent4,
  31. 105: ObjectContent5, // 发送简历
  32. }
  33. // 注册消息体
  34. function initRegister (type) {
  35. class ObjectContent extends MessageContent {
  36. constructor(text) {
  37. super();
  38. this.content = text
  39. }
  40. get conversationDigest() {
  41. // 这里需要实现具体的逻辑
  42. return this.content
  43. }
  44. get contentType() {
  45. // 这里需要实现具体的逻辑
  46. return type; // 示例实现
  47. }
  48. decodeJSON(content) {
  49. this.content = content.text;
  50. }
  51. encodeJSON() {
  52. return {
  53. content: this.content
  54. };
  55. }
  56. }
  57. // 注册101类型为面试
  58. WKSDK.shared().register(type, () => new ObjectContent())
  59. return {
  60. ObjectContent
  61. }
  62. }
  63. const HISTORY_QUERY = {
  64. limit: 30,
  65. startMessageSeq: 0,
  66. endMessageSeq: 0,
  67. pullMode: 1
  68. }
  69. const ConnectStatus = {
  70. Disconnect: 0, // 断开连接
  71. Connected: 1, // 连接成功
  72. Connecting: 2, // 连接中
  73. ConnectFail: 3, // 连接错误
  74. ConnectKick: 4, // 连接被踢,服务器要求客户端断开(一般是账号在其他地方登录,被踢)
  75. }
  76. // api 接入
  77. export function useDataSource () {
  78. const userStore = useUserStore()
  79. // 最近会话数据源
  80. WKSDK.shared().config.provider.syncConversationsCallback = async () => {
  81. const query = {
  82. msg_count: 1
  83. }
  84. if (isEnterprise()) {
  85. Object.assign(query, { enterpriseId: userStore.entBaseInfo.enterpriseId })
  86. }
  87. const resultConversations = []
  88. const resp = await getConversationSync(query)
  89. const conversationList = resp
  90. if (conversationList) {
  91. conversationList.forEach(conversation => {
  92. conversation.channel = new Channel(conversation.channel_id, conversation.channel_type)
  93. conversation.unread = +(conversation.unread || 0)
  94. resultConversations.push(conversation)
  95. })
  96. }
  97. return resultConversations
  98. }
  99. // 同步频道消息数据源
  100. WKSDK.shared().config.provider.syncMessagesCallback = async function(channel) {
  101. // 后端提供的获取频道消息列表的接口数据 然后构建成 Message对象数组返回
  102. let resultMessages = new Array()
  103. const {
  104. startMessageSeq: start_message_seq,
  105. endMessageSeq: end_message_seq,
  106. limit,
  107. pullMode: pull_mode
  108. } = HISTORY_QUERY
  109. const query = {
  110. channel_id: channel.channelID,
  111. channel_type: channel.channelType,
  112. start_message_seq,
  113. end_message_seq,
  114. limit,
  115. pull_mode,
  116. }
  117. if (isEnterprise()) {
  118. Object.assign(query, { enterpriseId: userStore.entBaseInfo.enterpriseId })
  119. }
  120. const resp = await getMessageSync(query)
  121. const messageList = resp && resp["messages"]
  122. if (messageList) {
  123. messageList.forEach((msg) => {
  124. msg.payload = JSON.parse(Base64.decode(msg.payload))
  125. if (contentType[msg.payload.type]) {
  126. msg.payload.content = JSON.parse(msg.payload.content ?? '{}')
  127. }
  128. resultMessages.push(msg)
  129. })
  130. }
  131. const more = resp.more === 1
  132. return {
  133. more,
  134. resultMessages
  135. }
  136. }
  137. }
  138. async function getKey () {
  139. const userStore = useUserStore()
  140. const keyQuery = {
  141. userId: userStore.accountInfo.userId
  142. }
  143. if (isEnterprise()) {
  144. Object.assign(keyQuery, { enterpriseId: userStore.entBaseInfo.enterpriseId })
  145. console.log('企业模式', keyQuery)
  146. }
  147. const { uid, wssUrl, token } = await getChatKey(keyQuery)
  148. return {
  149. uid, wssUrl, token
  150. }
  151. }
  152. export const useIM = () => {
  153. useDataSource()
  154. const key = ref(0)
  155. const IM = useIMStore()
  156. onMounted( async () => {
  157. // 通过自身userId和企业id获取token和uid
  158. await resetConfig()
  159. // 连接状态监听
  160. WKSDK.shared().connectManager.addConnectStatusListener(connectStatusListener)
  161. // 常规消息监听
  162. WKSDK.shared().chatManager.addMessageListener(messageListen)
  163. // 连接
  164. WKSDK.shared().connectManager.connect()
  165. })
  166. onUnmounted(() => {
  167. WKSDK.shared().connectManager.removeConnectStatusListener(connectStatusListener)
  168. // 常规消息监听移除
  169. WKSDK.shared().chatManager.removeMessageListener(messageListen)
  170. // 连接状态监听移除
  171. WKSDK.shared().connectManager.disconnect()
  172. })
  173. async function messageListen (message) {
  174. console.log('收到消息', message)
  175. IM.setFromChannel(message.channel.channelID)
  176. setUnreadCount()
  177. }
  178. async function connectStatusListener (status) {
  179. // console.log('连接状态', status === ConnectStatus.Connected)
  180. // 连接成功 获取点击数
  181. const connected = status === ConnectStatus.Connected
  182. IM.setConnected(connected)
  183. if (connected) {
  184. // 必须同步最近会话才能获取未读总数
  185. await syncConversation()
  186. setUnreadCount()
  187. }
  188. }
  189. function setUnreadCount () {
  190. const count = WKSDK.shared().conversationManager.getAllUnreadCount()
  191. key.value++
  192. IM.setNewMsg(key.value)
  193. IM.setUnreadCount(count)
  194. console.log('未读消息总数', count)
  195. }
  196. async function resetConfig () {
  197. const { uid, wssUrl, token } = await getKey()
  198. IM.setUid(uid)
  199. // 单机模式可以直接设置地址
  200. WKSDK.shared().config.addr = 'wss://' + wssUrl // 默认端口为5200
  201. // 认证信息
  202. WKSDK.shared().config.uid = uid // 用户uid(需要在悟空通讯端注册过)
  203. WKSDK.shared().config.token = token // 用户token (需要在悟空通讯端注册过)
  204. }
  205. return {
  206. resetConfig
  207. }
  208. }
  209. export function initConnect (callback = () => {}) {
  210. useDataSource()
  211. const IM = useIMStore()
  212. const conversationList = ref([])
  213. const messageItems = ref([])
  214. watch(
  215. () => IM.newMsg,
  216. async () => {
  217. // 未读消息变化
  218. updateConversation()
  219. // 拉取最新消息 查看是否是自己的数据
  220. },
  221. {
  222. deep: true,
  223. immediate: true
  224. }
  225. )
  226. onMounted(async () => {
  227. // 消息发送状态监听
  228. WKSDK.shared().chatManager.addMessageStatusListener(statusListen)
  229. // 常规消息监听
  230. // WKSDK.shared().chatManager.addMessageListener(messageListen)
  231. })
  232. onUnmounted(() => {
  233. // 消息发送状态监听移除
  234. WKSDK.shared().chatManager.removeMessageStatusListener(statusListen)
  235. // 常规消息监听移除
  236. // WKSDK.shared().chatManager.removeMessageListener(messageListen)
  237. })
  238. // 消息发送状态监听
  239. function statusListen (packet) {
  240. if (packet.reasonCode === 1) {
  241. // 发送成功
  242. console.log('发送成功')
  243. // 添加一组成功数据
  244. callback(true)
  245. } else {
  246. // 发送失败
  247. console.log('发送失败')
  248. // 添加一组失败数据
  249. callback(false)
  250. }
  251. }
  252. async function updateConversation () {
  253. const res = await syncConversation()
  254. conversationList.value = res
  255. }
  256. function updateUnreadCount () {
  257. const count = WKSDK.shared().conversationManager.getAllUnreadCount()
  258. IM.setUnreadCount(count)
  259. }
  260. async function deleteConversations (channel, enterpriseId) {
  261. const query = {
  262. channel_id: channel.channelID,
  263. channel_type: channel.channelType,
  264. enterpriseId
  265. }
  266. await deleteConversation(query)
  267. }
  268. async function resetUnread (channel, enterpriseId) {
  269. const query = {
  270. channel_id: channel.channelID,
  271. channel_type: channel.channelType,
  272. enterpriseId,
  273. unread: 0
  274. }
  275. const res = await setUnread(query)
  276. return res
  277. }
  278. return {
  279. resetUnread,
  280. deleteConversations,
  281. updateConversation,
  282. updateUnreadCount,
  283. conversationList,
  284. messageItems,
  285. // channel
  286. }
  287. }
  288. // 同步最近会话
  289. async function syncConversation () {
  290. const res = await WKSDK.shared().conversationManager.sync()
  291. return res
  292. }
  293. // 发起聊天
  294. export async function initChart (userId, enterpriseId) {
  295. const channel = ref()
  296. // const list = ref([])
  297. const query = {
  298. userId,
  299. enterpriseId
  300. }
  301. // 创建聊天频道
  302. const { uid } = await getChatKey(query)
  303. const _channel = new Channel(uid, ChannelTypePerson)
  304. channel.value = _channel
  305. const conversation = WKSDK.shared().conversationManager.findConversation(_channel)
  306. if(!conversation) {
  307. // 如果最近会话不存在,则创建一个空的会话
  308. WKSDK.shared().conversationManager.createEmptyConversation(_channel)
  309. }
  310. const res = await getMoreMessages(1, _channel)
  311. return {
  312. channel,
  313. ...res
  314. }
  315. }
  316. // 翻页
  317. export async function getMoreMessages (pageSize, channel) {
  318. const list = ref([])
  319. Object.assign(HISTORY_QUERY, {
  320. startMessageSeq: (pageSize - 1) * HISTORY_QUERY.limit
  321. })
  322. const { resultMessages, more } = await WKSDK.shared().chatManager.syncMessages(channel)
  323. list.value = resultMessages
  324. return {
  325. list,
  326. more
  327. }
  328. }
  329. /**
  330. *
  331. * @param {*} text
  332. * @param {*} _channel
  333. * @param { Number } type : 101 面试主体
  334. * @returns
  335. */
  336. // 发送职位使用101
  337. export function send (text, _channel, type) {
  338. let _text
  339. if (contentType[type]) {
  340. _text = new contentType[type](text)
  341. console.log(_text)
  342. WKSDK.shared().chatManager.send(_text, _channel)
  343. return
  344. }
  345. // if (type === 101) {
  346. // _text = new ObjectContent(text)
  347. // WKSDK.shared().chatManager.send(_text, _channel)
  348. // // console.log(_text)
  349. // return
  350. // }
  351. // if (type === 102) {
  352. // _text = new ObjectContent2(text)
  353. // WKSDK.shared().chatManager.send(_text, _channel)
  354. // // console.log(_text)
  355. // return
  356. // }
  357. // // 求职者拒绝面试邀请
  358. // if (type === 103) {
  359. // _text = new ObjectContent3(text)
  360. // WKSDK.shared().chatManager.send(_text, _channel)
  361. // return
  362. // }
  363. // // 求职者接受面试邀请
  364. // if (type === 104) {
  365. // _text = new ObjectContent4(text)
  366. // WKSDK.shared().chatManager.send(_text, _channel)
  367. // return
  368. // }
  369. _text = new MessageText(text)
  370. // console.log(_text)
  371. WKSDK.shared().chatManager.send(_text, _channel)
  372. }
  373. // 对话开场白 用户 to 企业
  374. export async function prologue ({userId, enterpriseId, text}) {
  375. const { channel } = await checkConversation(userId, enterpriseId)
  376. send(text, channel, 102)
  377. }
  378. // 企业 to 用户
  379. export async function talkToUser ({userId, text}) {
  380. const { channel } = await checkConversation(userId)
  381. // if (!isNewTalk) send(text, channel)
  382. send(text, channel)
  383. }
  384. // 检测是否存在频道
  385. export async function checkConversation (userId, enterpriseId) {
  386. const query = {
  387. userId,
  388. enterpriseId
  389. }
  390. // 创建聊天频道
  391. const { uid } = await getChatKey(query)
  392. const _channel = new Channel(uid, ChannelTypePerson)
  393. const conversation = WKSDK.shared().conversationManager.findConversation(_channel)
  394. const isNewTalk = ref(false)
  395. if(!conversation) {
  396. // 如果最近会话不存在,则创建一个空的会话
  397. WKSDK.shared().conversationManager.createEmptyConversation(_channel)
  398. isNewTalk.value = true
  399. }
  400. return {
  401. channel: _channel,
  402. isNewTalk: isNewTalk.value
  403. }
  404. }