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

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

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

想要对接客服消息,首先要获取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;
}

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

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

相关文章

SpringBoot教程(九) | SpringBoot统一异常处理

SpringBoot教程(九) | SpringBoot统一异常处理 异常大家应该都很清楚&#xff0c;我们的项目总是不可避免的出现异常&#xff0c;那么应该如何优雅的进行异常处理使我们需要关注的一个问题&#xff0c;合理的异常封装既可以方便前端的处理&#xff0c;也能够简化后端的开发。 …

Stable Diffusion XL(SDXL)核心基础知识

文章目录 一、Stable Diffusion XL基本概念二、SDXL模型架构上的优化&#xff08;一&#xff09;SDXL的整体架构&#xff08;二&#xff09;VAE&#xff08;三&#xff09;U-Net&#xff08;四&#xff09;text encoder&#xff08;五&#xff09;refiner model 三、SDXL在训练…

软件测试|Python如何处理配置文件

配置文件在软件开发中起到了非常重要的作用&#xff0c;它允许开发者将应用程序的设置和参数存储在一个易于管理和修改的地方&#xff0c;而不是硬编码在代码中。Python有多种处理配置文件的方式&#xff0c;本文将介绍其中两种最常用的方法&#xff1a;使用configparser库和使…

HTML--CSS--超链接样式以及鼠标样式自定义

超链接伪类 再复习一下,超链接的定义方式如下&#xff1a; <!DOCTYPE html> <html> <head> <title>这是一个标题</title><meta charset"utf-8"/><style></style> </head> <body><a href"http…

Axure RP软件揭秘:设计师的秘密武器

Axure rp是一种快速原型设计工具&#xff0c;可以制作高度互动的HTML原型。设计师不仅可以使用Axure绘制线框图和原型&#xff0c;还可以在Axure rp中完成一系列用户体验设计。在本文中&#xff0c;我们将根据用户体验设计师的真实经验&#xff0c;触发用户体验设计师的实际工作…

如何实现本地USB设备共享服务映射到外网实现跨网USB共享通信访问

文章目录 前言1. 安装下载软件1.1 内网安装使用USB Redirector1.2 下载安装cpolar内网穿透 2. 完成USB Redirector服务端和客户端映射连接3. 设置固定的公网地址 前言 USB Redirector是一款方便易用的USB设备共享服务应用程序&#xff0c;它提供了共享和访问本地或互联网上的U…

vue的element ui使用el-table组件实现懒加载树、默认自动展开层级(一层,二层)、并且解决新增、删除、修改之后树节点不刷新问题

1.整体思路 问题&#xff1a;数据量太大了&#xff0c;导致接口返回数据时间较长。解决: 将ElementUi中Table组件加载改为懒加载&#xff08;查看文档&#xff09;。思路&#xff1a;初始化打开页面时只显示第一级菜单,用户点击展开菜单之后往后端发送请求,然后加载出一级子菜…

适当催一下没确认订单的国外客户

在一片美丽的森林里&#xff0c;住着两只小鸟。其中一只非常勤奋&#xff0c;每天都早早起床&#xff0c;练习飞翔和觅食。而另外一个小鸟却非常懒惰&#xff0c;每天总是赖在窝里&#xff0c;不愿意努力&#xff0c;懒惰的小鸟总是想&#xff1a;反正有那只勤奋的鸟儿在&#…

【Linux】初识Linux及几个基本指令

Hello everybody!算算时间我已经有一个多月没有更新啦&#xff01;因为本专业是纺织工程&#xff0c;所以一直在复习应付期末考试\(0^◇^0)/。那好&#xff0c;废话不多说。让我们进入今天的主题&#xff01; 关于Linux系统可能很多同学不是很熟悉&#xff0c;有的人可能听过&…

如何在网络爬虫中解决CAPTCHA?使用Python进行网络爬虫

网络爬虫是从网站提取数据的重要方法。然而&#xff0c;在进行网络爬虫时&#xff0c;常常会遇到一个障碍&#xff0c;那就是CAPTCHA&#xff08;全自动公共图灵测试以区分计算机和人类&#xff09;。本文将介绍在网络爬虫中解决CAPTCHA的最佳方法&#xff0c;并重点介绍CapSol…

软件测试|使用matplotlib绘制多种饼图

简介 Matplotlib是一个强大的数据可视化库&#xff0c;它允许我们创建各种类型的图表&#xff0c;包括饼图。饼图是一种用于显示数据分布的常见图表类型。在本文中&#xff0c;我们将介绍如何使用Matplotlib创建不同类型的饼图&#xff0c;并提供示例代码。 创建标准饼图 首…

Linux进程【2】进程地址空间(+页表详解哦)

fork 引言&#xff08;程序地址空间&#xff09;进程地址空间进程地址空间mm_struct 虚拟地址到物理地址的转化总结 引言&#xff08;程序地址空间&#xff09; 在之前的学习过程中&#xff0c;我们认识了内存与地址&#xff0c;并且了解了在程序地址空间中的基本分区&#xf…

2000年第五次人口普查数据,shp/excel格式均有,划分年龄段、性别占比等字段

基本信息. 数据名称: 第五次人口普查数据 数据格式: Shp、excel 数据时间: 2000年 数据几何类型: 面 数据坐标系: WGS84坐标系 数据来源&#xff1a;第五次人口普查数据 数据字段&#xff1a; 序号字段名称字段说明1a2000_zrks2000年_常住人口&#xff08;人&…

Eureka 本机集群实现

距离上次发布博客已经一年多了&#xff0c;主要就是因为考研&#xff0c;没时间学习技术的内容&#xff0c;现在有时间继续完成关于代码方面的心得&#xff0c;希望跟大家分享。 今天在做一个 Eureka 的集群实现&#xff0c;我是在本电脑上跑的&#xff0c;感觉这个挺有意思&a…

Linux的网络文件共享服务之FTP服务

一.存储类型 1.1 存储类型分为三种 直连式存储&#xff1a;Direct-Attached Storage&#xff0c;简称DAS 存储区域网络&#xff1a;Storage Area Network&#xff0c;简称SAN&#xff08;可以使用空间&#xff0c;管理也是你来管理&#xff09; 网络附加存储&#xff1a;Net…

Sqoop作业调度:自动化数据传输任务

自动化数据传输任务是大数据处理中的一个重要方面&#xff0c;可以定期执行Sqoop作业&#xff0c;确保数据在不同系统之间的同步。本文将深入探讨如何使用Sqoop作业调度来自动化数据传输任务&#xff0c;并提供详细的示例代码和全面的内容&#xff0c;以帮助大家更好地理解和应…

二叉树简介

二叉树 二叉树是每个节点最多有两个子树的树结构&#xff0c;通常子树被称作“左子树”和“右子树”。 二叉树的遍历 二叉树的遍历主要有三种方式&#xff1a;前序遍历、中序遍历和后序遍历。 前序遍历&#xff1a;访问根节点 --> 遍历左子树 --> 遍历右子树中序遍历&…

三、MyBatis 多表映射

本章概要 多表映射概念对一映射对多映射多表映射总结 多表映射优化多表映射总结 3.1 多表映射概念 多表查询结果映射思路 开发中有很多** 多表查询**需求&#xff0c;这种情况如何让进行处理&#xff1f; MyBatis 思想是&#xff1a;数据库不可能永远是你所想或所需的那个样…

用LED数码管显示计数器T0的计数值

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit SP3^2 ; //将S位定义为P3.2引脚 unsigned char Tab[ ]{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //段码表 unsigned char x; /************************************************************…

Arduino开发实例-手指心率传感器模块

手指心率传感器模块 文章目录 手指心率传感器模块1、手指心率传感器介绍2、硬件准备及接线3、代码实现1、手指心率传感器介绍 本次使用的心率传感器模块是为教育和娱乐目的而设计的,通过手指检测心血管脉搏波。 它使用 PPG (HRM-2511E) 探头进行数据传输。 该传感器使用红外线…