JAVA实现判断小程序用户是否关注公众号

本文主要描述了判断小程序用户是否关注公众号的逻辑实现及部分代码

首先阐述一下大致流程:

1、在将小程序和公众号绑定至同一个微信开发平台下;

2、后端拉取公众号已关注用户列表,并获取其中每一个用户的unionID, 建立已关注用户表;

3、后端可做定时任务更新该表;

4、用户在小程序中登录注册时后端用code拿到用户的unionID并保存;

5、前端请求查询时,后端根据发起请求用户的unionID查表,判断该用户是否已关注;

 一、数据库表和Mapper层

这里简单给出了建表语句和实体类, 具体mapper/service层可自行实现CREATE TABLE `public_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`open_id` varchar(50) DEFAULT NULL COMMENT 'openId',`union_id` varchar(50) DEFAULT NULL COMMENT 'union',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1DEFAULT CHARSET=utf8mb4;@Table(name = "public_user")
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class PublicUser implements Serializable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "open_id")private String openId;@Column(name = "union_id")private String unionId;}public interface PublicUserMapper {}

二、微信公众号对接接口相关逻辑代码

        1.三个基础接收对象类
 

@Data
public class WeixinUserInfoVo {private String openid;private String unionid;/*** 该值为0值 则没有openId和unionId*/private Integer subscribe;private Integer errcode;
}@Data
public class WeixinUserListVo {private Integer errcode;@ApiModelProperty("关注该公众账号的总用户数")private Integer total;@ApiModelProperty("拉取的OPENID个数,最大值为10000")private Integer count;@ApiModelProperty("列表数据,OPENID的列表")private WxOpenidInfo data;@ApiModelProperty("拉取列表的最后一个用户的OPENID")private String next_openid;
}@Data
public class WxOpenidInfo {private List<String> openid;
}

        2.调用微信接口相关

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.mzlion.easyokhttp.HttpClient;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import tk.mybatis.mapper.entity.Example;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;@Slf4j
@Service
public class WeixinPublicService {@Autowiredprivate RedisTemplate redisTemplate;//公众号appId@Value("${weixin.mp.account:xxxxxxxx}")private String miniappAppId;//公众号secret@Value("${weixin.mp.secret:xxxxxxxxxxxxxxxxx}")private String miniappSecret;@Resourceprivate PublicUserMapper publicUserMapper;public static final String ACCESS_TOKEN="weixin:access_token";/*** 判断小程序用户是否否关注的主要方法-根据unionId来查询用户观众公众号信息* @param unionId* @return*/public WeixinUserInfoVo selectWeixinPublicUserInfoByUnionId(String unionId) {//现查询数据库-根据unionid获取到openid-逻辑可自行实现PublicUser dbPublicUser = publicUserMapper.selectPublicUserByUnionId(unionId);WeixinUserInfoVo weixinUserInfoVo = null;if (Objects.isNull(dbPublicUser)) {//查询数据库-逻辑可自行实现List<PublicUser> existPublicUsers = publicUserMapper.selectPublicUserList();// 查询所有 openidHashSet<String> openidSet = getUserOpenIdList();if (!CollectionUtil.isEmpty(openidSet)) {if (!openidSet.isEmpty()) {// 差集for (PublicUser user : existPublicUsers) {openidSet.remove(user.getOpenId());}}}// 更新 未入库的 公众号信息String openid = null;RestTemplate restTemplate = new RestTemplate();List<PublicUser> publicUserList = new ArrayList<>();for (String id : openidSet) {// 根据openid查询unionIdString requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+ getAccessToken()+ "&openid=" + id + "&lang=zh_CN";weixinUserInfoVo = restTemplate.getForObject(requestUrl, WeixinUserInfoVo.class);if (!ObjectUtil.isNull(weixinUserInfoVo) && ObjectUtil.isNull(weixinUserInfoVo.getErrcode())) {if (!StrUtil.isEmpty(weixinUserInfoVo.getUnionid())) {publicUserList.add(PublicUser.builder().openId(weixinUserInfoVo.getOpenid()).unionId(weixinUserInfoVo.getUnionid()).build());if (unionId.equals(weixinUserInfoVo.getUnionid())) {openid = id;}}}}if (!CollectionUtil.isEmpty(publicUserList)) {this.publicUserMapper.insertList(publicUserList);}}else {RestTemplate restTemplate = new RestTemplate();String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+ getAccessToken()+ "&openid=" + dbPublicUsers.get(0).getOpenId() + "&lang=zh_CN";weixinUserInfoVo = restTemplate.getForObject(requestUrl, WeixinUserInfoVo.class);}return weixinUserInfoVo;}/*** 获取请求token* @return*/private String getAccessToken() {String accessToken = (String) this.redisTemplate.opsForValue().get(ACCESS_TOKEN);if (StrUtil.isEmpty(accessToken)) {String result = HttpClient.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET".replace("APPID", this.miniappAppId).replace("APPSECRET", this.miniappSecret)).asString();log.info("getAccessToken---result===>"+result);if (!Strings.isNullOrEmpty(result)) {Integer expiresIn = null;JSONObject jsonObject = JSON.parseObject(result);if (ObjectUtil.isNotNull(jsonObject)) {expiresIn = (Integer) jsonObject.get("expires_in");accessToken = (String) jsonObject.get("access_token");}log.info("getAccessToken---accessToken===>"+accessToken);if (StrUtil.isNotEmpty(accessToken) && ObjectUtil.isNotNull(expiresIn)) {this.redisTemplate.opsForValue().set(ACCESS_TOKEN, accessToken, expiresIn - 20);}}}return accessToken;}/*** 初始化所有公众号用户数据*/public void init() {//查询数据库-逻辑可自行实现List<PublicUser> dbPublicUsers = publicUserMapper.selectPublicUserList();List<String> existOpenIds = new ArrayList<>();if (dbPublicUsers != null && !dbPublicUsers.isEmpty()) {existOpenIds.addAll(dbPublicUsers.stream().map(PublicUser::getOpenId).collect(Collectors.toList()));}// 查询所有 openidHashSet<String> openidSet = getUserOpenIdList();//去除已经存在的openidSet.removeIf(existOpenIds::contains);// 更新 未入库的 公众号信息RestTemplate restTemplate = new RestTemplate();List<PublicUser> publicUserList = new ArrayList<>();for (String openId : openidSet) {// 根据openid查询unionIdString requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+ getAccessToken()+ "&openid=" + openId + "&lang=zh_CN";WeixinUserInfoVo weixinUserInfoVo = restTemplate.getForObject(requestUrl, WeixinUserInfoVo.class);if (!ObjectUtil.isNull(weixinUserInfoVo) && ObjectUtil.isNull(weixinUserInfoVo.getErrcode())) {if (!StrUtil.isEmpty(weixinUserInfoVo.getUnionid())) {publicUserList.add(PublicUser.builder().openId(weixinUserInfoVo.getOpenid()).unionId(weixinUserInfoVo.getUnionid()).build());}}//一次性插入一百条 防止服务断掉 导致全没拉下来if (!CollectionUtil.isEmpty(publicUserList) && publicUserList.size() == 100) {this.publicUserMapper.insertList(publicUserList);publicUserList.clear();}}if (!CollectionUtil.isEmpty(publicUserList)) {this.publicUserMapper.insertList(publicUserList);}}/*** 获取公众号关注用户列表的openid* @return*/public HashSet<String> getUserOpenIdList() {//获取最新的access_tokenString accessToken = getAccessToken();log.info("accessToken===>"+new String(accessToken));RestTemplate restTemplate = new RestTemplate();WeixinUserListVo openIdList = null;HashSet<String> openidSet = new HashSet<String>();synchronized (this) {try {//循环获取用户openid列表--一次获取10000String nextOpenid = null;do {//微信公众号获取用户列表信息接口地址String requestUrl = null;if (StringUtil.isBlank(nextOpenid)) {requestUrl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token="+ accessToken;} else {requestUrl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token="+ accessToken + "&next_openid=" + nextOpenid;}openIdList = restTemplate.getForObject(requestUrl, WeixinUserListVo.class);if (openIdList != null && Objects.nonNull(openIdList.getData())) {//获取用户关注列表对象WxOpenidInfo data = openIdList.getData();//获取当前循环的openid--10000条openidSet.addAll(data.getOpenid());//拉取列表的最后一个用户的OPENIDnextOpenid = openIdList.getNext_openid();}} while (Objects.nonNull(openIdList.getData()));} catch (Exception e) {log.debug("获取用户列表失败:{}", openIdList);return null;}}return openidSet;}}

3.判断是否关注的逻辑

根据调用selectWeixinPublicUserInfoByUnionId的返回判断是否关注

        ①已关注(subscribe字段判断 1:已关注) 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397967.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OCR调研

OCR调研 一、介绍 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是一种将图像中的文字转换为计算机可处理格式的技术。OCR技术经历了从传统OCR到基于深度学习的OCR的转变。深度学习OCR技术通过模拟人脑神经元结构处理文本和图像数据&am…

MATLAB - 强化学习(Reinforcement Learning)

系列文章目录 前言 一、什么是强化学习&#xff1f; 强化学习是一种以目标为导向的计算方法&#xff0c;计算机通过与未知的动态环境交互来学习执行任务。这种学习方法能让计算机在没有人工干预和明确编程的情况下&#xff0c;做出一系列决策&#xff0c;使任务的累积奖励最大化…

cmake 编译教程

参考链接&#xff1a;cmake使用详细教程&#xff08;日常使用这一篇就足够了&#xff09;_cmake教程-CSDN博客 一、只有一个源文件的程序编译 首先在当前目录下创建两个文件 hello.cpp CMakeLists.txt &#xff08;注意CMakeLists大小写&#xff0c;不要写错了&#xff09; …

推荐一个优秀的 .NET MAUI 组件库

目录 前言 组件介绍 组件展示 布局 按钮 复选框 进度条 导航栏 组件地址 最后 前言 .NET MAUI 的发布&#xff0c;项目中可以使用这个新的跨平台 UI 框架来轻松搭建的移动和桌面应用。 为了帮助大家更快地构建美观且功能丰富的应用&#xff0c;本文将推荐一款优秀…

AcCode核心思路

文章目录 在线OJ项目核心思路1. 项目介绍2.预备知识理解多进程编程为啥采用多进程而不使用多线程?标准输入&标准输出&标准错误 3.项目实现题目API实现相关实体类定义新增/修改题目获取题目列表 编译运行编译运行流程 4.统一功能处理 在线OJ项目核心思路 1. 项目介绍 …

有序转化数组(LeetCode)

题目 给你一个已经 排好序 的整数数组 和整数 、 、 。对于数组中的每一个元素 &#xff0c;计算函数值 &#xff0c;请 按升序返回数组 。 解题 在时间复杂度为解决问题 def sortTransformedArray(nums, a, b, c):def f(x):return a * x * x b * x cn len(nums)result…

4个从阿里毕业的P7打工人,当起了包子铺的老板

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&chksmc0e47813f793f105017fb8551c9b996dc7782987e19efb166ab665f44ca6d900210e6c4c0281&scene21#wechat_redirect 《网安面试指南》h…

学生公寓电费信息管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;公寓管理员管理&#xff0c;学生管理&#xff0c;楼层信息管理&#xff0c;用电情况管理&#xff0c;缴费清单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;用电情况…

【数据结构】六、图:4.图的遍历(深度优先算法DFS、广度优先算法BFS)

三、基本操作 文章目录 三、基本操作1.图的遍历1.1 深度优先遍历DFS1.1.1 DFS算法1.1.2 DFS算法的性能分析1.1.3 深度优先的生成树和生成森林 1.2 广度优先遍历BFS1.2.1 BFS算法1.2.2 BFS算法性能分析1.2.3 广度优先的生成树和生成森林 1.3 图的遍历与图的连通性 1.图的遍历 图…

Nginx系列-Nginx Location匹配规则

文章目录 Nginx系列-Nginx Location匹配规则1. 语法基础2. 匹配规则2.1 精确匹配&#xff08;&#xff09;2.2. 最长前缀匹配&#xff08;^~&#xff09;2.3. 正则表达式匹配&#xff08;~和~*&#xff09;2.4. 普通前缀匹配&#xff08;无修饰符&#xff09;2.5. 默认匹配&…

贷齐乐hpp+php特性注入

文章目录 运行过程waf第一层waf拦截第二层waf拦截 数据库查询语句注入思路注入 运行过程 foreach ($_REQUEST as $key > $value) {$_REQUEST[$key] dowith_sql($value);}$request_uri explode("?", $_SERVER[REQUEST_URI]);if (isset($request_uri[1])) {$rewr…

OpenGL3.3_C++_Windows(34)

demo 1 Fresnel-Schlick PBR直接光源 顾名思义&#xff1a;直接光源指有光源直接照射到点p 的辐射强度&#xff0c;由于一个光源只会有一个光线wi影响点p&#xff0c;所以和之前的计算没什么差异对于影响p的光源&#xff0c;并不需要积分计算半球形辐照度&#xff0c;遍历每个…

redis面试(十)锁释放

自动释放 首先锁的释放分为两种&#xff0c;一种是自动释放&#xff0c;加入说加锁的线程宕机了不在了&#xff0c;我们之前说过这个。 那这个线程中的对redis这个锁不断刷新过期时间的看门狗逻辑就没有了&#xff0c;所以这个锁最多等待30s的时间就会自动过期删除&#xff0c…

为什么选择在Facebook投放广告?

2024年了你还没对 Facebook 广告产生兴趣&#xff1f;那你可就亏大了&#xff01; 今天这篇文章&#xff0c;我们会分享它对你扩大业务的好处。要知道&#xff0c;Facebook 广告凭借它庞大的用户群和先进的定位选项&#xff0c;已经是企业主们有效接触目标受众的必备神器。接下…

【uniapp】uniapp+vue2微信小程序实现分享功能

uniappvue2做的微信小程序实现分享功能 问题描述 uniappvue2做的微信小程序&#xff0c;发布以后点击右上角三个点&#xff0c;分享小程序的时候&#xff0c;转发和分享按钮都是灰色 解决方案 转发、分享、复制链接这几个功能需要自己来手动写方法&#xff0c;考虑到每个页…

Unity入门3——脚本入门

本文使用的代码编辑器为VSCode 安装接口有&#xff1a; 通过将变量设置为public&#xff0c;可以直接在unity的Inspector面板中看到相关变量。此时可直接将需要的素材拖拽到变量处。 [SerializeField]可序列化&#xff1a;定义后可以使非公共的属性也显示在unity面板 [Range]…

搜维尔科技:【研究】大屏幕沉浸式系统的优势,视觉冲击强、‌分辨率高、‌画面层次感强以及沉浸式交互性体验好等!

大屏幕沉浸式系统的优势主要体现在视觉冲击强、‌分辨率高、‌画面层次感强以及沉浸式交互性体验好。‌ 视觉冲击强&#xff1a;‌大屏幕沉浸式系统通过使用多台投影机投射画面&#xff0c;‌结合高质量影片&#xff0c;‌营造出场景环境&#xff0c;‌通过视觉艺术直击体验者…

js 深入理解原型(prototype)及如何创建对象

目录 1. 概述2. 工厂模式3. 构造函数模式3.1 创建的格式3.2 JS内部执行步骤3.3 constructor 构造器3.4 构造函数也是函数3.5 构造函数的问题 4. 原型模式 prototype4.1 理解原型本质4.2 原型层级(访问一个属性&#xff0c;查询的次序&#xff09;4.2.1 查询次序&#xff1a;实例…

SeaTunnel 实战: Apache SeaTunnel 安装与部署

文章目录 一、准备工作1.1 环境1.2 下载 二、SeaTunnel安装2.1 解压安装包2.2.配置环境变量2.3.配置立刻生效2.4 下载SeaTunnel相关jar包2.5 测试验证2.6 启动服务 三、SeaTunnel Web 1.0.1安装3.1 将下载的压缩包解压缩到指定目录下3.2 设置 SeaTunnel Web 环境变量3.3 初始化…

pythonUI自动化008::allure测试报告(安装及应用)

allure报告预览 1 下载jdk&#xff0c;配置jdk Path变量&#xff1a; https://www.cnblogs.com/FBGG/p/15103119.html&#xff08;这里不作阐述&#xff0c;请看该偏文章配置即可&#xff09; 2 下载allure驱动&#xff0c;配置allure Path变量&#xff1a; 下载allure驱动&a…