网上看的这篇文章介绍的还是比较详细的:微信公众号开发基本流程
微信开发者文档
微信公众平台
微信开放平台
下面主要介绍一下接入公众号开发,前面账号申请,后台配置就不说了
创建公众号菜单
1、获取access_token: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET (GET方式)
2、创建菜单:http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
private static Menu getMenu() throws UnsupportedEncodingException {
CommonButton btn10 = new CommonButton();
btn10.setName("我的预约");
btn10.setType("view");
String my_appointment_redirect_url = WeChatConstants.WECHAT_URL + "/fe/pica_metronic/templates/frontend/pica_reservation_list.html";
String my_appointment_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConstants.APP_ID + "&redirect_uri=" + URLEncoder.encode(my_appointment_redirect_url, "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
btn10.setUrl(my_appointment_url);
CommonButton btn31 = new CommonButton();
btn31.setName("个人资料");
btn31.setType("view");
String news_redirect_uri = WeChatConstants.WECHAT_URL + "/fe/pica_metronic/templates/frontend/pica_wechat_patient_introduction.html";
String news_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConstants.APP_ID + "&redirect_uri=" + URLEncoder.encode(news_redirect_uri, "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
btn31.setUrl(news_url);
//32专家委员
CommonButton btn32 = new CommonButton();
btn32.setName("我的医生");
btn32.setType("view");
String name_list_redirect_uri = WeChatConstants.WECHAT_URL + "/fe/pica_metronic/templates/frontend/pica_doctor_list.html";
String name_list_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConstants.APP_ID + "&redirect_uri=" + URLEncoder.encode(name_list_redirect_uri, "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
btn32.setUrl(name_list_url);
CommonButton btn33 = new CommonButton();
btn33.setName("我的报告");
btn33.setType("view");
String report_list_redirect_uri = WeChatConstants.WECHAT_URL + "/fe/pica_metronic/templates/frontend/pica_report_list.html";
String report_list_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConstants.APP_ID + "&redirect_uri=" + URLEncoder.encode(report_list_redirect_uri, "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
btn33.setUrl(report_list_url);
/**小程序*/
CommonButton btn34 = new CommonButton();
btn34.setName("我的消息");
btn34.setType("miniprogram");
String add_doc_redirect_uri = WeChatConstants.WECHAT_URL + "/fe/pica_metronic/templates/frontend/pica_msg.html";
String add_doc_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConstants.APP_ID + "&redirect_uri=" + URLEncoder.encode(add_doc_redirect_uri, "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
btn34.setUrl(add_doc_url);
btn34.setAppid("wx180ede0973986101");
btn34.setPagepath("pages/health/mesList");
//12手动添加
CommonButton btn35 = new CommonButton();
btn35.setName("我的履约");
btn35.setType("view");
String agreement_date_redirect_uri = WeChatConstants.WECHAT_URL + "/fe/pica_metronic/templates/frontend/pica_doctor_schedule.html";
String agreement_date_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConstants.APP_ID + "&redirect_uri=" + URLEncoder.encode(agreement_date_redirect_uri, "UTF-8") + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";
btn35.setUrl(agreement_date_url);
ComplexButton mainBtn1 = new ComplexButton();
mainBtn1.setName("消息与日程");
mainBtn1.setSub_button(new CommonButton[]{btn10, btn35, btn34});
CommonButton btn22 = new CommonButton();
btn22.setName("健康漫画");
btn22.setType("view");
String h_knowledge_redirect_uri = "http://mp.weixin.qq.com/mp/homepage?__biz=MzIyOTY2MTI0MQ==&hid=1&sn=129b729545485bd3d759d6577566779c#wechat_redirect";
btn22.setUrl(h_knowledge_redirect_uri);
CommonButton btn23 = new CommonButton();
btn23.setName("体质检测");
btn23.setType("miniprogram");
String uri23 = "https://mp.weixin.qq.com/s/yEPBmwBp88-6bsBUtN9Asg";
btn23.setUrl(uri23);
btn23.setAppid("wxa394e63346c921a1");
btn23.setPagepath("pages/index");
ComplexButton mainBtn2 = new ComplexButton();
mainBtn2.setName("患者工具");
mainBtn2.setSub_button(new CommonButton[]{btn22, btn23});
ComplexButton mainBtn3 = new ComplexButton();
mainBtn3.setName("我的");
mainBtn3.setSub_button(new CommonButton[]{btn31, btn32, btn33});
Menu menu = new Menu();
menu.setButton(new Button[]{mainBtn1, mainBtn2, mainBtn3});
return menu;
}
package com.picahealth.wechat.wechat.pojo;
/**
* 按钮的基类
*
*/
public class Button {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.picahealth.wechat.wechat.pojo;
/**
* 普通按钮(子按钮)
*
*/
public class CommonButton extends Button {
private String type;
private String key;
private String url;
private String appid;
private String pagepath;
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getPagepath() {
return pagepath;
}
public void setPagepath(String pagepath) {
this.pagepath = pagepath;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
package com.picahealth.wechat.wechat.pojo;
/**
* 复杂按钮(父按钮)
*
*/
public class ComplexButton extends Button {
private Button[] sub_button;
public Button[] getSub_button() {
return sub_button;
}
public void setSub_button(Button[] sub_button) {
this.sub_button = sub_button;
}
}
package com.picahealth.wechat.wechat.pojo;
/**
* 菜单
*
*/
public class Menu {
private Button[] button;
public Button[] getButton() {
return button;
}
public void setButton(Button[] button) {
this.button = button;
}
}
回调接口
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
签名验证工具类,官网有提供:
package com.picahealth.wechat.wechat.utils;
import com.picahealth.wechat.constants.WeChatConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* 请求校验工具类
*
*/
public class SignUtil {
private static Logger logger = LoggerFactory.getLogger(SignUtil.class);
/**
* 与开发模式接口配置信息中的Token保持一致
*/
private static String token = WeChatConstants.TOKEN;
/**
* 微信生成的 ASEKey
*/
private static String encodingAesKey = WeChatConstants.EncodingAESKey;
/**
* 应用的AppId
*/
private static String appId = WeChatConstants.APP_ID;
/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { WeChatConstants.TOKEN, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
Arrays.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) {
logger.error("异常抛出exception ", e);
}
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}
消息处理转化工具类:
package com.picahealth.wechat.wechat.utils;
import com.picahealth.wechat.wechat.message.request.LinkMessage;
import com.picahealth.wechat.wechat.message.response.*;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 消息工具类
*
*/
public class MessageUtil {
/**
* 返回消息类型:文本
*/
public static final String RESP_MESSAGE_TYPE_TEXT = "text";
/**
* 返回消息类型:音乐
*/
public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
/**
* 返回消息类型:图文
*/
public static final String RESP_MESSAGE_TYPE_NEWS = "news";
/**
* 请求消息类型:文本
*/
public static final String REQ_MESSAGE_TYPE_TEXT = "text";
/**
* 请求消息类型:图片
*/
public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
/**
* 请求消息类型:链接
*/
public static final String REQ_MESSAGE_TYPE_LINK = "link";
/**
* 请求消息类型:地理位置
*/
public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
/**
* 请求消息类型:音频
*/
public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
/**
* 请求消息类型:推送
*/
public static final String REQ_MESSAGE_TYPE_EVENT = "event";
/**
* 事件类型:subscribe(订阅)
*/
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
/**
* 事件类型:unsubscribe(取消订阅)
*/
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
/**
* 事件类型:CLICK(自定义菜单点击事件)
*/
public static final String EVENT_TYPE_CLICK = "CLICK";
/**
* 事件类型:VIEW(自定义菜单点击事件)
*/
public static final String EVENT_TYPE_VIEW = "VIEW";
/**
* 事件类型:LOCATION(自定义菜单点击事件)
*/
public static final String EVENT_TYPE_LOCATION = "LOCATION";
/**
* 事件类型:LOCATION_SELECT(自定义菜单点击事件)
*/
public static final String EVENT_TYPE_LOCATION_SELECT = "LOCATION_SELECT";
/**
* 事件类型:SCAN(扫描二维码事件)
*/
public static final String EVENT_TYPE_SCAN = "SCAN";
/**
* 解析微信发来的请求(XML)
*
* @param request
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
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();
inputStream = null;
return map;
}
/**
* 文本消息对象转换成xml
*
* @param textMessage 文本消息对象
* @return xml
*/
public static String textMessageToXml(TextMessage textMessage) {
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
/**
* 音乐消息对象转换成xml
*
* @param musicMessage 音乐消息对象
* @return xml
*/
public static String musicMessageToXml(MusicMessage musicMessage) {
xstream.alias("xml", musicMessage.getClass());
return xstream.toXML(musicMessage);
}
/**
* link消息对象转换成xml
*
* @param linkMessage link消息对象
* @return xml
*/
public static String linkMessageToXml(LinkMessage linkMessage) {
xstream.alias("xml", linkMessage.getClass());
return xstream.toXML(linkMessage);
}
/**
* link消息对象转换成xml
*
* @param viewMessage link消息对象
* @return xml
*/
public static String viewMessageToXml(ViewMessage viewMessage) {
xstream.alias("xml", viewMessage.getClass());
return xstream.toXML(viewMessage);
}
/**
* 图文消息对象转换成xml
*
* @param newsMessage 图文消息对象
* @return xml
*/
public static String newsMessageToXml(NewsMessage newsMessage) {
xstream.alias("xml", newsMessage.getClass());
xstream.alias("item", new Article().getClass());
return xstream.toXML(newsMessage);
}
/**
* 扩展xstream,使其支持CDATA块
*
*/
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 对所有xml节点的转换都增加CDATA标记
boolean cdata = true;
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
}
package com.picahealth.wechat.wechat.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.picahealth.common.utils.StringUtil;
import com.picahealth.wechat.constants.CommonWechatConstants;
import com.picahealth.wechat.constants.WeChatConstants;
import com.picahealth.wechat.entity.mysql.PICAPDocPatMapping;
import com.picahealth.wechat.entity.mysql.PICAPPatient;
import com.picahealth.wechat.entity.mysql.PICAPQrcodeParamMapping;
import com.picahealth.wechat.entity.mysql.PICAPWechatUser;
import com.picahealth.wechat.model.PICAPDoctorModel;
import com.picahealth.wechat.model.cloud.PatPatientInfo;
import com.picahealth.wechat.model.cloud.WechatRequest;
import com.picahealth.wechat.model.wechat.WechatReplyLogAddModel;
import com.picahealth.wechat.util.CommonHttpUtils;
import com.picahealth.wechat.wechat.message.response.TextMessage;
import com.picahealth.wechat.wechat.utils.MessageUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 微信业务处理类
*
*/
public class CoreService {
private static Logger logger = LoggerFactory.getLogger(CoreService.class);
/**
* 处理微信发来的请求
*
* @param request
* @return
*/
public static String processRequest(HttpServletRequest request) {
String respMessage = "";
try {
// 默认返回的文本消息内容
String respContent = "";
// xml请求解析
Map<String, String> requestMap = MessageUtil.parseXml(request);
logger.info("wechat response message:{}", JSON.toJSONString(requestMap));
// 发送方帐号(open_id)
String openId = requestMap.get("FromUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
//调用得到微信回复消息接口
String wechatName = "YQY";
// 获取该公众号在自动文字应答字典表中设置的回答内容
Map responseMap = CommonHttpUtils.getReplays(wechatName, requestMap.get("Content"));
String respContentTemp = "";
if (responseMap != null) {
if (StringUtil.isNotNull(responseMap.get("respContentTemp"))) {
respContentTemp = responseMap.get("respContentTemp").toString();
}
}
if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
// 文本消息
respContent = respContentTemp;
} else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
// 图片消息
respContent = "";
} else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
// 地理位置消息
respContent = "";
} else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
// 链接消息
respContent = "";
} else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
// 音频消息
respContent = "";
} else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
// 事件类型
String eventType = requestMap.get("Event");
if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
// 关注事件
// do something
} else if (eventType.equals(MessageUtil.EVENT_TYPE_SCAN)) {
// 扫码事件
// do something
} else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
// 取消订阅
// do something
} else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
// 自定义菜单点击事件
// 事件KEY值,与创建自定义菜单时指定的KEY值对应
String eventKey = requestMap.get("EventKey");
logger.info("eventKey:" + eventKey);
} else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION)) {
// 每隔5s会推送客户的地理位置信息
} else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION_SELECT)) {
} else if (eventType.equals(MessageUtil.EVENT_TYPE_VIEW)) {
// 事件KEY值,与创建自定义菜单时指定的KEY值对应
String eventKey = requestMap.get("EventKey");
logger.info("eventKey:" + eventKey);
}
}
if (!StringUtil.isEmpty(respContent)) {
respMessage = recordWxReplyLog(respMessage, respContent, requestMap, wechatName);
}
} catch (Exception e) {
logger.error("异常抛出exception ", e);
}
return respMessage;
}
private static String recordWxReplyLog(String respMessage, String respContent, Map<String, String> requestMap, String wechatName) {
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(requestMap.get("FromUserName"));
textMessage.setFromUserName(requestMap.get("ToUserName"));
textMessage.setCreateTime(System.currentTimeMillis());
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
textMessage.setContent(respContent);
respMessage = MessageUtil.textMessageToXml(textMessage);
// 自动回复存储日志
WechatReplyLogAddModel logAddModel = new WechatReplyLogAddModel();
logAddModel.setWechatName(wechatName);
logAddModel.setWechatKeyword(requestMap.get("Content"));
logAddModel.setWechatPublicNumber(requestMap.get("ToUserName"));
logAddModel.setWechatContent(respContent);
logAddModel.setMsgid(requestMap.get("MsgId"));
logAddModel.setOpenid(requestMap.get("FromUserName"));
logAddModel.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
CommonHttpUtils.insertWechatReplyLog(logAddModel);
return respMessage;
}
}
提供给微信回调的url:
package com.picahealth.wechat.wechat.servlet;
import com.picahealth.wechat.wechat.service.CoreService;
import com.picahealth.wechat.wechat.utils.SignUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 核心请求处理类
*
*/
public class CoreServlet extends HttpServlet {
private static Logger log = LoggerFactory.getLogger(CoreServlet.class);
private static final long serialVersionUID = 4440739483644821986L;
/**
* 确认请求来自微信服务器
*/
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("发送get请求");
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = response.getWriter();
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
out = null;
}
/**
* 处理微信服务器发来的消息
*/
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("发送post请求");
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//清除js缓存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
// 调用核心业务类接收消息、处理消息
String respMessage = CoreService.processRequest(request);
// 响应消息
PrintWriter out = response.getWriter();
log.info("返回结果:" + respMessage);
out.print(respMessage);
out.close();
}
}