2023-09-21 15:57:25 +08:00
|
|
|
|
package com.xinelu.common.utils;
|
|
|
|
|
|
|
|
|
|
|
|
import com.alibaba.fastjson2.JSON;
|
|
|
|
|
|
import com.xinelu.common.config.AppletChatConfig;
|
|
|
|
|
|
import com.xinelu.common.config.XinELuConfig;
|
|
|
|
|
|
import com.xinelu.common.constant.Constants;
|
|
|
|
|
|
import com.xinelu.common.entity.AppletAccessToken;
|
|
|
|
|
|
import com.xinelu.common.entity.AppletLoginVO;
|
|
|
|
|
|
import com.xinelu.common.entity.AppletPhoneVO;
|
|
|
|
|
|
import com.xinelu.common.exception.ServiceException;
|
|
|
|
|
|
import com.xinelu.common.utils.http.HttpUtils;
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
import org.apache.commons.lang3.BooleanUtils;
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
|
import org.apache.http.HttpResponse;
|
|
|
|
|
|
import org.apache.http.client.methods.HttpPost;
|
|
|
|
|
|
import org.apache.http.entity.StringEntity;
|
|
|
|
|
|
import org.apache.http.impl.client.CloseableHttpClient;
|
|
|
|
|
|
import org.apache.http.impl.client.HttpClientBuilder;
|
|
|
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @Description 微信小程序工具类
|
|
|
|
|
|
* @Author 纪寒
|
|
|
|
|
|
* @Date 2022-08-17 16:11:10
|
|
|
|
|
|
* @Version 1.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
@Component
|
|
|
|
|
|
public class AppletChatUtil {
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
2023-11-08 17:02:12 +08:00
|
|
|
|
private RedisTemplate<String, Object> redisTemplate;
|
2023-09-21 15:57:25 +08:00
|
|
|
|
@Resource
|
2023-11-08 17:02:12 +08:00
|
|
|
|
private AppletChatConfig appletChatConfig;
|
2023-09-21 15:57:25 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 返回成功状态码
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final int SUCCESS_CODE = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 小程序登录url
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String APPLET_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 小程序accessToken的url
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 小程序获取用户手机号的url
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取小程序二维码接口地址
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String GET_APPLET_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* JSON数据格式标识
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String JSON_DATA_FORMAT = "Content-Type: application/json; encoding=utf-8";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成小程序二维码图片失败标识
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String FAIL = "FAIL";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据登录编码获取微信用户信息
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param appletId 小程序id
|
|
|
|
|
|
* @param secret 小程序秘钥
|
|
|
|
|
|
* @param code 登录凭证码
|
|
|
|
|
|
* @param grantType 授权类型
|
|
|
|
|
|
* @return 登录信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static AppletLoginVO getAppletLoginInfo(String appletId, String secret, String code, String grantType) {
|
|
|
|
|
|
//请求地址
|
|
|
|
|
|
String appletLoginUrl = APPLET_LOGIN_URL
|
|
|
|
|
|
+ "?appid=" + appletId
|
|
|
|
|
|
+ "&secret=" + secret
|
|
|
|
|
|
+ "&js_code=" + code
|
|
|
|
|
|
+ "&grant_type=" + grantType;
|
|
|
|
|
|
//发送请求
|
|
|
|
|
|
String result = HttpUtils.sendGet(appletLoginUrl);
|
|
|
|
|
|
if (StringUtils.isBlank(result)) {
|
|
|
|
|
|
throw new ServiceException("获取微信小程序用户信息失败", 201);
|
|
|
|
|
|
}
|
|
|
|
|
|
return JSON.parseObject(result, AppletLoginVO.class);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取微信小程序的accessToken
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param appletId 小程序id
|
|
|
|
|
|
* @param secret 小程序秘钥
|
|
|
|
|
|
* @return accessToken信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static AppletAccessToken getAppletAccessToken(String appletId, String secret) {
|
|
|
|
|
|
//请求路径
|
|
|
|
|
|
String accessTokenUrl = ACCESS_TOKEN_URL + "&appid=" + appletId + "&secret=" + secret;
|
|
|
|
|
|
//发送请求
|
|
|
|
|
|
String result = HttpUtils.sendGet(accessTokenUrl);
|
|
|
|
|
|
if (StringUtils.isBlank(result)) {
|
|
|
|
|
|
throw new ServiceException("获取微信小程序accessToken信息失败", 201);
|
|
|
|
|
|
}
|
|
|
|
|
|
return JSON.parseObject(result, AppletAccessToken.class);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取微信小程序的手机号码
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param code 登录凭证
|
|
|
|
|
|
* @param accessToken 小程序accessToken
|
|
|
|
|
|
* @return 手机信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static AppletPhoneVO getAppletPhoneInfo(String code, String accessToken) {
|
|
|
|
|
|
//请求地址
|
|
|
|
|
|
String phoneUrl = PHONE_NUMBER_URL + accessToken;
|
|
|
|
|
|
//请求参数
|
|
|
|
|
|
Map<String, Object> paramMap = new HashMap<>();
|
|
|
|
|
|
paramMap.put("code", code);
|
|
|
|
|
|
String param = JSON.toJSONString(paramMap);
|
|
|
|
|
|
//发送POST请求
|
|
|
|
|
|
String result = HttpUtils.sendPostJson(phoneUrl, param);
|
|
|
|
|
|
if (StringUtils.isBlank(result)) {
|
|
|
|
|
|
throw new ServiceException("获取微信小程序手机号失败", 201);
|
|
|
|
|
|
}
|
|
|
|
|
|
return JSON.parseObject(result, AppletPhoneVO.class);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 递归获取小程序二维码信息
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param weChatCodeId 输入id
|
|
|
|
|
|
* @param failFlag 调用失败标识
|
|
|
|
|
|
* @param fileName 文件名称
|
|
|
|
|
|
* @param appletPageUrl 二维码跳转路径
|
|
|
|
|
|
* @param filePathWeChatCodeUrl 文件保存路径
|
|
|
|
|
|
* @return java.lang.String 二维码地址
|
|
|
|
|
|
*/
|
2023-11-08 17:02:12 +08:00
|
|
|
|
public String createAppletCode(Long weChatCodeId, boolean failFlag, String fileName, String appletPageUrl, String filePathWeChatCodeUrl) {
|
2023-09-21 15:57:25 +08:00
|
|
|
|
if (BooleanUtils.isTrue(failFlag)) {
|
|
|
|
|
|
//删除原有Redis中的key值
|
|
|
|
|
|
String accessTokenKey = Constants.NURSE_STATION_APPLET_ACCESS_TOKEN + "accessToken";
|
|
|
|
|
|
Object object = redisTemplate.opsForValue().get(accessTokenKey);
|
|
|
|
|
|
if (Objects.nonNull(object)) {
|
|
|
|
|
|
redisTemplate.delete(accessTokenKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//删除原有Redis中缓存的AccessToken
|
|
|
|
|
|
String appletAccessToken = getAppletAccessToken();
|
|
|
|
|
|
if (StringUtils.isBlank(appletAccessToken)) {
|
|
|
|
|
|
throw new ServiceException("获取小程序凭证信息失败,请联系管理员!");
|
|
|
|
|
|
}
|
|
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
|
|
|
|
params.put("scene", weChatCodeId);
|
|
|
|
|
|
params.put("page", appletPageUrl);
|
|
|
|
|
|
String body = JSON.toJSONString(params);
|
|
|
|
|
|
String filePath = XinELuConfig.getProfile() + filePathWeChatCodeUrl + "/" + weChatCodeId;
|
|
|
|
|
|
//调用微信接口获取小程序二维码并将其上传到服务器中
|
|
|
|
|
|
return getAppletCodePicture(appletAccessToken, body, filePath, fileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据小程序AccessToken获取小程序的二维码图片
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param accessToken 微信小程序的accessToken
|
|
|
|
|
|
* @param body 接口传输参数
|
|
|
|
|
|
* @param filePath 文件路径
|
|
|
|
|
|
* @param fileName 文件名称
|
|
|
|
|
|
* @return 小程序的二维码图片
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String getAppletCodePicture(String accessToken, String body, String filePath, String fileName) {
|
|
|
|
|
|
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
|
|
|
|
|
|
HttpPost httpPost = new HttpPost(GET_APPLET_CODE_URL + accessToken);
|
|
|
|
|
|
try {
|
|
|
|
|
|
StringEntity entity = new StringEntity(body, "UTF-8");
|
|
|
|
|
|
entity.setContentType("image/png");
|
|
|
|
|
|
httpPost.setEntity(entity);
|
|
|
|
|
|
HttpResponse response = httpClient.execute(httpPost);
|
|
|
|
|
|
String contentType = response.getEntity().getContentType().toString();
|
|
|
|
|
|
if (StringUtils.equals(contentType, JSON_DATA_FORMAT)) {
|
|
|
|
|
|
return FAIL;
|
|
|
|
|
|
}
|
|
|
|
|
|
String absolutePath = getAbsoluteFile(filePath, fileName).getAbsolutePath();
|
|
|
|
|
|
try (InputStream inputStream = response.getEntity().getContent(); FileOutputStream out = new FileOutputStream(absolutePath)) {
|
|
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
|
|
int len;
|
|
|
|
|
|
while ((len = inputStream.read(buffer)) != -1) {
|
|
|
|
|
|
out.write(buffer, 0, len);
|
|
|
|
|
|
}
|
|
|
|
|
|
out.flush();
|
|
|
|
|
|
}
|
|
|
|
|
|
return absolutePath;
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
log.error("获取小程序二维码接口调用失败,失败原因为:{}", e.getMessage());
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建文件路径
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param uploadDir 路径名称
|
|
|
|
|
|
* @param fileName 文件名称
|
|
|
|
|
|
* @return File 文件目录
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static File getAbsoluteFile(String uploadDir, String fileName) {
|
|
|
|
|
|
File file = new File(uploadDir + File.separator + fileName);
|
|
|
|
|
|
if (!file.exists()) {
|
|
|
|
|
|
if (!file.getParentFile().exists()) {
|
|
|
|
|
|
boolean mkdirs = file.getParentFile().mkdirs();
|
|
|
|
|
|
if (BooleanUtils.isFalse(mkdirs)) {
|
|
|
|
|
|
throw new ServiceException("文件路径创建失败,请联系管理员!");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return file;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取小程序AccessToken方法
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return 小程序的AccessToken
|
|
|
|
|
|
*/
|
2023-11-08 17:02:12 +08:00
|
|
|
|
public String getAppletAccessToken() {
|
2023-09-21 15:57:25 +08:00
|
|
|
|
String accessToken;
|
|
|
|
|
|
String accessTokenKey = Constants.NURSE_STATION_APPLET_ACCESS_TOKEN + "accessToken";
|
|
|
|
|
|
//从Redis中取出accessToken
|
|
|
|
|
|
Object object = redisTemplate.opsForValue().get(accessTokenKey);
|
|
|
|
|
|
if (Objects.isNull(object)) {
|
|
|
|
|
|
//没有,获取accessToken
|
|
|
|
|
|
AppletAccessToken appletAccessToken = AppletChatUtil.getAppletAccessToken(appletChatConfig.getAppletId(), appletChatConfig.getSecret());
|
|
|
|
|
|
if (Objects.isNull(appletAccessToken)) {
|
|
|
|
|
|
throw new ServiceException("获取微信小程序accessToken信息失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Objects.nonNull(appletAccessToken.getErrcode()) && appletAccessToken.getErrcode() != SUCCESS_CODE) {
|
|
|
|
|
|
throw new ServiceException("获取微信小程序accessToken信息失败,失败信息为:" + appletAccessToken.getErrmsg(), 201);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (StringUtils.isBlank(appletAccessToken.getAccessToken())) {
|
|
|
|
|
|
throw new ServiceException("accessToken信息为空");
|
|
|
|
|
|
}
|
|
|
|
|
|
//存入Redis中
|
|
|
|
|
|
redisTemplate.opsForValue().set(accessTokenKey, appletAccessToken.getAccessToken(), 3600, TimeUnit.SECONDS);
|
|
|
|
|
|
accessToken = appletAccessToken.getAccessToken();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
accessToken = (String) object;
|
|
|
|
|
|
}
|
|
|
|
|
|
return accessToken;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|