04.微信公众号获取access_token、消息排重

1. 获取 access_token 官方文档
  1. 说明

    1. access_token有效期目前为2小时,且获取access_token接口每天限制2000次。因此需要保存,选择保存到 redis
    2. access_token重复获取将导致上次获取的access_token失效,但是公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡。因此需要用分布式锁来限制刷新,选择 redisson 实现
  2. 代码实现

    @Override
    public String getAccessToken() {
        final String key = RedisConstant.WX_ACCESS_TOKEN_KEY + wxConfig.getAppID();
        /*
            1. 先获取 redis 上的 token
         */
        Object o = valueOperations.get(key);
        String token = "";
        if (Objects.nonNull(o) && StrUtil.isNotBlank(token = o.toString())) {
            return token;
        }
        // 锁
        RLock lock = redisLock.getRLock(RedisConstant.PARAM_ACCESS_TOKEN);
        lock.lock();
        try {
            // 防止不是第一个拿到锁
            o = valueOperations.get(key);
            if (Objects.nonNull(o) && StrUtil.isNotBlank(token = o.toString())) {
                return token;
            }
            token = forceRefreshAccessToken();
        } catch (Exception e) {
            log.error("微信公众号 {} 获取 access_token 失败", wxConfig.getAppID());
        } finally {
            lock.unlock();
        }
        return token;
    }
    
    @Override
    public String forceRefreshAccessToken() {
        final String key = RedisConstant.WX_ACCESS_TOKEN_KEY + wxConfig.getAppID();
        String token = "";
        try {
            /*
                调用 微信公众号接口,根据 appId、appSecret 获取 access_token
             */
            final String url = String.format(WxConstant.URL_ACCESS_TOKEN_GET, wxConfig.getAppID(), wxConfig.getAppSecret());
            String result = HttpUtil.get(url);
            WxAccessTokenBean tokenBean = objectMapper.readValue(result, WxAccessTokenBean.class);
            Long expiresIn = tokenBean.getExpiresIn();
            /*
                保存到 redis
             */
            if (StrUtil.isNotBlank(token = tokenBean.getAccessToken()) && Objects.nonNull(expiresIn)) {
                valueOperations.set(key, token, expiresIn, TimeUnit.SECONDS);
            } else {
                // access_token 异常
                log.error("errCode: {}, errMsg: {}", tokenBean.getErrCode(), tokenBean.getErrMsg());
                throw new RuntimeException("微信公众号 access_token 异常");
            }
        } catch (JsonProcessingException e) {
            log.error("微信公众号 {} 获取 access_token 失败", wxConfig.getAppID());
        }
        return token;
    }            
    
2. 消息排重
  1. 说明

    1. 微信服务器在将用户的消息发给公众号的开发者服务器地址(开发者中心处配置)后,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。
    2. 当微信服务器重试时,那么有可能给用户回复多次信息,因此需要排重
  2. 实现

    @Override
    public String msgHandle(WxInMsgBean bean, String signature, String timestamp, String nonce) {
        /*
            验证是否是正常请求
         */
        if (!checkToken(signature, timestamp, nonce, wxConfig.getToken())) {
            throw new IllegalArgumentException("微信-非法请求,可能属于伪造请求");
        }
        log.info("WxInMsgBean: {}", bean.toString());
    
        String msg = WxConstant.MSG_SUCCESS;
        // 排重
        if (isRepeat(bean.getMsgId(), bean.getFromUserName(), bean.getCreateTime())) {
            return msg;
        }
        
        /*
            处理...
        */
        
        return msg;
    }
    
    /**
     * 判断发送的消息是否是重复发送
     *
     * @param msgId        信息id
     * @param fromUserName 发送者
     * @param createTime   创建时间
     * @return 重复 true
     */
    private boolean isRepeat(Long msgId, String fromUserName, Long createTime) {
        /*
            设置到 redis 进行排重
         */
        final String key = RedisConstant.DE_WEIGHT_KEY + wxConfig.getAppID() + ":"
                + (Objects.nonNull(msgId) ? msgId : fromUserName + createTime);
        // 第一次设置会返回 true,每5秒重试一次,共重试 3 次
        // 为 null 或 false 代表 已存在,redis 不进行任何操作
        return !Objects.equals(valueOperations.setIfAbsent(key, 1, 25, TimeUnit.SECONDS), true);
    }
    
  3. 源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值