WeiXinRequestUtil.java 15 KB
package com.topdraw.weixin.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.topdraw.utils.RedisUtils;
import com.topdraw.utils.StringUtils;
import com.topdraw.weixin.beans.QrCode;
import com.topdraw.weixin.beans.WeiXinNotice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class WeiXinRequestUtil {

    @Autowired
    private RedisUtils redisUtils;

    /**
     *
     * */
    private static String doGet(String url, Map<String, String> param) {
        String result = null;
        try {
            String queryString = "";
            if (null != param) {
                for (Map.Entry<String, String> entry : param.entrySet()) {
                    queryString += entry.getKey() + "=" + URLEncoder.encode("" + entry.getValue(), "UTF-8") + "&";
                }
                if (queryString.length() > 0) {
                    queryString = queryString.substring(0, queryString.length() - 1);
                }
            }
            log.info("weixin request: " + url + "?" + queryString);
            HttpResponse response = HttpRequest.get(url + "?" + queryString).execute();
            if (response.isOk()) {
                result = response.body();
                log.info("weixin response: " + result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private String wx_token(String appid, String secret) {
        Map<String, String> param = new HashMap<>();
        param.put("grant_type", "client_credential");
        param.put("appid", appid);
        param.put("secret", secret);
        return this.doGet(WeChatConstants.HTTPS_TOKEN, param);
    }

    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public String wx_ticket_getticket(String appid, String secret) {
        String token = getToken(appid, secret,null);
        Map<String, String> param = new HashMap<>();
        param.put("access_token", token);
        param.put("type", "jsapi");
        return doGet(WeChatConstants.HTTPS_TICKET_GETTICKET, param);
    }

    public String wx_sns_oauth2_access_token(String appid, String secret, String code) {
        Map<String, String> param = new HashMap<>();
        param.put("appid", appid);
        param.put("secret", secret);
        param.put("code", code);
        param.put("grant_type", "authorization_code");
        return doGet(WeChatConstants.HTTPS_SNS_OAUTH2_ACCESS_TOKEN, param);
    }


    public String wx_sns_userinfo(String oauth2_access_token, String openid) {
        Map<String, String> param = new HashMap<>();
        param.put("access_token", oauth2_access_token);
        param.put("openid", openid);
        param.put("lang", "zh_CN");
        return doGet(WeChatConstants.HTTPS_SNS_USERINFO, param);
    }

    public String wx_get_userinfo(String oauth2_access_token, String openid) {
        Map<String, String> param = new HashMap<>();
        param.put("access_token", oauth2_access_token);
        param.put("openid", openid);
        param.put("lang", "zh_CN");
        return doGet(WeChatConstants.GET_USER_INFO, param);
    }


    public String getToken(String appid, String secret,String code) {
        String token = (String) redisUtils.get(WeChatConstants.TOKEN_KEY + appid);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        token = this.getTokenNoRedis(appid, secret,code);
        return token;
    }

    public String getToken(String appid, String secret) {
        String token = (String) redisUtils.get(WeChatConstants.TOKEN_KEY + appid);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        token = this.getTokenNoRedis(appid, secret,null);
        return token;
    }

    public String getTokenNoRedis(String appid, String secret,String code) {
        String token = null;
        String response = this.wx_token(appid, secret);
//        String response = this.wx_sns_oauth2_access_token(appid, secret,code);
        try {
            JSONObject joToken = JSON.parseObject(response);
            if (null != joToken && null != joToken.getString("access_token")) {
                token = joToken.getString("access_token");
                Integer expiresIn = joToken.getInteger("expires_in");
                redisUtils.set(WeChatConstants.TOKEN_KEY + appid, token, expiresIn, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    public String getTokenNoRedis(String appid, String secret) {
        String token = null;
//        String response = this.wx_token(appid, secret);
        String response = this.wx_sns_oauth2_access_token(appid, secret,null);
        try {
            JSONObject joToken = JSON.parseObject(response);
            if (null != joToken && null != joToken.getString("access_token")) {
                token = joToken.getString("access_token");
                Integer expiresIn = joToken.getInteger("expires_in");
                redisUtils.set(WeChatConstants.TOKEN_KEY + appid, token, expiresIn, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    /**
     * 发送订阅消息,access_token错误时,重新获取进行重试
     *
     * @param weixinInfo 微信参数
     * @param request    订阅消息内容
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public void sendNotice(Map<String, String> weixinInfo, WeiXinNotice request) {
        String accessToken = getToken(weixinInfo.get("appid"), weixinInfo.get("secret"));
        String url = MessageFormat.format(WeChatConstants.SUBSCRIBE_SEND_URL, accessToken);
        log.info("send notice request : " + JSONObject.toJSONString(request));
        HttpResponse response = HttpRequest.post(url).body(JSONObject.toJSONString(request), "application/json;charset=utf-8").execute();
        if (!response.isOk()) {
            log.error("send notice error || {}", response);
            throw new RuntimeException("send notice error");
        }
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Integer errCode = jsonObject.getInteger(WeChatConstants.ERR_CODE);
        if (errCode != null && errCode != 0) {
            if (WeChatConstants.ACCESS_TOKEN_INVALID_CODE.equals(errCode.toString())) {
                getTokenNoRedis(weixinInfo.get("appid"), weixinInfo.get("secret"));
            }

//            返回errCode时,也输出返回的结果信息
            log.error("send notice error || {}", response.body());
            throw new RuntimeException("send notice error");
        }
        log.info("send notice response : " + response.body());
    }


    /**
     * 微信小程序客服发送消息,access_token错误时,重新获取进行重试
     *
     * @param weixinInfo 微信参数
     * @param body       消息内容
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public void sendMessage(Map<String, String> weixinInfo, String body) {
        String accessToken = getToken(weixinInfo.get("appid"), weixinInfo.get("secret"));
        String url = MessageFormat.format(WeChatConstants.CUSTOM_SEND_URL, accessToken);
        HttpResponse response = HttpRequest.post(url).body(body, "application/json;charset=utf-8").execute();
        if (!response.isOk()) {
            log.error("send message error || {}", response);
            throw new RuntimeException("send message error");
        }
        log.info("send message response || {}", response.body());
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Integer errCode = jsonObject.getInteger(WeChatConstants.ERR_CODE);
        if (errCode != null && errCode != 0) {
            if (WeChatConstants.ACCESS_TOKEN_INVALID_CODE.equals(errCode.toString())) {
                getTokenNoRedis(weixinInfo.get("appid"), weixinInfo.get("secret"));
            }
            throw new RuntimeException("send message error");
        }
    }

    /**
     * 上传临时素材返回mediaId,access_token错误时,重新获取进行重试
     *
     * @param weixinInfo 微信参数
     * @param imagePath  素材路径
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public String getMediaId(Map<String, String> weixinInfo, String imagePath) throws IOException {
        String accessToken = getToken(weixinInfo.get("appid"), weixinInfo.get("secret"));
        String mediaId = (String) redisUtils.get(WeChatConstants.WEIXIN_MEDIA_KEY + weixinInfo.get("appid"));
        if (StringUtils.isNotBlank(mediaId)) {
            return mediaId;
        }
        String url = MessageFormat.format(WeChatConstants.UPLOAD_URL, accessToken);
        HttpResponse response = HttpRequest.post(url).form("file", new File(imagePath)).execute();
        if (!response.isOk()) {
            log.error("upload image error || {}", response);
            throw new RuntimeException("upload image error");
        }
        log.info("upload image response || {}", response.body());
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Integer errCode = jsonObject.getInteger(WeChatConstants.ERR_CODE);
        if (errCode != null && errCode != 0) {
            if (WeChatConstants.ACCESS_TOKEN_INVALID_CODE.equals(errCode.toString())) {
                getTokenNoRedis(weixinInfo.get("appid"), weixinInfo.get("secret"));
            }
            throw new RuntimeException("upload image error");
        }
        mediaId = jsonObject.getString("media_id");
        redisUtils.set(WeChatConstants.WEIXIN_MEDIA_KEY + weixinInfo.get("appid"), mediaId, 60, TimeUnit.HOURS);
        return mediaId;
    }

    /**
     * 公众号获取带参数二维码,access_token错误时,重新获取进行重试
     *
     * @param weixinInfo 微信参数
     * @param qrCode     二维码信息
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public JSONObject getQrCode(Map<String, String> weixinInfo, QrCode qrCode) throws IOException {
        String accessToken = getToken(weixinInfo.get("appid"), weixinInfo.get("secret"));
        String url = MessageFormat.format(WeChatConstants.QR_CODE_URL, accessToken);
        HttpResponse response = HttpRequest.post(url).body(JSONObject.toJSONString(qrCode), "application/json;charset=utf-8").execute();
        if (!response.isOk()) {
            log.error("get qrCode error || {}", response);
            throw new RuntimeException("get qrCode error");
        }
        log.info("get qrCode response || {}", response.body());
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Integer errCode = jsonObject.getInteger(WeChatConstants.ERR_CODE);
        if (errCode != null && errCode != 0) {
            if (WeChatConstants.ACCESS_TOKEN_INVALID_CODE.equals(errCode.toString())) {
                getTokenNoRedis(weixinInfo.get("appid"), weixinInfo.get("secret"));
            }
            throw new RuntimeException("get qrCode error");
        }
        return jsonObject;
    }

    /**
     * 公众号获取用户信息,access_token错误时,重新获取进行重试
     *
     * @param weixinInfo 微信参数
     * @param openid     用户openid
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public JSONObject getUserInfo(Map<String, String> weixinInfo, String openid,String code) throws IOException {
        String accessToken = this.getToken(weixinInfo.get("appid"), weixinInfo.get("secret"),code);
        String url = MessageFormat.format(WeChatConstants.GET_USER_INFO, accessToken, openid);
//        String url = MessageFormat.format(WeChatConstants.HTTPS_SNS_USERINFO, accessToken, openid);
//        String url = MessageFormat.format(WeChatConstants.HTTPS_AUTHORIZE_WITH_SNSAPI_USERINFO, accessToken, openid);
        HttpResponse response = HttpRequest.get(url).execute();
        if (!response.isOk()) {
            log.error("get userInfo error || {}", response);
            throw new RuntimeException("get userInfo error");
        }
        log.info("get userInfo response || {}", response.body());
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Integer errCode = jsonObject.getInteger(WeChatConstants.ERR_CODE);
        if (errCode != null && errCode != 0) {
            if (WeChatConstants.ACCESS_TOKEN_INVALID_CODE.equals(errCode.toString())) {
                this.getTokenNoRedis(weixinInfo.get("appid"), weixinInfo.get("secret"));
            }
            throw new RuntimeException("get userInfo error");
        }
        return jsonObject;
    }

    /**
     * @param weixinInfo
     * @param nextOpenId
     * @return com.alibaba.fastjson.JSONObject
     * @description 公众号批量获取关注者列表,不传nextOpenId默认从头开始拉取,引用重试机制
     * @author Hongyan Wang
     * @date 2021/9/2 9:42 上午
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 0L, multiplier = 0.0))
    public JSONObject getUserList(Map<String, String> weixinInfo, String nextOpenId) {
        String accessToken = getToken(weixinInfo.get("appid"), weixinInfo.get("secret"));
        // 不传next_openid,默认从头开始拉取
        if (StrUtil.isBlank(nextOpenId))
            nextOpenId = "";
        String url = MessageFormat.format(WeChatConstants.GET_USER_LIST, accessToken, nextOpenId);
        HttpResponse response = HttpRequest.get(url).execute();
        if (!response.isOk()) {
            log.error("get userList error || {}", response);
            throw new RuntimeException("get userList error");
        }
        log.info("get userList response || {}", response.body());
        //返回Json样例
        // {
        //    "total":2,
        //    "count":2,
        //    "data":{
        //    "openid":["OPENID1","OPENID2"]},
        //    "next_openid":"NEXT_OPENID"
        //}
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Integer errCode = jsonObject.getInteger(WeChatConstants.ERR_CODE);
        if (errCode != null && errCode != 0) {
            if (WeChatConstants.ACCESS_TOKEN_INVALID_CODE.equals(errCode.toString())) {
                getTokenNoRedis(weixinInfo.get("appid"), weixinInfo.get("secret"));
            }
            throw new RuntimeException("get userList error");
        }
        return jsonObject;
    }

}