需求
小程序增加关注公众号功能,点击跳转至客服聊天页面,自动弹窗出现引导语以及公众号二维码;
实现步骤
1. 小程序前端 相对简单,设置button type即可
点击button会进入到小程序客服聊天页面,这时小程序会
向预先好的服务端接口发送信息,首次发送信息类型为【进入事件】
2. 配置服务器地址以及验证
路径:小程序公众平台 > 小程序管理页面 > 开发 > 开发设置 > 消息推送
首先开启消息推送,接着是配置url以及token等信息
注意,配置url需要进行验证。提交验证后微信会发送get请求
至你填写的服务器域名以及具体接口,需要在接口中校验请求
是否来之微信服务器,将指定数据返回;
3. 校验接口以及响应信息代码
GET请求仅用于初次配置时验证;
POST请则为每次用户打开微信客服页面,以及用户在客服中聊天中发送的每一条信息都会请求到接口,可进行处理;
接收微信请求时遇到一点小坑,配置后台url信息时如选择JSON格式传输数据,在springMVC中无法获取到消息类型等参数,在request中查看hard以及body中页没有参数,时间紧没来得及深入排查,最终使用XML格式就没有问题……
/**
* 处理小程序打开客服请求
* GET 验证调用者是否为小程序
* POST 信息接收响应
*
* @return
*/
@ResponseBody
@RequestMapping("handleCustomerServiceMsg")
public String handleCustomerServiceMsg(HttpServletRequest request) {
logger.info("customerService >>>>> 收到小程序客服消息请求 ");
String resultStr = "success";
try{
if("GET".equals(request.getMethod().toUpperCase())){
CustomerServiceVo customerServiceVo = new CustomerServiceVo();
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
customerServiceVo.setSignature(signature);
customerServiceVo.setTimestamp(timestamp);
customerServiceVo.setNonce(nonce);
customerServiceVo.setEchostr(echostr);
logger.info("customerService >>>>> {} ",JSONObject.toJSONString(customerServiceVo));
if(customerServiceVo == null ||
StringUtils.isBlank(customerServiceVo.getSignature()) ||
StringUtils.isBlank(customerServiceVo.getTimestamp()) ||
StringUtils.isBlank(customerServiceVo.getNonce()) ||
StringUtils.isBlank(customerServiceVo.getEchostr())
){
logger.info("customerService >>>>> error parameter is empty");
return resultStr;
}
String[] strArray = new String[] { customerServiceToken, customerServiceVo.getTimestamp(), customerServiceVo.getNonce() };
Arrays.sort(strArray);
StringBuilder sb = new StringBuilder();
for (String str : strArray) {
sb.append(str);
}
//10178941421571798817xcx-1570788125909-a742429ba186d
String encrypt = SHA1Util.encode(sb.toString());
//校验成功,原样返回随机字符串
if (encrypt.equals(customerServiceVo.getSignature())) {
return customerServiceVo.getEchostr();
}
}else{
request.setCharacterEncoding("UTF-8");
Map requestMap = MessageUtil.parseXML(request);
CustomerServiceVo customerServiceVo = (CustomerServiceVo) Utils.mapToJavaBean(CustomerServiceVo.class, requestMap);
logger.info("xml参数为{}",JSONObject.toJSONString(customerServiceVo));
// xml请求解析 处理客服消息
logger.info("customerService >>>>> 处理客服消息 parameters : {}",JSONObject.toJSONString(customerServiceVo));
return customerServiceService.acceptMessage(customerServiceVo);
}
}catch (Exception e){
logger.info("{}",e);
}
return resultStr;
}
4. 发送客服消息
上面步骤做好之后已经能接收到客服消息了;接下来我们需要对
收到的客服消息进行处理。我的需求是需要发送一张公众号二维
码图片以及一段文本内容;
小程序发送消息API
customerServiceMessage.send
注意:小程序对于该接口发送客服消息有限制,必须用户主动发送消息之后才能回复;
另外发送图片消息,需要mediaID,mediaID必须调用微信API上传素材之后获得,而且三天后失效,需要定时更新!
/**
* 处理,回复客服消息
* @param customerServiceVo
* @return
* @throws Exception
*/
public String acceptMessage(CustomerServiceVo customerServiceVo) throws Exception{
String resultStr = "success";
// 消息类型
String msgType = customerServiceVo.getMsgType();
String touser = customerServiceVo.getFromUserName();
//公众帐号 小程序原始码?
String toUserName = customerServiceVo.getToUserName();
if(StringUtils.isBlank(msgType) || StringUtils.isBlank(touser)){
throw new Exception("parameter is error");
}
// 除事件之外的类型,都发送关注公众号消息
if(!"event".equals(msgType)){
//获取mediaId,mediaId需要先使用微信上传素材接口上传指定图片后获取到mediaId,
// 且每次上传的素材3天后失效,需要定时更新
String wxMediaId = (String)redisTemplate.opsForValue().get("WX_MEDIA_ID");
if(StringUtils.isBlank(wxMediaId)){
logger.error("wx mediaId fail ");
}
//发送二维码图片
JSONObject imageJson = new JSONObject();
imageJson.put("touser",touser);
imageJson.put("msgtype","image");
Map imageMap = new HashMap();
imageMap.put("media_id",wxMediaId);
imageJson.put("image",imageMap);
wxApiService.sendCustom(imageJson);
// 文本消息
JSONObject textJson = new JSONObject();
Map contentMap = new HashMap<>();
contentMap.put("content","我是文本消息~");
textJson.put("touser",touser);
textJson.put("text",contentMap);
textJson.put("msgtype","text");
wxApiService.sendCustom(textJson);
// 其它类型消息,暂时不作处理
}else{
logger.error("客服信息>>>>> 用户发送消息类型为{},暂不作处理",msgType);
}
return resultStr;
}
/**
* 向微信客服号发送消息
* @param paramJson
*/
public boolean sendCustom(JSONObject paramJson) {
try{
String access_token = getAccess_token();
String url = wxSendCustomURL+access_token;
String result = HttpUtils.postJsonString(url, paramJson);
JSONObject repoJson = JSONObject.parseObject(result);
if(repoJson == null || repoJson.get("errcode") == null ){
logger.info("============微信发送客服接口响应错误{}============",result);
return false;
}
Integer errcode = (Integer)repoJson.get("errcode");
if(errcode != 0){
logger.info("=====微信发送客服接口:校验失败:{}======",repoJson.get("errmsg"));
return false;
}
}catch (Exception e){
logger.info("[发送客服消息失败]:{}",e);
return false;
}
return true;
}
5.关于meidaId,采用定时任务上传放在缓存中,三天刷新一次
/**
* 上传微信临时素材
*/
public String uploadWechatTeacherImages() {
String mediaId = "";
try{
//读取resource下图片
ClassPathResource classPathResource = new ClassPathResource("images/xxxxRcode.png");
InputStream inputStream = classPathResource.getInputStream();
//存入临时文件夹
File file = new File("/opt/zion/image_temp/xxxQRcode.png");
Utils.inputStreamToFile(inputStream,file);
//发送图片
String url = uploadMediaUrl + getAccess_token();
String result = HttpUtils.uploadMedia(file, url);
logger.info("upload weixin media result {}",result);
if(StringUtils.isNotBlank(result)){
JSONObject resultJson = JSONObject.parseObject(result);
mediaId = (String)resultJson.get("media_id");
}
}catch (Exception e){
logger.error("上传微信临时素材失败 {}",e);
}
return mediaId;
}
public static String uploadMedia(File file, String url) {
try{
logger.info(">>>>>>>send upload weixin media url {}",url);
RestTemplate restTemplate = new RestTemplate();
url += "&type=image";
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("multipart/form-data");
headers.setContentType(type);
FileSystemResource fileSystemResource = new FileSystemResource(file);
MultiValueMap form = new LinkedMultiValueMap<>();
form.add("file", fileSystemResource);
form.add("filename",file.getName());
org.springframework.http.HttpEntity> files = new org.springframework.http.HttpEntity<>(form, headers);
return restTemplate.postForObject(url, files, String.class);
}catch (Exception e){
logger.error("send weixin media error {}",e);
return "fail";
}
}
最后,有问题可以私信我~