java对接微信小程序客服功能实现(包含自动回复文本消息、图片消息,进入人工客服)

第一步:请求校验(确认请求来自微信服务器)
代码如下:

@ApiOperation(value = " 微信消息通知-请求校验(确认请求来自微信服务器)")@RequestMapping(value = "/signature")public String signature(HttpServletRequest request, HttpServletResponse response) {if (request.getMethod().equals("GET")) {//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数String signature = request.getParameter("signature");// 时间戳String timestamp = request.getParameter("timestamp");// 随机数String nonce = request.getParameter("nonce");// 随机字符串String echostr = request.getParameter("echostr");log.info("echostr>>>>>>>>>>" + echostr);//通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败if (checkSignature(signature, timestamp, nonce)) {log.info("echostr>>>>>>>>>>>>" + echostr);return echostr;}log.info("客服消息验证url验证结果:{}", echostr);}

checkSignature()方法如下:

/*** 校验(确认请求来自微信服务器)** @param signature* @param timestamp* @param nonce* @return*/public boolean checkSignature(String signature, String timestamp, String nonce) {String[] params = {aliProperties.getAccessToken(), timestamp, nonce};log.info("params>>>>>>>>>>>>>" + params);//1、将token、timestamp、nonce三个参数进行字典序排序Arrays.sort(params);//2、将三个参数字符串拼接成一个字符串StringBuilder sb = new StringBuilder();for (String param : params) {sb.append(param);}log.info("字符串拼接>>>>>>>>>>>>>>>>>" + sb);MessageDigest md = null;String tpmStr = null;try {md = MessageDigest.getInstance("SHA-1");log.info("md>>>>>>>>>>>>" + md);//3、将三个参数字符串拼接成一个字符串进行sha1加密byte[] digest = md.digest(sb.toString().getBytes());log.info("digest>>>>>>>>>>>" + digest);tpmStr = StringUtil.byteToHex(digest);log.info("加密串>>>>>>>>>>>" + tpmStr);} catch (Exception e) {log.info("错误信息>>>>>>>>>>>>" + e.getMessage());e.printStackTrace();}//4、将sha1加密后的字符串可与signature对比,标识该请求来源于微信return tpmStr != null && tpmStr.equals(signature);}

上面代码就是做的第一步,校验。根据小程序对接文档可知,校验时为GET请求(一定)。

第二步:对接小程序客服功能
代码如下:

else {try {// 接收消息并返回消息String result = acceptMessage(request, response);log.info("接受消息并返回消息result>>>>>>" + result);// 响应消息PrintWriter out = response.getWriter();log.info("out>>>>>>>>" + out);out.print(result);out.close();} catch (Exception ex) {log.error("微信帐号接口配置失败!" + ex.getMessage());ex.printStackTrace();}}return null;}

这里的else是连在上面if后面的(因为对接小程序,只能写在一个接口中,校验时是GET,对接时是POST,所以if为校验,else才为真正对接类容)

acceptMessage()方法

 public String acceptMessage(HttpServletRequest request, HttpServletResponse response) {//返回值String result = "success";try {// 将请求、响应的编码均设置为UTF-8(防止中文乱码)request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");Map<String, String> requestMap = MessageUtil.parseXml(request);log.info("requestMap>>>>>>>>>>" + requestMap);// 发送者的openidString fromUserName = requestMap.get("FromUserName");// 小程序的原始IDString toUserName = requestMap.get("ToUserName");// 消息类型String msgType = requestMap.get("MsgType");// 文本消息内容String content = requestMap.get("Content");// 事件类型String event = requestMap.get("Event");log.info("fromUserName=" + fromUserName + ",toUserName=" + toUserName + ",msgType=" + msgType + ",content=" + content + ",event=" + event);StringBuffer contentMessage = new StringBuffer();if (msgType.equals("event")) {contentMessage.append("欢迎进入XXX客服咨询!人工服务请输入0").append("\n");log.info(sendCustomerTextMessage(fromUserName, contentMessage.toString(), wxXCXTempSendUtil.getToken()));} else if (msgType.equals("text")) {//文本消息if (content.equals("0")) {return switchCustomerService(fromUserName, toUserName);} else {contentMessage.append("欢迎进入XXX客服咨询!人工服务请输入0").append("\n");log.info(sendCustomerTextMessage(fromUserName, contentMessage.toString(), wxXCXTempSendUtil.getToken()));}} else {contentMessage.append("欢迎进入XXX客服咨询!人工服务请输入0").append("\n");log.info(sendCustomerTextMessage(fromUserName, contentMessage.toString(), wxXCXTempSendUtil.getToken()));}} catch (Exception e) {log.info("错误信息打印>>>>>>>>>>>" + e.getMessage());e.printStackTrace();}return result;}

switchCustomerService()方法:
小程序对接人工服务的返回固定数据结构

 /*** 人工服务** @param fromUserName* @param toUserName* @param requestMap* @return*/public String switchCustomerService(String fromUserName, String toUserName) {String returnXml = "<xml>\n" +"    <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +"    <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +"    <CreateTime>" + System.currentTimeMillis() / 1000 + "</CreateTime>\n" +"    <MsgType><![CDATA[transfer_customer_service]]></MsgType>\n" +"</xml>";log.info("人工服务result>>>>>>>" + returnXml);return returnXml;}

sendCustomerTextMessage()方法:
文本消息返回固定数据结构

/*** 文本消息** @param openid* @param text* @param accessToken* @return*/private static String SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send";//固定发送地址public String sendCustomerTextMessage(String openid, String text, String accessToken) throws IOException {HashMap<String, Object> map_content = new HashMap<>();map_content.put("content", text);HashMap<String, Object> map = new HashMap<>();map.put("touser", openid);map.put("msgtype", "text");map.put("text", map_content);String content = JSON.toJSONString(map);log.info("文本消息content" + content);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntity httpEntity = new HttpEntity(map, headers);String str = HttpUtil.sendPostRequest(SEND_URL + "?access_token=" + accessToken, HttpMethod.POST, httpEntity);log.info("文本消息str>>>>>>>>>>"+str);return str;}

上面代码中,accessToken是生成的。
代码中使用到的工具类如下:
WxXCXTempSendUtil()方法,获取Token的方法,运用到redis,token过期自动获取

public class WxXCXTempSendUtil {@Autowiredprivate RedisKit redisKit;@Autowiredprivate AliProperties aliProperties;private static String token_url = "https://api.weixin.qq.com/cgi-bin/token?";private static final String TOKEN_KEY = "token333";public String getToken() {if (redisKit.hasKey(TOKEN_KEY)) {return redisKit.get(TOKEN_KEY).toString();} else {//获取tokenString accessToken = getAccessToken();log.info("从微信服务器获取的token======" + accessToken);redisKit.set(TOKEN_KEY, accessToken, 100 * 60);return accessToken;}}/*** 获取小程序Token** @return*/public String getAccessToken() {String url = token_url + "grant_type=" + aliProperties.getGrantType() + "&appid=" + aliProperties.getAppId() + "&secret=" + aliProperties.getAppSecret();try {String result = HttpUtil.sendGetRequest(url);JSONObject jsonObject = JSONObject.parseObject(result);log.info("token>>>>>>>>>>>" + jsonObject);return jsonObject.getString("access_token");} catch (Exception e) {log.info("获取token失败!");log.info(e.getMessage());e.printStackTrace();}return "";}
}

上面代码中grant_type、appid、secret三个参数分别是你对接小程序时自己的公众平台上的,复制过来即可

HttpUtil工具(GET和POST):

public class HttpUtil {public static String sendGetRequest(String url) {RestTemplate restTemplate = new RestTemplate();String result = restTemplate.getForObject(url, String.class);return result;}public static String sendPostRequest(String url, HttpMethod method, HttpEntity<Map<String, Object>> httpEntity) {RestTemplate restTemplate = new RestTemplate();ResponseEntity<String> response = restTemplate.exchange(url, method, httpEntity, String.class);return response.getBody();}
}

MessageUtil工具(xml解析工具):

public class MessageUtil {public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {// 将解析结果存储在HashMap 中Map<String, String> map = new HashMap<String, String>();// 从request 中取得输入流InputStream inputStream = request.getInputStream();// 读取输入流SAXReader reader = new SAXReader();Document document = reader.read(inputStream);// 得到xml 根元素Element root = document.getRootElement();// 得到根元素的所有子节点List<Element> elementList = root.elements();// 遍历所有子节点for (Element e : elementList) {map.put(e.getName(), e.getText());}// 释放资源inputStream.close();return map;}
}

StringUtil工具:

/*** 十六进制字节数组转为字符串** @param hash* @return*/public class StringUtil {public static String byteToHex(final byte[] hash) {Formatter formatter = new Formatter();for (byte b : hash) {formatter.format("%02x", b);}String result = formatter.toString();formatter.close();return result;}}

RedisKit工具:

@Component
public class RedisKit {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public RedisKit(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 指定缓存失效时间* @param key 键* @param time 时间(秒)* @return*/public boolean expire(String key,long time){try {if(time>0){redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间* @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key){return redisTemplate.getExpire(key,TimeUnit.SECONDS);}/*** 判断key是否存在* @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key){try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存* @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public void del(String ... key){if(key!=null&&key.length>0){if(key.length==1){redisTemplate.delete(key[0]);}else{redisTemplate.delete(CollectionUtils.arrayToList(key));}}}//============================String=============================/*** 普通缓存获取* @param key 键* @return 值*/public Object get(String key){return key==null?null:redisTemplate.opsForValue().get(key);}/*** 普通缓存放入* @param key 键* @param value 值* @return true成功 false失败*/public boolean set(String key,Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间* @param key 键* @param value 值* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key,Object value,long time){try {if(time>0){redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);}else{set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增* @param key 键* @param delta 要增加几(大于0)* @return*/public long incr(String key, long delta){if(delta<0){throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减* @param key 键* @param delta 要减少几(小于0)* @return*/public long decr(String key, long delta){if(delta<0){throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}//================================Map=================================/*** HashGet* @param key 键 不能为null* @param item 项 不能为null* @return 值*/public Object hget(String key,String item){return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值* @param key 键* @return 对应的多个键值*/public Map<Object,Object> hmget(String key){return redisTemplate.opsForHash().entries(key);}/*** HashSet* @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String,Object> map){try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间* @param key 键* @param map 对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String,Object> map, long time){try {redisTemplate.opsForHash().putAll(key, map);if(time>0){expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建* @param key 键* @param item 项* @param value 值* @return true 成功 false失败*/public boolean hset(String key,String item,Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建* @param key 键* @param item 项* @param value 值* @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key,String item,Object value,long time) {try {redisTemplate.opsForHash().put(key, item, value);if(time>0){expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值* @param key 键 不能为null* @param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item){redisTemplate.opsForHash().delete(key,item);}/*** 判断hash表中是否有该项的值* @param key 键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item){return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回* @param key 键* @param item 项* @param by 要增加几(大于0)* @return*/public double hincr(String key, String item,double by){return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减* @param key 键* @param item 项* @param by 要减少记(小于0)* @return*/public double hdecr(String key, String item,double by){return redisTemplate.opsForHash().increment(key, item,-by);}//============================set=============================/*** 根据key获取Set中的所有值* @param key 键* @return*/public Set<Object> sGet(String key){try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在* @param key 键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key,Object value){try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存* @param key 键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object...values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存* @param key 键* @param time 时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key,long time,Object...values) {try {Long count = redisTemplate.opsForSet().add(key, values);if(time>0) {expire(key, time);}return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度* @param key 键* @return*/public long sGetSetSize(String key){try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的* @param key 键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object ...values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}//===============================list=================================/*** 获取list缓存的内容* @param key 键* @param start 开始* @param end 结束  0 到 -1代表所有值* @return*/public List<Object> lGet(String key, long start, long end){try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度* @param key 键* @return*/public long lGetListSize(String key){try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值* @param key 键* @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return*/public Object lGetIndex(String key,long index){try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存* @param key 键* @param value 值* @return*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存* @param key 键* @param value 值* @param time 时间(秒)* @return*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存* @param key 键* @param value 值* @return*/public boolean lSet(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存* @param key 键* @param value 值* @param time 时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据* @param key 键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index,Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value* @param key 键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key,long count,Object value) {try {Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 模糊查询获取key值* @param pattern* @return*/public Set keys(String pattern){return redisTemplate.keys(pattern);}/*** 使用Redis的消息队列* @param channel* @param message 消息内容*/public void convertAndSend(String channel, Object message){redisTemplate.convertAndSend(channel,message);}//=========BoundListOperations 用法 start============/***将数据添加到Redis的list中(从右边添加)* @param listKey* @param expireEnum 有效期的枚举类* @param values 待添加的数据*/public void addToListRight(String listKey, Status.ExpireEnum expireEnum, Object... values) {//绑定操作BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);//插入数据boundValueOperations.rightPushAll(values);//设置过期时间boundValueOperations.expire(expireEnum.getTime(),expireEnum.getTimeUnit());}/*** 根据起始结束序号遍历Redis中的list* @param listKey* @param start  起始序号* @param end  结束序号* @return*/public List<Object> rangeList(String listKey, long start, long end) {//绑定操作BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);//查询数据return boundValueOperations.range(start, end);}/*** 弹出右边的值 --- 并且移除这个值* @param listKey*/public Object rifhtPop(String listKey){//绑定操作BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);return boundValueOperations.rightPop();}//=========BoundListOperations 用法 End============}

在此,所有对接小程序客服功能完成!!!!!!!!!!!

注意:校验时的token和对接类容时的token不是同一个token,校验时的token是最初在小程序平台开启客服功能对接时生成的,这个token和你那边的token一定要一致,真正对接时的token是自动生成的

二、如需在点击“联系客服”按钮时,自动回复图片(代码如下):

1、首先根据api文档编写发送图片的接口
注意:media_id是从素材库中获取的)

/*** 图片消息** @param openid* @param* @param accessToken* @return*/public String sendCustomerImageMessage(String openid, String mediaId, String accessToken) {HashMap<String, Object> map_content = new HashMap<>();map_content.put("media_id", mediaId);HashMap<String, Object> map = new HashMap<>();map.put("touser", openid);map.put("msgtype", "image");map.put("image", map_content);String content = JSON.toJSONString(map);log.info("消息content" + content);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntity httpEntity = new HttpEntity(map, headers);String str = HttpUtil.sendPostRequest(wechatConfig.getSendUrl() + "?access_token=" + accessToken, HttpMethod.POST, httpEntity);log.info("图片消息str>>>>>>>>>>" + str);return str;}

2、如何获取上文中的media_id呢?(如下图)

public String getMedia(String fileType) throws Exception {// 返回结果String result = null;File file = null;// 指定要自动回复的图片(我这里是oss地址图片,也就是远程图片地址。所以需要做读取流的处理)String destUrl = "https://realcyun.oss-cn-shanghai.aliyuncs.com/file/media/kf.png";HttpURLConnection httpUrl = (HttpURLConnection) new URL(destUrl).openConnection();try {httpUrl.connect();//远程图片地址处理工具类inputStreamToFile,大家可选则的处理,如果是本地的地址不需要处理,远程地址就需要处理file = inputStreamToFile(httpUrl.getInputStream(), "kefuqr.png");} catch (Exception e) {} finally {httpUrl.disconnect();}if (!file.exists() || !file.isFile()) {log.info("文件不存在");throw new IOException("文件不存在");}String token = wxXCXTempSendUtil.getToken();if (token == null) {log.info("未获取到token");throw new IOException("未获取到token");}// 调用api里的获取临时素材的接口地址(https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE)分别代入token和type(type给image就可以了,写死)对接成功后小程序端会给你返回一个url地址,拿到地址做如下处理就行String urlString = wechatConfig.getMediaUrl().replace("ACCESS_TOKEN", token).replace("TYPE", fileType);URL url = new URL(urlString);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();conn.setRequestMethod("POST");// 以POST方式提交表单conn.setDoInput(true);conn.setDoOutput(true);conn.setUseCaches(false);// POST方式不能使用缓存// 设置请求头信息conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("Charset", "UTF-8");// 设置边界String BOUNDARY = "----------" + System.currentTimeMillis();conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);// 请求正文信息// 第一部分StringBuilder sb = new StringBuilder();sb.append("--");// 必须多两条道sb.append(BOUNDARY);sb.append("\r\n");sb.append("Content-Disposition: form-data;name=\"media\"; filename=\"" + file.getName() + "\"\r\n");sb.append("Content-Type:application/octet-stream\r\n\r\n");log.debug("sb:" + sb);// 获得输出流OutputStream out = new DataOutputStream(conn.getOutputStream());// 输出表头out.write(sb.toString().getBytes("UTF-8"));// 文件正文部分// 把文件以流的方式 推送道URL中DataInputStream din = new DataInputStream(new FileInputStream(file));int bytes = 0;byte[] buffer = new byte[1024];while ((bytes = din.read(buffer)) != -1) {out.write(buffer, 0, bytes);}din.close();// 结尾部分byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");// 定义数据最后分割线out.write(foot);out.flush();out.close();if (HttpsURLConnection.HTTP_OK == conn.getResponseCode()) {StringBuffer strbuffer = null;BufferedReader reader = null;try {strbuffer = new StringBuffer();reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String lineString = null;while ((lineString = reader.readLine()) != null) {strbuffer.append(lineString);}if (result == null) {result = strbuffer.toString();log.info("result:" + result);}} catch (IOException e) {log.error("发送POST请求出现异常!", e);e.printStackTrace();} finally {if (reader != null) {reader.close();}}}JSONObject jsonObject = JSONObject.parseObject(result);//解析出小程序给的地址后获得里面的media_id(注意:每次请求小程序临时素材的接口后,media_id有效期为3天,我这里使用了redis来处理得到类似永久media_id,每过接近三天重新获取一遍)String mediaId = jsonObject.getString("media_id");redisKit.set("mediaId", mediaId, 60 * 60 * 24 * 3 - 60 * 60);return mediaId;}
/*** 工具类* inputStream 转 File*/public static File inputStreamToFile(InputStream ins, String name) throws Exception {File file = new File(System.getProperty("java.io.tmpdir") + File.separator + name);if (file.exists()) {return file;}OutputStream os = new FileOutputStream(file);int bytesRead;int len = 8192;byte[] buffer = new byte[len];while ((bytesRead = ins.read(buffer, 0, len)) != -1) {os.write(buffer, 0, bytesRead);}os.close();ins.close();return file;}

3、上面都处理完之后,最后调用即可

public String acceptMessage(HttpServletRequest request, HttpServletResponse response) {//返回值String result = "success";try {// 将请求、响应的编码均设置为UTF-8(防止中文乱码)request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");Map<String, String> requestMap = MessageUtil.parseXml(request);log.info("requestMap>>>>>>>>>>" + requestMap);// 发送者的openidString fromUserName = requestMap.get("FromUserName");// 小程序的原始IDString toUserName = requestMap.get("ToUserName");// 消息类型String msgType = requestMap.get("MsgType");// 文本消息内容String content = requestMap.get("Content");// 事件类型String event = requestMap.get("Event");log.info("fromUserName=" + fromUserName + ",toUserName=" + toUserName + ",msgType=" + msgType + ",content=" + content + ",event=" + event);StringBuffer contentMessage = new StringBuffer();if (msgType.equals("event")) {contentMessage.append("欢迎进入Realc客服咨询!长按识别下方二维码添加专属顾问,如需人工服务请输入0");log.info(sendCustomerTextMessage(fromUserName, contentMessage.toString(), wxXCXTempSendUtil.getToken()));return sendImageMessage(result, fromUserName);} else if (msgType.equals("text")) {//文本消息if (content.equals("0")) {return switchCustomerService(fromUserName, toUserName);} else {contentMessage.append("欢迎进入Realc客服咨询!长按识别下方二维码添加专属顾问,如需人工服务请输入0");log.info(sendCustomerTextMessage(fromUserName, contentMessage.toString(), wxXCXTempSendUtil.getToken()));//自动回复图片消息return sendImageMessage(result, fromUserName);}} else {contentMessage.append("欢迎进入Realc客服咨询!长按识别下方二维码添加专属顾问,如需人工服务请输入0");log.info(sendCustomerTextMessage(fromUserName, contentMessage.toString(), wxXCXTempSendUtil.getToken()));return sendImageMessage(result, fromUserName);}} catch (Exception e) {log.info("错误信息打印>>>>>>>>>>>" + e.getMessage());e.printStackTrace();}return result;}//自动回复图片消息public String sendImageMessage(String result, String fromUserName) throws Exception {if (String.valueOf(redisKit.get("mediaId")).equals(null)) {String mediaId = String.valueOf(redisKit.get("mediaId"));log.info("redis中获取的mediaId>>>>>>>>>>>>>"+mediaId);sendCustomerImageMessage(fromUserName, mediaId, wxXCXTempSendUtil.getToken());return result;} else {String mediaId = getMedia("image");log.info("生成的mediaId>>>>>>>>>>>>>"+mediaId);sendCustomerImageMessage(fromUserName, mediaId, wxXCXTempSendUtil.getToken());return result;}}

上面的acceptMessage方法你的代码其它不用动,只在自动回复文本消息下面
加一个return sendImageMessage(result, fromUserName);

以上就是自动回复文本加图片和进入人工服务的所有内容,各位有不懂的,欢迎留言

最全的java对接微信小程序客服功能实现(包含自动回复文本消息、图片消息,进入人工客服)相关推荐

  1. 微信小程序实现购物车功能,包含完整小程序代码和运行效果截图

    微信小程序实现购物车功能,在商场比较常见,今天刚刚做好,效果不错. 下面从js文件,json文件,wxml文件和wxss文件,分享给大家. 直接上代码: 目录 1.index.js文件内容 2.ind ...

  2. java对接微信小程序(登录获取用户信息)

    需求说明: 用户通过小程序登录,进入到平台系统,进行各功能操作: 解决方案: 首先通过对接小程序,用户通过小程序登录及授权获取用户信息,后端调用接口获取微信用户信息,进行保存到数据库,然后返回toke ...

  3. 微信小程序优惠劵功能(包含用户需求,axure原型设计,数据库设计,后台功能,微信小程序功能)

    1.用户需求 优惠券功能有: 1.后台可以设置优惠券和查看已发出优惠券的状态 2.平台自动给新用户发放优惠劵,或者手动给某些用户发放优惠券 3.用户在小程序中手动领取优惠券 4.用户中心新增" ...

  4. CRMEB全开源Java版微信小程序商城,附源码

    CRMEB-JAVA版简介 CRMEB商城JAVA版,SpringBoot + Maven + Swagger + Mybatis Plus + Redis + Uniapp +Vue 包含移动端.小 ...

  5. 微信小程序上传图片到腾讯云服务器,微信小程序 (发帖功能), 上传本地图片到腾讯云怎么实现?...

    1 我刚开始用 lin-ui组件的 imagePicker组件,但是只能实现图片本地上传,本地预览.删除等功能, 无法跟腾讯云cos对象存储交互. cos对象代码 cos.putObject({ Bu ...

  6. java获取微信小程序二维码图片并保存到本地

    java获取微信小程序二维码保存到本地并返回图片,下次如果检查到本地有图片,那么就返回本地的图片,不再发起请求,否则重新发起请求并保存到本地,直接看代码吧. 获取access_token的方法可以参考 ...

  7. Java实现微信小程序校验图片是否含有违法违规内容

    文章目录 1.Java实现微信小程序校验图片是否含有违法违规内容(security.imgSecCheck) 2.接口文档简述 3.Java实现对接接口 4.压缩图片(Thumbnails) 5.整合 ...

  8. SpringBoot对接微信小程序支付功能开发(一,下单功能)

    1,接入前准备: 接入模式选择直连模式: 申请小程序,得到APPID,并开通微信支付: 申请微信商户号,得到mchid,并绑定APPID: 配置商户API key,下载并配置商户证书,根据微信官方文档 ...

  9. SpringBoot对接微信小程序支付功能开发(二,支付回调功能)

    接着上一篇: SpringBoot对接微信小程序支付功能开发(一,下单功能) 在上一篇下单功能中我们有传支付结果回调地址. 下面是回调接口实现 package com.office.miniapp.c ...

最新文章

  1. Android 分享机顶盒项目的封装类《GridView》
  2. 一个在菜场看到的,神一般的大爷!
  3. xml信息管理系统_WPF信息管理系统项目实战教程二:使用XAML实现页面布局
  4. java 方法返回的值,java方法返回值问题
  5. Python学习笔记:错误,测试,调试(承)
  6. docker数据卷volume详解
  7. 线性Transformer应该不是你要等的那个模型
  8. 01-CoreData简介
  9. php扩展memcached和memcache的安装配置方法
  10. 移动app部分机型无法唤起h5支付宝支付_用这段代码对App说:喂,醒醒!App,到你出场了!...
  11. 如何实现滑动scrollview上下隐藏
  12. java请假系统毕业设计_jsp企业员工考勤管理系统
  13. python showinfo函数_Python 函数
  14. Allegro中的NET到NET之间的间距设置-网络之间的间距
  15. iOS - iOS6 越狱及必装源、软件
  16. win10专业版和企业版的区别_Win10专业版和家庭版有什么区别?
  17. 360校招笔试题总结1
  18. 深恶痛绝……残忍虐杀何时终止?
  19. [Ansible专栏]Ansible条件判断的介绍和使用
  20. SAS中的intnx函数

热门文章

  1. 产品经理的必备软件推荐
  2. 2021高考成绩查询抖音,高考冲刺祝福语2021抖音最新 高考你好励志朋友圈文案
  3. 打开文件和关闭文件(CreateFile函数 和 CloseHandle函数)
  4. 一文搞定Void与void
  5. 03-定制PPT主题
  6. python海龟绘图画圆_Python启蒙之海龟作图
  7. java 批量写入文件_Java批量写入文件和下载图片的示例代码
  8. setInterval和setTimeout停止的方法
  9. 思科考试注册流程详解----VUE考点现场演示-晁海江-专题视频课程
  10. CDH6.3.2搭建HIVE ON TEZ步骤