公司新需求实现生成微信公众号带参数二维码和对应扫码后回复相应信息的功能。
1.实现带参数二维码功能
public class QrTest {
private static Logger logger = LogManager.getLogger(QrTest.class);
private final static String subPlatform = "prod1";
private final static String appid = "111111111111111111111";
private final static String secret = "";
//access_token 获取接口
private final static String WECHAT_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid+"&secret="+secret;
public static void main(String[] args) {
try {
String access_token = QrTest.getAccessToken(subPlatform);
String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+access_token;
JsonObject data = new JsonObject();
//data.addProperty("action_name", "QR_LIMIT_STR_SCENE");
data.addProperty("action_name", "QR_SCENE");
data.addProperty("expire_seconds", "604800");
JsonObject scene = new JsonObject();
//scene.addProperty("scene_str", "123&&456");
scene.addProperty("scene_id", "123&&456");
JsonObject actionInfo = new JsonObject();
actionInfo.add("scene", scene);
data.add("action_info", actionInfo);
logger.error(data.toString());
String result = HttpConnectionUtil.AccessUrl(url,data.toString());
QrCodeReceiveData jsonToJavaBean = JsonUtil.jsonToJavaBean(result, QrCodeReceiveData.class);
if(jsonToJavaBean != null) {
logger.error(jsonToJavaBean.getUrl());
}
logger.error(result);
/*} catch (OAuthException e) {
logger.debug(e.getMessage());*/
}catch (Exception e) {
}
}
/**
* 获取access_token
* @return
* @throws OAuthException
*/
private static String getAccessToken(String subPlatform) throws OAuthException{
String access_token = null;
if(subPlatform != null && !subPlatform.equals("")){
String tokenUrl = "http://www.baidu.com";
logger.debug("getAccess_token tokenUrl:" + tokenUrl);
access_token = HttpConnectionUtil.AccessUrlGet(tokenUrl);
logger.debug("getAccess_token access_token:" + access_token);
}else{
access_token = QrTest.getAccessToken();
}
return access_token;
}
private static String getAccessToken() {
String access_token = null;
try{
logger.debug("getAccessToken i(ss):" );
//获取access_token
String accessTokenURL = HttpConnectionUtil.AccessUrlGet(WECHAT_URL);
logger.debug("new accessTokenResult URL :"+accessTokenURL);
if(accessTokenURL != null && !accessTokenURL.equals("")){
AccessToken accessToken = (AccessToken) JsonUtil.JsonToObject(accessTokenURL, AccessToken.class);
if(accessToken != null){
access_token = accessToken.getAccess_token();;
logger.debug("new_access_token============:"+access_token);
}else{
logger.error("accessTokenResult error:"+accessToken);
}
}
}catch(Exception e){
logger.error("getAccessToken Exception:" + e);
}
return access_token;
}
}
接收参数实体类
public class QrCodeReceiveData {
private String ticket;
private int expire_seconds;
private String url;
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = ticket;
}
public int getExpire_seconds() {
return expire_seconds;
}
public void setExpire_seconds(int expire_seconds) {
this.expire_seconds = expire_seconds;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
说明:以上代码可以实现带参数二维码url获取,这里我只需要url链接,我们的项目会保存该链接,根据链接生成对应的二维码,方便重复使用。该段代码是获取临时二维码,若要获取永久二维码,更改相应参数即可,但需要控制生成数量目前为最多10万个。
2.被动回复消息
被动回复消息需要开通开发者模式
首先需要完善以上信息,同时需要开发接口进行验证
@RequestMapping("/testServlet")
public String verifyWXToken(HttpServletRequest request) throws AesException {
logger.error("进入验证servlet!!!!!");
String msgSignature = request.getParameter("signature");
String msgTimestamp = request.getParameter("timestamp");
String msgNonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
if (WXPublicUtils.verifyUrl(msgSignature, msgTimestamp, msgNonce)) {
return echostr;
}
return null;
}
以上代码即可完成验证。
验证通过后,将代码中的内容注释掉即可。节逻辑改为下面的逻辑
@RequestMapping("/testServlet")
@ResponseBody
public String handlePublicMsg(HttpServletRequest request) throws Exception {
logger.error("进入推送消息方法");
// 获得微信端返回的xml数据
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
is = request.getInputStream();
isr = new InputStreamReader(is, "utf-8");
br = new BufferedReader(isr);
String str = null;
StringBuffer returnXml= new StringBuffer();
while ((str = br.readLine()) != null) {
//返回的是xml数据
returnXml.append(str);
}
Map<String, String> encryptMap = VatifyToken.xmlToMap(returnXml.toString());
logger.error(encryptMap.toString());
// 得到公众号传来的加密信息并解密,得到的是明文xml数据
//String decryptXml = WXPublicUtils.decrypt(encryptMap.get("Encrypt"));
// 将xml数据转换为map
/*logger.error(decryptXml.toString());
Map<String, String> decryptMap = VatifyToken.xmlToMap(decryptXml.toString());*/
Map<String, String> decryptMap = encryptMap;
// 区分消息类型
String msgType = decryptMap.get("MsgType");
//场景信息
String eventKey = decryptMap.get("EventKey");
// 普通消息
if ("text".equals(msgType)) { // 文本消息
// todo 处理文本消息
} else if ("image".equals(msgType)) { // 图片消息
// todo 处理图片消息
} else if ("voice".equals(msgType)) { //语音消息
// todo 处理语音消息
} else if ("video".equals(msgType)) { // 视频消息
// todo 处理视频消息
} else if ("shortvideo".equals(msgType)) { // 小视频消息
// todo 处理小视频消息
} else if ("location".equals(msgType)) { // 地理位置消息
// todo 处理地理位置消息
} else if ("link".equals(msgType)) { // 链接消息
// todo 处理链接消息
}
// 事件推送
else if ("event".equals(msgType)) { // 事件消息
// 区分事件推送
String event = decryptMap.get("Event");
logger.error("区分事件推送:"+event);
if ("subscribe".equals(event)) { // 订阅事件 或 未关注扫描二维码事件
String mapToXml = getReturnMessage2(decryptMap,eventKey);
logger.error("发送数据:"+mapToXml);
return mapToXml;
} else if ("unsubscribe".equals(event)) { // 取消订阅事件
// todo 处理取消订阅事件
} else if ("SCAN".equals(event)) { // 已关注扫描二维码事件
String mapToXml = getReturnMessage1(decryptMap,eventKey);
logger.error("发送数据:"+mapToXml);
return mapToXml;
} else if ("LOCATION".equals(event)) { // 上报地理位置事件
// todo 处理上报地理位置事件
} else if ("CLICK".equals(event)) { // 点击菜单拉取消息时的事件推送事件
// todo 处理点击菜单拉取消息时的事件推送事件
} else if ("VIEW".equals(event)) { // 点击菜单跳转链接时的事件推送
// todo 处理点击菜单跳转链接时的事件推送
}
}
} catch (Exception e) {
logger.error("处理微信公众号请求信息,失败", e);
} finally {
if (null != is) {
is.close();
}
if (null != isr) {
isr.close();
}
if (null != br) {
br.close();
}
}
return null;
}
public String getReturnMessage1(Map<String, String> decryptMap,String eventKey) throws Exception {
logger.error("decryptMap:"+decryptMap.toString());
logger.error("eventKey:"+eventKey);
if(eventKey!= null && !"".equals(eventKey)) {
if("123".equals(eventKey)) {
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(decryptMap.get("FromUserName"));
textMessage.setFromUserName(decryptMap.get("ToUserName"));
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType("text");
textMessage.setContent("http://xxx/wechat/homePage");
logger.error(textMessage.toString());
String returnMap = getXmlString1(textMessage);
logger.error(returnMap);
return returnMap;
} else if("456".equals(eventKey)) {
Article article1 = new Article();
article1.setDescription("试卷描述1");
article1.setPicUrl("http://xxx//res//prod//SSEP/uploadfiles/knowledgeCourseImage/2020729407976775.png");
article1.setTitle("图文标题1");
article1.setUrl("http://xxx/wechat/paperInfo?id=1904201517405234&productId=1904201517405234");
Article article2 = new Article();
article2.setDescription("试卷描述2");
article2.setPicUrl("http://xxx//res//prod//SSEP/uploadfiles/knowledgeCourseImage/2020347456563018.png");
article2.setTitle("图文标题2");
article2.setUrl("http://xxx/wechat/paperInfo?id=1901141911032222&productId=1901141911032222");
List<Article> list = new ArrayList<>();
list.add(article1);
list.add(article2);
NewsMessage newMessage = new NewsMessage();
newMessage.setArticleCount(4);
newMessage.setCreateTime(new Date().getTime());
newMessage.setFromUserName(decryptMap.get("ToUserName"));
newMessage.setToUserName(decryptMap.get("FromUserName"));
newMessage.setMsgType("news");
newMessage.setArticles(list);
newMessage.setFuncFlag(1);
String returnMap = getXmlString2(newMessage);
logger.error(returnMap);
return returnMap;
} else {
}
}
return null;
}
说明:
public String getXmlString2(NewsMessage newMessage) {
String xml = "";
if(newMessage != null) {
List<Article> articles = newMessage.getArticles();
xml = "<xml>";
xml += "<ToUserName><![CDATA[";
xml += newMessage.getToUserName();
xml += "]]></ToUserName>";
xml += "<FromUserName><![CDATA[";
xml += newMessage.getFromUserName();
xml += "]]></FromUserName>";
xml += "<CreateTime>";
xml += newMessage.getCreateTime();
xml += "</CreateTime>";
xml += "<MsgType><![CDATA[";
xml += newMessage.getMsgType();
xml += "]]></MsgType>";
xml += "<ArticleCount>";
xml += newMessage.getArticleCount();
xml += "</ArticleCount>";
xml += "<Articles>";
for (Article article : articles) {
xml += "<item>";
xml += "<Title><![CDATA[";
xml += article.getTitle();
xml += "]]></Title>";
xml += "<Description><![CDATA[";
xml += article.getDescription();
xml += "]]></Description>";
xml += "<PicUrl><![CDATA[";
xml += article.getPicUrl();
xml += "]]></PicUrl>";
xml += "<Url><![CDATA[";
xml += article.getUrl();
xml += "]]></Url>";
xml += "</item>";
}
xml += "</Articles>";
xml += "</xml>";
}
return xml;
}
说明:以上缺少类型自己可以进行封装,但注意,发送文本消息和图文消息要特别注意,文本消息不需要更换发送者和接受者的信息,但图文消息需要调换才可正常发送,同事关注和未关注的decryptMap.get(“EventKey”)的值是有区别的,要注意改地方。同时,若有多个公众调用同一接口,可根据接收到的ToUserName进行区分。