In the recent project, we need to use the group message sending function of the enterprise wechat application, which is used to notify the customer of the alarm messages generated by the system through the group message of the enterprise wechat, so as to achieve the purpose of timely troubleshooting. It is relatively simple to use it, and the process is recorded here for future reference.

I. Introduction and explanation of basic terms

1.corpid

Each enterprise has a unique CORPID. To obtain this information, you can view the Enterprise ID under “My Enterprise” – “Enterprise Information” in the management background (administrator permission is required).

2.userid

Each member has a unique userID, known as an “account”. In the management background -> “Address book” -> click into a member’s details page, you can see.

3. The department id

Each department has a unique ID, which can be seen by clicking the dot on the right of a department in the administrative background – “Address Book” – “Organization Structure” –

4.tagid

Each label has a unique label ID. In the management background -> Address Book -> Labels, select a label and click the “Label Details” button in the upper right corner to view it

5.agentid

Each application has a unique Agentid. In the admin background -> Applications and Applets -> Applications, click on an application, you can see AgentiD.

6.secret

Secret is the “key” used to ensure data security in enterprise applications. Each application has an independent access key. To ensure data security, secret must not be leaked.

Self-built application Secret. In the admin background -> "Applications and applets" -> "Applications" -> "Self-build", click an application, you can see. Basic application Secret. Some basic applications, such as "approve" and "punch out" applications, support operations through apis. In the management background -> "Application and Applets" -> "Application ->" "Basic", click an application, click the "API" button, you can see. Address book management secret. View it in "Management Tools" - "Address book Synchronization" (need to enable "API Interface synchronization"); Customer contact management Secret. In the "customer contact" column, click on the "API" button, you can see.Copy the code

7.access_token

Access_token is an important instrument for the enterprise background to obtain information from the enterprise wechat background, which is generated by Corpid and Secret. All interfaces need to carry this information during communication to verify the access permission of the interface

2. Group sending of application messages through enterprise wechat server API

1. Prepare the Http interface invocation tool class

Because you need to use Http call, here are two Http call tools prepared in advance, one is the Java Jdk with its own implementation, does not rely on any JAR package, can be used for testing, if you do not use much call, can also be used for production. The other is an Apache-dependent httpClient with connection pooling capabilities that is recommended for production. The code is at the end of the article because it takes up so much of the content

2. Obtain access_token

Official interface documentation: https://work.weixin.qq.com/api/doc/90000/90135/91039 request: GET (HTTPS) request URL:qyapi.weixin.qq.com/cgi-bin/get…

Note: The validity period is usually 2 hours. You need to log in again when the access_token is about to expire or has expired. The return result codes 40014 and 42001 can be used to indicate the invalid access_token and expired access_token respectively. The two error codes can be used to determine and update the access_token

Test code: debug using a pure Jdk implementation utility class:

	public static void main(String[] args) throws Exception {
		
		String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww160d****&corpsecret=BAtr2B***************";
		HttpRestUtils util = new HttpRestUtils();
		String result = util.doGet(url);
		System.out.println(result);
		
	}
Copy the code

Or use Apache’s httpClient utility class to debug:

	public static void main(String[] args) throws InterruptedException {
		// Replace your corpid and corpsecret with the correct ones
		String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww160d****&corpsecret=BAtr2B***************";
		System.out.println(HttpsKit.get(url));

		// Close connection pooling. Do not close this in a formal environment
		HttpsKit.closeConnectionPool();
		
	}
Copy the code

Response result:

{"errcode":0."errmsg":"ok"."access_token":"vMpg1HNU8PHf0qqNSfVGMXw2Gg0HN16LnvMH3J4LXeoY5MMA25PiO2ZabcdHJ6bRi5PqUuyLf94aRBb3yTKs344h5eU35doprLeIKtuf9xfKOk8VQ6F_GeT uxmcV_qQH0CLOrc5y9cXT9SCEi7LpQCiS4F4ssdff0zu-jyGmlEtUBplqSF8xDQBJ3aj6-hfg"."expires_in":7200}
Copy the code

3. Invoke the group application message sending interface

Group application message interface the official document: https://work.weixin.qq.com/api/doc/90000/90135/90236

Applications can push texts, images, videos, files, and images. Request: the POST request (HTTPS) address: https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN

In the case of a text message, the access_token is in the request address, the other parameters are wrapped in JSON, and the other parameters can be obtained by referring to the document above for details. The screenshot below, for example, is taken from official documentation

The test code is as follows: debug using a pure Jdk implementation utility class:

	public static void main(String[] args) throws Exception {
		
		String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=JhLKJB2cZssssssssssssssssssssssssssssssssssssU2HHQ";
		HttpRestUtils util = new HttpRestUtils();
		String paramStr = "{\" touser \ ": \" @ all \ ", \ "msgtype \" : \ "text \", \ "agentid \" : 1000001, \ "text \" : {\ "content \", \ "the application under test mass alarm message, excuse me, please ignore. \ "}}";
		String result = util.doPost(url, paramStr);
		System.out.println(result);
		
	}
Copy the code

Or use Apache’s httpClient utility class to debug:

	public static void main(String[] args) throws InterruptedException {
		
		String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=JhLKJB2cZssssssssssssssssssssssssssssssssssssU2HHQ";
		String data = "{\" touser \ ": \" @ all \ ", \ "msgtype \" : \ "text \", \ "agentid \" : 1000001, \ "text \" : {\ "content \", \ "the application under test mass alarm message, excuse me, please ignore. \ "}}";
		System.out.println(HttpsKit.postJson(url, data));
		
		// Close connection pooling. Do not close this in a formal environment
		HttpsKit.closeConnectionPool();
		
	}
Copy the code

4. The code of the Http interface invocation tool class is provided as follows

Tool class 1: the use of pure JDK URLConnection implementation

package cn.gzsendi.system.utils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class HttpRestUtils {
	
	private final String GET = "GET";
	private final String POST = "POST";
	private final String CharacterSet = "UTF-8";
	
	public String doGet(String url) throws Exception{
		String method = this.GET;
		return load(url,null,method);
	}
	
	public String doPost(String url, String params) throws Exception{
		String method = this.POST;
		return load(url,params,method);
	}
	
	/** * @param url * @param params * @param method * @return * @throws Exception */
	public String load(String url,String params,String method) throws Exception {
		
		HttpURLConnection conn = null;
		
		try {
			
			boolean isHttps = url.startsWith("https");
			
			URL restURL = new URL(url);
			conn = (HttpURLConnection) restURL.openConnection();	
			
			// HTTPS requests need extra processing
			if(isHttps) {
				
				TrustManager[] tm = { new MyX509TrustManager() };
			    SSLContext sslContext = SSLContext.getInstance("SSL"."SunJSSE");
			    sslContext.init(null, tm, new java.security.SecureRandom());
			    SSLSocketFactory ssf = sslContext.getSocketFactory();
			    ((HttpsURLConnection)conn).setSSLSocketFactory(ssf);
			    ((HttpsURLConnection)conn).setHostnameVerifier(new TrustAnyHostnameVerifier());
				
			}
			
			conn.setDoOutput(true);
			conn.setAllowUserInteraction(false);
			conn.setUseCaches(false); 
			conn.setRequestMethod(method);
			conn.connect();
			
			OutputStreamWriter out = null;
			OutputStream outputStream = null;
			if(this.POST.equals(method) && params ! = null){ outputStream = conn.getOutputStream(); out =new OutputStreamWriter(outputStream, this.CharacterSet);
				out.write(params);
				out.close();
			}
			
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, this.CharacterSet);
			BufferedReader bReader = new BufferedReader(inputStreamReader);
			String line = "";
			
			StringBuffer resultStr = new StringBuffer();
			while(null ! = (line = bReader.readLine())) { resultStr.append(line); }// Release resources
			bReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			if(out! =null) out.close();if(outputStream! =null)outputStream.close();return resultStr.toString();
			
		} catch (Exception e) {
			
			e.printStackTrace();
			
		} finally {
			if(conn ! = null) conn.disconnect(); }return null;
		
	}
	
	private class TrustAnyHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            // Return true
            return true; }}private class MyX509TrustManager implements X509TrustManager {

		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public X509Certificate[] getAcceptedIssuers() {
			returnnull; }}}Copy the code

Utility class 2: Write the implementation classes using Apache’s HttpClient. Pom dependencies. I use common-io, HttpClient, and log4j in my utility class. Log4j can be removed if you don’t want it, and common-io can be removed if you want it.

<! -- httpclient start --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.512.</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpmime</artifactId>
			<version>4.512.</version> </dependency> <! -- httpclient end --> <! -- common-io start --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.7</version> </dependency> <! -- common-io end --> <! --log start -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.725.</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>1.23.</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.23.</version> </dependency> <! --log end -->
Copy the code
package cn.gzsendi.system.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;

import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.IOUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpsKit {

	private static Logger logger = LoggerFactory.getLogger(HttpsKit.class);
	private static final int CONNECT_TIMEOUT = 10000;// Set the timeout period for connection establishment to 10000ms
	private static final int SOCKET_TIMEOUT = 30000; // How much time there is no data transfer
	private static final int HttpIdelTimeout = 30000;// Free time
	private static final int HttpMonitorInterval = 10000;// Check how often
	private static final int MAX_CONN = 200; // Maximum number of connections
	private static final int Max_PRE_ROUTE = 200; // Set the maximum number of connections to routes.
	private static CloseableHttpClient httpClient; // The client singleton that sends the request
	private static PoolingHttpClientConnectionManager manager; // Connection pool management class
	private static ScheduledExecutorService monitorExecutor;
	
	private static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
	private static final String APPLICATION_JSON = "application/json";

	private final static Object syncLock = new Object(); // Equivalent to a thread lock for thread safety
	
	private static RequestConfig requestConfig = RequestConfig.custom()
			.setConnectionRequestTimeout(CONNECT_TIMEOUT)
			.setConnectTimeout(CONNECT_TIMEOUT)
			.setSocketTimeout(SOCKET_TIMEOUT).build();

	private static CloseableHttpClient getHttpClient(a) {

		if (httpClient == null) {
			// Multiple threads calling getHttpClient at the same time can cause repeated creation of httpClient objects
			synchronized (syncLock) {
				if (httpClient == null) {
					
					try {
						httpClient = createHttpClient();
					} catch (KeyManagementException e) {
						logger.error("error",e);
					} catch (NoSuchAlgorithmException e) {
						logger.error("error",e);
					} catch (KeyStoreException e) {
						logger.error("error",e);
					}
					
					// Enable the monitoring thread to close abnormal and idle threads
					monitorExecutor = Executors.newScheduledThreadPool(1);
					monitorExecutor.scheduleAtFixedRate(new TimerTask() {
						@Override
						public void run() {
							
							// Close the abnormal connection
							manager.closeExpiredConnections();
							
							// Close the idle connection for 5 seconds
							manager.closeIdleConnections(HttpIdelTimeout,TimeUnit.MILLISECONDS);
							
							logger.info(manager.getTotalStats().toString());
							//logger.info("close expired and idle for over "+HttpIdelTimeout+"ms connection");} }, HttpMonitorInterval, HttpMonitorInterval, TimeUnit.MILLISECONDS); }}}return httpClient;
	}

	/** * Build an httpClient instance * @return * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws KeyManagementException */
	private static CloseableHttpClient createHttpClient(a) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
		
		SSLContextBuilder builder = new SSLContextBuilder();
        // All trust does not do the identification
        builder.loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return true; }}); ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory(); LayeredConnectionSocketFactory sslSocketFactory =new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
		Registry<ConnectionSocketFactory> registry = RegistryBuilder
				.<ConnectionSocketFactory> create()
				.register("http", plainSocketFactory)
				.register("https", sslSocketFactory).build();

		manager = new PoolingHttpClientConnectionManager(registry);
		// Set connection parameters
		manager.setMaxTotal(MAX_CONN); // Maximum number of connections
		manager.setDefaultMaxPerRoute(Max_PRE_ROUTE); // Maximum number of connections

		// If the request fails, retry the request
		HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
			
			@Override
			public boolean retryRequest(IOException e, int i,	HttpContext httpContext) {
				
				if (i > 3) {
					// Retry more than three times, aborting the request
					logger.error("retry has more than 3 time, give up request");
					return false;
				}
				if (e instanceof NoHttpResponseException) {
					// The server is not responding. Maybe the server is disconnected and should try again
					logger.error("receive no response from server, retry");
					return true;
				}
				if (e instanceof SSLHandshakeException) {
					// The SSL handshake is abnormal
					logger.error("SSL hand shake exception");
					return false;
				}
				if (e instanceof InterruptedIOException) {
					/ / timeout
					logger.error("InterruptedIOException");
					return false;
				}
				if (e instanceof UnknownHostException) {
					// The server is unreachable
					logger.error("server host unknown");
					return false;
				}
				if (e instanceof ConnectTimeoutException) {
					// Connection timed out
					logger.error("Connection Time out");
					return false;
				}
				if (e instanceof SSLException) {
					logger.error("SSLException");
					return false;
				}

				HttpClientContext context = HttpClientContext.adapt(httpContext);
				HttpRequest request = context.getRequest();
				
				if(! (request instanceof HttpEntityEnclosingRequest)) {// If the request is not a request to close the connection
					return true;
				}
				return false; }}; CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).setRetryHandler(handler).build();return client;
	}

	public static String get(String url) {
		
		HttpGet httpGet = new HttpGet(url);
		httpGet.setHeader("User-Agent"."Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36");
		httpGet.setConfig(requestConfig);
		
		CloseableHttpResponse response = null;
		InputStream in = null;

		String result = null;
		
		try {
			
			response = getHttpClient().execute(httpGet,HttpClientContext.create());
			
			HttpEntity entity = response.getEntity();
			if(entity ! = null) { in = entity.getContent(); result = IOUtils.toString(in,"utf-8"); }}catch (IOException e) {
			logger.error("error",e);
		} finally {
			try {
				if(in ! = null) in.close(); }catch (IOException e) {
				logger.error("error",e);
			}
			
			try {
				if(response ! = null) response.close(); }catch (IOException e) {
				logger.error("error",e); }}return result;
	}
	
	public static String postJson(String url,Map<String,Object> requestParams) {
		return postJson(url, JsonUtil.toJSONString(requestParams));
	}
	
	public static String postJson(String url,Map<String,Object> requestParams,Map<String,Object> headerParams) {
		return postJson(url, JsonUtil.toJSONString(requestParams),headerParams);
	}
	
	public static String postJson(String url,String requestParamStr) {
		return postJson(url, requestParamStr, null);
	}
	
	public static String postJson(String url,String requestParamStr,Map<String,Object> headerParams) {
		
		HttpPost httppost = new HttpPost(url);
		
		httppost.setHeader("Content-Type", APPLICATION_JSON+"; charset=" + CharEncoding.UTF_8);
        httppost.setHeader("Accept",APPLICATION_JSON+"; charset=" +CharEncoding.UTF_8);
        httppost.setHeader("User-Agent"."Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36");
        
        if(headerParams ! = null && headerParams.size()>0) {for(String headerName : headerParams.keySet()) {
        		httppost.setHeader(headerName,headerParams.get(headerName)+"");
            }
        }
        
        StringEntity se = new StringEntity(requestParamStr,CharEncoding.UTF_8);
        se.setContentType(APPLICATION_JSON+"; charset=" +CharEncoding.UTF_8);
        httppost.setEntity(se);
        
        httppost.setConfig(requestConfig);
		
		CloseableHttpResponse response = null;
		InputStream in = null;

		String result = null;
		
		try {
			
			response = getHttpClient().execute(httppost,HttpClientContext.create());
			
			HttpEntity entity = response.getEntity();
			if(entity ! = null) { in = entity.getContent(); result = IOUtils.toString(in,"utf-8"); }}catch (IOException e) {
			logger.error("error",e);
		} finally {
			try {
				if(in ! = null) in.close(); }catch (IOException e) {
				logger.error("error",e);
			}
			
			try {
				if(response ! = null) response.close(); }catch (IOException e) {
				logger.error("error",e); }}return result;
	}
	
	//requestParamStr---------->>> name=test&age=12
	public static String postFormUrlencoded(String url,String requestParamStr) {
		return postFormUrlencoded(url, requestParamStr ,null);
		
	}
	
	public static String postFormUrlencoded(String url,String requestParamStr,Map<String,Object> headerParams) {
		Map<String,String> requestParams = new HashMap<String,String>();
		
		String[] strs = requestParamStr.split("&");
		for(String str : strs) {
			String[] keyValues = str.split("=");
			if(keyValues.length == 2) {
				requestParams.put(keyValues[0], keyValues[1]); }}return postFormUrlencoded(url, requestParams,headerParams);
		
	}
	
	public static String postFormUrlencoded(String url,Map<String,String> requestParams) {
		return postFormUrlencoded(url,requestParams,null);
	}
	
	public static String postFormUrlencoded(String url,Map<String,String> requestParams,Map<String,Object> headerParams) {
		
		HttpPost httppost = new HttpPost(url);
		
		//application/json
		httppost.setHeader("Content-Type", APPLICATION_FORM_URLENCODED+"; charset=" + CharEncoding.UTF_8);
        httppost.setHeader("Accept",APPLICATION_FORM_URLENCODED+"; charset=" +CharEncoding.UTF_8);
        httppost.setHeader("User-Agent"."Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36");
        
        if(headerParams ! = null && headerParams.size()>0) {for(String headerName : headerParams.keySet()) {
        		httppost.setHeader(headerName,headerParams.get(headerName)+"");
            }
        }
        
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        
        for(String keyStr : requestParams.keySet()) {
            formparams.add(new BasicNameValuePair(keyStr, requestParams.get(keyStr)));
        }
        
        UrlEncodedFormEntity uefe = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
        httppost.setEntity(uefe);
        
        httppost.setConfig(requestConfig);
		
		CloseableHttpResponse response = null;
		InputStream in = null;

		String result = null;
		
		try {
			
			response = getHttpClient().execute(httppost,HttpClientContext.create());
			
			HttpEntity entity = response.getEntity();
			if(entity ! = null) { in = entity.getContent(); result = IOUtils.toString(in,"utf-8"); }}catch (IOException e) {
			logger.error("error",e);
		} finally {
			try {
				if(in ! = null) in.close();if(response ! = null) response.close(); }catch (IOException e) {
				logger.error("error",e); }}return result;
		 
	}

	/** * close connection pool */
	public static void closeConnectionPool(a) {
		try {
			if(httpClient ! = null) httpClient.close();if(manager ! = null) manager.close();if(monitorExecutor ! = null) monitorExecutor.shutdown(); }catch (IOException e) {
			logger.error("error",e); }}}Copy the code