使用WxJava微信开发时,调用接口获取access_token,如果多个服务部署,就需要使用到缓存来保存access_token以达到重复利用,WxJava 也提供了相关的实现类WxMaRedisConfigImpl
,但是这个是基于jedis客户端的实现,最新版本的springboot-redis都开始采用lettuce客户端进行连接了,所以我们只能自己手动实现一个类似于WxMaRedisConfigImpl
的类
使用的版本
com.github.binarywang weixin-java-miniapp 3.5.0
WxMaLettuceRedisConfigImpl基于lettuce的工具类
package cn.shiyue.config.ma;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
public class WxMaLettuceRedisConfigImpl extends WxMaDefaultConfigImpl {
private StringRedisTemplate stringRedisTemplate;
private static final String ACCESS_TOKEN_KEY = "wa:access_token:";
private String accessTokenKey;
public WxMaLettuceRedisConfigImpl(StringRedisTemplate stringRedisTemplate){
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 每个公众号生成独有的存储key.
*/
@Override
public void setAppid(String appId) {
super.setAppid(appId);
this.accessTokenKey = ACCESS_TOKEN_KEY.concat(appId);
}
@Override
public String getAccessToken() {
return stringRedisTemplate.opsForValue().get(accessTokenKey);
}
@Override
public boolean isAccessTokenExpired() {
Long expireTime = stringRedisTemplate.getExpire(accessTokenKey);
if (ObjectUtils.isEmpty(expireTime)) {
return true;
}
// 到期时间小于2秒就算作过期了,就重新调用接口获取
return expireTime < 2;
}
@Override
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
stringRedisTemplate.opsForValue().set(accessTokenKey, accessToken, expiresInSeconds - 200, TimeUnit.SECONDS);
}
@Override
public void expireAccessToken() {
stringRedisTemplate.expire(accessTokenKey, 0, TimeUnit.SECONDS);
}
@Override
public long getExpiresTime() {
Long expire = stringRedisTemplate.getExpire(accessTokenKey);
return expire == null ? 0 : expire;
}
@Override
public void setExpiresTime(long expiresTime) {
stringRedisTemplate.expire(accessTokenKey, expiresTime, TimeUnit.SECONDS);
}
}
WxMaConfiguration配置
package cn.shiyue.config.ma;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
private WxMaProperties properties;
private static Map routers = Maps.newHashMap();
private static Map maServices = Maps.newHashMap();
// 注入StringRedisTemplate
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
public WxMaConfiguration(WxMaProperties properties) {
this.properties = properties;
}
public static WxMaService getMaService(String appid) {
WxMaService wxService = maServices.get(appid);
if (wxService == null) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
return wxService;
}
public static WxMaMessageRouter getRouter(String appid) {
return routers.get(appid);
}
@PostConstruct
public void init() {
List configs = this.properties.getConfigs();
if (configs == null) {
throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
}
maServices = configs.stream()
.map(a -> {
// 在当前引用
WxMaDefaultConfigImpl config = new WxMaLettuceRedisConfigImpl(stringRedisTemplate);
//WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl ();
config.setAppid(a.getAppid());
config.setSecret(a.getSecret());
config.setToken(a.getToken());
config.setAesKey(a.getAesKey());
config.setMsgDataFormat(a.getMsgDataFormat());
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(config);
routers.put(a.getAppid(), this.newRouter(service));
return service;
}).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
}
private WxMaMessageRouter newRouter(WxMaService service) {
final WxMaMessageRouter router = new WxMaMessageRouter(service);
router
.rule().handler(logHandler).next()
.rule().async(false).content("模板").handler(templateMsgHandler).end()
.rule().async(false).content("文本").handler(textHandler).end()
.rule().async(false).content("图片").handler(picHandler).end()
.rule().async(false).content("二维码").handler(qrcodeHandler).end();
return router;
}
private final WxMaMessageHandler templateMsgHandler = (wxMessage, context, service, sessionManager) ->{
service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.builder()
.templateId("此处更换为自己的模板id")
.formId("自己替换可用的formid")
.data(Lists.newArrayList(
new WxMaTemplateData("keyword1", "339208499", "#173177")))
.toUser(wxMessage.getFromUser())
.build());
return null;
};
private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
System.out.println("收到消息:" + wxMessage.toString());
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
.toUser(wxMessage.getFromUser()).build());
return null;
};
private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) ->{
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
.toUser(wxMessage.getFromUser()).build());
return null;
};
private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
try {
WxMediaUploadResult uploadResult = service.getMediaService()
.uploadMedia("image", "png",
ClassLoader.getSystemResourceAsStream("tmp.png"));
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.newImageBuilder()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
return null;
};
private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
try {
final File file = service.getQrcodeService().createQrcode("123", 430);
WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.newImageBuilder()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
return null;
};
}
WxMaProperties文件
package cn.shiyue.config.ma;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
private List configs;
public static class Config {
/**
* 设置微信小程序的appid
*/
private String appid;
/**
* 设置微信小程序的Secret
*/
private String secret;
/**
* 设置微信小程序消息服务器配置的token
*/
private String token;
/**
* 设置微信小程序消息服务器配置的EncodingAESKey
*/
private String aesKey;
/**
* 消息格式,XML或者JSON
*/
private String msgDataFormat;
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getAesKey() {
return aesKey;
}
public void setAesKey(String aesKey) {
this.aesKey = aesKey;
}
public String getMsgDataFormat() {
return msgDataFormat;
}
public void setMsgDataFormat(String msgDataFormat) {
this.msgDataFormat = msgDataFormat;
}
}
public List getConfigs() {
return configs;
}
public void setConfigs(List configs) {
this.configs = configs;
}
}
还有一种方式在WxMaConfiguration 文件中配置,这个WxMaRedisBetterConfigImpl类需要高版本的weixin-java-miniapp的jar
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 方式二才需要用上
@Bean
public WxRedisOps getWxRedisOps() {
return new RedisTemplateWxRedisOps(stringRedisTemplate);
}
// 方式一:自己实现一个lettuce的redis配置对象
WxMaDefaultConfigImpl config = new WxMaLettuceRedisConfigImpl(stringRedisTemplate);
// 方式二: 使用wxRedisOps对象来包装redis连接
// WxMaDefaultConfigImpl config = new WxMaRedisBetterConfigImpl(getWxRedisOps(), "wa:access_token:");