全文目录,一步到位

1.前言简介1.1 专栏传送门

2. 微信小程序公用功能2.1 配置准备工作2.1.1 配置文件准备(单体放`yml`中 微服务放`配置中心`)2.1.2 获取配置文件中的小程序配置2.1.3 设置redis配置

2.2 创建不同功能工具类2.2.1 创建微信服务工具类`WechatServiceUtils`2.2.2 创建RedisCache

2.3 各个功能传送门2.3.1 获取accessToken2.3.2 获取手机号2.3.3 获取小程序二维码(不限制)2.3.4 获取openId与unionId

3. 文章的总结3.1 本文总结3.2 本文统一说明

1.前言简介

本文是微信小程序公共类 以下功能均使用 此文章 代码

获取accessToken手机号小程序二维码openId与UnionId

最下面有传送门, 传送到每个功能 避免多次封装 远程调用均使用restTemplate (springboot自带, 操作简单) 使用其他请随意…

1.1 专栏传送门

===> 微信小程序相关操作专栏 <===

2. 微信小程序公用功能

2.1 配置准备工作

2.1.1 配置文件准备(单体放yml中 微服务放配置中心)

(特别注意 这里面包含大部分微信小程序配置) appid 和 secret 填写上 ps: 里面部分配置可以删除

过期时间启动执行登录url其他业务 如获取手机号, 获取小程序二维码等

# 微信核心参数

wechat:

# 小程序

mini-app:

# 请求url

requestUrl: /wxMiniLogin

# 请求方法

requestMethod: POST

# appid

appId: ?

# app秘钥

appSecret: ?

# 微信基础请求网址

wxBaseRequestUrl: https://api.weixin.qq.com

# 获取access_token必备参数

grantType: client_credential

# 微信获取access_token的url

aTokenUrl: ${wechat.mini-app.wxBaseRequestUrl}/cgi-bin/token?grant_type=${wechat.mini-app.grantType}&appid=%s&secret=%s

# 要分钟(尽量少于120分钟) 一天上限2000次 这里一天最多请求24次 可增加定时刷新缓存等

expiredTime: 70

# 是否执行服务启动执行

serverStartAutoRun: false

# 微信登录提供的接口(GET)

wxLoginUrlTemplate: ${wechat.mini-app.wxBaseRequestUrl}/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code

# 微信获取手机号接口

wxGetPhoneUrl: ${wechat.mini-app.wxBaseRequestUrl}/wxa/business/getuserphonenumber?access_token=%s

#微信获取二维码(不限制)post请求

wxACodeUnLimitUrl: ${wechat.mini-app.wxBaseRequestUrl}/wxa/getwxacodeunlimit?access_token=%s

#微信获取二维码(限制)post请求

wxACodeUrl: ${wechat.mini-app.wxBaseRequestUrl}/wxa/getwxacode?access_token=%s

# 微信获取二维码(限制)post请求

wxAQrcodeUrl: ${wechat.mini-app.wxBaseRequestUrl}/cgi-bin/wxaapp/createwxaqrcode?access_token=%s

2.1.2 获取配置文件中的小程序配置

使用@ConfigurationProperties注解对应

具体介绍我没有写: 找了一篇写的比较全的链接=> 传送门: @ConfigurationProperties使用方式当然 可以使用@Value替换上面这种(二选一)

代码如下, 名字对应配置文件名称, 注释自己加一下吧

import lombok.Data;

import lombok.extern.slf4j.Slf4j;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

/**

* 小程序获取配置类

*

* @author pzy

* @version 0.1.0

* @description TODO

*/

@Slf4j

@Data

@Component

@ConfigurationProperties(prefix = "wechat.mini-app")

public class WechatConfigProperties {

private String requestUrl;

private String requestMethod;

private String appId;

private String appSecret;

private String wxBaseRequestUrl;

private String grantType;

private String aTokenUrl;

private Integer expiredTime;

private Boolean serverStartAutoRun;

private String wxLoginUrlTemplate;

/**

* 获取手机号

*/

private String wxGetPhoneUrl;

/**

* 二维码无限制url(主)

*/

private String wxACodeUnLimitUrl;

private String wxACodeUrl;

private String wxAQrcodeUrl;

/**

* 生成微信登录请求地址

*

* @param code code

* @return 请求地址

*/

public String getWxLoginUrl(String code) {

return String.format(wxLoginUrlTemplate, appId, appSecret, code);

}

/**

* 生成微信ACCESS_TOKEN请求地址

*

* 模板样式: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s

*

* @return 请求地址

*/

public String getATokenUrl() {

return String.format(aTokenUrl, appId, appSecret);

}

/**

* 获取手机号url post请求

*

* @param accessToken

* @return

*/

public String getPhoneUrl(String accessToken) {

return String.format(wxGetPhoneUrl, accessToken);

}

/**

* 获取不限制的微信二维码url

*

* @param accessToken

* @return

*/

public String getWxACodeUnLimitUrl(String accessToken) {

return String.format(wxACodeUnLimitUrl, accessToken);

}

/**

* 获取(限制一)的微信二维码url

*

* @param accessToken

* @return

*/

public String getWxACodeUrl(String accessToken) {

return String.format(wxACodeUrl, accessToken);

}

/**

* 获取(限制二)的微信二维码url

*

* @param accessToken

* @return

*/

public String getWxAQrcodeUrl(String accessToken) {

return String.format(wxAQrcodeUrl, accessToken);

}

}

2.1.3 设置redis配置

spring:

redis:

# 地址

host: 192.168.1.130

# 端口,默认为6379

port: 6379

# 数据库索引

database: 0

# 密码

password: 123456

# 连接超时时间

timeout: 10s

lettuce:

pool:

# 连接池中的最小空闲连接

min-idle: 5

# 连接池中的最大空闲连接

max-idle: 10

# 连接池的最大数据库连接数

max-active: 50

# 连接池最大阻塞等待时间(使用负值表示没有限制)

max-wait: 5000ms

2.2 创建不同功能工具类

2.2.1 创建微信服务工具类WechatServiceUtils

RestTemplate 直接可以使用 WechatConfigProperties 请见 2.1.2 RedisCache 请见2.2.2

/**

* 微信服务工具类

*

* @author pzy

* @version 0.1.0

* @description: TODO

*/

@Slf4j

@RequiredArgsConstructor

@Component

public class WechatServiceUtils {

/**

* 远程调用

*/

private final RestTemplate restTemplate;

/**

* redis缓存

*/

private final RedisCache redisCache;

/**

* 微信统一配置

*/

private final WechatConfigProperties wechatConfigProperties;

/**

* 1. 获取微信登录认证信息

*

* @param wxCommonReqDto

* @return

*/

public Map getWxMiniAuth(WxCommonReqDto wxCommonReqDto) {

String code = wxCommonReqDto.getWxCode();

//秘钥

String encryptedIv = wxCommonReqDto.getIv();

//加密数据

String encryptedData = wxCommonReqDto.getEncryptedData();

JSONObject wxAuthResponse = null;

String sessionKey = "";

String openid = "";

try {

//1. 想微信服务器发送请求获取用户信息

String url = wechatConfigProperties.getWxLoginUrl(code);

log.info("===> 请求微信url是: {}", url);

//2. 远程调用微信接口

String res = restTemplate.getForObject(url, String.class);

wxAuthResponse = JSONObject.parseObject(res);

//3. 解析返回参数 报错则进行对应处理

/*校验1: wx请求不是null*/

if (wxAuthResponse != null) {

/*校验2: 响应对象是否正确*/

CheckUtils.responseCheck(wxAuthResponse);

//3.1 获取session_key和openid

sessionKey = wxAuthResponse.getString("session_key");

openid = wxAuthResponse.getString("openid");

log.info("===> openid: {}", openid);

/*校验3: 响应信息是否正常*/

if (StringUtils.isBlank(sessionKey) || StringUtils.isBlank(openid)) {

log.error("小程序授权失败,session_key或open_id是空!");

throw new ServiceException("抱歉, 小程序授权失败,缺少关键返回参数!");

}

log.info("===> 微信回调信息: {}", wxAuthResponse);

}

} catch (Exception e) {

e.printStackTrace();

throw new ServiceException("抱歉, 小程序授权失败!");

}

//4. 获取微信信息并制作token

/*校验:(选用)如果获取union_id 需要进行解密*/

Map map = new HashMap<>();

String token = "";

if (StringUtils.isNotBlank(encryptedIv) && StringUtils.isNotBlank(encryptedData)) {

String decryptResult = "";

try {

//如果没有绑定微信开放平台,解析结果是没有unionid的。

decryptResult = AESUtils.decrypt(sessionKey, encryptedIv, encryptedData);

if (StringUtils.hasText(decryptResult)) {

//如果解析成功,获取token

map.put("type", String.valueOf(1));

map.put("decryptResult", decryptResult);

}

} catch (Exception e) {

e.printStackTrace();

throw new ServiceException("微信登录失败!");

}

} else {

//如果前端只传wxCode 没有unionId的需求 只要openId的

map.put("type", String.valueOf(2));

map.put("decryptResult", openid);

}

/*校验: 数据为空的情况*/

if (StringUtils.isBlank(map.get("type")) || StringUtils.isBlank(map.get("decryptResult"))) {

throw new ServiceException(ResponseEnum.A10007);

}

return map;

}

/**

* 2. 获取缓存中的AccessToken

*

* 没有从微信拉取[可配合定时]

*

* @return accessToken

*/

public String getRedisCacheAccessToken() {

/*校验: 缓存中有accessToken的key*/

if (redisCache.hasKey(CacheConstants.WX_ACCESS_TOKEN)) {

log.info("二级缓存数据取出accessToken成功!");

return redisCache.getCacheObject(CacheConstants.WX_ACCESS_TOKEN);

}

//这里不用三目(不好看~~)

return getWxMiniAccessToken();

}

/**

* 3. 访问微信官方获取两小时的 accessToken

*

* @return accessToken

*/

public String getWxMiniAccessToken() {

Map query = new HashMap<>();

query.put("grant_type", wechatConfigProperties.getGrantType());//client_credential

query.put("secret", wechatConfigProperties.getAppSecret());

query.put("appid", wechatConfigProperties.getAppId());

try {

String aTokenUrl = wechatConfigProperties.getATokenUrl();

// ResponseEntity responseEntity = restTemplate.postForEntity(aTokenUrl, query, JSONObject.class);

ResponseEntity responseEntity = restTemplate.getForEntity(aTokenUrl, JSONObject.class, query);

HttpStatus statusCode = responseEntity.getStatusCode(); //状态码

// System.out.println(responseEntity.getHeaders());//获取到头信息

/*校验: 如果接口成功 200*/

if (Objects.equals(statusCode.value(), 200)) {

JSONObject responseJsonBody = responseEntity.getBody();//响应体

log.info("[请求微信小程序官方接口] => 获取accessToken请求成功返回值:{}", responseJsonBody);

if (responseJsonBody == null) {

log.info("微信小程序获取accessToken请求返回result是null!");

throw new ServiceException(ResponseEnum.A10005);

}

//获取accessToken

String accessToken = responseJsonBody.getString("access_token");

if (StringUtils.isBlank(accessToken)) {

log.info("微信小程序获取accessToken请求返回access_token是null!");

throw new ServiceException(ResponseEnum.A10005);

}

//放入缓存中

redisCache.setCacheObject(CacheConstants.WX_ACCESS_TOKEN, accessToken, wechatConfigProperties.getExpiredTime(), TimeUnit.MINUTES);

return accessToken;

} else {

log.error("微信HttpStatus的StatusCode不是200 {}", statusCode.value());

throw new ServiceException(ResponseEnum.A10005);

}

} catch (Exception e) {

e.printStackTrace();

log.info("微信小程序获取accessToken请求异常信息 {}", e.getMessage());

throw new ServiceException(ResponseEnum.A10005);

}

}

/**

* 错误码 错误描述 解决方案

* -1 system error [系统繁忙,此时请开发者稍候再试]

* 40029 code 无效 [js_code无效]

* 45011 api minute-quota reach limit mustslower retry [next minute API 调用太频繁,请稍候再试]

* 40013 invalid appid [请求appid身份与获取code的小程序appid不匹配]

* 错误码

*

* @param code js_code

* @return phone

*/

public String getPhoneByCode(String code) {

String phoneUrl = wechatConfigProperties.getPhoneUrl(getRedisCacheAccessToken());

Map map = new HashMap<>();

map.put("code", code);

JSONObject jsonObject = sendPostRestTemplate(phoneUrl, map, JSONObject.class);

System.out.println(jsonObject);

if (jsonObject.containsKey("errcode")) {

/*如果异常码是0 说明正常*/

if (!Objects.equals(String.valueOf(jsonObject.get("errcode")), "0")) {

log.error("===> 获取手机号的异常信息 : {}", jsonObject + "");

throw new ServiceException("获取失败: " + jsonObject.get("errmsg"), (Integer) jsonObject.get("errcode"));

}

}

JSONObject phoneInfo = jsonObject.getJSONObject("phone_info");

return phoneInfo.getString("phoneNumber");

}

/**

* 生成小程序带参数二维码

* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html#HTTPS%20%E8%B0%83%E7%94%A8

*/

@SneakyThrows

public InputStream getUnlimitedWxQrCode(WxCodeUnlimitedReqDTO wxCodeUnlimitedReqDTO, String accessToken) {

Map params = new HashMap<>();

params.put("scene", wxCodeUnlimitedReqDTO.getScene());

params.put("page", wxCodeUnlimitedReqDTO.getPage());

params.put("path", wxCodeUnlimitedReqDTO.getPage());

params.put("env_version", wxCodeUnlimitedReqDTO.getEnvVersion());

params.put("width", wxCodeUnlimitedReqDTO.getWidth());

params.put("auto_color", wxCodeUnlimitedReqDTO.getAutoColor());//自动配置线条颜色

ResponseEntity response = restTemplate.postForEntity(wechatConfigProperties.getWxACodeUnLimitUrl(accessToken), JSON.toJSONString(params), byte[].class);

System.out.println(JSON.toJSONString(params));

byte[] buffer = response.getBody();

// System.out.println(Base64.getEncoder().encodeToString(buffer));

assert buffer != null;

return new ByteArrayInputStream(buffer);

}

/**

* 远程调用 restTemplate方法 post请求

*

* @param url

* @param body

* @return

*/

public T sendPostRestTemplate(String url, Map body, Class responseType) {

return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(body, null), responseType).getBody();

}

}

2.2.2 创建RedisCache

redis的增删改查操作 记得配置序列化与反序列化 文章传送门: ===> redis高级(序列化与反序列化) <===

@Component

public class RedisCache {

@Autowired

public RedisTemplate redisTemplate;

/**

* 缓存基本的对象,Integer、String、实体类等

*

* @param key 缓存的键值

* @param value 缓存的值

*/

public void setCacheObject(final String key, final T value) {

redisTemplate.opsForValue().set(key, value);

}

/**

* 缓存基本的对象,Integer、String、实体类等

*

* @param key 缓存的键值

* @param value 缓存的值

* @param timeout 时间

* @param timeUnit 时间颗粒度

*/

public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {

redisTemplate.opsForValue().set(key, value, timeout, timeUnit);

}

/**

* 设置有效时间

*

* @param key Redis键

* @param timeout 超时时间

* @return true=设置成功;false=设置失败

*/

public boolean expire(final String key, final long timeout) {

return expire(key, timeout, TimeUnit.SECONDS);

}

/**

* 设置有效时间

*

* @param key Redis键

* @param timeout 超时时间

* @param unit 时间单位

* @return true=设置成功;false=设置失败

*/

public boolean expire(final String key, final long timeout, final TimeUnit unit) {

return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));

}

/**

* 获取有效时间

*

* @param key Redis键

* @return 有效时间

*/

public long getExpire(final String key) {

if (StringUtils.isBlank(key)) {

throw new IllegalArgumentException("key cannot be null");

}

return redisTemplate.getExpire(key);

}

/**

* 判断 key是否存在

*

* @param key 键

* @return true 存在 false不存在

*/

public Boolean hasKey(String key) {

return redisTemplate.hasKey(key);

}

/**

* 获得缓存的基本对象。

*

* @param key 缓存键值

* @return 缓存键值对应的数据

*/

public T getCacheObject(final String key) {

ValueOperations operation = redisTemplate.opsForValue();

return operation.get(key);

}

/**

* 删除单个对象

*

* @param key

*/

public boolean deleteObject(final String key) {

return Boolean.TRUE.equals(redisTemplate.delete(key));

}

/**

* 删除集合对象

*

* @param collection 多个对象

* @return

*/

public boolean deleteObject(final Collection collection) {

return redisTemplate.delete(collection) > 0;

}

/**

* 缓存List数据

*

* @param key 缓存的键值

* @param dataList 待缓存的List数据

* @return 缓存的对象

*/

public long setCacheList(final String key, final List dataList) {

Long count = redisTemplate.opsForList().rightPushAll(key, dataList);

return count == null ? 0 : count;

}

/**

* 获得缓存的list对象

*

* @param key 缓存的键值

* @return 缓存键值对应的数据

*/

public List getCacheList(final String key) {

return redisTemplate.opsForList().range(key, 0, -1);

}

/**

* 缓存Set

*

* @param key 缓存键值

* @param dataSet 缓存的数据

* @return 缓存数据的对象

*/

public BoundSetOperations setCacheSet(final String key, final Set dataSet) {

BoundSetOperations setOperation = redisTemplate.boundSetOps(key);

Iterator it = dataSet.iterator();

while (it.hasNext()) {

setOperation.add(it.next());

}

return setOperation;

}

/**

* 获得缓存的set

*

* @param key

* @return

*/

public Set getCacheSet(final String key) {

return redisTemplate.opsForSet().members(key);

}

/**

* 缓存Map

*

* @param key

* @param dataMap

*/

public void setCacheMap(final String key, final Map dataMap) {

if (dataMap != null) {

redisTemplate.opsForHash().putAll(key, dataMap);

}

}

/**

* 获得缓存的Map

*

* @param key

* @return

*/

public Map getCacheMap(final String key) {

return redisTemplate.opsForHash().entries(key);

}

/**

* 往Hash中存入数据

*

* @param key Redis键

* @param hKey Hash键

* @param value 值

*/

public void setCacheMapValue(final String key, final String hKey, final T value) {

redisTemplate.opsForHash().put(key, hKey, value);

}

/**

* 获取Hash中的数据

*

* @param key Redis键

* @param hKey Hash键

* @return Hash中的对象

*/

public T getCacheMapValue(final String key, final String hKey) {

HashOperations opsForHash = redisTemplate.opsForHash();

return opsForHash.get(key, hKey);

}

/**

* 获取多个Hash中的数据

*

* @param key Redis键

* @param hKeys Hash键集合

* @return Hash对象集合

*/

public List getMultiCacheMapValue(final String key, final Collection hKeys) {

return redisTemplate.opsForHash().multiGet(key, hKeys);

}

/**

* 删除Hash中的某条数据

*

* @param key Redis键

* @param hKey Hash键

* @return 是否成功

*/

public boolean deleteCacheMapValue(final String key, final String hKey) {

return redisTemplate.opsForHash().delete(key, hKey) > 0;

}

/**

* 获得缓存的基本对象列表(全部的key)

*

* @param pattern 字符串前缀

* @return 对象列表

*/

public Collection keys(final String pattern) {

return redisTemplate.keys(pattern);

}

}

2.3 各个功能传送门

2.3.1 获取accessToken

官方文档地址: => 获取接口调用凭据 <= 文章传送门 : => 微信小程序01: springboot获取accessToken方式 <=

2.3.2 获取手机号

官方文档地址: => 获取手机号 <= 文章传送门: => 微信小程序02: 使用微信快速验证组件code获取手机号

2.3.3 获取小程序二维码(不限制)

官方文档地址: => 获取不限制的小程序码 <= 文章传送门:

2.3.4 获取openId与unionId

官方文档地址: => 小程序登录 <= 文章传送门:

3. 文章的总结

3.1 本文总结

本文使用的技术栈

springboot相关操作restTemplate远程调用使用方式redisTemplate 操作redis的操作redis的序列化与反序列化

3.2 本文统一说明

本篇涵盖大部分的微信小程序操作(无支付), 统一封装 在 2.3 中统一传送到具体功能配置 避免多次配置, 传送门内的功能细节不在这篇介绍 特别注意: 文章传送门会在近期完善, 这是本专栏的第一篇, 之后也会围绕此篇进行更新, 接入支付等等

作者: pingzhuyan

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: 

发表评论

返回顶部暗黑模式