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;
}