第一步:先在后台接入配置URL
如图:
第二步:在后台服务器配置接入接口 接入URL配置是 后台使用Get接收。注意 后台Token需要与微信小程序端配置的Token一致。
接入URL代码如下:
@ApiOperation("小程序消息推送接口")
@RequestMapping(value = "/sendTempMess", method = {RequestMethod.GET, RequestMethod.POST})
public void sendTempMess(HttpServletRequest request, HttpServletResponse response) {
boolean isGet = request.getMethod().toLowerCase().equals("get");
if (isGet) {//首次验证token
// 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = null;
try {
out = response.getWriter();
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败
if (WechatUtil.checkSignature(signature, timestamp, nonce)) {
System.out.println("成功");
out.print(echostr);
out.flush(); //必须刷新
}
System.out.println("失败");
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
} else {//进行客服操作
try {
// JSON.toJSONString(Tools.resolveParam(request));
//log.info("小程序返回消息:{}" , JSON.toJSONString(Tools.resolveParam(request)));
// 进入POST聊天处理
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 接收消息并返回消息
String result = wxService.acceptMessage(request, response);
// 响应消息
PrintWriter out = response.getWriter();
out.print(result);
out.close();
} catch (Exception ex) {
log.error("微信帐号接口配置失败!", ex);
ex.printStackTrace();
}
}
}
WechatUtil:
此处token,需要与微信小程序后端配置的token一致
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[]{token, timestamp, nonce};
// 将token、timestamp、nonce三个参数进行字典序排序
// Arrays.sort(arr);
sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null && tmpStr.equals(signature.toUpperCase());
}
接入成功之后,进行客服对话。文本。会话,卡片,图片等。
客服对话代码:入口参考上面接入的接口,同一个。
WxService:
private Logger logger = LoggerFactory.getLogger(WxService.class);
private static String SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
@Autowired
private WxXCXTempSend wxXCXTempSend;
@Autowired
public WxService(UserDao userDao) {
super(userDao);
}
public String acceptMessage(HttpServletRequest request, HttpServletResponse response) {
// {CreateTime=1548042266, Event=user_enter_tempsession, ToUserName=gh_e6198220cbff,
// FromUserName=oZvme4q2Oi7Dz3FChXc43kqw28, MsgType=event, SessionFrom=wxapp}
String respMessage = "";
try {
// xml请求解析
Map<String, String> requestMap = MessageUtil.parseApplet(request);
logger.info(">>>>>>>>>>>>>"+requestMap);
// 发送方帐号(open_id)
String fromUserName = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
//人工服务
String content = requestMap.get("Content");
String accessToken=getAccessToken();
//此处我默认为直接人工服务 可根据实际业务调整
return switchCustomerService(fromUserName,toUserName,requestMap);
//小程序客服 文本信息
/* if(msgType.equals("text")){
if("人工服务".equals(content)){
HashMap<String, Object> resultMap = new HashMap<>();
resultMap.put("ToUserName",fromUserName);
resultMap.put("FromUserName",toUserName);
resultMap.put("CreateTime", requestMap.get("CreateTime"));
resultMap.put("MsgType","transfer_customer_service");
String json = JSON.toJSONString(resultMap);
JSONObject result = JSONObject.parseObject(json);
logger.info("POST result" + result);
return result;
}
sendCustomerTextMessage(fromUserName,"你好,欢迎使用人工服务",accessToken);
}else if(msgType.equals("event")){//会话功能
String sessionFrom = (String) requestMap.get("SessionFrom");
logger.info("SessionFrom SessionFrom" + sessionFrom);
int i = sessionFrom.indexOf("+");
String sessionFromFirst = "1";
String appId = wxXCXTempSend.APP_ID;
if( i > 0){
sessionFromFirst = sessionFrom.substring(0, i); //标志位 1 2 3 4 5 6
logger.info("SessionFrom sessionFromFirst " + sessionFromFirst);
String sessionFromLast = sessionFrom.substring(i+1); //{"appId":"","data":"test"}
logger.info("SessionFrom sessionFromLast " + sessionFromLast);
if(JSONObject.parseObject(sessionFromLast).get("appId") != null){
appId = (String) JSONObject.parseObject(sessionFromLast).get("appId");
}
sendCustomerTextMessage(fromUserName,"你好,欢迎使用会话服务",accessToken);
}
}else if(msgType.equals("image")){
logger.info("公众号接受图片..........");
sendCustomerImageMessage(fromUserName,requestMap.get("MediaId"),accessToken);
}else{
}*/
} catch (Exception e) {
e.printStackTrace();
}
return respMessage;
}
private String getAccessToken() {
String accessToken = null;
try {
accessToken = wxXCXTempSend.getAccessToken();
if ("0".equals(accessToken)) {
logger.error("微信小程序获取token调用失败");
accessToken = "0";
} else if ("1".equals(accessToken)) {
logger.error("微信小程序获取token系统繁忙");
accessToken = "0";
} else if ("2".equals(accessToken)) {
logger.error("微信小程序获取token,AppSecret错误或者AppID不合法");
accessToken = "0";
} else {
logger.error("微信小程序获取token失败。");
}
} catch (Exception e) {
logger.error("微信小程序获取token异常");
e.printStackTrace();
accessToken = "0";
}
return accessToken;
}
/**
* 文本事件
* */
public String sendCustomerTextMessage(String openid,String text,String accessToken)throws Exception{
Map<String,Object> map_content = new HashMap<>();
map_content.put("content",text);
Map<String,Object> map = new HashMap<>();
map.put("touser",openid);
map.put("msgtype","text");
map.put("text",map_content);
String content = JSON.toJSONString(map);
return HttpClientUtils.httpPost(SEND_URL+"?access_token="+accessToken,content);
}
/**
* 会话事件
* */
public String sendFirstMessage(String openid, String text,String accessToken) throws Exception {
Map<String, Object> map_content = new HashMap<>();
map_content.put("content", text);
Map<String, Object> map = new HashMap<>();
map.put("touser", openid);
map.put("msgtype", "text");
map.put("text", map_content);
String content = JSON.toJSONString(map);
return HttpClientUtils.httpPost(SEND_URL + "?access_token=" + accessToken, content);
}
/***
* 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/conversation.html
* 发送的图片消息
*/
public String sendCustomerImageMessage (String openid, String mediaId,String accessToken)throws Exception{
Map<String, Object> map_content = new HashMap<>();
map_content.put("media_id", mediaId);
Map<String, Object> map = new HashMap<>();
map.put("touser", openid);
map.put("msgtype", "image");
map.put("image", map_content);
String content = JSON.toJSONString(map);
return HttpClientUtils.httpPost(SEND_URL + "?access_token=" + accessToken, content);
}
/***
* 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/conversation.html
* 转发至人工客服 现在默认都为人工客服
*/
public String switchCustomerService (String fromUserName, String toUserName,Map requestMap)throws Exception{
HashMap<String, Object> resultMap = new HashMap<>();
resultMap.put("ToUserName",fromUserName);
resultMap.put("FromUserName",toUserName);
resultMap.put("CreateTime", requestMap.get("CreateTime"));
resultMap.put("MsgType","transfer_customer_service");
String json = JSON.toJSONString(resultMap);
JSONObject result = JSONObject.parseObject(json);
logger.info("POST result" + result);
return result.toString();
}
获取AccessToken的工具类如下
WxXCXTempSend:
@Slf4j
@Component
public class WxXCXTempSend {
@Autowired
private RedisLite redisLite;
private static String TEMP_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send";
public static final String APP_ID = "wx97bad4e6906f882e";
private static final String APP_SECRET = "f4c25b849e24f0b02826a540edbe95f7";
/**
* 获取小程序token,(ps:0=token获取失败)
*
* @return
*/
public String getAccessToken() {
return SimpleCache.get(RedisCacheKeys.WxXCX.wxAccessToken, String.class, redisLite, 60*105, () -> {
String accessToken = "0";
try {
//此处APP_ID APP_SECRET 在微信小程序后端可见
String accessTokenUrl = String.format(WechatConfig.XCX_ACCESS_TOKEN_URL, APP_ID, APP_SECRET);
String result = HttpClientUtils.httpGet(accessTokenUrl, null, null);
Map<String, Object> resultMap = JSON.parseObject(result, Map.class);
if(resultMap.containsKey("access_token")) {
accessToken = resultMap.get("access_token").toString();
}
} catch (IOException ioe) {
log.error("小程序http请求异常");
ioe.printStackTrace();
}
return accessToken;
});
}
}
RedisCacheKeys:
/**
* 微信小程序有关缓存key
*
*/
public class WxXCX {
public static final String wxAccessToken = BasePrefix + "wx:xcx:accessToken";
}
HttpClientUtils:
/**
* http请求工具类,get请求
*
* @param url
* @param params
* @param resonseCharSet
* @return
* @throws Exception
*/
public static String httpGet(String url, Map<String, Object> params, String... resonseCharSet) throws IOException {
DefaultHttpClient defaultHttpClient = null;
BufferedReader bufferedReader = null;
try {
defaultHttpClient = new DefaultHttpClient();
if (params != null) {
StringBuilder stringBuilder = new StringBuilder();
Iterator<String> iterator = params.keySet().iterator();
String key;
while (iterator.hasNext()) {
key = iterator.next();
Object val = params.get(key);
if (val instanceof List) {
List v = (List) val;
for (Object o : v) {
stringBuilder.append(key).append("=").append(o.toString()).append("&");
}
} else {
stringBuilder.append(key).append("=").append(val.toString()).append("&");
}
}
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
url = url + "?" + stringBuilder.toString();
log.info("url:{}", url);
}
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Content-Type", "application/json;charset=ut-8");
HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() != 200) {
String errorLog = "请求失败,errorCode:" + httpResponse.getStatusLine().getStatusCode();
log.info(errorLog);
throw new HttpRequestException(url + errorLog);
}
//读取返回信息
String charSet = "utf-8";
if (resonseCharSet != null && resonseCharSet.length > 0)
charSet = resonseCharSet[0];
String output;
bufferedReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(), charSet));
StringBuilder dataBuilder = new StringBuilder();
while ((output = bufferedReader.readLine()) != null) {
dataBuilder.append(output);
}
return dataBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (defaultHttpClient != null)
defaultHttpClient.getConnectionManager().shutdown();
if (bufferedReader != null)
bufferedReader.close();
}
}
/**
* http请求工具类,post请求
*
* @param url url
* @param param 参数值 仅支持String
* @return
* @throws Exception
*/
public static String httpPost(String url, String param) throws IOException {
DefaultHttpClient defaultHttpClient = null;
BufferedReader bufferedReader = null;
try {
defaultHttpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-Type", "application/json;charset=ut-8");
if (StringUtils.isNotBlank(param)) {
log.info("参数值:{}", param);
HttpEntity httpEntity = new StringEntity(param, "utf-8");
httpPost.setEntity(httpEntity);
}
HttpResponse httpResponse = defaultHttpClient.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() != 200) {
String errorLog = "请求失败,errorCode:" + httpResponse.getStatusLine().getStatusCode();
log.info(errorLog);
throw new HttpRequestException(url + errorLog);
}
//读取返回信息
String output;
bufferedReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(), "utf-8"));
StringBuilder stringBuilder = new StringBuilder();
while ((output = bufferedReader.readLine()) != null) {
stringBuilder.append(output);
}
return stringBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (defaultHttpClient != null)
defaultHttpClient.getConnectionManager().shutdown();
if (bufferedReader != null)
bufferedReader.close();
}
}
如果有什么疑问,可以联系我QQ 976452322 有时间 一起讨论 此部分代码,暂时无Git