微信小程序与公众号实现数据互通的方法
作者:一叶知秋Plus 发布时间:2024-11-17 21:45:14
公司因小程序项目先上线,公众号后开发,接到上级的安排实现小程序打通任务,看文档后发现:同一开发者账号只要是在微信开放平台绑定小程序与公众号以后,会有一个唯一的unionid,这个unionid腾讯公司下产品共享。这个unionid就是我们进行打通的关键。
先说一下思路:
1.微信小程序与公众号进行绑定后,在小程序调用wx.login()方法后会自动获取unionid,公众号根据官方文档在获取用户基本信息后会拿到相同的unionid,openid,nickname。。。等相关信息;
2.将小程序拿到的unionid进行数据库的更新操作,公众号拿到的unionid等信息,新建数据库表A进行存储;(注:在这一步,因为我们公司的原因,我们的公众号之前就有人关注了,那么在这之前,我通过公众号获取关注用户列表获取openid的列表,进行循环openid列表,在调用公众号获取用户基本信息列表进行储存数据库表A,循环结束后之前关注的人的信息就储存在数据库A,然后在进行,这一步的操作)
3.通过公众号关注/取关的事件相应,来进行数据库表A的增删操作,维护数据的新鲜度;
4.进行关联查询,到这一步我们会发现,通过unionid进行表的关联后我们已经实现数据的互通了
洋洋洒洒的说了一大堆,其实就是公众号的两个接口至关重要(1.关注/取关的事件相应接口 2.获取用户的基本信息接口)
有关于公众号的安全域名配置,服务器域名配置以及获取token就不在这里说了,百度一下一大堆。
代码实现:
第一步,获取公众号用户的openid列表操作,根据opneid进进行用户的基本信息的查询,存入数据库操作(因为我们公司的公众号关注人数只有1000+,所以我只调用了一次获取关注列表的接口)
//主要代码逻辑
//获取token
AccessToken accessToken=wxUtils.getAccessToken();
String url="https://api.weixin.qq.com/cgi-bin/user/get?access_token="+accessToken.getAccessToken()+"&next_openid=";//获取所有用户openid
JSONObject jsonObject = httpRequest(url, "GET", null);
try {
if(jsonObject.getString("errcode")!=null){
}
}catch(Exception e) {
}
WeixinUserList userList = (WeixinUserList)JSONObject.toBean(jsonObject, WeixinUserList.class);
if(null==userList) {
return "无用户";
}
userList.getTotal();//关注总人数
//用户openId 列表
WxOpenidInfo wxOpenidInfo=userList.getData();
List<String> openIdList=null;
if(null!=wxOpenidInfo) {
openIdList=wxOpenidInfo.getOpenid();//公众号返回的openid列表数据
if(null!=openIdList && openIdList.size()>0) {
for(String opendid:openIdList) {
//获取用户的基本信息(unionid机制)
url="https://api.weixin.qq.com/cgi-bin/user/info? access_token="+accessToken.getAccessToken()+"&openid="+opendid+"&lang=zh_CN";//通过openid获取用户信息
jsonObject = httpRequest(url, "GET", null);
WeixinUser wxUser=(WeixinUser)JSONObject.toBean(jsonObject, WeixinUser.class);
//进行数据库表A的储存操作
int row = gzhService.addGZHUser(wxUser);
}
}
}
/**
* 用户列表
* @author 一叶知秋plus
*
*/
public class WeixinUserList{
private Integer total;//关注该公众账号的总用户数
private Integer count;//拉取的OPENID个数,最大值为10000
private WxOpenidInfo data;//列表数据,OPENID的列表
private String next_openid;//拉取列表的最后一个用户的OPENID
private int errcode;//错误编码
private String errmsg="ok";//错误提示
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public String getNext_openid() {
return next_openid;
}
public void setNext_openid(String next_openid) {
this.next_openid = next_openid;
}
public WxOpenidInfo getData() {
return data;
}
public void setData(WxOpenidInfo data) {
this.data = data;
}
public int getErrcode() {
return errcode;
}
public void setErrcode(int errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}
/**
* 用户基本信息
* @author 一叶知秋plus
*
*/
public class WeixinUser {
private String subscribe;// 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
private String openid;// 用户的标识,对当前公众号唯一
private String nickname;// 用户的昵称
private String sex;// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
private String city;// 用户所在城市
private String country;// 用户所在国家
private String province;// 用户所在省份
private String language;// 用户的语言,简体中文为zh_CN
private List<String> tagid_list;//用户被打上的标签ID列表
private String unionid; //用户的unionid
private String headimgurl;//用户的头像
public String getHeadimgurl() {
return headimgurl;
}
public void setHeadimgurl(String headimgurl) {
this.headimgurl = headimgurl;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
public String getSubscribe() {
return subscribe;
}
public void setSubscribe(String subscribe) {
this.subscribe = subscribe;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public List<String> getTagid_list() {
return tagid_list;
}
public void setTagid_list(List<String> tagid_list) {
this.tagid_list = tagid_list;
}
}
public class WxOpenidInfo {
private List<String> openid;
public List<String> getOpenid() {
return openid;
}
public void setOpenid(List<String> openid) {
this.openid = openid;
}
}
步骤二:关注/取关的事件响应接口
/**
* 请求校验工具类
*/
public class SignUtil {
// 与接口配置信息中的Token要一致,我的是明文格式
private static String token = "填写你服务器配置时写的token";
public static boolean checkSignature(String signature, String timestamp,
String nonce) {
//从请求中(也就是微信服务器传过来的)拿到的token, timestamp, nonce
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
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()) : false;
}
//将加密后的字节数组变成字符串
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
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;
}
//用于字典排序
public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
}
//事件响应的接口
@RequestMapping(value="/GZHConcern.do")
public void GZHConcern(HttpServletRequest request, HttpServletResponse response) throws IOException {
String message = "success";
// 微信加密签名
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);
//在这里相应微信的操作
}
try {
Map<String, String> map = XmlUtil.xmlToMap(request);
String fromUserName = map.get("FromUserName");//消息来源用户标识
String toUserName = map.get("ToUserName");//消息目的用户标识
String msgType = map.get("MsgType");//消息类型
String content = map.get("Content");//消息内容
String eventType = map.get("Event");
WeixinUser weixinUser = new WeixinUser();
if(MessageUtil.MSGTYPE_EVENT.equals(msgType)){//如果为事件类型
if(MessageUtil.MESSAGE_SUBSCIBE.equals(eventType)){//处理订阅事件
//获取token
String token = WXUtil.getGZHToken();
weixinUser = WXUtil.getUnionid(fromUserName, token);
//进行数据库的操作
weixinUser.setNickname(weixinUser.getNickname());
int row = gzhService.addGZHUser(weixinUser);
//通过openid获取用户的数据
message = MessageUtil.subscribeForText(toUserName, fromUserName);
}else if(MessageUtil.MESSAGE_UNSUBSCIBE.equals(eventType)){//处理取消订阅事件
message = MessageUtil.unsubscribe(toUserName, fromUserName);
weixinUser.setOpenid(fromUserName);
//进行数据库的操作
int row = gzhService.deleteGZHUser(weixinUser);
}
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
out.close();
}
out = null;
}
/*
* 消息处理工具类
*/
public class MessageUtil {
public static final String MSGTYPE_EVENT = "event";//消息类型--事件
public static final String MESSAGE_SUBSCIBE = "subscribe";//消息事件类型--订阅事件
public static final String MESSAGE_UNSUBSCIBE = "unsubscribe";//消息事件类型--取消订阅事件
public static final String MESSAGE_TEXT = "text";//消息类型--文本消息
/*
* 组装文本消息
*/
public static String textMsg(String toUserName,String fromUserName,String content){
TextMessage text = new TextMessage();
text.setFromUserName(toUserName);
text.setToUserName(fromUserName);
text.setMsgType(MESSAGE_TEXT);
text.setCreateTime(new Date().getTime());
text.setContent(content);
return XmlUtil.textMsgToxml(text);
}
/*
* 响应订阅事件--回复文本消息
*/
public static String subscribeForText(String toUserName,String fromUserName){
return textMsg(toUserName, fromUserName, "欢迎关注,精彩内容不容错过!!!");
}
/*
* 响应取消订阅事件
*/
public static String unsubscribe(String toUserName,String fromUserName){
//TODO 可以进行取关后的其他后续业务处理
System.out.println("用户:"+ fromUserName +"取消关注~");
return "";
}
}
/*
* xml处理工具类
*/
public class XmlUtil {
/*
* xml转map
*/
public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException{
HashMap<String, String> map = new HashMap<String,String>();
SAXReader reader = new SAXReader();
InputStream ins = request.getInputStream();
Document doc = reader.read(ins);
Element root = doc.getRootElement();
@SuppressWarnings("unchecked")
List<Element> list = (List<Element>)root.elements();
for(Element e:list){
map.put(e.getName(), e.getText());
}
ins.close();
return map;
}
/*
* 文本消息对象转xml
*/
public static String textMsgToxml(TextMessage textMessage){
XStream xstream = new XStream();
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
}
ok,到这一步数据库中有了小程序opneid unionid 公众号opneid unionid等用户信息,进行关联后就可以进行数据的查询操作,当然小程序也可以发送公众号模板的相应操作了。如果有更好的实现方式,欢迎各位大佬不吝赐教~
来源:https://blog.csdn.net/KayLoveJie/article/details/90208170


猜你喜欢
- 前言Go语言中,协程创建和启动非常简单,但是如何才能正确关闭协程呢,和开车一样,前进总是很容易,但是如何正确的把车停在指定的地方总是不容易的
- 1.什么是Pillow首先我们需要了解一下PIL(Python Imaging Library),它是Python2中非常强大的图像处理标准
- 前言好记性不如烂笔头!最近在接口测试,以及爬虫相关,需要用到Python中的requests库,之前用过,但是好久没有用又忘了,这次就把这块
- 一、mysql主从复制介绍mysql的主从复制并不是数据库磁盘上的文件直接拷贝,而是通过逻辑的binlog日志复制到要同步的服务器本地,然后
- WebSocket的作用WebSock其实在平常使用,我们是时常见到的,用于实时通讯,例如我们常用的实时聊天、服务端向客户端消息推送、也可以
- 例如你的原路径是 http://localhost/test/index.php/index/add那么现在的地址是 http://loca
- 背景:pony是公司的首席体验官、首席产品经理。这次在产品峰会上pony将自己平时经验的积累与大家交流,体验较细。这次分享研发管理部,设计中
- 需 求 分 析 1、读取指定目录下的所有文件2、读取指定文件,输出文件内容3、创建一个文件并保存到指定目录实 现 过 程Python写代码简
- PyQt5 Qt Designer (Qt设计师)PyQt5是对Qt所有类进行封装, Qt能开发的东西, PyQt都能开发.Qt是强大的GU
- 模块安装:数据操作用到的模块pymysql,需要通过pip install pymysql进行安装。redis操作用的模块是redis,需要
- 在项目中时间一律显示为2014-10-20 10:22显得很呆板。在微博、QQ空间等网站通常会显示为几秒前,几分钟前,几小时前等容易阅读的时
- 有一道算法题题目的意思是在二维数组里找到一个峰值。要求复杂度为n。解题思路是找田字(四边和中间横竖两行)中最大值,用分治法递归下一个象限的田
- 模块导入方式: import osos模块是Python标准库中的一个用于访问操作系统相关功能的模块,os模块提供了一种可移植的使
- 一、使用我使用的是python3,可以自行搜索下载二、安装phone模块pip install phone三、测试代码如下:from pho
- 如果没有了解栅格系统是什么,建议看完这篇文章以后再回来。其实有很种 CSS 写法实现栅格系统,很多 CSS 库也都会提供类似的栅格系统实现(
- asp.net MVC中Action参数不只是一些基本类型,也支持实体参数。那么从客户端传来的数据如何映射或转换成实体对象呢?就是通过实体绑
- function setPage(opt){ if(!opt.pageDivId || opt.allPageNum < opt.cu
- 一、相关知识点讲解1.1 需要使用的相关库import numpy as npimport pand
- vue+element中的表格,直接定位到指定的元素。需求:点击某一个节点,弹窗,直接定位到点击的节点,高亮并显示数据。<el-tab
- 格式化字符串由占位符和普通字符组合而成。占位符由'%'打头,动词结尾。占位符由五类元素组成: 标志位(flag),宽度,精度