当前位置: 首页 > news >正文

微信公众号对接--客服消息

当你关注公众号,然后在公众号里面发送消息,会收到回复,这个就是客服消息

参考文档:接收普通消息 接收事件推送  客服接口-发消息

想要对接客服消息,首先要获取access_token,这个可以参考我之前的文章:对接微信公众号-CSDN博客

 回复消息的方式

回复消息的方式有两种,一种是接收到用户消息后直接在response里面回复,文档:回复文本消息 | 微信开放文档

这种方式比较简单,缺点是必须保证在5s内回复,否则会重试,不知道你有没有遇到过以下场景,当你发送一条消息,过了一会突然收到好几条相同的信息,就是卡顿重试造成的。

还有一种方式是客服消息,这种是后台主动给用户发消息, 功能更加强大,不仅仅可以回复用户消息,还可以在用户关注公众号,扫描二维码,点击菜单时回复用户消息,参考文档:客服接口-发送消息

 由于客服消息是异步发送,在发送的过程中我们可以调用客服输入状态接口,实现以下效果

用户体验更好,参考文档: 客服输入状态

客服消息规则如下,就是说如果用户发送了消息,你可以在48小时内下发最多5条消息

场景下发额度额度有效期
用户发送消息5条48小时
点击自定义菜单3条1分钟
关注公众号3条1分钟
扫描二维码3条1分钟

如何对接服务器,如何接收消息可以看我之前写的博客: 对接微信公众号-CSDN博客

 Java代码实现

以下代码实现了客服回复文本消息,图文消息和图片消息

当用户输入文本则回复你好,输入图文则返回一篇新闻,输入图片则返回一张图片

@RestController
@RequestMapping("/wx")
@Api(tags = "微信管理")
public class WxController {@Autowiredprivate WeixinService weixinService;@RequestMapping("receiveWeixin")@ApiOperation(value = "接收微信消息")public void receiveWeixin(String signature, String timestamp, String nonce, String echostr,HttpServletRequest request, HttpServletResponse response) {try {//服务器校验String token = "123456";List<String> list = new ArrayList<>();list.add(token);list.add(timestamp);list.add(nonce);list = CollectionUtil.sort(list, (o1, o2) -> o1.compareTo(o2));String str = CollectionUtil.join(list, "");if (!signature.equals(SecureUtil.sha1(str))) {return;}if (StringUtils.isNotEmpty(echostr)) {//校验服务器response.getOutputStream().write(echostr.getBytes());response.getOutputStream().flush();return;}//接收消息Document document = XmlUtil.readXML(request.getInputStream());Map map = XmlUtil.xmlToMap(document);JSONObject json = JSONObject.parseObject(JSONObject.toJSON(map).toString()).getJSONObject("xml");System.out.println(json);//消息类型String msgType = json.getString("MsgType");//公众号微信账号String account = json.getString("ToUserName");if (msgType.equals("event")) {// 处理事件消息,比如当用户关注公众号可以下发个欢迎消息} else if (msgType.equals("text")) {// 处理用户消息WeixinReceiveText weixinReceiveText = JSONObject.parseObject(json.toJSONString(), WeixinReceiveText.class);ThreadUtil.execAsync(() -> weixinService.replyMessage(weixinReceiveText.getFromUserName(), weixinReceiveText.getContent()));}} catch (Exception e) {e.printStackTrace();}}@PostMapping(value = "/uploadMaterial")@ApiOperation(value = "上传资源")public JSONObject uploadMaterial(MultipartFile file) {return weixinService.addMaterial(file, "image");}
}
@Service
public class WeixinService {protected static Logger logger = LoggerFactory.getLogger(WeixinService.class);private final String TOKEN_KEY = "WEIXIN_TOKEN:";@Autowiredprivate RedisTemplate redisTemplate;private final static String appId = "你的appid";private final static String appsecret = "你的appsecret";public WxUser getUserInfoByCode(String code) {String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code";url = StrUtil.format(url, appId, appsecret, code);String res = HttpUtil.get(url);JSONObject tokenInfo = handleResult(res);String token = tokenInfo.getString("access_token");String openid = tokenInfo.getString("openid");String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token={}&openid={}&lang=zh_CN";userInfoUrl = StrUtil.format(userInfoUrl, token, openid);res = HttpUtil.get(userInfoUrl);JSONObject userInfoJson = handleResult(res);return JSONObject.parseObject(userInfoJson.toJSONString(), WxUser.class);}public synchronized String getToken() {String redisKey = TOKEN_KEY + appId;String token = (String) redisTemplate.opsForValue().get(redisKey);if (StrUtil.isNotEmpty(token)) return token;JSONObject params = new JSONObject();params.put("grant_type", "client_credential");params.put("appid", appId);params.put("secret", appsecret);String str = HttpUtil.post(WeixinConstants.ACCESS_TOKEN_URL, params.toJSONString());JSONObject result = handleResult(str);token = result.getString("access_token");redisTemplate.opsForValue().set(redisKey, token, result.getIntValue("expires_in"), TimeUnit.SECONDS);return token;}private JSONObject handleResult(String str) {JSONObject result = JSONObject.parseObject(str);if (StrUtil.isNotBlank(result.getString("errcode"))) {if (result.getString("errcode").equals("0")) {return result;}if (result.getString("errcode").equals("40014")) {clearToken(appId);}throw new RuntimeException(StrUtil.format("微信接口出错, errcode: {}, msg: {}", result.getString("errcode"), result.getString("errmsg")));}return result;}public void clearToken(String appId) {String redisKey = TOKEN_KEY + appId;redisTemplate.delete(redisKey);}public JSONObject getMenuInfo() {String token = getToken();String str = HttpUtil.get(StrUtil.format(WeixinConstants.GET_MENU_INFO_URL, token));JSONObject result = handleResult(str);JSONObject menuInfo = result.getJSONObject("selfmenu_info");if (menuInfo == null) {menuInfo = new JSONObject();}return menuInfo;}public void createMenu(String menu) {String token = getToken();String str = HttpUtil.post(StrUtil.format(WeixinConstants.CREATE_MENU_URL, token), menu);handleResult(str);}public void customSend(String openId, String message) {String token = getToken();JSONObject body = new JSONObject();body.put("touser", openId);body.put("msgtype", "text");JSONObject content = new JSONObject();body.put("text", content);content.put("content", message);String str = HttpUtil.post(StrUtil.format(WeixinConstants.CUSTOM_SEND_URL, token), body.toJSONString());handleResult(str);}/*** 发送图片消息*/public void customSendPic(String openId, String mediaId) {String token = getToken();JSONObject body = new JSONObject();body.put("touser", openId);body.put("msgtype", "image");JSONObject image = new JSONObject();body.put("image", image);image.put("media_id", mediaId);String str = HttpUtil.post(StrUtil.format(WeixinConstants.CUSTOM_SEND_URL, token), body.toJSONString());handleResult(str);}public void customSendPicText(String openId, WeixinPicTextVO weixinPicTextVO) {String token = getToken();JSONObject body = new JSONObject();body.put("touser", openId);body.put("msgtype", "news");JSONObject news = new JSONObject();body.put("news", news);JSONArray articles = new JSONArray();news.put("articles", articles);articles.add(weixinPicTextVO);String str = HttpUtil.post(StrUtil.format(WeixinConstants.CUSTOM_SEND_URL, token), body.toJSONString());handleResult(str);}// type: image, voice, video, thumbpublic JSONObject addMaterial(MultipartFile file, String type) {File dir = null;try {String token = getToken();String url = StrUtil.format(WeixinConstants.ADD_METERIAL_URL, token, type);//获取临时文件夹目录String tempPath = System.getProperty("java.io.tmpdir");String dirName = UUID.randomUUID().toString();dir = new File(tempPath + dirName);dir.mkdirs();File tempFile = new File(dir.getAbsolutePath() + File.separator + file.getOriginalFilename());tempFile.deleteOnExit();file.transferTo(tempFile);HashMap<String, Object> paramMap = new HashMap<>();paramMap.put("media", tempFile);String str = HttpUtil.post(url, paramMap);return handleResult(str);} catch (Exception e) {throw new RuntimeException("上传文件出错", e);} finally {FileUtil.del(dir);}}// command: Typing,CancelTypingpublic void customTyping(String openId, String command) {String token = getToken();JSONObject body = new JSONObject();body.put("touser", openId);body.put("command", command);String str = HttpUtil.post(StrUtil.format(WeixinConstants.CUSTOM_TYPING_URL, token), body.toJSONString());handleResult(str);}public void replyMessage(String openId, String content) {try {customTyping(openId, "Typing");if ("文本".equals(content)) {customSend(openId, "你好");} else if ("图文".equals(content)) {WeixinPicTextVO weixinPicTextVO = new WeixinPicTextVO();weixinPicTextVO.setTitle("年轻人开始流行三无婚礼");weixinPicTextVO.setDescription("近年来,越来越多的年轻人告别繁杂冗余的俗套,简化仪式和环节,选择流程简单、时间充裕的极简婚礼");weixinPicTextVO.setUrl("https://www.jnnews.tv/guanzhu/p/2024-01/15/1027166.html");weixinPicTextVO.setPicurl("https://www.jnnews.tv/a/10001/202401/bdabe3e9925a31e867137ed95f722edc.jpeg");customSendPicText(openId, weixinPicTextVO);} else if ("图片".equals(content)) {// mediaId通过addMaterial获得customSendPic(openId, "im8rzmF--MD8vDCSnju1oif7lPzvgBMUepg0X1gh8CR6iD2L27CcxLhouIMigR2F");}} catch (Exception e) {logger.error("微信客服消息出错: {}", e);} finally {customTyping(openId, "CancelTyping");}}
}
public class WeixinConstants {/*** 获取token*/public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token";/*** 获取菜单*/public static final String GET_MENU_INFO_URL = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token={}";/*** 创建菜单*/public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={}";/*** 客服接口-发消息*/public static final String CUSTOM_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={}";/*** 客服接口-输入状态*/public static final String CUSTOM_TYPING_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token={}";/*** 添加素材*/public static final String ADD_METERIAL_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={}&type={}";
}
@Data
@ApiModel(value = "WeixinReceiveText", description = "WeixinReceiveText")
public class WeixinReceiveText extends WeixinBaseVO {private String Content;private String MsgId;private String MsgDataId;private String Idx;
}
@Data
public class WeixinPicTextVO {private String title;private String description;private String url;private String picurl;
}

相关文章:

微信公众号对接--客服消息

当你关注公众号&#xff0c;然后在公众号里面发送消息&#xff0c;会收到回复&#xff0c;这个就是客服消息 参考文档:接收普通消息 接收事件推送 客服接口-发消息 想要对接客服消息&#xff0c;首先要获取access_token,这个可以参考我之前的文章:对接微信公众号-CSDN博客 回…...

花几分钟整点jmeter花活,轻松超越90%软件测试

jmeter 可以做性能测试&#xff0c;这个很多人都知道&#xff0c;那你知道&#xff0c;jmeter 可以在启动运行时&#xff0c;指定线程数和运行时间&#xff0c;自定义性能场景吗&#xff1f; jmeter 性能测试&#xff0c;动态设定性能场景 平时&#xff0c;我们使用 jmeter 进…...

类脑研究之脑组成及神经系统相关理论!大脑是什么?大脑和脑有什么区别?大脑皮层和脑膜什么关系?人的神经系统有哪些?

目录 1 引言2 神经系统3 脑组成3.1 大脑成分3.2 大脑外部&#xff1a;脑膜3.3 大脑中部&#xff1a;大脑皮层3.4 大脑内部3.5 脑干3.6 小脑 1 引言 为了深入研究类脑&#xff0c;必须了解大脑的结构和机制。从神经系统分级和脑组成两个角度出发&#xff0c;详细介绍了大脑的生…...

【Vue按键修饰符详细介绍】

Vue按键修饰符详细介绍 1. 按键修饰符2. 实现原理3. 使用方法4. 常用的按键修饰符5. 自定义按键修饰符6. 系统修饰键7. 事件修饰符的链式使用8. .exact 修饰符 1. 按键修饰符 Vue.js 中的按键修饰符使得键盘事件处理变得十分简单&#xff0c;它们通常与 v-on 指令&#xff08;…...

url 地址中的敏感信息脱敏处理

url 跳转时&#xff0c;系统自动加密解密处理&#xff0c;适用于调用方不适合加密处理的情况 // 定义一个名为encodeURIUrlParams的函数 encodeURIUrlParams() { // 创建一个URLSearchParams对象&#xff0c;该对象用于处理URL的查询字符串部分 const urlParams new URLS…...

慢速 HTTP 攻击 Slow HTTP Attack

漏洞名称 &#xff1a;Slow Http attack、慢速攻击 漏洞描述&#xff1a;慢速攻击基于HTTP协议&#xff0c;通过精心的设计和构造&#xff0c;这种特殊的请求包会造成服务器延时&#xff0c;而当服务器负载能力消耗过大即会导致拒绝服务。HTTP协议规定&#xff0c;HTTP Reques…...

2024年“计算机视觉处理设计开发工程师”最后几天报考中!

为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求&#xff0c;深入实施人才强国战略和创新驱动发展战略&#xff0c;加强全国数字化人才队伍建设&#xff0c;持续推进人工智能专业人员…...

基于ssm的教务信息平台的设计与实现+jsp论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统教务信息管理难度大&#xff0c;容错率低&#xff0c;管理…...

哪种护眼灯对眼睛好?五款高品质考研台灯推荐

眼睛是我们感知世界的窗户&#xff0c;眼睛对光的敏感度非常高。长时间接触强光或不适宜的光线环境可能会对眼睛造成伤害。因此&#xff0c;选择一款适合自己的护眼台灯非常重要。护眼台灯能够模拟自然光的光谱&#xff0c;减少眼睛对不良光线的伤害。它具备调节光线亮度&#…...

安防视频云平台/可视化监控云平台ARM版EasyCVR无法下载录像文件,如何解决?

视频集中存储/云存储/视频监控管理平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅、全网分发、智能分析等。GB28181视频监控/AI智能大数据视频分析EasyCVR平台已经广泛应用在工地…...

如何用Docker部署Nacos服务并结合内网穿透实现公网访问管理界面?

文章目录 1. Docker 运行Nacos2. 本地访问Nacos3. Linux安装Cpolar4. 配置Nacos UI界面公网地址5. 远程访问 Nacos UI界面6. 固定Nacos UI界面公网地址7. 固定地址访问Plik Nacos是阿里开放的一款中间件,也是一款服务注册中心&#xff0c;它主要提供三种功能&#xff1a;持久化…...

Logback框架基本认识

文章目录 一.什么是Logback1.1 初识Logbcak 二.Logbcak的结构三.日志的级别四.配置组件详解4.1 logger 日志记录器属性的介绍如何在配置文件里配置 4.2 appender 附加器 配合日志记录器的输出格式4.2.1 控制台附加器4.2.2 文件附加器4.3.3滚动文件附加器 4.3 Filter: 过滤器&am…...

移动安全-certutil

1 需求 需求1&#xff1a;获取应用文件的MD5 CertUtil -hashfile 文件路径 MD5 2 语法 C:\>certutil -?动词:-dump -- 转储配置信息或文件-dumpPFX -- 转储 PFX 结构-asn -- 分析 ASN.1 文件-decodehex -- 解码十六进制编码的…...

【HarmonyOS4.0】第九篇-ArkUI布局容器组件(一)

容器组件指的是它可以包含一个或多个子组件的组件&#xff0c;除了前边介绍过的公共属性外。 一、线性布局容器&#xff08;Row、Column&#xff09; 线性容器类表示按照水平方向或者竖直方向排列子组件的容器&#xff0c;ArkUI开发框架通过 Row 和 Colum 来实现线性布局。 …...

在macos上查看当前进程的栈信息

概述 在调试程序时&#xff0c;如cpu莫名的高或低&#xff0c;一个常用的方式就是打印当前进行的调用栈&#xff0c;然后确认各线程的执行函数是否有异常。 在linux系统中可以使用pstack命令&#xff0c;直接打印各线程的栈信息&#xff0c;可惜在macos上没有该命令。一种解决…...

医院患者满意度调查指标设计

医院患者满意度调查指标的设计是确保调查能够准确反映患者体验和医院服务质量的关键步骤。以下是一些常见的医院患者满意度调查指标&#xff0c;可以根据特定需求和目标进行定制&#xff1a; 整体满意度&#xff1a;通过一个综合评分或问卷问题来评估患者对整体医院体验的满意…...

2023年全国职业院校技能大赛软件测试赛题—单元测试卷④

任务二 单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应分析处理并显示结果。返回结果“ax&#xff1a;”&#xff08;x为2、3或4&#xff09;&#xff1b;其中变量x、y均须为整型。编写程序代码&#xff0c;使用JUnit框架编写测试类对编写的程序代码…...

Open CV 图像处理基础:(一)Open CV 在windows环境初始化和 Java 动态库加载方式介绍

Open CV 在windows环境初始化和 Java 动态库加载方式介绍 目录 Open CV 在windows环境初始化和 Java 动态库加载方式介绍前言OpenCV安装opencv-4.4.0下载安装 加载opencv-4.4.0.jar包jar包引入mavn-init.cmdjar包装载到本地maven仓库pom.xml加载动态库 加载动态库opencv_java44…...

云联接:揭开SD-WAN神秘面纱,颠覆你对网络的认知!

云联接&#xff08;Cloud Connect&#xff09;源于软件定义广域网&#xff08;SD-WAN&#xff09;。 软件定义广域网由于技术应用性强&#xff0c;近年来从一个由软件定义网络&#xff08;SDN&#xff09;部分衍生的分支概念发展为大规模普适的实践技术&#xff0c;已成为建立…...

拓展操作(四) 使用nginx反向代理jenkins

让清单成为一种习惯 互联网时代的变革,不再是简单的开发部署上线,持续,正确,安全地把事情做好尤其重要;把事情做好的前提是做一个可量化可执行的清单,让工程师就可以操作的清单而不是专家才能操作: 设定检查点 根据节点执行检查程序操作确认或边读边做 二者选其一不要太…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

云原生周刊:k0s 成为 CNCF 沙箱项目

开源项目推荐 HAMi HAMi&#xff08;原名 k8s‑vGPU‑scheduler&#xff09;是一款 CNCF Sandbox 级别的开源 K8s 中间件&#xff0c;通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度&#xff0c;为容器提供统一接口&#xff0c;实现细粒度资源配额…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

表单设计器拖拽对象时添加属性

背景&#xff1a;因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...