tim-manager.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import TIM from 'tim-wx-sdk';
  2. class TIMManager {
  3. constructor() {
  4. this.tim = null;
  5. this.isLogin = false;
  6. this.userId = null;
  7. this.messageCallbacks = [];
  8. }
  9. /**
  10. * 初始化 TIM SDK
  11. */
  12. init(sdkAppID) {
  13. // 创建 SDK 实例
  14. this.tim = TIM.create({
  15. SDKAppID: sdkAppID
  16. });
  17. // 设置日志级别(开发时设为 0,生产设为 1)
  18. this.tim.setLogLevel(0);
  19. // 注册监听事件
  20. this.registerEvents();
  21. console.log('✅ TIM SDK 初始化完成');
  22. }
  23. /**
  24. * 注册事件监听
  25. */
  26. registerEvents() {
  27. // SDK 进入 ready 状态
  28. this.tim.on(TIM.EVENT.SDK_READY, this.onSdkReady.bind(this));
  29. // SDK 未 ready
  30. this.tim.on(TIM.EVENT.SDK_NOT_READY, this.onSdkNotReady.bind(this));
  31. // 收到新消息
  32. this.tim.on(TIM.EVENT.MESSAGE_RECEIVED, this.onMessageReceived.bind(this));
  33. // 会话列表更新
  34. this.tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, this.onConversationListUpdated.bind(this));
  35. // 被踢下线
  36. this.tim.on(TIM.EVENT.KICKED_OUT, this.onKickedOut.bind(this));
  37. // 网络状态变化
  38. this.tim.on(TIM.EVENT.NET_STATE_CHANGE, this.onNetStateChange.bind(this));
  39. }
  40. /**
  41. * SDK Ready
  42. */
  43. onSdkReady() {
  44. console.log('✅ TIM SDK Ready');
  45. this.isLogin = true;
  46. }
  47. /**
  48. * SDK Not Ready
  49. */
  50. onSdkNotReady() {
  51. console.log('⚠️ TIM SDK Not Ready');
  52. this.isLogin = false;
  53. }
  54. /**
  55. * 收到新消息
  56. */
  57. onMessageReceived(event) {
  58. const messageList = event.data;
  59. console.log('📥 收到新消息:', messageList.length, '条');
  60. // 同步接收到的消息到MySQL(双重保障)
  61. messageList.forEach(message => {
  62. this.syncReceivedMessageToMySQL(message);
  63. });
  64. // 触发回调
  65. this.messageCallbacks.forEach(callback => {
  66. callback(messageList);
  67. });
  68. }
  69. /**
  70. * 同步接收到的消息到MySQL
  71. */
  72. async syncReceivedMessageToMySQL(timMessage) {
  73. try {
  74. // 获取消息类型
  75. const getMessageType = (msg) => {
  76. const typeMap = {
  77. 'TIMTextElem': 1,
  78. 'TIMImageElem': 2,
  79. 'TIMSoundElem': 3,
  80. 'TIMVideoFileElem': 4,
  81. 'TIMFileElem': 5
  82. };
  83. return typeMap[msg.type] || 1;
  84. };
  85. // 获取消息内容
  86. const getMessageContent = (msg) => {
  87. switch (msg.type) {
  88. case 'TIMTextElem':
  89. return msg.payload.text || '';
  90. case 'TIMImageElem':
  91. return '[图片]';
  92. case 'TIMSoundElem':
  93. return '[语音]';
  94. case 'TIMVideoFileElem':
  95. return '[视频]';
  96. case 'TIMFileElem':
  97. return '[文件]';
  98. default:
  99. return '[未知消息]';
  100. }
  101. };
  102. const syncData = {
  103. messageId: timMessage.ID,
  104. fromUserId: timMessage.from,
  105. toUserId: timMessage.to,
  106. messageType: getMessageType(timMessage),
  107. content: getMessageContent(timMessage),
  108. sendTime: timMessage.time
  109. };
  110. // 调用后端同步接口
  111. const res = await uni.request({
  112. url: 'http://localhost:1004/api/chat/syncTIMMessage',
  113. method: 'POST',
  114. data: syncData,
  115. header: {
  116. 'Content-Type': 'application/json'
  117. }
  118. });
  119. if (res[1].data.code === 200) {
  120. console.log('✅ 接收消息已同步到MySQL:', timMessage.ID);
  121. }
  122. } catch (error) {
  123. console.error('❌ 同步接收消息失败:', error);
  124. // 同步失败不影响主流程
  125. }
  126. }
  127. /**
  128. * 会话列表更新
  129. */
  130. onConversationListUpdated(event) {
  131. console.log('📋 会话列表更新:', event.data.length, '个');
  132. }
  133. /**
  134. * 被踢下线
  135. */
  136. onKickedOut(event) {
  137. console.log('❌ 被踢下线:', event.data.type);
  138. uni.showModal({
  139. title: '下线通知',
  140. content: '您的账号在其他设备登录',
  141. showCancel: false
  142. });
  143. }
  144. /**
  145. * 网络状态变化
  146. */
  147. onNetStateChange(event) {
  148. console.log('🌐 网络状态:', event.data.state);
  149. }
  150. /**
  151. * 登录
  152. */
  153. async login(userID, userSig) {
  154. try {
  155. console.log('📱 开始登录 TIM, userID:', userID);
  156. const res = await this.tim.login({
  157. userID: String(userID),
  158. userSig: userSig
  159. });
  160. console.log('✅ TIM 登录成功');
  161. this.userId = userID;
  162. return res;
  163. } catch (error) {
  164. console.error('❌ TIM 登录失败:', error);
  165. throw error;
  166. }
  167. }
  168. /**
  169. * 登出
  170. */
  171. async logout() {
  172. try {
  173. await this.tim.logout();
  174. console.log('✅ TIM 登出成功');
  175. this.isLogin = false;
  176. this.userId = null;
  177. } catch (error) {
  178. console.error('❌ TIM 登出失败:', error);
  179. }
  180. }
  181. /**
  182. * 发送文本消息
  183. */
  184. async sendTextMessage(toUserId, text) {
  185. try {
  186. // 验证参数
  187. if (!toUserId || toUserId === 'undefined' || toUserId === 'null') {
  188. console.error('❌ 接收者ID无效:', toUserId);
  189. throw new Error('接收者ID无效: ' + toUserId);
  190. }
  191. if (!this.userId || this.userId === 'undefined' || this.userId === 'null') {
  192. console.error('❌ 发送者ID无效:', this.userId);
  193. throw new Error('发送者ID未登录或无效');
  194. }
  195. const toUserIdStr = String(toUserId);
  196. console.log('📤 准备发送消息:');
  197. console.log(' - 发送者ID:', this.userId, '(类型:', typeof this.userId, ')');
  198. console.log(' - 接收者ID:', toUserIdStr, '(类型:', typeof toUserIdStr, ')');
  199. console.log(' - 消息内容:', text);
  200. // 创建文本消息
  201. const message = this.tim.createTextMessage({
  202. to: toUserIdStr,
  203. conversationType: TIM.TYPES.CONV_C2C, // 单聊
  204. payload: {
  205. text: text
  206. }
  207. });
  208. console.log('📤 消息已创建,准备发送...');
  209. // 发送消息
  210. const res = await this.tim.sendMessage(message);
  211. console.log('✅ 消息发送成功:', res.data.message);
  212. return res.data.message;
  213. } catch (error) {
  214. console.error('❌ 消息发送失败:', error);
  215. console.error(' - 错误详情:', error.message || error);
  216. console.error(' - 错误代码:', error.code || 'N/A');
  217. throw error;
  218. }
  219. }
  220. /**
  221. * 发送图片消息
  222. */
  223. async sendImageMessage(toUserId, filePath) {
  224. try {
  225. const message = this.tim.createImageMessage({
  226. to: String(toUserId),
  227. conversationType: TIM.TYPES.CONV_C2C,
  228. payload: {
  229. file: filePath
  230. },
  231. onProgress: (percent) => {
  232. console.log('📊 上传进度:', percent);
  233. }
  234. });
  235. const res = await this.tim.sendMessage(message);
  236. console.log('✅ 图片发送成功');
  237. return res.data.message;
  238. } catch (error) {
  239. console.error('❌ 图片发送失败:', error);
  240. throw error;
  241. }
  242. }
  243. /**
  244. * 获取会话列表
  245. */
  246. async getConversationList() {
  247. try {
  248. const res = await this.tim.getConversationList();
  249. console.log('📋 会话列表:', res.data.conversationList.length, '个');
  250. return res.data.conversationList;
  251. } catch (error) {
  252. console.error('❌ 获取会话列表失败:', error);
  253. throw error;
  254. }
  255. }
  256. /**
  257. * 获取聊天记录
  258. */
  259. async getMessageList(conversationID, count = 15) {
  260. try {
  261. const res = await this.tim.getMessageList({
  262. conversationID: conversationID,
  263. count: count
  264. });
  265. console.log('💬 聊天记录:', res.data.messageList.length, '条');
  266. return res.data.messageList;
  267. } catch (error) {
  268. console.error('❌ 获取聊天记录失败:', error);
  269. throw error;
  270. }
  271. }
  272. /**
  273. * 将消息设为已读
  274. */
  275. async setMessageRead(conversationID) {
  276. try {
  277. await this.tim.setMessageRead({ conversationID });
  278. console.log('✅ 消息已读');
  279. } catch (error) {
  280. console.error('❌ 设置已读失败:', error);
  281. }
  282. }
  283. /**
  284. * 撤回消息
  285. */
  286. async revokeMessage(message) {
  287. try {
  288. await this.tim.revokeMessage(message);
  289. console.log('✅ 消息已撤回');
  290. } catch (error) {
  291. console.error('❌ 撤回失败:', error);
  292. throw error;
  293. }
  294. }
  295. /**
  296. * 监听消息
  297. */
  298. onMessage(callback) {
  299. this.messageCallbacks.push(callback);
  300. }
  301. /**
  302. * 移除监听
  303. */
  304. offMessage(callback) {
  305. const index = this.messageCallbacks.indexOf(callback);
  306. if (index > -1) {
  307. this.messageCallbacks.splice(index, 1);
  308. }
  309. }
  310. }
  311. // 导出单例
  312. const timManager = new TIMManager();
  313. export default timManager;