微信小程序(uniapp)实现腾讯云 IM 消息撤回

news2025/6/5 4:43:56

uniapp 实现腾讯云 IM 消息撤回功能实战指南

一、功能实现原理

腾讯云 IM 的消息撤回功能通过 消息修订(Message Revision) 机制实现,核心流程如下:

  1. 发送方调用撤回 API 删除指定消息
  2. 云端生成撤回通知消息(类型为 TIM.TYPES.MSG_REVOKED
  3. 接收方收到通知后执行本地消息删除
  4. 全平台自动同步消息状态(需开启消息漫游)

二、核心实现步骤

1. 发送方撤回逻辑

// services/im.js
export async function revokeMessage(message) {
  const tim = initIM()
  
  try {
    // 执行消息撤回操作
    const res = await tim.revokeMessage(message)
    
    // 更新本地消息状态(立即生效)
    if (res.data.revokeMessage) {
      const conv = tim.getConversationProfile(message.conversationID)
      conv.setMessageRevoked(message.clientMsgID)
    }
    
    return res
  } catch (error) {
    console.error('撤回失败:', error)
    throw new Error('消息撤回失败,请检查网络')
  }
}

2. 接收方消息处理

// 消息监听器(全局注册)
export function setupMessageListener(callback) {
  const tim = initIM()
  
  tim.on(tim.EVENT.MESSAGE_RECEIVED, (event) => {
    event.data.forEach(msg => {
      // 处理撤回通知
      if (msg.type === tim.TYPES.MSG_REVOKED) {
        handleRevokeNotice(msg)
        return
      }
      
      callback(msg)
    })
  })
}

// 撤回通知处理
function handleRevokeNotice(notice) {
  const { revokedMessageClientMsgID, operator } = notice.payload
  
  // 查找本地对应消息
  const conversation = tim.getConversationProfile(notice.conversationID)
  const originalMsg = conversation.getMessage(revokedMessageClientMsgID)
  
  if (!originalMsg) return
  
  // 权限验证(仅允许发送者撤回)
  if (originalMsg.from !== operator.userID) {
    console.warn('非法撤回操作', operator)
    return
  }
  
  // 执行本地删除
  conversation.deleteMessage(revokedMessageClientMsgID)
  
  // 触发UI更新(示例)
  uni.$emit('message-revoked', {
    conversationID: notice.conversationID,
    clientMsgID: revokedMessageClientMsgID
  })
}

3. UI 层集成示例

<template>
  <view class="message-list">
    <view 
      v-for="(msg, index) in messages"
      :key="msg.clientMsgID"
      class="message-item"
    >
      <!-- 消息内容 -->
      <template v-if="!msg.isRevoked">
        {{ msg.payload.text }}
      </template>
      
      <!-- 撤回提示 -->
      <view v-else class="revoked-tip">
        "{{ msg.payload.description }}" 已被撤回
      </view>
      
      <!-- 长按操作菜单 -->
      <view 
        v-if="canRevoke(msg)"
        class="action-menu"
        @longpress="showActionSheet(msg)"
      ></view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      messages: []
    }
  },
  methods: {
    // 权限校验
    canRevoke(msg) {
      return msg.from === this.currentUser.userID && 
             !msg.isRevoked &&
             Date.now() - msg.time < 2 * 60 * 1000 // 2分钟内可撤回
    },
    
    // 执行撤回
    async handleRevoke(msg) {
      try {
        await revokeMessage(msg)
        uni.showToast({ title: '撤回成功', icon: 'none' })
      } catch (error) {
        uni.showToast({ title: error.message, icon: 'none' })
      }
    }
  }
}
</script>

三、关键问题处理

1. 撤回时间限制

// 配置中心(建议)
const IM_CONFIG = {
  REVOKE_TIME_LIMIT: 2 * 60 * 1000 // 2分钟
}

// 权限校验时使用
if (Date.now() - msg.time > IM_CONFIG.REVOKE_TIME_LIMIT) {
  throw new Error('超过可撤回时间')
}

2. 消息状态同步

// 消息漫游配置(初始化时)
tim = TIM.create({
  SDKAppID: config.SDKAppID
})

// 开启消息漫游(需在控制台配置)
tim.setMessageRevokeMode({
  mode: TIM.TYPES.REVOKE_MODE_SENDER, // 仅发送方可撤回
  syncOtherMachine: true // 同步到其他端
})

3. 异常场景处理

// 撤回失败重试机制
export async function revokeWithRetry(msg, retries = 3) {
  try {
    return await revokeMessage(msg)
  } catch (error) {
    if (retries <= 0) throw error
    
    await new Promise(resolve => setTimeout(resolve, 1000))
    return revokeWithRetry(msg, retries - 1)
  }
}

四、高级功能扩展

1. 富媒体消息撤回

// 自定义撤回描述(图片/文件等)
function getRevokeDescription(msg) {
  switch(msg.type) {
    case TIM.TYPES.MSG_IMAGE:
      return '[图片]'
    case TIM.TYPES.MSG_FILE:
      return '[文件]'
    case TIM.TYPES.MSG_CUSTOM:
      return JSON.parse(msg.payload.data).description || '[自定义消息]'
    default:
      return msg.payload.text || '[未知消息]'
  }
}

2. 撤回动画效果

/* 添加CSS过渡 */
.message-item.revoking {
  animation: fadeOut 0.3s forwards;
}

@keyframes fadeOut {
  to {
    opacity: 0;
    transform: translateX(20px);
  }
}

3. 服务端日志记录

// 撤回事件上报(示例)
async function logRevokeEvent(msg, operator) {
  await axios.post('/api/im/revoke-log', {
    sdk_app_id: process.env.SDKAppID,
    group_id: msg.groupID,
    operator_id: operator.userID,
    target_msg_id: msg.clientMsgID,
    timestamp: Date.now()
  })
}

五、常见问题排查

  1. Q: 撤回后对方仍显示消息
    A: 检查消息漫游是否开启,确认双方客户端版本 ≥ 2.15.0

  2. Q: 无法撤回超过2分钟的消息
    A: 腾讯云默认限制为2分钟,需在控制台申请延长权限

  3. Q: 群聊中非群主成员撤回失败
    A: 确认群类型是否为 Private(私有群),Public 群需群主操作

  4. Q: 撤回通知不显示描述
    A: 检查自定义消息解析逻辑,确保 payload 格式正确

六、性能优化建议

  1. 使用 tim.getMessageRevokeStatus() 批量查询消息状态
  2. 对已撤回消息进行本地缓存,避免重复查询
  3. 添加防抖处理,防止快速连续撤回导致性能问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397365.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt实现的水波进度条和温度进度条

一.效果 二.原理 1.水波 要模拟波浪,就要首先画出一条波浪线,正弦余弦曲线就很适合。 y=A*sin(ω*x+φ)+k y=A*cos(ω*x+φ)+k 这是正弦余弦曲线的公式,要想实现水波效果,那需要两条曲线,一条曲线的波峰对着另外一条曲线的波谷,要实现这样的曲线效果,只有让正弦曲线前移…

WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM

目录 一、适配方案 二、VM布局 ​编辑 三、vh布局 四、案例—酷我音乐 一、适配方案 二、VM布局 三、vh布局 四、案例—酷我音乐

【Zephyr 系列 3】多线程与调度机制:让你的 MCU 同时干多件事

好的,下面是Zephyr 系列第 3 篇:聚焦 多线程与调度机制的实践应用,继续面向你这样的 Ubuntu + 真板实战开发者,代码清晰、讲解通俗、结构规范,符合 CSDN 高质量博客标准。 🧠关键词:Zephyr、线程调度、k_thread、k_sleep、RTOS、BluePill 📌适合人群:想从裸机开发进…

Kotlin-特殊类型

文章目录 数据类型枚举类型匿名类和伴生对象单例类伴生对象 数据类型 声明一个数据类非常简单: //在class前面添加data关键字表示为一个数据类 data class Student(var name: String, var age: Int)数据类声明后,编译器会根据主构造函数中声明的所有属性自动为其生成以下函数…

nssctf第二题[SWPUCTF 2021 新生赛]简简单单的逻辑

这是题目&#xff0c;下载后得到一个python文件,打开 解读代码&#xff1a; for i in range(len(list)):key (list[i]>>4)((list[i] & 0xf)<<4)result str(hex(ord(flag[i])^key))[2:].zfill(2)list[i]>>4&#xff1a;从列表中取数字同时高4位向右位…

《Discuz! X3.5开发从入门到生态共建》第3章 Discuz! X3.5 核心目录结构解析-优雅草卓伊凡

《Discuz! X3.5开发从入门到生态共建》第3章 Discuz! X3.5 核心目录结构解析-优雅草卓伊凡 3.1 系统核心目录结构 Discuz! X3.5采用模块化设计&#xff0c;主要目录结构如下&#xff1a; discuz_root/ ├─ api/ // API接口目录 ├─ config/ …

【HarmonyOS 5】鸿蒙应用实现发票扫描、文档扫描输出PDF图片或者表格的功能

【HarmonyOS 5】鸿蒙应用实现发票扫描、文档扫描输出PDF图片或者表格的功能 一、前言 图(1-1) HarmonyOS 系统提供的核心场景化视觉服务,旨在帮助开发者快速实现移动端文档数字化功能。 其核心能力包括:扫描合同、票据、会议记录并保存为 PDF 分享。拍摄课堂 PPT、书籍章…

Python_day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 进阶&#xff1a;并拆分成多个文件 关于 Dataset 从谷歌图片中抓取了 1000 多张猫和狗的图片。问题陈述是构建一个模型&#xff0c;该模型可以尽可能准确地在图像…

STM32CubeDAC及DMA配置

STM32CubeDAC及DMA配置 一&#xff0c;问题1二&#xff0c;解决11&#xff0c;宏观思路CubeMX配置2&#xff0c;HAL_TIM_Base_Start(&htim6) 的作用1&#xff0c;作用1&#xff1a;使能TIM6的时钟并让它开始计数2&#xff0c;作用2&#xff1a;当 TIM6 溢出时&#xff0c;会…

行业案例 | OPPO借助Azure AI Speech国际服务实现音频文件智能转录

OPPO是全球领先的智能终端与移动互联网服务提供商&#xff0c;业务覆盖50余国&#xff0c;通过超40万销售网点和2500个服务中心与全球用户共享科技。作为软硬服一体化科技公司&#xff0c;OPPO以ColorOS为核心优化软件平台&#xff0c;为4.4亿月活用户打造智能操作系统&#xf…

基于 OpenCV 和 DLib 实现面部特征调整(眼间距、鼻子、嘴巴)

摘 要 本文介绍如何利用Dlib面部特征点检测和OpenCV图像处理技术&#xff0c;通过Python实现面部特征的精准调整。我们将以改变眼间距为例&#xff0c;演示包括地标检测、三角剖分变形等关键技术&#xff0c;该方法可扩展至嘴唇、眉毛等面部特征的调整。 技术栈 Python 3.8 …

spring-boot接入websocket教程以及常见问题解决

我们使用spring-boot接入websocket有三种方式&#xff1a;使用EnableWebSocket、EnableWebSocketMessageBroker以及ServerEndpoint&#xff0c;本文主要介绍使用ServerEndpoint方式的流程以及碰到的问题解决 接入方式 添加依赖 确保spring-boot-starter-websocket依赖 <d…

迈向分布式智能:解析MCP到A2A的通信范式迁移

智能体与外部世界的桥梁之言&#xff1a; 在深入探讨智能体之间的协作机制之前&#xff0c;我们有必要先厘清一个更基础的问题&#xff1a;**单个智能体如何与外部世界建立连接&#xff1f;** 这就引出了我们此前介绍过的 **MCP&#xff08;Model Context Protocol&…

深度学习|pytorch基本运算-hadamard积、点积和矩阵乘法

【1】引言 pytorch对张量的基本运算和线性代数课堂的教学有一些区别&#xff0c;至少存在hadamard积、点积和矩阵乘法三种截然不同的计算方法。 【2】hadamard积 hadamard积是元素对位相乘&#xff0c;用“*”连接张量&#xff0c;代码&#xff1a; # 导入包 import torch …

FFmpeg移植教程(linux平台)

目录 第三方源码编译三部曲关于 configure 的说明 FFmpeg 移植流程获取源码方法一&#xff1a;git 远程克隆方法二&#xff1a;官网下载压缩包解压 配置安装 第三方源码编译三部曲 Linux平台下有许多开源的第三方库和服务&#xff0c;这些开源代码一般都符合GNU-autotools编码…

Mybatis:灵活掌控SQL艺术

在前面的文章中&#xff0c;小编分享了spring中相关的知识&#xff0c;但是没有分享到&#xff0c;如何去更高效操作数据库。 操作数据库传统的方法就是通过JDBC来进行操作。 这个传统方法使用上可谓是够麻烦的 1.首先创建一个数据源对象 2.设置该数据源的属性&#xff08;…

2025.05.28【Choropleth】群体进化学专用图:区域数据可视化

Load geospatial data Start by loading your geospatial data in R, and build a basic plot. Data from the package The cartography comes with a set of geospatial data included. Learn how to use it to build a choropleth map. 文章目录 Load geospatial dataData …

【春秋云镜】CVE-2022-26965 靶场writeup

知识点 网站的主题或者模块位置一般是可以上传文件的&#xff0c;不过一般为压缩包形式主题或者模块可以上github上找到和cms匹配的源码主题被解压后会放到加入到对应的文件夹中&#xff0c;而且还会自动执行对应的info.php文件(需要主题和cms配套才行)我这里取巧了&#xff0…

JSP、HTML和Tomcat

9x9上三角乘法表 乘法表的实现 <% page contentType"text/html;charsetUTF-8" language"java" %> <!DOCTYPE html> <html> <head><title>99 上三角乘法表</title><style>body {font-family: monospace;padding…

(1)pytest简介和环境准备

1. pytest简介 pytest是python的一种单元测试框架&#xff0c;与python自带的unittest测试框架类似&#xff0c;但是比unittest框架使用起来更简洁&#xff0c;效率更高。根据pytest的官方网站介绍&#xff0c;它具有如下特点&#xff1a; 非常容易上手&#xff0c;入门简单&a…