package com.walker.wxtools.support; import com.fasterxml.jackson.databind.node.ObjectNode; import com.walker.infrastructure.utils.JsonUtils; import com.walker.wxtools.AbstractOfficialAccountEngine; import com.walker.wxtools.BasicAccessToken; import com.walker.wxtools.Constants; import com.walker.wxtools.JsTicket; import com.walker.wxtools.WxRemoteException; import com.walker.wxtools.WxUserInfo; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 微信公众号引擎默认实现。 *

子类可继承该对象,重写方法,以满足自己应用,例如:缓存'Redis'缓存等。

* @author 时克英 * @date 2023-04-20 */ public class DefaultOfficialAccountEngine extends AbstractOfficialAccountEngine { @Override protected JsTicket acquireCachedJsTicket(String cacheKey) { return this.innerJsTicketCache.get(cacheKey); } @Override protected void writeJsTicketToCache(String cacheKey, JsTicket jsTicket) { if(this.innerJsTicketCache.get(cacheKey) != null){ this.innerJsTicketCache.remove(cacheKey); } this.innerJsTicketCache.put(cacheKey, jsTicket); logger.info("写入'jsTicket'缓存"); } @Override protected JsTicket acquireRemoteJsTicket(String basicAccessToken) throws WxRemoteException { this.checkRestTemplate(); Map paramMap = new HashMap<>(4); paramMap.put("type", "jsapi"); paramMap.put("access_token", basicAccessToken); try { String entity = restTemplate.getForObject(Constants.URL_JS_TICKET, String.class, paramMap); if(entity == null){ throw new RuntimeException("调用'jsTicket'返回空数据, basicAccessToken = " + basicAccessToken); } if(this.logger.isDebugEnabled()){ this.logger.debug(entity); } ObjectNode objectNode = JsonUtils.jsonStringToObjectNode(entity); if(objectNode.get(Constants.NAME_ERROR_CODE).intValue() != 0){ throw new WxRemoteException(objectNode.get(Constants.NAME_ERROR_MESSAGE).asText(), "微信获取'jsTicket'返回错误", null); } JsTicket jsTicket = new JsTicket(); jsTicket.setTicket(objectNode.get("ticket").asText()); jsTicket.setExpireSeconds(objectNode.get(Constants.NAME_EXPIRES_IN).longValue()); jsTicket.setCreateTime(System.currentTimeMillis()); return jsTicket; } catch (Exception ex){ StringBuilder error = new StringBuilder("微信远程获取'jsTicket'失败, basicAccessToken=").append(basicAccessToken); if(ex instanceof WxRemoteException){ WxRemoteException wex = (WxRemoteException) ex; throw wex; } else { error.append(", error = ").append(ex.getMessage()); } throw new WxRemoteException(error.toString(), ex.getMessage(), ex); } } @Override protected BasicAccessToken acquireCachedBasicAccessToken(String cacheKey) { // 默认实现,基于内存,获取时,需要看是否超时 return this.innerAccessTokenCache.get(cacheKey); } @Override protected void writeBasicAccessTokenToCache(String cacheKey, BasicAccessToken basicAccessToken) { if(this.innerAccessTokenCache.get(cacheKey) != null){ this.innerAccessTokenCache.remove(cacheKey); } this.innerAccessTokenCache.put(cacheKey, basicAccessToken); logger.info("写入'basicAccessToken'缓存"); } @Override protected BasicAccessToken acquireRemoteBasicAccessToken(String appId, String secret) throws WxRemoteException { this.checkRestTemplate(); Map paramMap = new HashMap<>(4); paramMap.put("appid", appId); paramMap.put("secret", secret); paramMap.put("grant_type", "client_credential"); try { // 必须用:get 方式 // ResponseEntity entity = restTemplate.postForEntity(Constants.URL_BASIC_ACCESS_TOKEN, paramMap, String.class); String entity = restTemplate.getForObject(Constants.URL_BASIC_ACCESS_TOKEN, String.class, paramMap); if(entity == null){ throw new RuntimeException("调用'BasicAccessToken'返回空数据, appId = " + appId); } if(this.logger.isDebugEnabled()){ // this.logger.debug(entity.getBody()); this.logger.debug(entity); } // ObjectNode objectNode = JsonUtils.jsonStringToObjectNode(entity.getBody()); ObjectNode objectNode = JsonUtils.jsonStringToObjectNode(entity); if(objectNode.has(Constants.NAME_ERROR_CODE)){ throw new WxRemoteException(objectNode.get(Constants.NAME_ERROR_MESSAGE).asText(), "微信获取'BasicAccessToken'返回错误", null); } BasicAccessToken basicAccessToken = new BasicAccessToken(); basicAccessToken.setAccessToken(objectNode.get(Constants.NAME_ACCESS_TOKEN).asText()); basicAccessToken.setExpireSeconds(objectNode.get(Constants.NAME_EXPIRES_IN).longValue()); basicAccessToken.setCreateTime(System.currentTimeMillis()); return basicAccessToken; } catch (Exception ex){ StringBuilder error = new StringBuilder("微信远程获取'BasicAccessToken'失败, appId="); error.append(appId); if(ex instanceof WxRemoteException){ WxRemoteException wex = (WxRemoteException) ex; error.append(", error = ").append(wex.getError()); } throw new WxRemoteException(error.toString(), ex.getMessage(), ex); } } @Override protected WxUserInfo acquireRemoteUserInfoSub(String openId, String basicAccessToken) throws WxRemoteException { this.checkRestTemplate(); Map paramMap = new HashMap<>(4); paramMap.put("access_token", basicAccessToken); paramMap.put("openid", openId); paramMap.put("lang", "zh_CN"); try { String entity = restTemplate.getForObject(Constants.URL_USER_INFO_SUB, String.class, paramMap); if(entity == null){ throw new RuntimeException("调用'userInfoSub'返回空数据, openId = " + openId + ", access_token=" + basicAccessToken); } ObjectNode objectNode = JsonUtils.jsonStringToObjectNode(entity); if(objectNode.has(Constants.NAME_ERROR_CODE)){ throw new WxRemoteException(objectNode.get(Constants.NAME_ERROR_MESSAGE).asText(), "微信获取'userInfoSub'返回错误", null); } WxUserInfo userInfo = JsonUtils.jsonStringToObject(entity, WxUserInfo.class); return userInfo; } catch (Exception ex){ StringBuilder error = new StringBuilder("微信远程获取'userInfoSub'失败, openId="); error.append(openId); if(ex instanceof WxRemoteException){ WxRemoteException wex = (WxRemoteException) ex; // error.append(", error = ").append(wex.getError()); throw wex; } else { error.append(", error = ").append(ex.getMessage()); } throw new WxRemoteException(error.toString(), ex.getMessage(), ex); } } private void checkRestTemplate(){ if(this.restTemplate == null){ throw new IllegalArgumentException("RestTemplate未配置!"); } } public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } private final Map innerJsTicketCache = new ConcurrentHashMap<>(8); private final Map innerAccessTokenCache = new ConcurrentHashMap<>(8); private RestTemplate restTemplate; }