com.wallellen.wechat.cp.api.WxCpServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.wallellen.wechat.cp.api.WxCpServiceImpl.java

Source

/*
 * This file Copyright (c) 2016. Walle.
 * (http://www.wallellen.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the
 * Walle Agreement (WA) and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or WA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Walle Agreement (WA), this file
 * and the accompanying materials are made available under the
 * terms of the WA which accompanies this distribution, and
 * is available at http://www.wallellen.com/agreement.html
 *
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 */

package com.wallellen.wechat.cp.api;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.wallellen.wechat.common.bean.WxAccessToken;
import com.wallellen.wechat.common.bean.WxJsapiSignature;
import com.wallellen.wechat.common.bean.WxMenu;
import com.wallellen.wechat.common.bean.result.WxError;
import com.wallellen.wechat.common.bean.result.WxMediaUploadResult;
import com.wallellen.wechat.common.exception.WxErrorException;
import com.wallellen.wechat.common.session.StandardSessionManager;
import com.wallellen.wechat.common.session.WxSession;
import com.wallellen.wechat.common.session.WxSessionManager;
import com.wallellen.wechat.common.util.RandomUtils;
import com.wallellen.wechat.common.util.StringUtils;
import com.wallellen.wechat.common.util.crypto.SHA1;
import com.wallellen.wechat.common.util.fs.FileUtils;
import com.wallellen.wechat.common.util.http.MediaDownloadRequestExecutor;
import com.wallellen.wechat.common.util.http.MediaUploadRequestExecutor;
import com.wallellen.wechat.common.util.http.RequestExecutor;
import com.wallellen.wechat.common.util.http.SimpleGetRequestExecutor;
import com.wallellen.wechat.common.util.http.SimplePostRequestExecutor;
import com.wallellen.wechat.common.util.http.URIUtil;
import com.wallellen.wechat.common.util.json.GsonHelper;
import com.wallellen.wechat.cp.bean.WxCpDepart;
import com.wallellen.wechat.cp.bean.WxCpMessage;
import com.wallellen.wechat.cp.bean.WxCpTag;
import com.wallellen.wechat.cp.bean.WxCpUser;
import com.wallellen.wechat.cp.util.json.WxCpGsonBuilder;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.UUID;

public class WxCpServiceImpl implements WxCpService {

    protected final Logger log = LoggerFactory.getLogger(WxCpServiceImpl.class);

    /**
     * ?access token?
     */
    protected final Object globalAccessTokenRefreshLock = new Object();

    /**
     * ?jsapi_ticket?
     */
    protected final Object globalJsapiTicketRefreshLock = new Object();

    protected WxCpConfigStorage wxCpConfigStorage;

    protected CloseableHttpClient httpClient;

    protected HttpHost httpProxy;
    protected WxSessionManager sessionManager = new StandardSessionManager();
    /**
     * 
     */
    protected File tmpDirFile;
    private int retrySleepMillis = 1000;
    private int maxRetryTimes = 5;

    public static void main(String[] args) {
        Float a = 3.1f;
        System.out.println(3.1d);
        System.out.println(new BigDecimal(3.1d));
        System.out.println(new BigDecimal(a));
        System.out.println(a.toString());
        System.out.println(a.doubleValue());
    }

    public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) {
        try {
            return SHA1.gen(wxCpConfigStorage.getToken(), timestamp, nonce, data).equals(msgSignature);
        } catch (Exception e) {
            return false;
        }
    }

    public void userAuthenticated(String userId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId;
        get(url, null);
    }

    public String getAccessToken() throws WxErrorException {
        return getAccessToken(false);
    }

    public String getAccessToken(boolean forceRefresh) throws WxErrorException {
        if (forceRefresh) {
            wxCpConfigStorage.expireAccessToken();
        }
        if (wxCpConfigStorage.isAccessTokenExpired()) {
            synchronized (globalAccessTokenRefreshLock) {
                if (wxCpConfigStorage.isAccessTokenExpired()) {
                    String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + "&corpid="
                            + wxCpConfigStorage.getCorpId() + "&corpsecret=" + wxCpConfigStorage.getCorpSecret();
                    try {
                        HttpGet httpGet = new HttpGet(url);
                        if (httpProxy != null) {
                            RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
                            httpGet.setConfig(config);
                        }
                        CloseableHttpClient httpclient = getHttpclient();
                        String resultContent = null;
                        try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
                            resultContent = new BasicResponseHandler().handleResponse(response);
                        }
                        WxError error = WxError.fromJson(resultContent);
                        if (error.getErrorCode() != 0) {
                            throw new WxErrorException(error);
                        }
                        WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
                        wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(),
                                accessToken.getExpiresIn());
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return wxCpConfigStorage.getAccessToken();
    }

    public String getJsapiTicket() throws WxErrorException {
        return getJsapiTicket(false);
    }

    public String getJsapiTicket(boolean forceRefresh) throws WxErrorException {
        if (forceRefresh) {
            wxCpConfigStorage.expireJsapiTicket();
        }
        if (wxCpConfigStorage.isJsapiTicketExpired()) {
            synchronized (globalJsapiTicketRefreshLock) {
                if (wxCpConfigStorage.isJsapiTicketExpired()) {
                    String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
                    String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
                    JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
                    JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
                    String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
                    int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
                    wxCpConfigStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds);
                }
            }
        }
        return wxCpConfigStorage.getJsapiTicket();
    }

    public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException {
        long timestamp = System.currentTimeMillis() / 1000;
        String noncestr = RandomUtils.getRandomStr();
        String jsapiTicket = getJsapiTicket(false);
        try {
            String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, "noncestr=" + noncestr,
                    "timestamp=" + timestamp, "url=" + url);
            WxJsapiSignature jsapiSignature = new WxJsapiSignature();
            jsapiSignature.setTimestamp(timestamp);
            jsapiSignature.setNoncestr(noncestr);
            jsapiSignature.setUrl(url);
            jsapiSignature.setSignature(signature);
            return jsapiSignature;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public void messageSend(WxCpMessage message) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
        post(url, message.toJson());
    }

    @Override
    public void menuCreate(WxMenu menu) throws WxErrorException {
        menuCreate(wxCpConfigStorage.getAgentId(), menu);
    }

    @Override
    public void menuCreate(String agentId, WxMenu menu) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" + wxCpConfigStorage.getAgentId();
        post(url, menu.toJson());
    }

    @Override
    public void menuDelete() throws WxErrorException {
        menuDelete(wxCpConfigStorage.getAgentId());
    }

    @Override
    public void menuDelete(String agentId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId;
        get(url, null);
    }

    @Override
    public WxMenu menuGet() throws WxErrorException {
        return menuGet(wxCpConfigStorage.getAgentId());
    }

    @Override
    public WxMenu menuGet(String agentId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId;
        try {
            String resultContent = get(url, null);
            return WxMenu.fromJson(resultContent);
        } catch (WxErrorException e) {
            // 46003 ????
            if (e.getError().getErrorCode() == 46003) {
                return null;
            }
            throw e;
        }
    }

    public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream)
            throws WxErrorException, IOException {
        return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
    }

    public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType;
        return execute(new MediaUploadRequestExecutor(), url, file);
    }

    public File mediaDownload(String media_id) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get";
        return execute(new MediaDownloadRequestExecutor(wxCpConfigStorage.getTmpDirFile()), url,
                "media_id=" + media_id);
    }

    public Integer departCreate(WxCpDepart depart) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create";
        String responseContent = execute(new SimplePostRequestExecutor(), url, depart.toJson());
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id"));
    }

    public void departUpdate(WxCpDepart group) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update";
        post(url, group.toJson());
    }

    public void departDelete(Integer departId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId;
        get(url, null);
    }

    public List<WxCpDepart> departGet() throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list";
        String responseContent = get(url, null);
        /*
         * ?API { group : { id : ..., name : ...} }
         *  { groups : [ { id : ..., name : ..., count : ... }, ... ] }
         */
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return WxCpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("department"),
                new TypeToken<List<WxCpDepart>>() {
                }.getType());
    }

    @Override
    public void userCreate(WxCpUser user) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create";
        post(url, user.toJson());
    }

    @Override
    public void userUpdate(WxCpUser user) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update";
        post(url, user.toJson());
    }

    @Override
    public void userDelete(String userid) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userid;
        get(url, null);
    }

    @Override
    public void userDelete(String[] userids) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete";
        JsonObject jsonObject = new JsonObject();
        JsonArray jsonArray = new JsonArray();
        for (int i = 0; i < userids.length; i++) {
            jsonArray.add(new JsonPrimitive(userids[i]));
        }
        jsonObject.add("useridlist", jsonArray);
        post(url, jsonObject.toString());
    }

    @Override
    public WxCpUser userGet(String userid) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid;
        String responseContent = get(url, null);
        return WxCpUser.fromJson(responseContent);
    }

    @Override
    public List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId;
        String params = "";
        if (fetchChild != null) {
            params += "&fetch_child=" + (fetchChild ? "1" : "0");
        }
        if (status != null) {
            params += "&status=" + status;
        } else {
            params += "&status=0";
        }

        String responseContent = get(url, params);
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return WxCpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("userlist"),
                new TypeToken<List<WxCpUser>>() {
                }.getType());
    }

    @Override
    public List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status)
            throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId;
        String params = "";
        if (fetchChild != null) {
            params += "&fetch_child=" + (fetchChild ? "1" : "0");
        }
        if (status != null) {
            params += "&status=" + status;
        } else {
            params += "&status=0";
        }

        String responseContent = get(url, params);
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return WxCpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("userlist"),
                new TypeToken<List<WxCpUser>>() {
                }.getType());
    }

    @Override
    public String tagCreate(String tagName) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create";
        JsonObject o = new JsonObject();
        o.addProperty("tagname", tagName);
        String responseContent = post(url, o.toString());
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return tmpJsonElement.getAsJsonObject().get("tagid").getAsString();
    }

    @Override
    public void tagUpdate(String tagId, String tagName) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update";
        JsonObject o = new JsonObject();
        o.addProperty("tagid", tagId);
        o.addProperty("tagname", tagName);
        post(url, o.toString());
    }

    @Override
    public void tagDelete(String tagId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId;
        get(url, null);
    }

    @Override
    public List<WxCpTag> tagGet() throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list";
        String responseContent = get(url, null);
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return WxCpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("taglist"),
                new TypeToken<List<WxCpTag>>() {
                }.getType());
    }

    @Override
    public List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId;
        String responseContent = get(url, null);
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return WxCpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("userlist"),
                new TypeToken<List<WxCpUser>>() {
                }.getType());
    }

    @Override
    public void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers";
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("tagid", tagId);
        if (userIds != null) {
            JsonArray jsonArray = new JsonArray();
            for (String userId : userIds) {
                jsonArray.add(new JsonPrimitive(userId));
            }
            jsonObject.add("userlist", jsonArray);
        }
        if (partyIds != null) {
            JsonArray jsonArray = new JsonArray();
            for (String userId : partyIds) {
                jsonArray.add(new JsonPrimitive(userId));
            }
            jsonObject.add("partylist", jsonArray);
        }
        post(url, jsonObject.toString());
    }

    @Override
    public void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers";
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("tagid", tagId);
        JsonArray jsonArray = new JsonArray();
        for (String userId : userIds) {
            jsonArray.add(new JsonPrimitive(userId));
        }
        jsonObject.add("userlist", jsonArray);
        post(url, jsonObject.toString());
    }

    @Override
    public String oauth2buildAuthorizationUrl(String redirectUri, String state) {
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?";
        url += "appid=" + wxCpConfigStorage.getCorpId();
        url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri);
        url += "&response_type=code";
        url += "&scope=snsapi_base";
        if (state != null) {
            url += "&state=" + state;
        }
        url += "#wechat_redirect";
        return url;
    }

    @Override
    public String[] oauth2getUserInfo(String code) throws WxErrorException {
        return oauth2getUserInfo(wxCpConfigStorage.getAgentId(), code);
    }

    @Override
    public String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" + "code=" + code + "&agendid="
                + agentId;
        String responseText = get(url, null);
        JsonElement je = Streams.parse(new JsonReader(new StringReader(responseText)));
        JsonObject jo = je.getAsJsonObject();
        return new String[] { GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId") };
    }

    @Override
    public int invite(String userId, String inviteTips) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send";
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("userid", userId);
        if (StringUtils.isNotEmpty(inviteTips)) {
            jsonObject.addProperty("invite_tips", inviteTips);
        }
        String responseContent = post(url, jsonObject.toString());
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        return tmpJsonElement.getAsJsonObject().get("type").getAsInt();
    }

    @Override
    public String[] getCallbackIp() throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip";
        String responseContent = get(url, null);
        JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
        JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray();
        String[] ips = new String[jsonArray.size()];
        for (int i = 0; i < jsonArray.size(); i++) {
            ips[i] = jsonArray.get(i).getAsString();
        }
        return ips;
    }

    public String get(String url, String queryParam) throws WxErrorException {
        return execute(new SimpleGetRequestExecutor(), url, queryParam);
    }

    public String post(String url, String postData) throws WxErrorException {
        return execute(new SimplePostRequestExecutor(), url, postData);
    }

    /**
     * ????access_token????
     *
     * @param executor
     * @param uri
     * @param data
     * @return
     * @throws WxErrorException
     */
    public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
        int retryTimes = 0;
        do {
            try {
                return executeInternal(executor, uri, data);
            } catch (WxErrorException e) {
                WxError error = e.getError();
                /**
                 * -1 ?, 1000ms??
                 */
                if (error.getErrorCode() == -1) {
                    int sleepMillis = retrySleepMillis * (1 << retryTimes);
                    try {
                        log.debug("?{}ms ??({})", sleepMillis, retryTimes + 1);
                        Thread.sleep(sleepMillis);
                    } catch (InterruptedException e1) {
                        throw new RuntimeException(e1);
                    }
                } else {
                    throw e;
                }
            }
        } while (++retryTimes < maxRetryTimes);

        throw new RuntimeException("??");
    }

    protected synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data)
            throws WxErrorException {
        if (uri.contains("access_token=")) {
            throw new IllegalArgumentException("uri???access_token: " + uri);
        }
        String accessToken = getAccessToken(false);

        String uriWithAccessToken = uri;
        uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken
                : "&access_token=" + accessToken;

        try {
            return executor.execute(getHttpclient(), httpProxy, uriWithAccessToken, data);
        } catch (WxErrorException e) {
            WxError error = e.getError();
            /*
             * ??access_token
             * 40001 ?access_tokenAppSecretaccess_token
             * 42001 access_token
             */
            if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
                // wxCpConfigStorageaccess tokenaccess token
                wxCpConfigStorage.expireAccessToken();
                return execute(executor, uri, data);
            }
            if (error.getErrorCode() != 0) {
                throw new WxErrorException(error);
            }
            return null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    protected CloseableHttpClient getHttpclient() {
        return httpClient;
    }

    public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) {
        this.wxCpConfigStorage = wxConfigProvider;

        String http_proxy_host = wxCpConfigStorage.getHttp_proxy_host();
        int http_proxy_port = wxCpConfigStorage.getHttp_proxy_port();
        String http_proxy_username = wxCpConfigStorage.getHttp_proxy_username();
        String http_proxy_password = wxCpConfigStorage.getHttp_proxy_password();

        if (StringUtils.isNotBlank(http_proxy_host)) {
            // ??
            if (StringUtils.isNotBlank(http_proxy_username)) {
                // ????
                CredentialsProvider credsProvider = new BasicCredentialsProvider();
                credsProvider.setCredentials(new AuthScope(http_proxy_host, http_proxy_port),
                        new UsernamePasswordCredentials(http_proxy_username, http_proxy_password));
                httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
            } else {
                // ???
                httpClient = HttpClients.createDefault();
            }
            httpProxy = new HttpHost(http_proxy_host, http_proxy_port);
        } else {
            httpClient = HttpClients.createDefault();
        }
    }

    @Override
    public void setRetrySleepMillis(int retrySleepMillis) {
        this.retrySleepMillis = retrySleepMillis;
    }

    @Override
    public void setMaxRetryTimes(int maxRetryTimes) {
        this.maxRetryTimes = maxRetryTimes;
    }

    @Override
    public WxSession getSession(String id) {
        if (sessionManager == null) {
            return null;
        }
        return sessionManager.getSession(id);
    }

    @Override
    public WxSession getSession(String id, boolean create) {
        if (sessionManager == null) {
            return null;
        }
        return sessionManager.getSession(id, create);
    }

    @Override
    public void setSessionManager(WxSessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    @Override
    public String replaceParty(String mediaId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty";
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("media_id", mediaId);
        return post(url, jsonObject.toString());
    }

    @Override
    public String replaceUser(String mediaId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser";
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("media_id", mediaId);
        return post(url, jsonObject.toString());
    }

    @Override
    public String getTaskResult(String joinId) throws WxErrorException {
        String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId;
        return get(url, null);
    }

    public File getTmpDirFile() {
        return tmpDirFile;
    }

    public void setTmpDirFile(File tmpDirFile) {
        this.tmpDirFile = tmpDirFile;
    }
}