微信小程序(uniapp)实现腾讯云 IM 消息撤回
uniapp 实现腾讯云 IM 消息撤回功能实战指南
一、功能实现原理
腾讯云 IM 的消息撤回功能通过 消息修订(Message Revision) 机制实现,核心流程如下:
- 发送方调用撤回 API 删除指定消息
- 云端生成撤回通知消息(类型为
TIM.TYPES.MSG_REVOKED
) - 接收方收到通知后执行本地消息删除
- 全平台自动同步消息状态(需开启消息漫游)
二、核心实现步骤
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 errorawait 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()})
}
五、常见问题排查
-
Q: 撤回后对方仍显示消息
A: 检查消息漫游是否开启,确认双方客户端版本 ≥ 2.15.0 -
Q: 无法撤回超过2分钟的消息
A: 腾讯云默认限制为2分钟,需在控制台申请延长权限 -
Q: 群聊中非群主成员撤回失败
A: 确认群类型是否为 Private(私有群),Public 群需群主操作 -
Q: 撤回通知不显示描述
A: 检查自定义消息解析逻辑,确保 payload 格式正确
六、性能优化建议
- 使用
tim.getMessageRevokeStatus()
批量查询消息状态 - 对已撤回消息进行本地缓存,避免重复查询
- 添加防抖处理,防止快速连续撤回导致性能问题
相关文章:
微信小程序(uniapp)实现腾讯云 IM 消息撤回
uniapp 实现腾讯云 IM 消息撤回功能实战指南 一、功能实现原理 腾讯云 IM 的消息撤回功能通过 消息修订(Message Revision) 机制实现,核心流程如下: 发送方调用撤回 API 删除指定消息云端生成撤回通知消息(类型为 T…...
设计学生管理系统的数据库
在设计学生管理系统的数据库时,需要考虑多个实体及其关系。以下是一个基本的学生管理系统表结构设计,涵盖了核心实体和关系: 1. 用户表 (user) 存储所有系统用户的基本信息,包括学生、教师和管理员。 sql CREATE TABLE user (u…...
ArcGIS Pro 3.4 二次开发 - 图形图层
环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 图形图层1.1 创建图形图层1.2 访问GraphicsLayer1.3 复制图形元素1.4 移除图形元素2 创建图形元素2.1 使用CIMGraphic创建点图形元素2.2 使用CIMGraphic创建线图元素2.3 使用 CIMGraphic 的多边形图形元素2.4 使用CIMGraphic创建多…...
Linux配置DockerHub镜像源配置
个人博客地址:Linux配置DockerHub镜像源配置 | 一张假钞的真实世界 因为某些原因,DockerHub官方镜像源已不可用,国内一些镜像源也已不可用,大家可以搜索可用的镜像源并修改配置。推荐一篇良心博文:https://zhuanlan.z…...
JDK21深度解密 Day 11:云原生环境中的JDK21应用
【JDK21深度解密 Day 111】云原生环境中的JDK21应用 本文是《JDK21深度解密:从新特性到生产实践的全栈指南》专栏的第11天内容,聚焦云原生环境中的JDK21应用。我们将深入探讨如何在容器化、微服务、Serverless等云原生架构中充分发挥JDK21的技术优势,提升Java应用的性能、稳…...
如何学习才能更好地理解人工智能工程技术专业和其他信息技术专业的关联性?
要深入理解人工智能工程技术专业与其他信息技术专业的关联性,需要跳出单一专业的学习框架,通过 “理论筑基 - 实践串联 - 跨学科整合” 的路径构建系统性认知。以下是分阶段、可落地的学习方法: 一、建立 “专业关联” 的理论认知框架 绘制知…...

Qt实现的水波进度条和温度进度条
一.效果 二.原理 1.水波 要模拟波浪,就要首先画出一条波浪线,正弦余弦曲线就很适合。 y=A*sin(ω*x+φ)+k y=A*cos(ω*x+φ)+k 这是正弦余弦曲线的公式,要想实现水波效果,那需要两条曲线,一条曲线的波峰对着另外一条曲线的波谷,要实现这样的曲线效果,只有让正弦曲线前移…...
3516cv610在sample_aiisp上多创一路编码流,方法
3516cv610在sample_aiisp上多创一路编码流,方法 首先确保 vpss grp0有视频流 最好保证 已经有一路视频流能推出来 多创一路编码流思路为 将 vpss grp0又绑定给 vpss_chn1 vpss_chn1有绑定给 venc_chn1 这样我们就多创了一路视频流。 这里思路完全正确 可以实现…...

WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM
目录 一、适配方案 二、VM布局 编辑 三、vh布局 四、案例—酷我音乐 一、适配方案 二、VM布局 三、vh布局 四、案例—酷我音乐...
Android第十一次面试补充篇
Livedata内存泄漏解决 1. 未正确绑定 LifecycleOwner 原因: 使用 observe() 时未传入正确的 LifecycleOwner(如 Activity/Fragment),或误用 Application 等长生命周期对象,导致观察者无法自动解除绑定。 …...

【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 新生赛]简简单单的逻辑
这是题目,下载后得到一个python文件,打开 解读代码: 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:从列表中取数字同时高4位向右位…...

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

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

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

STM32CubeDAC及DMA配置
STM32CubeDAC及DMA配置 一,问题1二,解决11,宏观思路CubeMX配置2,HAL_TIM_Base_Start(&htim6) 的作用1,作用1:使能TIM6的时钟并让它开始计数2,作用2:当 TIM6 溢出时,会…...
SQL快速入门【转自牛客网】
来源:牛客网 1、SQL 基础查询 在 SQL 中,SELECT 语句是最基本的查询语句,用于从数据库表中检索数据。通过 SELECT 语句,可以选择表中的所有列或特定列,并根据需要进行过滤和排序。 基本语法 SELECT 语句的基本语法如下: SELECT column1, column2, ... FROM table_na…...

行业案例 | OPPO借助Azure AI Speech国际服务实现音频文件智能转录
OPPO是全球领先的智能终端与移动互联网服务提供商,业务覆盖50余国,通过超40万销售网点和2500个服务中心与全球用户共享科技。作为软硬服一体化科技公司,OPPO以ColorOS为核心优化软件平台,为4.4亿月活用户打造智能操作系统…...

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

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

迈向分布式智能:解析MCP到A2A的通信范式迁移
智能体与外部世界的桥梁之言: 在深入探讨智能体之间的协作机制之前,我们有必要先厘清一个更基础的问题:**单个智能体如何与外部世界建立连接?** 这就引出了我们此前介绍过的 **MCP(Model Context Protocol&…...

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

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

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

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 …...
Java设计模式详解:策略模式(Strategy Pattern)
在软件开发中,设计模式是解决常见问题的经典方法。策略模式(Strategy Pattern)作为一种行为型设计模式,能够将算法或行为的定义与使用分离,使得算法可以独立于客户端代码进行变化和扩展。本文将深入解析策略模式的核心…...

【春秋云镜】CVE-2022-26965 靶场writeup
知识点 网站的主题或者模块位置一般是可以上传文件的,不过一般为压缩包形式主题或者模块可以上github上找到和cms匹配的源码主题被解压后会放到加入到对应的文件夹中,而且还会自动执行对应的info.php文件(需要主题和cms配套才行)我这里取巧了࿰…...
爬虫的几种方式(使用什么技术来进行一个爬取数据)
在网页数据爬取中,确实存在多种数据呈现和获取形式,远不止静态HTML解析和简单JS渲染。理解这些形式对于应对不同的反爬机制至关重要: 主要数据获取形式与应对策略 纯静态HTML (基础形式) 特点: 数据直接嵌入在服务器返回的初始HT…...
XML 编码:结构化数据的基石
XML 编码:结构化数据的基石 引言 XML(可扩展标记语言)作为互联网上广泛使用的数据交换格式,已经成为结构化数据存储和传输的重要工具。本文旨在深入探讨XML编码的原理、应用场景以及编码规范,帮助读者更好地理解和运用XML。 XML编码概述 1. XML的起源 XML诞生于1998年…...