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 private RedisTemplate redisTemplate; @Resource private AppletChatConfig appletChatConfig; /** * 返回成功状态码 */ 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 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 二维码地址 */ public String createAppletCode(Long weChatCodeId, boolean failFlag, String fileName, String appletPageUrl, String filePathWeChatCodeUrl) { 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 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 */ public String getAppletAccessToken() { 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; } }