Files
jsowell-charger-web/jsowell-admin/src/main/java/com/jsowell/service/AgentDevService.java

403 lines
17 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.jsowell.service;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.jsowell.common.constant.CacheConstants;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.http.HttpUtils;
import com.jsowell.common.util.wxplatform.AesException;
import com.jsowell.common.util.wxplatform.WXBizMsgCrypt;
import com.jsowell.common.util.wxplatform.WXXmlToMapUtil;
import com.jsowell.pile.domain.agentDev.AuditItem;
import com.jsowell.pile.domain.agentDev.CategoryInfo;
import com.jsowell.pile.dto.agentDev.CommitCodeDTO;
import com.jsowell.pile.dto.agentDev.GetComponentTokenDTO;
import com.jsowell.pile.dto.agentDev.SubmitAuditDTO;
import com.jsowell.pile.vo.agentDev.AuthInfoVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 代开发小程序Service
*
* @author Lemon
* @Date 2023/7/27 15:58
*/
@Service
public class AgentDevService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private RedisCache redisCache;
/**
* 第三方平台 appid
*/
private static final String PLATFORM_APP_ID = "wxac3b282a58b9a4a8";
/**
* 第三方平台 secret
*/
private static final String PLATFORM_APP_SECRET = "eb8adc2689b4f27be7ae75a4b646dbe6";
/**
* 第三方平台 消息加解密Key
*/
private static final String PLATFORM_AES_KEY = "9TDTYLBLYGG1IOU9VOLXHNIKKJ65NU40S0ITJ0BFQOU";
/**
* 第三方平台 消息校验Token
*/
private static final String PLATFORM_COMPONENT_TOKEN = "XD83ZUJWTVRB4OSN";
/**
* 解析请求
*
* @param timeStamp
* @param nonce
* @param msgSignature
* @param postData
* @return
*/
public String parseRequest(String timeStamp, String nonce, String msgSignature, String postData) {
logger.info("==============================开始授权事件接收URL=================================");
try {
//这个类是微信官网提供的解密类,需要用到消息校验Token 消息加密Key和服务平台appid
WXBizMsgCrypt pc = new WXBizMsgCrypt(PLATFORM_COMPONENT_TOKEN, PLATFORM_AES_KEY, PLATFORM_APP_ID);
String xml = pc.decryptMsg(msgSignature, timeStamp, nonce, postData);
Map<String, String> result = WXXmlToMapUtil.xmlToMap(xml);// 将xml转为map
logger.info("微信第三方平台推送消息解析后 result:{}", result);
// 获取 infoType 确认是哪个事件
String infoType = MapUtils.getString(result, "InfoType");
if (StringUtils.equals("component_verify_ticket", infoType)) {
// 授权票据事件
verifyTicket(result);
} else if (StringUtils.equals("authorized", infoType)
|| StringUtils.equals("updateauthorized", infoType)
|| StringUtils.equals("unauthorized", infoType)) {
// 授权成功通知 authorized、授权更新通知 updateauthorized、 取消授权通知 unauthorized
// 小程序/公众号授权事件
authorizedInform(result);
}
} catch (AesException e) {
e.printStackTrace();
}
logger.info("==============================结束授权事件接收URL=================================");
return "success";
}
/**
* 验证票据事件
*
* @param map 微信第三方平台推送消息解析后的 map
* @return
*/
private String verifyTicket(Map<String, String> map) {
// 获取验证票据
String componentVerifyTicket = MapUtils.getString(map, "ComponentVerifyTicket");
if (StringUtils.isEmpty(componentVerifyTicket)) {
throw new RuntimeException("微信开放平台,第三方平台获取【验证票据】为空");
}
String redisKey = CacheConstants.COMPONENT_VERIFY_TICKET + PLATFORM_APP_ID;
// 查缓存,看是否已经过期
String verifyTicket = redisCache.getCacheObject(redisKey);
if (verifyTicket != null) {
// 先删除旧缓存
redisCache.deleteObject(redisKey);
}
// 存入Redis 过期时间: 官方12小时但十分钟推送一次, 因此可设置 20 分钟过期
redisCache.setCacheObject(redisKey, componentVerifyTicket, 60 * 20, TimeUnit.SECONDS);
verifyTicket = redisCache.getCacheObject(redisKey);
// 存储平台授权票据,保存ticket
// redisTemplate.opsForValue().set("component_verify_ticket", componentVerifyTicket, 60 * 12, TimeUnit.SECONDS);
// String verifyTicket = redisTemplate.opsForValue().get("component_verify_ticket").toString();
logger.info("====================授权票据【ComponentVerifyTicket】" + verifyTicket + "】====================");
return verifyTicket;
}
/**
* 小程序/公众号授权事件
*
* @param map
*/
private void authorizedInform(Map<String, String> map) {
// 获取通知类型
String infoType = MapUtils.getString(map, "InfoType");
// 小程序appid
String authorizerAppid = MapUtils.getString(map, "AuthorizerAppid");
if (StringUtils.equals("unauthorized", infoType)) {
logger.info("微信开放平台,第三方平台,用户:{} 取消授权", authorizerAppid);
return;
}
logger.info("微信开放平台,第三方平台小程序/公众号授权事件 params:{}", map);
// 用户授权码 redisKey
String authCodeRedisKey = CacheConstants.COMPONENT_AUTHORIZATION_CODE + authorizerAppid;
// 用户预授权码 redisKey 过期时间 1800 秒
String preAuthCodeRedisKey = CacheConstants.COMPONENT_PRE_AUTHORIZATION_CODE + authorizerAppid;
// 如果此用户之前有缓存,需把原来的删除
if (StringUtils.isNotBlank(authCodeRedisKey)) {
redisCache.deleteObject(authCodeRedisKey);
}
if (StringUtils.isNotBlank(preAuthCodeRedisKey)) {
redisCache.deleteObject(preAuthCodeRedisKey);
}
// 获取授权码
String authorizationCode = MapUtils.getString(map, "AuthorizationCode");
// 预授权码
String preAuthCode = MapUtils.getString(map, "PreAuthCode");
// 过期时间 (授权码用)
int expiredTime = (Integer) MapUtils.getObject(map, "AuthorizationCodeExpiredTime");
if (StringUtils.isBlank(authorizationCode)
|| StringUtils.isBlank(authorizerAppid)) {
throw new RuntimeException("微信开放平台,第三方平台获取【授权码】为空");
}
redisCache.setCacheObject(authCodeRedisKey, authorizationCode, expiredTime, TimeUnit.SECONDS);
redisCache.setCacheObject(preAuthCodeRedisKey, preAuthCode, 60 * 30, TimeUnit.SECONDS);
logger.info("微信开放平台,第三方平台小程序/公众号授权事件 success");
}
/**
* 获取第三方平台 token
*
* @param dto
* @return
*/
public String getComponentToken(GetComponentTokenDTO dto) {
String verifyTicket = dto.getVerifyTicket();
String redisKey = CacheConstants.COMPONENT_ACCESS_TOKEN + dto.getAppId();
// 先判断缓存中是否有 token
String token = redisCache.getCacheObject(redisKey);
if (StringUtils.isNotBlank(token)) {
// 不为空直接返回
return token;
}
// 为空再去请求获取新令牌
String url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
if (StringUtils.isBlank(verifyTicket)) {
// 获取缓存中的票据
verifyTicket = redisCache.getCacheObject(CacheConstants.COMPONENT_VERIFY_TICKET + dto.getAppId());
logger.info("获取第三方平台缓存中票据:{}", verifyTicket);
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("component_appid", dto.getAppId());
jsonObject.put("component_appsecret", dto.getAppSecret());
jsonObject.put("component_verify_ticket", verifyTicket);
String result = HttpUtils.sendPost(url, jsonObject.toString());
JSONObject jsonResult = JSONObject.parseObject(result);
logger.info("获取第三方平台 token 请求结果:{}", JSONObject.toJSONString(jsonResult));
// 获取返回值中的 token
token = jsonResult.getString("component_access_token");
if (token == null) {
throw new RuntimeException("获取第三方平台 token 异常!");
}
logger.info("获取第三方平台 token component_access_token:{}", token);
// 存入redis, 有效期 1小时50分, 官方 2 小时
redisCache.setCacheObject(redisKey, token, 60 * 110, TimeUnit.SECONDS);
return token;
}
/**
* 使用授权码获取授权信息
* 并将授权信息中的 接口调用令牌、刷新令牌存入缓存
*
* @param authorizationCode
*/
public AuthInfoVO getAuthInfoByAuthCode(String authorizationCode) {
// 获取 component_access_token
GetComponentTokenDTO dto = GetComponentTokenDTO.builder()
.appId(PLATFORM_APP_ID)
.appSecret(PLATFORM_APP_SECRET)
.verifyTicket(null)
.build();
String componentToken = getComponentToken(dto);
// 使用授权码获取授权信息 url
String url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=" + componentToken;
JSONObject jsonObject = new JSONObject();
jsonObject.put("component_appid", PLATFORM_APP_ID);
jsonObject.put("authorization_code", authorizationCode);
String result = HttpUtils.sendPost(url, JSONObject.toJSONString(jsonObject));
logger.info("获取第三方平台 使用授权码获取授权信息 请求参数:{}, 请求结果:{}", JSONObject.toJSONString(jsonObject), result);
AuthInfoVO authInfoVO = JSONObject.parseObject(result, AuthInfoVO.class);
if (authInfoVO == null) {
throw new RuntimeException("获取第三方平台 使用授权码获取授权信息 error");
}
String authorizerAccessToken = authInfoVO.getAuthorizerAccessToken(); // 接口调用令牌, 默认有效期 7200 秒
String authorizerRefreshToken = authInfoVO.getAuthorizerRefreshToken(); // 刷新令牌 永久保存
String authorizerAppid = authInfoVO.getAuthorizerAppid(); // 授权方 appid
String authAccessTokenKey = CacheConstants.AUTHORIZER_ACCESS_TOKEN + authorizerAppid;
redisCache.setCacheObject(authAccessTokenKey, authorizerAccessToken, authInfoVO.getExpiredTime(), TimeUnit.SECONDS);
String authRefreshTokenKey = CacheConstants.AUTHORIZER_REFRESH_TOKEN + authorizerAppid;
redisCache.setCacheObject(authRefreshTokenKey, authorizerRefreshToken);
return authInfoVO;
}
/**
* 提交代码并生成体验版小程序
*
* @param dto
* @return
*/
public String commitCode(CommitCodeDTO dto) {
String redisKey = CacheConstants.AUTHORIZER_ACCESS_TOKEN + dto.getAuthorizerAppid();
String authAccessToken = redisCache.getCacheObject(redisKey);
if (StringUtils.isBlank(authAccessToken)) {
throw new RuntimeException("微信第三方平台 提交代码 error: authAccessToken为空");
}
// 提交代码 url
String url = "https://api.weixin.qq.com/wxa/commit?access_token=" + authAccessToken;
JSONObject jsonObject = new JSONObject();
jsonObject.put("template_id", dto.getTemplateId());
jsonObject.put("ext_json", dto.getExtJson());
jsonObject.put("user_version", dto.getUserVersion());
jsonObject.put("user_desc", dto.getUserDesc());
String result = HttpUtils.sendPost(url, JSONObject.toJSONString(jsonObject));
logger.info("获取第三方平台 使用授权码获取授权信息 请求参数:{}, 请求结果:{}", JSONObject.toJSONString(jsonObject), result);
// 将返回结果转为json对象
JSONObject resultJson = JSONObject.parseObject(result);
return resultJson.getString("errmsg");
}
/**
* 获取类目列表
*
* @param authorizerAppid 用户appid
* @return
*/
public List<CategoryInfo> getAllCategoryName(String authorizerAppid) {
String redisKey = CacheConstants.AUTHORIZER_ACCESS_TOKEN + authorizerAppid;
String authAccessToken = redisCache.getCacheObject(redisKey);
if (StringUtils.isBlank(authAccessToken)) {
throw new RuntimeException("微信第三方平台 获取类目名称信息 error: authAccessToken为空");
}
String url = "https://api.weixin.qq.com/wxa/get_category?access_token=" + authAccessToken;
String result = HttpUtils.sendGet(url);
logger.info("获取第三方平台 获取类目名称信息 请求结果:{}", result);
// 将返回结果转为json对象
JSONObject resultJson = JSONObject.parseObject(result);
int errCode = (int) resultJson.get("errcode");
if (errCode != 0) {
// 不为 0 则说明有错误
logger.error("获取第三方平台 获取类目名称信息 error:{}", resultJson.getString("errmsg"));
return new ArrayList<>();
}
// 将 jsonArray 转成 List
JSONArray categoryList = resultJson.getJSONArray("category_list");
List<CategoryInfo> categoryInfos = categoryList.toList(CategoryInfo.class);
if (CollectionUtils.isEmpty(categoryInfos)) {
logger.info("获取第三方平台 获取类目名称信息 error");
return new ArrayList<>();
}
return categoryInfos;
}
/**
* 提交代码审核
*
* @param dto
* @return
*/
public String submitAudit(SubmitAuditDTO dto) {
String redisKey = CacheConstants.AUTHORIZER_ACCESS_TOKEN + dto.getAuthorizerAppid();
String authAccessToken = redisCache.getCacheObject(redisKey);
if (StringUtils.isBlank(authAccessToken)) {
throw new RuntimeException("微信第三方平台 提交代码审核 error: authAccessToken为空");
}
List<AuditItem> itemList = dto.getAuditItems();
String url = "https://api.weixin.qq.com/wxa/submit_audit?access_token=" + authAccessToken;
// List --> JsonArray
JSONArray itemArray = JSONArray.parseArray(JSONObject.toJSONString(itemList));
// 发送请求
JSONObject jsonObject = new JSONObject();
jsonObject.put("item_list", itemArray);
String result = HttpUtils.sendPost(url, JSONObject.toJSONString(jsonObject));
logger.info("微信第三方平台 提交代码审核 请求参数:{}, 请求结果:{}", JSONObject.toJSONString(jsonObject), result);
// 将返回结果转为json对象
JSONObject resultJson = JSONObject.parseObject(result);
int errCode = (int) resultJson.get("errcode");
if (errCode != 0) {
String errMsg = resultJson.getString("errmsg");
logger.info("微信第三方平台 提交代码审核 error, {}", errMsg);
return errMsg;
}
// 获取审核编码并返回
long auditId = (long) resultJson.get("auditid");
return String.valueOf(auditId);
}
/**
* 获取预授权码
*
* @return
*/
public String getPreAuthorizerCode() {
// 获取平台令牌
GetComponentTokenDTO dto = GetComponentTokenDTO.builder()
.appId(PLATFORM_APP_ID)
.appSecret(PLATFORM_APP_SECRET)
.verifyTicket(null)
.build();
String componentToken = getComponentToken(dto);
// 拼接参数
String url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=" + componentToken;
JSONObject jsonObject = new JSONObject();
jsonObject.put("component_appid", PLATFORM_APP_ID);
// 发送请求
String result = HttpUtils.sendPost(url, JSONObject.toJSONString(jsonObject));
logger.info("微信第三方平台 获取预授权码请求 params:{}, result:{}", JSONObject.toJSONString(jsonObject), result);
// 将返回结果转成json对象
JSONObject resultJson = JSONObject.parseObject(result);
String preAuthCode = resultJson.getString("pre_auth_code");
if (StringUtils.isBlank(preAuthCode)) {
logger.error("微信第三方平台 获取预授权码为空");
return null;
}
return preAuthCode;
}
public static void main(String[] args) {
JSONArray array = new JSONArray();
array.set(0, "1");
array.set(1, "2");
array.set(2, "3");
List<String> list = array.toList(String.class);
System.out.println(list);
}
}