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 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 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 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 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 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 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 list = array.toList(String.class); System.out.println(list); } }