TanJingyu 1 year ago
commit
f3ff9328b0

+ 39 - 0
pom.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.tzld.commons</groupId>
+        <artifactId>supom</artifactId>
+        <version>1.0.9</version>
+    </parent>
+
+    <groupId>com.cybertogether</groupId>
+    <artifactId>WX-Notice</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.5.12</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 18 - 0
src/main/java/com/cybertogether/wx/notice/Application.java

@@ -0,0 +1,18 @@
+package com.cybertogether.wx.notice;
+
+import com.cybertogether.wx.notice.infrastructure.Downstream;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+/**
+ * @author: TanJingyu
+ * @create:2023-06-14 13:28:17
+ **/
+@SpringBootApplication
+@EnableConfigurationProperties(value = Downstream.class)
+public class Application {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 23 - 0
src/main/java/com/cybertogether/wx/notice/infrastructure/Downstream.java

@@ -0,0 +1,23 @@
+package com.cybertogether.wx.notice.infrastructure;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+/**
+ * @description:
+ * @author: TanJingyu
+ * @create:2023-06-14 14:19:18
+ **/
+@ConfigurationProperties(prefix = "downstream")
+public class Downstream {
+    private List<String> urls;
+
+    public List<String> getUrls() {
+        return urls;
+    }
+
+    public void setUrls(List<String> urls) {
+        this.urls = urls;
+    }
+}

+ 629 - 0
src/main/java/com/cybertogether/wx/notice/infrastructure/HttpClientUtils.java

@@ -0,0 +1,629 @@
+package com.cybertogether.wx.notice.infrastructure;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Consts;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHeaders;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.*;
+import org.apache.http.config.*;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+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.ssl.SSLContexts;
+import org.apache.http.util.EntityUtils;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.security.KeyManagementException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+/**
+ * @description:
+ * @author: TanJingyu
+ * @create:2023-06-14 14:11:21
+ **/
+public class HttpClientUtils {
+
+    static Log log = LogFactory.getLog(HttpClientUtils.class);
+
+    private static CloseableHttpClient client;
+    private static RequestConfig requestConfigDefault;
+    private static PoolingHttpClientConnectionManager connManager = null;
+    // 默认请求获取数据的超时时间
+    private static Integer readTimeoutDefault = 15000;
+    // 默认连接超时时间
+    private static Integer connectTimeoutDefault = 5000;
+    // 默认从connectManager获取Connection超时时间
+    private static Integer getConnectionTimeoutDefault = 1000;
+    // 默认的字符集
+    private static String charsetDefault = "UTF-8";
+
+    public static final String contentTypeJson = "application/json";
+    public static final String contentTypeXml = "application/xml";
+
+    private enum MethodType {
+        GET, POST
+    }
+
+    static {
+        try {
+            SSLContext sslContext = SSLContexts.createDefault();
+            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
+
+                public void checkClientTrusted(X509Certificate[] arg0, String arg1)
+                        throws CertificateException {
+                }
+
+                public void checkServerTrusted(X509Certificate[] arg0, String arg1)
+                        throws CertificateException {
+                }
+
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+            }}, null);
+            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
+                    .register("http", PlainConnectionSocketFactory.INSTANCE)
+                    .register("https", new SSLConnectionSocketFactory(sslContext)).build();
+            connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+            SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
+            connManager.setDefaultSocketConfig(socketConfig);
+            MessageConstraints messageConstraints = MessageConstraints.custom().build();
+            ConnectionConfig connectionConfig = ConnectionConfig.custom()
+                    .setMalformedInputAction(CodingErrorAction.IGNORE)
+                    .setUnmappableInputAction(CodingErrorAction.IGNORE).setCharset(Consts.UTF_8)
+                    .setMessageConstraints(messageConstraints).build();
+            connManager.setDefaultConnectionConfig(connectionConfig);
+            connManager.setMaxTotal(1000);
+            connManager.setDefaultMaxPerRoute(500);
+            connManager.setValidateAfterInactivity(3000);
+        } catch (KeyManagementException e) {
+            log.error(e);
+        }
+
+        requestConfigDefault = RequestConfig.custom().setConnectionRequestTimeout(getConnectionTimeoutDefault)
+                .setConnectTimeout(connectTimeoutDefault).setSocketTimeout(readTimeoutDefault).build();
+        client = HttpClients.custom().useSystemProperties().setConnectionManager(connManager).setDefaultRequestConfig(requestConfigDefault)
+                .build();
+
+    }
+
+    /**
+     * get请求
+     *
+     * @param url 请求的url
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent get(String url) {
+        return get(url, null, connectTimeoutDefault, readTimeoutDefault, null);
+    }
+
+    /**
+     * @param url     请求的url
+     * @param headers 请求需要携带的header信息
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent get(String url, Map<String, String> headers) {
+        return get(url, null, connectTimeoutDefault, readTimeoutDefault, headers);
+    }
+
+    /**
+     * get请求
+     *
+     * @param url         请求的url
+     * @param contentType contentType,例如:text/plain
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent get(String url, String contentType) {
+        return get(url, contentType, connectTimeoutDefault, readTimeoutDefault, null);
+    }
+
+    /**
+     * get请求
+     *
+     * @param url            请求的url
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent get(String url, int connectTimeout, int readTimeout) {
+        return get(url, null, connectTimeout, readTimeout, null);
+    }
+
+    /**
+     * get请求
+     *
+     * @param url            请求的url
+     * @param contentType    contentType,例如:text/plain
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @param headers        请求需要携带的header信息
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent get(String url, String contentType, int connectTimeout, int readTimeout, Map<String, String> headers) {
+        return getOrPostUrl(MethodType.GET, url, contentType, connectTimeout, readTimeout, headers);
+    }
+
+    /**
+     * post请求,参数在url中
+     *
+     * @param url 请求的url
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postUrl(String url) {
+        return postUrl(url, null, connectTimeoutDefault, readTimeoutDefault);
+    }
+
+    /**
+     * post请求,参数在url中
+     *
+     * @param url         请求的url
+     * @param contentType contentType,例如:text/plain
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postUrl(String url, String contentType) {
+        return postUrl(url, contentType, connectTimeoutDefault, readTimeoutDefault);
+    }
+
+    /**
+     * post请求,参数在url中
+     *
+     * @param url            请求的url
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postUrl(String url, int connectTimeout, int readTimeout) {
+        return postUrl(url, null, connectTimeout, readTimeout);
+    }
+
+    /**
+     * post请求,参数在url中
+     *
+     * @param url            请求的url
+     * @param contentType    contentType,例如:text/plain
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postUrl(String url, String contentType, int connectTimeout, int readTimeout) {
+        return getOrPostUrl(MethodType.POST, url, contentType, connectTimeout, readTimeout);
+    }
+
+    /**
+     * get或者post请求,参数在url中
+     *
+     * @param methodType     GET/POST
+     * @param url            请求的url
+     * @param contentType    contentType,例如:text/plain
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    private static HttpResponseContent getOrPostUrl(MethodType methodType, String url, String contentType,
+                                                    int connectTimeout, int readTimeout) {
+        return getOrPostUrl(methodType, url, contentType, connectTimeout, readTimeout, null);
+    }
+
+    private static HttpResponseContent getOrPostUrl(MethodType methodType, String url, String contentType,
+                                                    int connectTimeout, int readTimeout, Map<String, String> extHeaders) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        if (methodType == MethodType.GET) {
+            request = new HttpGet(url);
+        } else {
+            request = new HttpPost(url);
+        }
+        // 设置contentType
+        if (contentType != null) {
+            request.addHeader(HttpHeaders.CONTENT_TYPE, contentType);
+        }
+        //add by nieqi since 2022-3-23
+        if (Objects.nonNull(extHeaders)) {
+            for (String s : extHeaders.keySet()) {
+                if (Objects.nonNull(s)) {
+                    String headerValue = extHeaders.get(s);
+                    if (Objects.nonNull(headerValue)) {
+                        request.addHeader(s, headerValue);
+                    }
+                }
+            }
+        }
+        hrc = executeHttpRequest(request, connectTimeout, readTimeout);
+        return hrc;
+    }
+
+    /**
+     * post请求,请求数据在data中
+     *
+     * @param url  请求的url
+     * @param data 请求数据
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postData(String url, String data) {
+        return postData(url, data, null, connectTimeoutDefault, readTimeoutDefault);
+
+    }
+
+    /**
+     * post请求,请求数据在data中
+     *
+     * @param url         请求的url
+     * @param data        请求数据
+     * @param contentType contentType,例如:text/plain
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postData(String url, String data, String contentType) {
+        return postData(url, data, contentType, connectTimeoutDefault, readTimeoutDefault);
+    }
+
+    /**
+     * post请求,请求数据在data中
+     *
+     * @param url            请求的url
+     * @param data           请求数据
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postData(String url, String data, int connectTimeout, int readTimeout) {
+        return postData(url, data, null, connectTimeout, readTimeout);
+    }
+
+    /**
+     * post请求,请求数据在data中
+     *
+     * @param url            请求的url
+     * @param data           请求数据
+     * @param contentType    contentType,例如:text/plain
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postData(String url, String data, String contentType, int connectTimeout,
+                                               int readTimeout) {
+        Map<String, String> headerMap = new HashMap<String, String>();
+        headerMap.put(HttpHeaders.CONTENT_TYPE, contentType);
+        return postDataAddHeader(url, data, headerMap, connectTimeout, readTimeout);
+
+    }
+
+    /**
+     * post请求,请求数据在data中
+     *
+     * @param url            请求的url
+     * @param data           请求数据
+     * @param headerMap      http请求头,key-value放在map中
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postDataAddHeader(String url, String data, Map<String, String> headerMap,
+                                                        int connectTimeout, int readTimeout) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setEntity(new StringEntity(data, charsetDefault));
+        request = httpPost;
+        // 设置header
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                request.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+        hrc = executeHttpRequest(request, connectTimeout, readTimeout);
+        return hrc;
+
+    }
+
+    /**
+     * post请求,form表单,不能包含二进制数据
+     *
+     * @param url       请求的url
+     * @param paramsMap form表单参数map
+     * @param headerMap form表单header
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postForm(String url, Map<String, String> paramsMap, Map<String, String> headerMap) {
+        return postForm(url, paramsMap, connectTimeoutDefault, readTimeoutDefault, headerMap);
+    }
+
+    /**
+     * post请求,form表单,不能包含二进制数据
+     *
+     * @param url            请求的url
+     * @param paramsMap      form表单参数map
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @param headerMap      form表单header
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postForm(String url, Map<String, String> paramsMap, int connectTimeout,
+                                               int readTimeout, Map<String, String> headerMap) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        HttpPost httpPost = new HttpPost(url);
+        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
+        if (Objects.nonNull(paramsMap)) {
+            Iterator<String> iterator = paramsMap.keySet().iterator();
+            while (iterator.hasNext()) {
+                String key = iterator.next();
+                Object value = paramsMap.get(key);
+                nvps.add(new BasicNameValuePair(key, String.valueOf(value)));
+            }
+        }
+        // 设置header
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                httpPost.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+        try {
+            httpPost.setEntity(new UrlEncodedFormEntity(nvps, charsetDefault));
+        } catch (UnsupportedEncodingException e) {
+            log.error(e);
+        }
+        request = httpPost;
+        hrc = executeHttpRequest(request, connectTimeout, readTimeout);
+        return hrc;
+    }
+
+    /**
+     * post请求,multipart,支持File,byte[]这两种二进制数据
+     *
+     * @param url       请求的url
+     * @param paramsMap 请求参数map
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postMultipart(String url, Map<String, Object> paramsMap) {
+        return postMultipart(url, paramsMap, connectTimeoutDefault, readTimeoutDefault, null);
+    }
+
+    public static HttpResponseContent postMultipart(String url, Map<String, Object> paramsMap, Map<String, String> headerMap) {
+        return postMultipart(url, paramsMap, connectTimeoutDefault, readTimeoutDefault, headerMap);
+    }
+
+    /**
+     * post请求,multipart,支持File,byte[]这两种二进制数据
+     *
+     * @param url            请求的url
+     * @param paramsMap      请求参数map
+     * @param connectTimeout 连接超时设置,毫秒
+     * @param readTimeout    读取超时设置,毫秒
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    public static HttpResponseContent postMultipart(String url, Map<String, Object> paramsMap, int connectTimeout,
+                                                    int readTimeout, Map<String, String> headerMap) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        HttpPost httpPost = new HttpPost(url);
+        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
+        multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+        Iterator<String> iterator = paramsMap.keySet().iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next();
+            Object value = paramsMap.get(key);
+            if (value instanceof File) {
+                multipartEntityBuilder.addBinaryBody(key, (File) value);
+            } else if (value instanceof byte[]) {
+                multipartEntityBuilder.addBinaryBody(key, (byte[]) value);
+            } else {
+                multipartEntityBuilder.addTextBody(key, String.valueOf(value),
+                        ContentType.create("text/plain", charsetDefault));
+            }
+        }
+        httpPost.setEntity(multipartEntityBuilder.build());
+        // 设置header
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                httpPost.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+        request = httpPost;
+        hrc = executeHttpRequest(request, connectTimeout, readTimeout);
+        return hrc;
+    }
+
+    public static HttpResponseContent postRequestBody(String url, Object s, Map<String, String> headerMap) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        HttpPost httpPost = new HttpPost(url);
+
+        StringEntity stringEntity = new StringEntity((s instanceof String) ? (String) s : JSONObject.toJSONString(s), Consts.UTF_8);
+        stringEntity.setContentEncoding(Consts.UTF_8.name());
+        stringEntity.setContentType("application/json");
+        httpPost.setEntity(stringEntity);
+        // 设置header
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                httpPost.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+        request = httpPost;
+        hrc = executeHttpRequest(request, connectTimeoutDefault, readTimeoutDefault);
+        return hrc;
+    }
+
+    public static HttpResponseContent deleteRequestBody(String url, Object s, HashMap<String, String> headerMap) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        HttpDeleteExpand httpDelete = new HttpDeleteExpand(url);
+
+        StringEntity stringEntity = new StringEntity(JSONObject.toJSONString(s), Consts.UTF_8);
+        stringEntity.setContentEncoding(Consts.UTF_8.name());
+        stringEntity.setContentType("application/json");
+        httpDelete.setEntity(stringEntity);
+        // 设置header
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                httpDelete.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+        request = httpDelete;
+        hrc = executeHttpRequest(request, connectTimeoutDefault, readTimeoutDefault);
+        return hrc;
+    }
+
+    public static HttpResponseContent putRequestBody(String url, Object s, HashMap<String, String> headerMap) {
+        HttpRequestBase request = null;
+        HttpResponseContent hrc = null;
+        HttpPut httpPut = new HttpPut(url);
+
+        StringEntity stringEntity = new StringEntity(JSONObject.toJSONString(s), Consts.UTF_8);
+        stringEntity.setContentEncoding(Consts.UTF_8.name());
+        stringEntity.setContentType("application/json");
+        httpPut.setEntity(stringEntity);
+        // 设置header
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                httpPut.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+        request = httpPut;
+        hrc = executeHttpRequest(request, connectTimeoutDefault, readTimeoutDefault);
+        return hrc;
+    }
+
+    /**
+     * 执行Http请求
+     *
+     * @param request
+     * @param connectTimeout
+     * @param readTimeout
+     * @return HttpResponseContent对象,如果http请求出现异常,返回null
+     */
+    private static HttpResponseContent executeHttpRequest(HttpRequestBase request, int connectTimeout,
+                                                          int readTimeout) {
+        CloseableHttpResponse response = null;
+        HttpResponseContent hrc = null;
+        try {
+            // 设置请求配置
+            RequestConfig.Builder configBuilder = RequestConfig.custom();
+            // 设置连接超时
+            configBuilder.setConnectTimeout(connectTimeout);
+            // 设置读取超时
+            configBuilder.setSocketTimeout(readTimeout);
+            // 设置从连接池获取连接实例的超时
+            configBuilder.setConnectionRequestTimeout(getConnectionTimeoutDefault);
+            RequestConfig requestConfig = configBuilder.build();
+            request.setConfig(requestConfig);
+            log.debug("开始执行Http请求, uri:" + request.getURI());
+            response = client.execute(request);
+            hrc = getHttpResponseContent(response);
+            return hrc;
+        } catch (Exception e) {
+            log.error("执行Http请求异常, uri:" + request.getURI(), e);
+        } finally {
+            close(request, response);
+        }
+        return hrc;
+
+    }
+
+    /**
+     * 封装HTTP响应报文
+     *
+     * @param response
+     * @return
+     */
+    private static HttpResponseContent getHttpResponseContent(CloseableHttpResponse response) {
+        // 获取响应实体
+        HttpEntity entity = response.getEntity();
+        if (entity == null) {
+            return null;
+        }
+        HttpResponseContent hrc = new HttpResponseContent();
+        hrc.setHeaders(response.getAllHeaders());
+        hrc.setStatusCode(response.getStatusLine().getStatusCode());
+        ContentType contentType = ContentType.getOrDefault(entity);
+        hrc.setMimeType(contentType.getMimeType());
+        if (contentType.getCharset() != null) {
+            hrc.setCharsetName(contentType.getCharset().name());
+        }
+        try {
+            hrc.setContentBytes(EntityUtils.toByteArray(entity));
+        } catch (IOException e) {
+            log.error("封装HTTP响应报文异常", e);
+        }
+        return hrc;
+
+    }
+
+    /**
+     * 关闭资源
+     *
+     * @param request
+     * @param response
+     */
+    private static void close(HttpRequestBase request, CloseableHttpResponse response) {
+        try {
+            if (request != null)
+                request.releaseConnection();
+            if (response != null)
+                response.close();
+        } catch (Exception e) {
+            log.error("关闭资源异常", e);
+        }
+    }
+
+    /**
+     * url编码
+     *
+     * @param url
+     * @param charset
+     * @return
+     */
+    public static String encodeURL(String url, String charset) {
+        if (url == null || charset == null) {
+            return null;
+        }
+        int p = url.indexOf("?");
+        if (p < 0) {
+            return url;
+        }
+        StringBuilder sb = new StringBuilder();
+        String preStr = url.substring(0, p + 1);
+        sb.append(preStr);
+        String queryStr = url.substring(p + 1, url.length());
+        String[] array = queryStr.split("&");
+        for (int i = 0; i < array.length; i++) {
+            String str = array[i];
+            int pp = str.indexOf("=");
+            if (pp > -1) {
+                sb.append(str.substring(0, pp + 1));
+                try {
+                    sb.append(URLEncoder.encode(str.substring(pp + 1, str.length()), charset));
+                } catch (UnsupportedEncodingException e) {
+                    e.printStackTrace();
+                }
+                if (i < array.length - 1) {
+                    sb.append("&");
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+}
+

+ 24 - 0
src/main/java/com/cybertogether/wx/notice/infrastructure/HttpDeleteExpand.java

@@ -0,0 +1,24 @@
+package com.cybertogether.wx.notice.infrastructure;
+
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+
+import java.net.URI;
+
+public class HttpDeleteExpand extends HttpEntityEnclosingRequestBase {
+    public static final String METHOD_NAME = "DELETE";
+
+    public HttpDeleteExpand() {
+    }
+
+    public HttpDeleteExpand(URI uri) {
+        this.setURI(uri);
+    }
+
+    public HttpDeleteExpand(String uri) {
+        this.setURI(URI.create(uri));
+    }
+
+    public String getMethod() {
+        return "DELETE";
+    }
+}

+ 102 - 0
src/main/java/com/cybertogether/wx/notice/infrastructure/HttpResponseContent.java

@@ -0,0 +1,102 @@
+package com.cybertogether.wx.notice.infrastructure;
+
+import org.apache.http.Header;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * HTTP请求响应报文封装类
+ * 
+ * @author liuzhiheng
+ * @since 1.0
+ * @date 2017-4-7 下午12:14:16
+ */
+public class HttpResponseContent {
+
+	private int statusCode; // 响应状态码
+	private String mimeType; // mime类型
+	private String charsetName; // 字符集
+	private byte[] contentBytes; // 响应报文主体内容的二进制数据
+	private Header[] headers; // 响应头
+
+	/**
+	 * 获取响应报文主体内容,字符串,使用响应报文中的字符集编码
+	 * 
+	 * @return
+	 */
+	public String getBodyContent() {
+		return this.getBodyContent(charsetName);
+	}
+
+	/**
+	 * 获取响应报文主体内容,字符串
+	 * 
+	 * @param charsetName
+	 *            指定的字符编码
+	 * @return
+	 */
+	public String getBodyContent(String charsetName) {
+		String str = null;
+		if (charsetName == null) {
+			charsetName = "UTF-8";
+		}
+		try {
+			str = new String(contentBytes, charsetName);
+		} catch (UnsupportedEncodingException e) {
+			// 忽略异常
+			e.printStackTrace();
+		}
+		return str;
+	}
+
+	public boolean isSuccessful(){
+		return statusCode == 200;
+	}
+
+	public int getStatusCode() {
+		return statusCode;
+	}
+
+	public void setStatusCode(int statusCode) {
+		this.statusCode = statusCode;
+	}
+
+	public String getMimeType() {
+		return mimeType;
+	}
+
+	public void setMimeType(String mimeType) {
+		this.mimeType = mimeType;
+	}
+
+	public String getCharsetName() {
+		return charsetName;
+	}
+
+	public void setCharsetName(String charsetName) {
+		this.charsetName = charsetName;
+	}
+
+	public byte[] getContentBytes() {
+		return contentBytes;
+	}
+
+	public void setContentBytes(byte[] contentBytes) {
+		this.contentBytes = contentBytes;
+	}
+
+	public Header[] getHeaders() {
+		return headers;
+	}
+
+	public void setHeaders(Header[] headers) {
+		this.headers = headers;
+	}
+
+	@Override
+	public String toString() {
+		return "HttpResponseContent [statusCode=" + statusCode + ", mimeType=" + mimeType + ", charsetName=" + charsetName
+				+ ", bodyContent=" + getBodyContent() + "]";
+	}
+
+}

+ 57 - 0
src/main/java/com/cybertogether/wx/notice/web/WxPushReceptionController.java

@@ -0,0 +1,57 @@
+package com.cybertogether.wx.notice.web;
+
+import com.cybertogether.wx.notice.infrastructure.Downstream;
+import com.cybertogether.wx.notice.infrastructure.HttpClientUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+
+/**
+ * 接收微信推送通知
+ * 授权流程文档
+ * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=
+ */
+@RestController
+@RequestMapping("/wx/pushReception")
+public class WxPushReceptionController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(WxPushReceptionController.class);
+    @Autowired
+    Downstream downstream;
+
+    /**
+     * 接收微信的授权事件通知
+     * 开发配置-开发资料-授权事件接收配置
+     * localhost:8080/pandora/wx/pushReception/authorization?timestamp=34353535&nonce=4342344&msg_signature=ee661ba5dbbf82d488aaf18883808411d9c73b31
+     */
+    @PostMapping("/authorization")
+    public String receptionAuthorizationEvent(@RequestParam("timestamp") String timestamp,
+                                              @RequestParam("nonce") String nonce,
+                                              @RequestParam("msg_signature") String msgSignature,
+                                              @RequestBody String encryptedXmlBodyText) {
+
+        String urlParams = String.format("?timestamp=%s&nonce=%s&msg_signature=%s", timestamp, nonce, msgSignature);
+
+        for (String url : downstream.getUrls()) {
+            try {
+                HttpClientUtils.postData(url + urlParams, encryptedXmlBodyText);
+            }catch (Exception e) {
+                // ignore
+            }
+        }
+
+        return "success";
+    }
+
+    /**
+     * 接收微信消息与事件通知
+     * 开发配置-开发资料-消息与事件接收配置
+     */
+    @PostMapping("/msgEvent/{appId}")
+    public String receptionMsgEvent(@RequestBody String requestBody, @PathVariable String appId) {
+        LOGGER.info("收到微信[消息与事件]推送通知,appId:" + appId + " requestBody: " + requestBody);
+        return "success";
+    }
+}

+ 19 - 0
src/main/resources/application.yml

@@ -0,0 +1,19 @@
+spring:
+  application:
+    name: wx-notice
+
+server:
+  tomcat:
+    threads:
+      max: 1000
+    uri-encoding: UTF-8
+    accept-count: 1000
+    connection-timeout: 30000
+  servlet:
+    context-path: /wxAuth
+    session:
+      timeout: 60
+downstream:
+  urls:
+    - https://pandora-testapi.denet.me/pandora/wx/pushReception/authorization
+    - https://www.baidu.com