@@ -19,7 +19,7 @@ weixin-java-tools | |||
<dependency> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-mp</artifactId> | |||
<version>1.0.4</version> | |||
<version>1.0.5</version> | |||
</dependency> | |||
``` | |||
@@ -29,7 +29,7 @@ weixin-java-tools | |||
<dependency> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-cp</artifactId> | |||
<version>1.0.4</version> | |||
<version>1.0.5</version> | |||
</dependency> | |||
``` | |||
@@ -5,7 +5,7 @@ | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.4</version> | |||
<version>1.0.5</version> | |||
<packaging>pom</packaging> | |||
<name>WeiXin Java Tools - Parent</name> | |||
<description>微信公众号、企业号上级POM</description> | |||
@@ -70,11 +70,6 @@ | |||
<artifactId>gson</artifactId> | |||
<version>2.2.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-lang3</artifactId> | |||
<version>3.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-codec</groupId> | |||
<artifactId>commons-codec</artifactId> | |||
@@ -0,0 +1,229 @@ | |||
<?xml version="1.0"?> | |||
<project | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.5-SNAPSHOT</version> | |||
<packaging>pom</packaging> | |||
<name>WeiXin Java Tools - Parent</name> | |||
<description>微信公众号、企业号上级POM</description> | |||
<url>https://github.com/chanjarster/weixin-java-tools</url> | |||
<licenses> | |||
<license> | |||
<name>The Apache License, Version 2.0</name> | |||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |||
</license> | |||
</licenses> | |||
<developers> | |||
<developer> | |||
<name>Daniel Qian</name> | |||
<email>chanjarster@gmail.com</email> | |||
</developer> | |||
</developers> | |||
<scm> | |||
<connection>scm:git:https://github.com/chanjarster/weixin-java-tools.git</connection> | |||
<developerConnection>scm:git:git@github.com:chanjarster/weixin-java-tools.git</developerConnection> | |||
<url>https://github.com/chanjarster/weixin-java-tools</url> | |||
</scm> | |||
<modules> | |||
<module>weixin-java-common</module> | |||
<module>weixin-java-cp</module> | |||
<module>weixin-java-mp</module> | |||
</modules> | |||
<properties> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<downloadJavadocs>true</downloadJavadocs> | |||
<downloadSources>true</downloadSources> | |||
<httpclient.version>4.3.5</httpclient.version> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.apache.httpcomponents</groupId> | |||
<artifactId>fluent-hc</artifactId> | |||
<version>${httpclient.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.httpcomponents</groupId> | |||
<artifactId>httpmime</artifactId> | |||
<version>${httpclient.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.xml.bind</groupId> | |||
<artifactId>jaxb-api</artifactId> | |||
<version>2.2.7</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.sun.xml.bind</groupId> | |||
<artifactId>jaxb-impl</artifactId> | |||
<version>2.2.7</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.gson</groupId> | |||
<artifactId>gson</artifactId> | |||
<version>2.2.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-codec</groupId> | |||
<artifactId>commons-codec</artifactId> | |||
<version>1.9</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
<version>2.4</version> | |||
</dependency> | |||
</dependencies> | |||
<dependencyManagement> | |||
<dependencies> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.11</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.inject</groupId> | |||
<artifactId>guice</artifactId> | |||
<version>3.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.testng</groupId> | |||
<artifactId>testng</artifactId> | |||
<version>6.8.7</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.9.5</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-server</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-servlet</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
</dependencyManagement> | |||
<distributionManagement> | |||
<snapshotRepository> | |||
<id>ossrh</id> | |||
<url>https://oss.sonatype.org/content/repositories/snapshots</url> | |||
</snapshotRepository> | |||
<repository> | |||
<id>ossrh</id> | |||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> | |||
</repository> | |||
</distributionManagement> | |||
<profiles> | |||
<profile> | |||
<id>release</id> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-source-plugin</artifactId> | |||
<version>2.2.1</version> | |||
<executions> | |||
<execution> | |||
<id>attach-sources</id> | |||
<goals> | |||
<goal>jar-no-fork</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-javadoc-plugin</artifactId> | |||
<version>2.9.1</version> | |||
<executions> | |||
<execution> | |||
<id>attach-javadocs</id> | |||
<goals> | |||
<goal>jar</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
<configuration> | |||
<charset>UTF-8</charset> | |||
<locale>zh_CN</locale> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-gpg-plugin</artifactId> | |||
<version>1.5</version> | |||
<executions> | |||
<execution> | |||
<id>sign-artifacts</id> | |||
<phase>verify</phase> | |||
<goals> | |||
<goal>sign</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</profile> | |||
</profiles> | |||
<build> | |||
<pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<version>2.17</version> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<version>1.6.3</version> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<serverId>ossrh</serverId> | |||
<nexusUrl>https://oss.sonatype.org/</nexusUrl> | |||
<autoReleaseAfterClose>false</autoReleaseAfterClose> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-release-plugin</artifactId> | |||
<version>2.5</version> | |||
<configuration> | |||
<autoVersionSubmodules>true</autoVersionSubmodules> | |||
<useReleaseProfile>false</useReleaseProfile> | |||
<releaseProfiles>release</releaseProfiles> | |||
<goals>deploy</goals> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -6,7 +6,7 @@ | |||
<parent> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.4</version> | |||
<version>1.0.5</version> | |||
</parent> | |||
<artifactId>weixin-java-common</artifactId> | |||
@@ -0,0 +1,68 @@ | |||
<?xml version="1.0"?> | |||
<project | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.5-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>weixin-java-common</artifactId> | |||
<name>WeiXin Java Tools - Common</name> | |||
<description>微信公众号、企业号Java SDK Common</description> | |||
<dependencies> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.testng</groupId> | |||
<artifactId>testng</artifactId> | |||
<version>6.8.7</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.9.5</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.inject</groupId> | |||
<artifactId>guice</artifactId> | |||
<version>3.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-server</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-servlet</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<configuration> | |||
<suiteXmlFiles> | |||
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||
</suiteXmlFiles> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -128,4 +128,12 @@ public class WxConsts { | |||
/** 弹出地理位置选择器 */ | |||
public static final String LOCATION_SELECT = "location_select"; | |||
/////////////////////// | |||
// oauth2网页授权的scope | |||
/////////////////////// | |||
/** 不弹出授权页面,直接跳转,只能获取用户openid */ | |||
public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; | |||
/** 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 */ | |||
public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; | |||
} |
@@ -0,0 +1,100 @@ | |||
package me.chanjar.weixin.common.util; | |||
/** | |||
* copy from apache-commons-lang3 | |||
*/ | |||
public class StringUtils { | |||
/** | |||
* <p>Checks if a CharSequence is whitespace, empty ("") or null.</p> | |||
* | |||
* <pre> | |||
* StringUtils.isBlank(null) = true | |||
* StringUtils.isBlank("") = true | |||
* StringUtils.isBlank(" ") = true | |||
* StringUtils.isBlank("bob") = false | |||
* StringUtils.isBlank(" bob ") = false | |||
* </pre> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is null, empty or whitespace | |||
* @since 2.0 | |||
* @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) | |||
*/ | |||
public static boolean isBlank(CharSequence cs) { | |||
int strLen; | |||
if (cs == null || (strLen = cs.length()) == 0) { | |||
return true; | |||
} | |||
for (int i = 0; i < strLen; i++) { | |||
if (Character.isWhitespace(cs.charAt(i)) == false) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
/** | |||
* <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> | |||
* | |||
* <pre> | |||
* StringUtils.isNotBlank(null) = false | |||
* StringUtils.isNotBlank("") = false | |||
* StringUtils.isNotBlank(" ") = false | |||
* StringUtils.isNotBlank("bob") = true | |||
* StringUtils.isNotBlank(" bob ") = true | |||
* </pre> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is | |||
* not empty and not null and not whitespace | |||
* @since 2.0 | |||
* @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) | |||
*/ | |||
public static boolean isNotBlank(CharSequence cs) { | |||
return !StringUtils.isBlank(cs); | |||
} | |||
/** | |||
* <p>Checks if a CharSequence is empty ("") or null.</p> | |||
* | |||
* <pre> | |||
* StringUtils.isEmpty(null) = true | |||
* StringUtils.isEmpty("") = true | |||
* StringUtils.isEmpty(" ") = false | |||
* StringUtils.isEmpty("bob") = false | |||
* StringUtils.isEmpty(" bob ") = false | |||
* </pre> | |||
* | |||
* <p>NOTE: This method changed in Lang version 2.0. | |||
* It no longer trims the CharSequence. | |||
* That functionality is available in isBlank().</p> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is empty or null | |||
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) | |||
*/ | |||
public static boolean isEmpty(CharSequence cs) { | |||
return cs == null || cs.length() == 0; | |||
} | |||
/** | |||
* <p>Checks if a CharSequence is not empty ("") and not null.</p> | |||
* | |||
* <pre> | |||
* StringUtils.isNotEmpty(null) = false | |||
* StringUtils.isNotEmpty("") = false | |||
* StringUtils.isNotEmpty(" ") = true | |||
* StringUtils.isNotEmpty("bob") = true | |||
* StringUtils.isNotEmpty(" bob ") = true | |||
* </pre> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is not empty and not null | |||
* @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) | |||
*/ | |||
public static boolean isNotEmpty(CharSequence cs) { | |||
return !StringUtils.isEmpty(cs); | |||
} | |||
} |
@@ -1,12 +1,9 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.InputStreamResponseHandler; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.Utf8ResponseHandler; | |||
import org.apache.commons.lang3.StringUtils; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
import org.apache.http.Header; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.ClientProtocolException; | |||
@@ -0,0 +1,47 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import java.io.UnsupportedEncodingException; | |||
public class URIUtil { | |||
private static final String ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; | |||
public static String encodeURIComponent(String input) { | |||
if (StringUtils.isEmpty(input)) { | |||
return input; | |||
} | |||
int l = input.length(); | |||
StringBuilder o = new StringBuilder(l * 3); | |||
try { | |||
for (int i = 0; i < l; i++) { | |||
String e = input.substring(i, i + 1); | |||
if (ALLOWED_CHARS.indexOf(e) == -1) { | |||
byte[] b = e.getBytes("utf-8"); | |||
o.append(getHex(b)); | |||
continue; | |||
} | |||
o.append(e); | |||
} | |||
return o.toString(); | |||
} catch (UnsupportedEncodingException e) { | |||
e.printStackTrace(); | |||
} | |||
return input; | |||
} | |||
private static String getHex(byte buf[]) { | |||
StringBuilder o = new StringBuilder(buf.length * 3); | |||
for (int i = 0; i < buf.length; i++) { | |||
int n = (int) buf[i] & 0xff; | |||
o.append("%"); | |||
if (n < 0x10) { | |||
o.append("0"); | |||
} | |||
o.append(Long.toString(n, 16).toUpperCase()); | |||
} | |||
return o.toString(); | |||
} | |||
} |
@@ -6,7 +6,7 @@ | |||
<parent> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.4</version> | |||
<version>1.0.5</version> | |||
</parent> | |||
<artifactId>weixin-java-cp</artifactId> | |||
@@ -0,0 +1,73 @@ | |||
<?xml version="1.0"?> | |||
<project | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.5-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>weixin-java-cp</artifactId> | |||
<name>WeiXin Java Tools - CP</name> | |||
<description>微信企业号Java SDK</description> | |||
<dependencies> | |||
<dependency> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.testng</groupId> | |||
<artifactId>testng</artifactId> | |||
<version>6.8.7</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.9.5</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.inject</groupId> | |||
<artifactId>guice</artifactId> | |||
<version>3.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-server</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-servlet</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<configuration> | |||
<suiteXmlFiles> | |||
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||
</suiteXmlFiles> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -27,6 +27,8 @@ public interface WxCpConfigStorage { | |||
public int getExpiresIn(); | |||
public String getOauth2redirectUri(); | |||
public String getHttp_proxy_host(); | |||
public int getHttp_proxy_port(); | |||
@@ -18,6 +18,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||
protected String agentId; | |||
protected int expiresIn; | |||
protected String oauth2redirectUri; | |||
protected String http_proxy_host; | |||
protected int http_proxy_port; | |||
protected String http_proxy_username; | |||
@@ -88,6 +90,15 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||
this.agentId = agentId; | |||
} | |||
@Override | |||
public String getOauth2redirectUri() { | |||
return this.oauth2redirectUri; | |||
} | |||
public void setOauth2redirectUri(String oauth2redirectUri) { | |||
this.oauth2redirectUri = oauth2redirectUri; | |||
} | |||
public String getHttp_proxy_host() { | |||
return http_proxy_host; | |||
} | |||
@@ -16,8 +16,9 @@ public interface WxCpMessageHandler { | |||
* | |||
* @param wxMessage | |||
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | |||
* @param wxCpService | |||
* @return xml格式的消息,如果在异步规则里处理的话,可以返回null | |||
*/ | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context); | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService); | |||
} |
@@ -15,8 +15,9 @@ public interface WxCpMessageInterceptor { | |||
* 拦截微信消息 | |||
* @param wxMessage | |||
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | |||
* @param wxCpService | |||
* @return true代表OK,false代表不OK | |||
*/ | |||
public boolean intercept(WxCpXmlMessage wxMessage, Map<String, Object> context); | |||
public boolean intercept(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService); | |||
} |
@@ -41,16 +41,30 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
*/ | |||
public class WxCpMessageRouter { | |||
private static final int DEFAULT_THREAD_POOL_SIZE = 20; | |||
private final List<Rule> rules = new ArrayList<Rule>(); | |||
private final ExecutorService es = Executors.newCachedThreadPool(); | |||
private final ExecutorService executorService; | |||
private final WxCpService wxCpService; | |||
public WxCpMessageRouter(WxCpService wxCpService) { | |||
this.wxCpService = wxCpService; | |||
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | |||
} | |||
public WxCpMessageRouter(WxCpService wxMpService, int threadPoolSize) { | |||
this.wxCpService = wxMpService; | |||
this.executorService = Executors.newFixedThreadPool(threadPoolSize); | |||
} | |||
/** | |||
* 开始一个新的Route规则 | |||
* @return | |||
*/ | |||
public Rule rule() { | |||
return new Rule(this); | |||
return new Rule(this, wxCpService); | |||
} | |||
/** | |||
@@ -73,7 +87,7 @@ public class WxCpMessageRouter { | |||
if (matchRules.get(0).async) { | |||
// 只要第一个是异步的,那就异步执行 | |||
// 在另一个线程里执行 | |||
es.submit(new Runnable() { | |||
executorService.submit(new Runnable() { | |||
public void run() { | |||
for (final Rule rule : matchRules) { | |||
rule.service(wxMessage); | |||
@@ -101,8 +115,12 @@ public class WxCpMessageRouter { | |||
private final WxCpMessageRouter routerBuilder; | |||
private final WxCpService wxCpService; | |||
private boolean async = true; | |||
private String fromUser; | |||
private String msgType; | |||
private String event; | |||
@@ -121,8 +139,9 @@ public class WxCpMessageRouter { | |||
private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>(); | |||
protected Rule(WxCpMessageRouter routerBuilder) { | |||
protected Rule(WxCpMessageRouter routerBuilder, WxCpService wxCpService) { | |||
this.routerBuilder = routerBuilder; | |||
this.wxCpService = wxCpService; | |||
} | |||
/** | |||
@@ -195,6 +214,16 @@ public class WxCpMessageRouter { | |||
return this; | |||
} | |||
/** | |||
* 如果fromUser等于某值 | |||
* @param fromUser | |||
* @return | |||
*/ | |||
public Rule fromUser(String fromUser) { | |||
this.fromUser = fromUser; | |||
return this; | |||
} | |||
/** | |||
* 设置微信消息拦截器 | |||
* @param interceptor | |||
@@ -265,6 +294,8 @@ public class WxCpMessageRouter { | |||
protected boolean test(WxCpXmlMessage wxMessage) { | |||
return | |||
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||
&& | |||
(this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | |||
&& | |||
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | |||
@@ -288,7 +319,7 @@ public class WxCpMessageRouter { | |||
Map<String, Object> context = new HashMap<String, Object>(); | |||
// 如果拦截器不通过 | |||
for (WxCpMessageInterceptor interceptor : this.interceptors) { | |||
if (!interceptor.intercept(wxMessage, context)) { | |||
if (!interceptor.intercept(wxMessage, context, wxCpService)) { | |||
return null; | |||
} | |||
} | |||
@@ -297,7 +328,7 @@ public class WxCpMessageRouter { | |||
WxCpXmlOutMessage res = null; | |||
for (WxCpMessageHandler handler : this.handlers) { | |||
// 返回最后handler的结果 | |||
res = handler.handle(wxMessage, context); | |||
res = handler.handle(wxMessage, context, wxCpService); | |||
} | |||
return res; | |||
} | |||
@@ -274,6 +274,27 @@ public interface WxCpService { | |||
*/ | |||
public void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 构造oauth2授权的url连接 | |||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | |||
* </pre> | |||
* @param state | |||
* @return code | |||
*/ | |||
public String oauth2buildAuthorizationUrl(String state); | |||
/** | |||
* <pre> | |||
* 用oauth2获取用户信息 | |||
* http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息 | |||
* 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。 | |||
* </pre> | |||
* @param code | |||
* @return [userid, deviceid] | |||
*/ | |||
public String[] oauth2getUserInfo(String code) throws WxErrorException; | |||
/** | |||
* 移除标签成员 | |||
* | |||
@@ -1,25 +1,27 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.StringReader; | |||
import java.util.List; | |||
import java.util.UUID; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
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 me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.bean.WxMenu; | |||
import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.bean.*; | |||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.common.util.crypto.SHA1; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
import me.chanjar.weixin.common.util.http.*; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpTag; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.auth.AuthScope; | |||
import org.apache.http.auth.UsernamePasswordCredentials; | |||
@@ -33,20 +35,13 @@ import org.apache.http.impl.client.BasicResponseHandler; | |||
import org.apache.http.impl.client.CloseableHttpClient; | |||
import org.apache.http.impl.client.HttpClients; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.internal.Streams; | |||
import com.google.gson.reflect.TypeToken; | |||
import com.google.gson.stream.JsonReader; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.StringReader; | |||
import java.util.List; | |||
import java.util.UUID; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
public class WxCpServiceImpl implements WxCpService { | |||
@@ -321,6 +316,41 @@ public class WxCpServiceImpl implements WxCpService { | |||
execute(new SimplePostRequestExecutor(), url, jsonObject.toString()); | |||
} | |||
@Override | |||
public String oauth2buildAuthorizationUrl(String state) { | |||
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | |||
url += "appid=" + wxCpConfigStorage.getCorpId(); | |||
url += "&redirect_uri=" + URIUtil.encodeURIComponent(wxCpConfigStorage.getOauth2redirectUri()); | |||
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 { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"; | |||
url += "access_token=" + wxCpConfigStorage.getAccessToken(); | |||
url += "&code=" + code; | |||
url += "agendid=" + wxCpConfigStorage.getAgentId(); | |||
try { | |||
RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||
String responseText = executor.execute(getHttpclient(), httpProxy, 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")}; | |||
} catch (ClientProtocolException e) { | |||
throw new RuntimeException(e); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
public String get(String url, String queryParam) throws WxErrorException { | |||
return execute(new SimpleGetRequestExecutor(), url, queryParam); | |||
} | |||
@@ -10,7 +10,7 @@ import java.util.List; | |||
@XmlRootElement(name = "xml") | |||
@XmlAccessorType(XmlAccessType.FIELD) | |||
public class WxCpXmlOutMewsMessage extends WxCpXmlOutMessage { | |||
public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage { | |||
@XmlElement(name = "ArticleCount") | |||
protected int articleCount; | |||
@@ -19,7 +19,7 @@ public class WxCpXmlOutMewsMessage extends WxCpXmlOutMessage { | |||
@XmlElement(name = "item") | |||
protected final List<Item> articles = new ArrayList<Item>(); | |||
public WxCpXmlOutMewsMessage() { | |||
public WxCpXmlOutNewsMessage() { | |||
this.msgType = WxConsts.XML_MSG_NEWS; | |||
} | |||
@@ -3,14 +3,14 @@ package me.chanjar.weixin.cp.bean.outxmlbuilder; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMewsMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMewsMessage.Item; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage.Item; | |||
/** | |||
* 图文消息builder | |||
* @author Daniel Qian | |||
*/ | |||
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutMewsMessage> { | |||
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutNewsMessage> { | |||
protected final List<Item> articles = new ArrayList<Item>(); | |||
@@ -19,8 +19,8 @@ public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutMewsMe | |||
return this; | |||
} | |||
public WxCpXmlOutMewsMessage build() { | |||
WxCpXmlOutMewsMessage m = new WxCpXmlOutMewsMessage(); | |||
public WxCpXmlOutNewsMessage build() { | |||
WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | |||
for(Item item : articles) { | |||
m.addArticle(item); | |||
} | |||
@@ -8,17 +8,12 @@ | |||
*/ | |||
package me.chanjar.weixin.cp.util.json; | |||
import java.lang.reflect.Type; | |||
import com.google.gson.*; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import com.google.gson.JsonArray; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonSerializationContext; | |||
import com.google.gson.JsonSerializer; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.lang.reflect.Type; | |||
/** | |||
* | |||
@@ -36,7 +31,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||
messageJson.addProperty("msgtype", message.getMsgType()); | |||
if (StringUtils.isNotBlank(message.getToParty())) { | |||
messageJson.addProperty("toparty", message.getToUser()); | |||
messageJson.addProperty("toparty", message.getToParty()); | |||
} | |||
if (StringUtils.isNotBlank(message.getToTag())) { | |||
messageJson.addProperty("totag", message.getToUser()); | |||
@@ -75,6 +70,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||
} | |||
if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | |||
JsonObject newsJsonObject = new JsonObject(); | |||
JsonArray articleJsonArray = new JsonArray(); | |||
for (WxCpMessage.WxArticle article : message.getArticles()) { | |||
JsonObject articleJson = new JsonObject(); | |||
@@ -84,7 +80,8 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||
articleJson.addProperty("picurl", article.getPicUrl()); | |||
articleJsonArray.add(articleJson); | |||
} | |||
messageJson.add("articles", articleJsonArray); | |||
newsJsonObject.add("articles", articleJsonArray); | |||
messageJson.add("news", newsJsonObject); | |||
} | |||
return messageJson; | |||
@@ -59,6 +59,7 @@ public class XmlTransformer { | |||
Marshaller m = JAXB_CONTEXT.createMarshaller(); | |||
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | |||
m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); | |||
m.setProperty(Marshaller.JAXB_FRAGMENT, true); | |||
m.marshal(object, writer); | |||
} | |||
@@ -79,7 +80,7 @@ public class XmlTransformer { | |||
return JAXBContext.newInstance( | |||
WxCpXmlOutMessage.class, | |||
WxCpXmlOutImageMessage.class, | |||
WxCpXmlOutMewsMessage.class, | |||
WxCpXmlOutNewsMessage.class, | |||
WxCpXmlOutTextMessage.class, | |||
WxCpXmlOutVideoMessage.class, | |||
WxCpXmlOutVoiceMessage.class, | |||
@@ -1,6 +1,6 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import org.apache.commons.lang3.StringUtils; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import org.testng.Assert; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
@@ -47,7 +47,7 @@ public class WxCpMessageRouterTest { | |||
@Test(dataProvider="messages-1") | |||
public void testSync(WxCpXmlMessage message, String expected) { | |||
StringBuffer sb = new StringBuffer(); | |||
WxCpMessageRouter router = new WxCpMessageRouter(); | |||
WxCpMessageRouter router = new WxCpMessageRouter(null); | |||
prepare(false, sb, router); | |||
router.route(message); | |||
Assert.assertEquals(sb.toString(), expected); | |||
@@ -56,7 +56,7 @@ public class WxCpMessageRouterTest { | |||
@Test(dataProvider="messages-1") | |||
public void testAsync(WxCpXmlMessage message, String expected) throws InterruptedException { | |||
StringBuffer sb = new StringBuffer(); | |||
WxCpMessageRouter router = new WxCpMessageRouter(); | |||
WxCpMessageRouter router = new WxCpMessageRouter(null); | |||
prepare(true, sb, router); | |||
router.route(message); | |||
Thread.sleep(500l); | |||
@@ -64,10 +64,10 @@ public class WxCpMessageRouterTest { | |||
} | |||
public void testConcurrency() throws InterruptedException { | |||
final WxCpMessageRouter router = new WxCpMessageRouter(); | |||
final WxCpMessageRouter router = new WxCpMessageRouter(null); | |||
router.rule().handler(new WxCpMessageHandler() { | |||
@Override | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) { | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService) { | |||
return null; | |||
} | |||
}).end(); | |||
@@ -149,7 +149,7 @@ public class WxCpMessageRouterTest { | |||
} | |||
@Override | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) { | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService) { | |||
sb.append(this.echoStr).append(','); | |||
return null; | |||
} | |||
@@ -83,8 +83,8 @@ public class WxCpMessageTest { | |||
article2.setTitle("Happy Day"); | |||
reply.getArticles().add(article2); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||
} | |||
public void testNewsBuild() { | |||
@@ -101,8 +101,8 @@ public class WxCpMessageTest { | |||
article2.setTitle("Happy Day"); | |||
WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||
} | |||
} |
@@ -13,8 +13,7 @@ public class WxCpXmlOutImageMessageTest { | |||
m.setFromUserName("from"); | |||
m.setToUserName("to"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -27,8 +26,7 @@ public class WxCpXmlOutImageMessageTest { | |||
public void testBuild() { | |||
WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -7,20 +7,19 @@ import org.testng.annotations.Test; | |||
public class WxCpXmlOutNewsMessageTest { | |||
public void test() { | |||
WxCpXmlOutMewsMessage m = new WxCpXmlOutMewsMessage(); | |||
WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | |||
m.setCreateTime(1122l); | |||
m.setFromUserName("fromUser"); | |||
m.setToUserName("toUser"); | |||
WxCpXmlOutMewsMessage.Item item = new WxCpXmlOutMewsMessage.Item(); | |||
WxCpXmlOutNewsMessage.Item item = new WxCpXmlOutNewsMessage.Item(); | |||
item.setDescription("description"); | |||
item.setPicUrl("picUrl"); | |||
item.setTitle("title"); | |||
item.setUrl("url"); | |||
m.addArticle(item); | |||
m.addArticle(item); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -46,20 +45,19 @@ public class WxCpXmlOutNewsMessageTest { | |||
} | |||
public void testBuild() { | |||
WxCpXmlOutMewsMessage.Item item = new WxCpXmlOutMewsMessage.Item(); | |||
WxCpXmlOutNewsMessage.Item item = new WxCpXmlOutNewsMessage.Item(); | |||
item.setDescription("description"); | |||
item.setPicUrl("picUrl"); | |||
item.setTitle("title"); | |||
item.setUrl("url"); | |||
WxCpXmlOutMewsMessage m = WxCpXmlOutMessage.NEWS() | |||
WxCpXmlOutNewsMessage m = WxCpXmlOutMessage.NEWS() | |||
.fromUser("fromUser") | |||
.toUser("toUser") | |||
.addArticle(item) | |||
.addArticle(item) | |||
.build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -13,8 +13,7 @@ public class WxCpXmlOutTextMessageTest { | |||
m.setFromUserName("from"); | |||
m.setToUserName("to"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -27,8 +26,7 @@ public class WxCpXmlOutTextMessageTest { | |||
public void testBuild() { | |||
WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -15,8 +15,7 @@ public class WxCpXmlOutVideoMessageTest { | |||
m.setFromUserName("fromUser"); | |||
m.setToUserName("toUser"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -39,8 +38,7 @@ public class WxCpXmlOutVideoMessageTest { | |||
.title("title") | |||
.description("ddfff") | |||
.build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -13,8 +13,7 @@ public class WxCpXmlOutVoiceMessageTest { | |||
m.setFromUserName("from"); | |||
m.setToUserName("to"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -27,8 +26,7 @@ public class WxCpXmlOutVoiceMessageTest { | |||
public void testBuild() { | |||
WxCpXmlOutVoiceMessage m = WxCpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -1,20 +1,94 @@ | |||
package me.chanjar.weixin.cp.demo; | |||
import me.chanjar.weixin.cp.api.*; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
import org.eclipse.jetty.server.Server; | |||
import org.eclipse.jetty.servlet.ServletHandler; | |||
import org.eclipse.jetty.servlet.ServletHolder; | |||
import javax.xml.bind.JAXBException; | |||
import java.io.InputStream; | |||
import java.util.Map; | |||
/** | |||
* @author Daniel Qian | |||
*/ | |||
public class WxCpDemoServer { | |||
private static WxCpConfigStorage wxCpConfigStorage; | |||
private static WxCpService wxCpService; | |||
private static WxCpMessageRouter wxCpMessageRouter; | |||
public static void main(String[] args) throws Exception { | |||
initWeixin(); | |||
Server server = new Server(8080); | |||
ServletHandler handler = new ServletHandler(); | |||
server.setHandler(handler); | |||
ServletHandler servletHandler = new ServletHandler(); | |||
server.setHandler(servletHandler); | |||
ServletHolder endpointServletHolder = new ServletHolder(new WxCpEndpointServlet(wxCpConfigStorage, wxCpService, wxCpMessageRouter)); | |||
servletHandler.addServletWithMapping(endpointServletHolder, "/*"); | |||
ServletHolder oauthServletHolder = new ServletHolder(new WxCpOAuth2Servlet(wxCpService)); | |||
servletHandler.addServletWithMapping(oauthServletHolder, "/oauth2/*"); | |||
handler.addServletWithMapping(WxCpDemoServlet.class, "/*"); | |||
server.start(); | |||
server.join(); | |||
} | |||
private static void initWeixin() { | |||
try { | |||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||
WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(is1); | |||
wxCpConfigStorage = config; | |||
wxCpService = new WxCpServiceImpl(); | |||
wxCpService.setWxCpConfigStorage(config); | |||
WxCpMessageHandler handler = new WxCpMessageHandler() { | |||
@Override | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, | |||
WxCpService wxCpService) { | |||
WxCpXmlOutTextMessage m = WxCpXmlOutMessage | |||
.TEXT() | |||
.content("测试加密消息") | |||
.fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()) | |||
.build(); | |||
return m; | |||
} | |||
}; | |||
WxCpMessageHandler oauth2handler = new WxCpMessageHandler() { | |||
@Override | |||
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, | |||
WxCpService wxCpService) { | |||
String href = "<a href=\"" + wxCpService.oauth2buildAuthorizationUrl(null) | |||
+ "\">测试oauth2</a>"; | |||
return WxCpXmlOutMessage | |||
.TEXT() | |||
.content(href) | |||
.fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()).build(); | |||
} | |||
}; | |||
wxCpMessageRouter = new WxCpMessageRouter(wxCpService); | |||
wxCpMessageRouter | |||
.rule() | |||
.async(false) | |||
.content("哈哈") // 拦截内容为“哈哈”的消息 | |||
.handler(handler) | |||
.end() | |||
.rule() | |||
.async(false) | |||
.content("oauth") | |||
.handler(oauth2handler) | |||
.end() | |||
; | |||
} catch (JAXBException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} |
@@ -1,11 +1,11 @@ | |||
package me.chanjar.weixin.cp.demo; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.cp.api.*; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | |||
import org.apache.commons.lang3.StringUtils; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServlet; | |||
@@ -19,58 +19,31 @@ import java.util.Map; | |||
/** | |||
* @author Daniel Qian | |||
*/ | |||
public class WxCpDemoServlet extends HttpServlet { | |||
public class WxCpEndpointServlet extends HttpServlet { | |||
protected WxCpService wxCpService; | |||
protected WxCpConfigStorage wxCpConfigStorage; | |||
protected WxCpService wxCpService; | |||
protected WxCpMessageRouter wxCpMessageRouter; | |||
@Override public void init() throws ServletException { | |||
// | |||
super.init(); | |||
try { | |||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||
WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(is1); | |||
wxCpConfigStorage = config; | |||
wxCpService = new WxCpServiceImpl(); | |||
wxCpService.setWxCpConfigStorage(config); | |||
WxCpMessageHandler handler = new WxCpMessageHandler() { | |||
@Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) { | |||
WxCpXmlOutTextMessage m = WxCpXmlOutMessage | |||
.TEXT() | |||
.content("测试加密消息") | |||
.fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()) | |||
.build(); | |||
return m; | |||
} | |||
}; | |||
wxCpMessageRouter = new WxCpMessageRouter(); | |||
wxCpMessageRouter | |||
.rule() | |||
.async(false) | |||
.content("哈哈") // 拦截内容为“哈哈”的消息 | |||
.handler(handler) | |||
.end(); | |||
} catch (JAXBException e) { | |||
throw new RuntimeException(e); | |||
} | |||
public WxCpEndpointServlet(WxCpConfigStorage wxCpConfigStorage, WxCpService wxCpService, | |||
WxCpMessageRouter wxCpMessageRouter) { | |||
this.wxCpConfigStorage = wxCpConfigStorage; | |||
this.wxCpService = wxCpService; | |||
this.wxCpMessageRouter = wxCpMessageRouter; | |||
} | |||
@Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||
@Override | |||
protected void service(HttpServletRequest request, HttpServletResponse response) | |||
throws ServletException, IOException { | |||
response.setContentType("text/html;charset=utf-8"); | |||
response.setStatus(HttpServletResponse.SC_OK); | |||
String msgSignature = request.getParameter("msg_signature"); | |||
String nonce = request.getParameter("nonce"); | |||
String timestamp = request.getParameter("timestamp"); | |||
String echostr = request.getParameter("echostr"); | |||
response.setContentType("text/html;charset=utf-8"); | |||
response.setStatus(HttpServletResponse.SC_OK); | |||
if (StringUtils.isNotBlank(echostr)) { | |||
if (!wxCpService.checkSignature(msgSignature, timestamp, nonce, echostr)) { | |||
// 消息签名不正确,说明不是公众平台发过来的消息 | |||
@@ -84,11 +57,9 @@ public class WxCpDemoServlet extends HttpServlet { | |||
return; | |||
} | |||
WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(request.getInputStream(), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||
WxCpXmlMessage inMessage = WxCpXmlMessage | |||
.fromEncryptedXml(request.getInputStream(), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||
WxCpXmlOutMessage outMessage = wxCpMessageRouter.route(inMessage); | |||
if (outMessage != null) { | |||
response.getWriter().write(outMessage.toEncryptedXml(wxCpConfigStorage)); | |||
} |
@@ -0,0 +1,48 @@ | |||
package me.chanjar.weixin.cp.demo; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServlet; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
/** | |||
* Created by qianjia on 14/11/28. | |||
*/ | |||
public class WxCpOAuth2Servlet extends HttpServlet { | |||
protected WxCpService wxCpService; | |||
public WxCpOAuth2Servlet(WxCpService wxCpService) { | |||
this.wxCpService = wxCpService; | |||
} | |||
@Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||
throws ServletException, IOException { | |||
response.setContentType("text/html;charset=utf-8"); | |||
response.setStatus(HttpServletResponse.SC_OK); | |||
String code = request.getParameter("code"); | |||
try { | |||
response.getWriter().println("<h1>code</h1>"); | |||
response.getWriter().println(code); | |||
String[] res = wxCpService.oauth2getUserInfo(code); | |||
response.getWriter().println("<h1>result</h1>"); | |||
response.getWriter().println(Arrays.toString(res)); | |||
} catch (WxErrorException e) { | |||
e.printStackTrace(); | |||
} | |||
response.getWriter().flush(); | |||
response.getWriter().close(); | |||
} | |||
} |
@@ -9,4 +9,5 @@ | |||
<userId>企业号通讯录里的某个userid</userId> | |||
<departmentId>企业号通讯录的某个部门id</departmentId> | |||
<tagId>企业号通讯录里的某个tagid</tagId> | |||
<oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri> | |||
</xml> |
@@ -6,7 +6,7 @@ | |||
<parent> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.4</version> | |||
<version>1.0.5</version> | |||
</parent> | |||
<artifactId>weixin-java-mp</artifactId> | |||
<name>WeiXin Java Tools - MP</name> | |||
@@ -0,0 +1,72 @@ | |||
<?xml version="1.0"?> | |||
<project | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>1.0.5-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>weixin-java-mp</artifactId> | |||
<name>WeiXin Java Tools - MP</name> | |||
<description>微信公众号Java SDK</description> | |||
<dependencies> | |||
<dependency> | |||
<groupId>me.chanjar</groupId> | |||
<artifactId>weixin-java-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.testng</groupId> | |||
<artifactId>testng</artifactId> | |||
<version>6.8.7</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-all</artifactId> | |||
<version>1.9.5</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.inject</groupId> | |||
<artifactId>guice</artifactId> | |||
<version>3.0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-server</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-servlet</artifactId> | |||
<version>9.3.0.M0</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<configuration> | |||
<suiteXmlFiles> | |||
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile> | |||
</suiteXmlFiles> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -25,6 +25,8 @@ public interface WxMpConfigStorage { | |||
public int getExpiresIn(); | |||
public String getOauth2redirectUri(); | |||
public String getHttp_proxy_host(); | |||
public int getHttp_proxy_port(); | |||
@@ -16,6 +16,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
protected String aesKey; | |||
protected int expiresIn; | |||
protected String oauth2redirectUri; | |||
protected String http_proxy_host; | |||
protected int http_proxy_port; | |||
protected String http_proxy_username; | |||
@@ -78,6 +80,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
this.expiresIn = expiresIn; | |||
} | |||
@Override | |||
public String getOauth2redirectUri() { | |||
return this.oauth2redirectUri; | |||
} | |||
public void setOauth2redirectUri(String oauth2redirectUri) { | |||
this.oauth2redirectUri = oauth2redirectUri; | |||
} | |||
public String getHttp_proxy_host() { | |||
return http_proxy_host; | |||
} | |||
@@ -16,8 +16,9 @@ public interface WxMpMessageHandler { | |||
* | |||
* @param wxMessage | |||
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | |||
* @param wxMpService | |||
* @return xml格式的消息,如果在异步规则里处理的话,可以返回null | |||
*/ | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context); | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService); | |||
} |
@@ -15,8 +15,9 @@ public interface WxMpMessageInterceptor { | |||
* 拦截微信消息 | |||
* @param wxMessage | |||
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 | |||
* @param wxMpService | |||
* @return true代表OK,false代表不OK | |||
*/ | |||
public boolean intercept(WxMpXmlMessage wxMessage, Map<String, Object> context); | |||
public boolean intercept(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService); | |||
} |
@@ -40,17 +40,31 @@ import java.util.regex.Pattern; | |||
* | |||
*/ | |||
public class WxMpMessageRouter { | |||
private static final int DEFAULT_THREAD_POOL_SIZE = 20; | |||
private final List<Rule> rules = new ArrayList<Rule>(); | |||
private final ExecutorService es = Executors.newCachedThreadPool(); | |||
private final ExecutorService executorService; | |||
private final WxMpService wxMpService; | |||
public WxMpMessageRouter(WxMpService wxMpService) { | |||
this.wxMpService = wxMpService; | |||
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | |||
} | |||
public WxMpMessageRouter(WxMpService wxMpService, int threadPoolSize) { | |||
this.wxMpService = wxMpService; | |||
this.executorService = Executors.newFixedThreadPool(threadPoolSize); | |||
} | |||
/** | |||
* 开始一个新的Route规则 | |||
* @return | |||
*/ | |||
public Rule rule() { | |||
return new Rule(this); | |||
return new Rule(this, wxMpService); | |||
} | |||
/** | |||
@@ -73,7 +87,7 @@ public class WxMpMessageRouter { | |||
if (matchRules.get(0).async) { | |||
// 只要第一个是异步的,那就异步执行 | |||
// 在另一个线程里执行 | |||
es.submit(new Runnable() { | |||
executorService.execute(new Runnable() { | |||
public void run() { | |||
for (final Rule rule : matchRules) { | |||
rule.service(wxMessage); | |||
@@ -101,7 +115,11 @@ public class WxMpMessageRouter { | |||
private final WxMpMessageRouter routerBuilder; | |||
private final WxMpService wxMpService; | |||
private boolean async = true; | |||
private String fromUser; | |||
private String msgType; | |||
@@ -119,8 +137,9 @@ public class WxMpMessageRouter { | |||
private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>(); | |||
protected Rule(WxMpMessageRouter routerBuilder) { | |||
protected Rule(WxMpMessageRouter routerBuilder, WxMpService wxMpService) { | |||
this.routerBuilder = routerBuilder; | |||
this.wxMpService = wxMpService; | |||
} | |||
/** | |||
@@ -182,7 +201,17 @@ public class WxMpMessageRouter { | |||
this.rContent = regex; | |||
return this; | |||
} | |||
/** | |||
* 如果fromUser等于某值 | |||
* @param fromUser | |||
* @return | |||
*/ | |||
public Rule fromUser(String fromUser) { | |||
this.fromUser = fromUser; | |||
return this; | |||
} | |||
/** | |||
* 设置微信消息拦截器 | |||
* @param interceptor | |||
@@ -252,7 +281,9 @@ public class WxMpMessageRouter { | |||
} | |||
protected boolean test(WxMpXmlMessage wxMessage) { | |||
return | |||
return | |||
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||
&& | |||
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | |||
&& | |||
(this.event == null || this.event.equals(wxMessage.getEvent())) | |||
@@ -274,7 +305,7 @@ public class WxMpMessageRouter { | |||
Map<String, Object> context = new HashMap<String, Object>(); | |||
// 如果拦截器不通过 | |||
for (WxMpMessageInterceptor interceptor : this.interceptors) { | |||
if (!interceptor.intercept(wxMessage, context)) { | |||
if (!interceptor.intercept(wxMessage, context, wxMpService)) { | |||
return null; | |||
} | |||
} | |||
@@ -283,7 +314,7 @@ public class WxMpMessageRouter { | |||
WxMpXmlOutMessage res = null; | |||
for (WxMpMessageHandler handler : this.handlers) { | |||
// 返回最后handler的结果 | |||
res = handler.handle(wxMessage, context); | |||
res = handler.handle(wxMessage, context, wxMpService); | |||
} | |||
return res; | |||
} | |||
@@ -329,6 +329,54 @@ public interface WxMpService { | |||
*/ | |||
WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 构造oauth2授权的url连接 | |||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 | |||
* </pre> | |||
* @param scope | |||
* @param state | |||
* @return code | |||
*/ | |||
public String oauth2buildAuthorizationUrl(String scope, String state); | |||
/** | |||
* <pre> | |||
* 用code换取oauth2的access token | |||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息 | |||
* </pre> | |||
* @param code | |||
* @return | |||
*/ | |||
public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 刷新oauth2的access token | |||
* </pre> | |||
* @param refreshToken | |||
* @return | |||
*/ | |||
public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以 | |||
* </pre> | |||
* @param oAuth2AccessToken | |||
* @param lang zh_CN, zh_TW, en | |||
*/ | |||
public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 验证oauth2的access token是否有效 | |||
* </pre> | |||
* @param oAuth2AccessToken | |||
* @return | |||
*/ | |||
public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); | |||
/** | |||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | |||
* @param url | |||
@@ -10,6 +10,7 @@ import me.chanjar.weixin.common.bean.WxMenu; | |||
import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.common.util.crypto.SHA1; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
import me.chanjar.weixin.common.util.http.*; | |||
@@ -18,7 +19,6 @@ import me.chanjar.weixin.mp.bean.*; | |||
import me.chanjar.weixin.mp.bean.result.*; | |||
import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; | |||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.auth.AuthScope; | |||
import org.apache.http.auth.UsernamePasswordCredentials; | |||
@@ -27,7 +27,6 @@ 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.client.methods.HttpPost; | |||
import org.apache.http.impl.client.BasicCredentialsProvider; | |||
import org.apache.http.impl.client.BasicResponseHandler; | |||
import org.apache.http.impl.client.CloseableHttpClient; | |||
@@ -38,7 +37,6 @@ import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.StringReader; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.UUID; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
@@ -299,6 +297,98 @@ public class WxMpServiceImpl implements WxMpService { | |||
return WxMpSemanticQueryResult.fromJson(responseContent); | |||
} | |||
@Override | |||
public String oauth2buildAuthorizationUrl(String scope, String state) { | |||
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | |||
url += "appid=" + wxMpConfigStorage.getAppId(); | |||
url += "&redirect_uri=" + URIUtil.encodeURIComponent(wxMpConfigStorage.getOauth2redirectUri()); | |||
url += "&response_type=code"; | |||
url += "&scope=" + scope; | |||
if (state != null) { | |||
url += "&state=" + state; | |||
} | |||
url += "#wechat_redirect"; | |||
return url; | |||
} | |||
@Override | |||
public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { | |||
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"; | |||
url += "appid=" + wxMpConfigStorage.getAppId(); | |||
url += "&secret=" + wxMpConfigStorage.getSecret(); | |||
url += "&code=" + code; | |||
url += "&grant_type=authorization_code"; | |||
try { | |||
RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||
String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||
return WxMpOAuth2AccessToken.fromJson(responseText); | |||
} catch (ClientProtocolException e) { | |||
throw new RuntimeException(e); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@Override | |||
public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { | |||
String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?"; | |||
url += "appid=" + wxMpConfigStorage.getAppId(); | |||
url += "&grant_type=refresh_token"; | |||
url += "&refresh_token=" + refreshToken; | |||
try { | |||
RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||
String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||
return WxMpOAuth2AccessToken.fromJson(responseText); | |||
} catch (ClientProtocolException e) { | |||
throw new RuntimeException(e); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@Override | |||
public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException { | |||
String url = "https://api.weixin.qq.com/sns/userinfo?"; | |||
url += "access_token=" + oAuth2AccessToken.getAccessToken(); | |||
url += "&openid=" + oAuth2AccessToken.getOpenId(); | |||
if (lang == null) { | |||
url += "&lang=zh_CN"; | |||
} else { | |||
url += "&lang=" + lang; | |||
} | |||
try { | |||
RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||
String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||
return WxMpUser.fromJson(responseText); | |||
} catch (ClientProtocolException e) { | |||
throw new RuntimeException(e); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@Override | |||
public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { | |||
String url = "https://api.weixin.qq.com/sns/auth?"; | |||
url += "access_token=" + oAuth2AccessToken; | |||
url += "&openid=" + oAuth2AccessToken.getOpenId(); | |||
try { | |||
RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||
executor.execute(getHttpclient(), httpProxy, url, null); | |||
} catch (ClientProtocolException e) { | |||
throw new RuntimeException(e); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} catch (WxErrorException e) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
public String get(String url, String queryParam) throws WxErrorException { | |||
return execute(new SimpleGetRequestExecutor(), url, queryParam); | |||
} | |||
@@ -10,7 +10,7 @@ import java.util.List; | |||
@XmlRootElement(name = "xml") | |||
@XmlAccessorType(XmlAccessType.FIELD) | |||
public class WxMpXmlOutMewsMessage extends WxMpXmlOutMessage { | |||
public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { | |||
@XmlElement(name = "ArticleCount") | |||
protected int articleCount; | |||
@@ -19,7 +19,7 @@ public class WxMpXmlOutMewsMessage extends WxMpXmlOutMessage { | |||
@XmlElement(name = "item") | |||
protected final List<Item> articles = new ArrayList<Item>(); | |||
public WxMpXmlOutMewsMessage() { | |||
public WxMpXmlOutNewsMessage() { | |||
this.msgType = WxConsts.XML_MSG_NEWS; | |||
} | |||
@@ -1,6 +1,6 @@ | |||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMewsMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutNewsMessage; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@@ -9,18 +9,18 @@ import java.util.List; | |||
* 图文消息builder | |||
* @author chanjarster | |||
*/ | |||
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutMewsMessage> { | |||
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMessage> { | |||
protected final List<WxMpXmlOutMewsMessage.Item> articles = new ArrayList<WxMpXmlOutMewsMessage.Item>(); | |||
protected final List<WxMpXmlOutNewsMessage.Item> articles = new ArrayList<WxMpXmlOutNewsMessage.Item>(); | |||
public NewsBuilder addArticle(WxMpXmlOutMewsMessage.Item item) { | |||
public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) { | |||
this.articles.add(item); | |||
return this; | |||
} | |||
public WxMpXmlOutMewsMessage build() { | |||
WxMpXmlOutMewsMessage m = new WxMpXmlOutMewsMessage(); | |||
for(WxMpXmlOutMewsMessage.Item item : articles) { | |||
public WxMpXmlOutNewsMessage build() { | |||
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | |||
for(WxMpXmlOutNewsMessage.Item item : articles) { | |||
m.addArticle(item); | |||
} | |||
setCommon(m); | |||
@@ -0,0 +1,75 @@ | |||
package me.chanjar.weixin.mp.bean.result; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
/** | |||
* Created by qianjia on 14/11/26. | |||
*/ | |||
public class WxMpOAuth2AccessToken { | |||
private String accessToken; | |||
private int expiresIn = -1; | |||
private String refreshToken; | |||
private String openId; | |||
private String scope; | |||
public String getRefreshToken() { | |||
return refreshToken; | |||
} | |||
public void setRefreshToken(String refreshToken) { | |||
this.refreshToken = refreshToken; | |||
} | |||
public String getOpenId() { | |||
return openId; | |||
} | |||
public void setOpenId(String openId) { | |||
this.openId = openId; | |||
} | |||
public String getScope() { | |||
return scope; | |||
} | |||
public void setScope(String scope) { | |||
this.scope = scope; | |||
} | |||
public String getAccessToken() { | |||
return accessToken; | |||
} | |||
public void setAccessToken(String accessToken) { | |||
this.accessToken = accessToken; | |||
} | |||
public int getExpiresIn() { | |||
return expiresIn; | |||
} | |||
public void setExpiresIn(int expiresIn) { | |||
this.expiresIn = expiresIn; | |||
} | |||
public static WxMpOAuth2AccessToken fromJson(String json) { | |||
return WxMpGsonBuilder.create().fromJson(json, WxMpOAuth2AccessToken.class); | |||
} | |||
@Override | |||
public String toString() { | |||
return "WxMpOAuth2AccessToken{" + | |||
"accessToken='" + accessToken + '\'' + | |||
", expiresIn=" + expiresIn + | |||
", refreshToken='" + refreshToken + '\'' + | |||
", openId='" + openId + '\'' + | |||
", scope='" + scope + '\'' + | |||
'}'; | |||
} | |||
} |
@@ -9,7 +9,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
*/ | |||
public class WxMpUser { | |||
protected boolean subscribe; | |||
protected Boolean subscribe; | |||
protected String openId; | |||
protected String nickname; | |||
protected String sex; | |||
@@ -18,13 +18,13 @@ public class WxMpUser { | |||
protected String province; | |||
protected String country; | |||
protected String headImgUrl; | |||
protected long subscribeTime; | |||
protected Long subscribeTime; | |||
protected String unionId; | |||
public boolean isSubscribe() { | |||
public Boolean isSubscribe() { | |||
return subscribe; | |||
} | |||
public void setSubscribe(boolean subscribe) { | |||
public void setSubscribe(Boolean subscribe) { | |||
this.subscribe = subscribe; | |||
} | |||
public String getOpenId() { | |||
@@ -75,10 +75,10 @@ public class WxMpUser { | |||
public void setHeadImgUrl(String headImgUrl) { | |||
this.headImgUrl = headImgUrl; | |||
} | |||
public long getSubscribeTime() { | |||
public Long getSubscribeTime() { | |||
return subscribeTime; | |||
} | |||
public void setSubscribeTime(long subscribeTime) { | |||
public void setSubscribeTime(Long subscribeTime) { | |||
this.subscribeTime = subscribeTime; | |||
} | |||
public String getUnionId() { | |||
@@ -91,5 +91,21 @@ public class WxMpUser { | |||
public static WxMpUser fromJson(String json) { | |||
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); | |||
} | |||
@Override | |||
public String toString() { | |||
return "WxMpUser{" + | |||
"subscribe=" + subscribe + | |||
", openId='" + openId + '\'' + | |||
", nickname='" + nickname + '\'' + | |||
", sex='" + sex + '\'' + | |||
", language='" + language + '\'' + | |||
", city='" + city + '\'' + | |||
", province='" + province + '\'' + | |||
", country='" + country + '\'' + | |||
", headImgUrl='" + headImgUrl + '\'' + | |||
", subscribeTime=" + subscribeTime + | |||
", unionId='" + unionId + '\'' + | |||
'}'; | |||
} | |||
} |
@@ -64,6 +64,7 @@ public class WxMpCustomMessageGsonAdapter implements JsonSerializer<WxMpCustomMe | |||
} | |||
if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | |||
JsonObject newsJsonObject = new JsonObject(); | |||
JsonArray articleJsonArray = new JsonArray(); | |||
for (WxMpCustomMessage.WxArticle article : message.getArticles()) { | |||
JsonObject articleJson = new JsonObject(); | |||
@@ -73,7 +74,8 @@ public class WxMpCustomMessageGsonAdapter implements JsonSerializer<WxMpCustomMe | |||
articleJson.addProperty("picurl", article.getPicUrl()); | |||
articleJsonArray.add(articleJson); | |||
} | |||
messageJson.add("articles", articleJsonArray); | |||
newsJsonObject.add("articles", articleJsonArray); | |||
messageJson.add("news", newsJsonObject); | |||
} | |||
return messageJson; | |||
@@ -4,6 +4,7 @@ import com.google.gson.Gson; | |||
import com.google.gson.GsonBuilder; | |||
import me.chanjar.weixin.mp.bean.*; | |||
import me.chanjar.weixin.mp.bean.result.*; | |||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||
public class WxMpGsonBuilder { | |||
@@ -24,6 +25,7 @@ public class WxMpGsonBuilder { | |||
INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter()); | |||
INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); | |||
INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); | |||
INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter()); | |||
} | |||
public static Gson create() { | |||
@@ -0,0 +1,38 @@ | |||
package me.chanjar.weixin.mp.util.json; | |||
import com.google.gson.*; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||
import java.lang.reflect.Type; | |||
/** | |||
* Created by qianjia on 14/11/28. | |||
*/ | |||
public class WxMpOAuth2AccessTokenAdapter implements JsonDeserializer<WxMpOAuth2AccessToken> { | |||
public WxMpOAuth2AccessToken deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws | |||
JsonParseException { | |||
WxMpOAuth2AccessToken accessToken = new WxMpOAuth2AccessToken(); | |||
JsonObject accessTokenJsonObject = json.getAsJsonObject(); | |||
if (accessTokenJsonObject.get("access_token") != null && !accessTokenJsonObject.get("access_token").isJsonNull()) { | |||
accessToken.setAccessToken(GsonHelper.getAsString(accessTokenJsonObject.get("access_token"))); | |||
} | |||
if (accessTokenJsonObject.get("expires_in") != null && !accessTokenJsonObject.get("expires_in").isJsonNull()) { | |||
accessToken.setExpiresIn(GsonHelper.getAsPrimitiveInt(accessTokenJsonObject.get("expires_in"))); | |||
} | |||
if (accessTokenJsonObject.get("refresh_token") != null && !accessTokenJsonObject.get("refresh_token").isJsonNull()) { | |||
accessToken.setRefreshToken(GsonHelper.getAsString(accessTokenJsonObject.get("refresh_token"))); | |||
} | |||
if (accessTokenJsonObject.get("openid") != null && !accessTokenJsonObject.get("openid").isJsonNull()) { | |||
accessToken.setOpenId(GsonHelper.getAsString(accessTokenJsonObject.get("openid"))); | |||
} | |||
if (accessTokenJsonObject.get("scope") != null && !accessTokenJsonObject.get("scope").isJsonNull()) { | |||
accessToken.setScope(GsonHelper.getAsString(accessTokenJsonObject.get("scope"))); | |||
} | |||
return accessToken; | |||
} | |||
} |
@@ -56,6 +56,7 @@ public class XmlTransformer { | |||
Marshaller m = JAXB_CONTEXT.createMarshaller(); | |||
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | |||
m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); | |||
m.setProperty(Marshaller.JAXB_FRAGMENT, true); | |||
m.marshal(object, writer); | |||
} | |||
@@ -76,7 +77,7 @@ public class XmlTransformer { | |||
return JAXBContext.newInstance( | |||
WxMpXmlOutMessage.class, | |||
WxMpMpXmlOutImageMessage.class, | |||
WxMpXmlOutMewsMessage.class, | |||
WxMpXmlOutNewsMessage.class, | |||
WxMpXmlOutMusicMessage.class, | |||
WxMpXmlOutTextMessage.class, | |||
WxMpXmlOutVideoMessage.class, | |||
@@ -1,6 +1,6 @@ | |||
package me.chanjar.weixin.mp.api; | |||
import org.apache.commons.lang3.StringUtils; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import org.testng.Assert; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
@@ -47,7 +47,7 @@ public class WxMpMessageRouterTest { | |||
@Test(dataProvider="messages-1") | |||
public void testSync(WxMpXmlMessage message, String expected) { | |||
StringBuffer sb = new StringBuffer(); | |||
WxMpMessageRouter router = new WxMpMessageRouter(); | |||
WxMpMessageRouter router = new WxMpMessageRouter(null); | |||
prepare(false, sb, router); | |||
router.route(message); | |||
Assert.assertEquals(sb.toString(), expected); | |||
@@ -56,7 +56,7 @@ public class WxMpMessageRouterTest { | |||
@Test(dataProvider="messages-1") | |||
public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException { | |||
StringBuffer sb = new StringBuffer(); | |||
WxMpMessageRouter router = new WxMpMessageRouter(); | |||
WxMpMessageRouter router = new WxMpMessageRouter(null); | |||
prepare(true, sb, router); | |||
router.route(message); | |||
Thread.sleep(500l); | |||
@@ -64,10 +64,10 @@ public class WxMpMessageRouterTest { | |||
} | |||
public void testConcurrency() throws InterruptedException { | |||
final WxMpMessageRouter router = new WxMpMessageRouter(); | |||
final WxMpMessageRouter router = new WxMpMessageRouter(null); | |||
router.rule().handler(new WxMpMessageHandler() { | |||
@Override | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) { | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService) { | |||
return null; | |||
} | |||
}).end(); | |||
@@ -149,7 +149,7 @@ public class WxMpMessageRouterTest { | |||
} | |||
@Override | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) { | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService) { | |||
sb.append(this.echoStr).append(','); | |||
return null; | |||
} | |||
@@ -108,7 +108,7 @@ public class WxMpCustomMessageTest { | |||
reply.getArticles().add(article2); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||
} | |||
public void testNewsBuild() { | |||
@@ -125,8 +125,8 @@ public class WxMpCustomMessageTest { | |||
article2.setTitle("Happy Day"); | |||
WxMpCustomMessage reply = WxMpCustomMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | |||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||
} | |||
} |
@@ -13,8 +13,7 @@ public class WxMpXmlOutImageMessageTest { | |||
m.setFromUserName("from"); | |||
m.setToUserName("to"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -27,8 +26,7 @@ public class WxMpXmlOutImageMessageTest { | |||
public void testBuild() { | |||
WxMpMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -17,8 +17,7 @@ public class WxXmlOutMusicMessageTest { | |||
m.setFromUserName("fromUser"); | |||
m.setToUserName("toUser"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -45,8 +44,7 @@ public class WxXmlOutMusicMessageTest { | |||
.musicUrl("musicUrl") | |||
.thumbMediaId("thumbMediaId") | |||
.build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -7,20 +7,19 @@ import org.testng.annotations.Test; | |||
public class WxXmlOutNewsMessageTest { | |||
public void test() { | |||
WxMpXmlOutMewsMessage m = new WxMpXmlOutMewsMessage(); | |||
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | |||
m.setCreateTime(1122l); | |||
m.setFromUserName("fromUser"); | |||
m.setToUserName("toUser"); | |||
WxMpXmlOutMewsMessage.Item item = new WxMpXmlOutMewsMessage.Item(); | |||
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | |||
item.setDescription("description"); | |||
item.setPicUrl("picUrl"); | |||
item.setTitle("title"); | |||
item.setUrl("url"); | |||
m.addArticle(item); | |||
m.addArticle(item); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -46,20 +45,19 @@ public class WxXmlOutNewsMessageTest { | |||
} | |||
public void testBuild() { | |||
WxMpXmlOutMewsMessage.Item item = new WxMpXmlOutMewsMessage.Item(); | |||
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | |||
item.setDescription("description"); | |||
item.setPicUrl("picUrl"); | |||
item.setTitle("title"); | |||
item.setUrl("url"); | |||
WxMpXmlOutMewsMessage m = WxMpXmlOutMessage.NEWS() | |||
WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS() | |||
.fromUser("fromUser") | |||
.toUser("toUser") | |||
.addArticle(item) | |||
.addArticle(item) | |||
.build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -13,8 +13,7 @@ public class WxXmlOutTextMessageTest { | |||
m.setFromUserName("from"); | |||
m.setToUserName("to"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -27,8 +26,7 @@ public class WxXmlOutTextMessageTest { | |||
public void testBuild() { | |||
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -15,8 +15,7 @@ public class WxXmlOutVideoMessageTest { | |||
m.setFromUserName("fromUser"); | |||
m.setToUserName("toUser"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -39,8 +38,7 @@ public class WxXmlOutVideoMessageTest { | |||
.title("title") | |||
.description("ddfff") | |||
.build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -13,8 +13,7 @@ public class WxXmlOutVoiceMessageTest { | |||
m.setFromUserName("from"); | |||
m.setToUserName("to"); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -27,8 +26,7 @@ public class WxXmlOutVoiceMessageTest { | |||
public void testBuild() { | |||
WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | |||
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" | |||
+ "<xml>" | |||
String expected = "<xml>" | |||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | |||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | |||
+ "<CreateTime>1122</CreateTime>" | |||
@@ -1,20 +1,124 @@ | |||
package me.chanjar.weixin.mp.demo; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.mp.api.*; | |||
import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||
import org.eclipse.jetty.server.Server; | |||
import org.eclipse.jetty.servlet.ServletHandler; | |||
import org.eclipse.jetty.servlet.*; | |||
import javax.xml.bind.JAXBException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Map; | |||
/** | |||
* @author Daniel Qian | |||
*/ | |||
public class WxMpDemoServer { | |||
private static WxMpConfigStorage wxMpConfigStorage; | |||
private static WxMpService wxMpService; | |||
private static WxMpMessageRouter wxMpMessageRouter; | |||
public static void main(String[] args) throws Exception { | |||
initWeixin(); | |||
Server server = new Server(8080); | |||
ServletHandler handler = new ServletHandler(); | |||
server.setHandler(handler); | |||
ServletHandler servletHandler = new ServletHandler(); | |||
server.setHandler(servletHandler); | |||
ServletHolder endpointServletHolder = new ServletHolder(new WxMpEndpointServlt(wxMpConfigStorage, wxMpService, wxMpMessageRouter)); | |||
servletHandler.addServletWithMapping(endpointServletHolder, "/*"); | |||
ServletHolder oauthServletHolder = new ServletHolder(new WxMpOAuth2Servlet(wxMpService)); | |||
servletHandler.addServletWithMapping(oauthServletHolder, "/oauth2/*"); | |||
handler.addServletWithMapping(WxMpDemoServlet.class, "/*"); | |||
server.start(); | |||
server.join(); | |||
} | |||
private static void initWeixin() { | |||
try { | |||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||
WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1); | |||
wxMpConfigStorage = config; | |||
wxMpService = new WxMpServiceImpl(); | |||
wxMpService.setWxMpConfigStorage(config); | |||
WxMpMessageHandler textHandler = new WxMpMessageHandler() { | |||
@Override | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | |||
WxMpService wxMpService) { | |||
WxMpXmlOutTextMessage m | |||
= WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()).build(); | |||
return m; | |||
} | |||
}; | |||
WxMpMessageHandler imageHandler = new WxMpMessageHandler() { | |||
@Override | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | |||
WxMpService wxMpService) { | |||
try { | |||
WxMediaUploadResult wxMediaUploadResult = wxMpService | |||
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg")); | |||
WxMpMpXmlOutImageMessage m | |||
= WxMpXmlOutMessage | |||
.IMAGE() | |||
.mediaId(wxMediaUploadResult.getMediaId()) | |||
.fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()) | |||
.build(); | |||
return m; | |||
} catch (WxErrorException e) { | |||
e.printStackTrace(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
return null; | |||
} | |||
}; | |||
WxMpMessageHandler oauth2handler = new WxMpMessageHandler() { | |||
@Override | |||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | |||
WxMpService wxMpService) { | |||
String href = "<a href=\"" + wxMpService.oauth2buildAuthorizationUrl(WxConsts.OAUTH2_SCOPE_USER_INFO, null) | |||
+ "\">测试oauth2</a>"; | |||
return WxMpXmlOutMessage | |||
.TEXT() | |||
.content(href) | |||
.fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()).build(); | |||
} | |||
}; | |||
wxMpMessageRouter = new WxMpMessageRouter(wxMpService); | |||
wxMpMessageRouter | |||
.rule() | |||
.async(false) | |||
.content("哈哈") // 拦截内容为“哈哈”的消息 | |||
.handler(textHandler) | |||
.end() | |||
.rule() | |||
.async(false) | |||
.content("图片") | |||
.handler(imageHandler) | |||
.end() | |||
.rule() | |||
.async(false) | |||
.content("oauth") | |||
.handler(oauth2handler) | |||
.end() | |||
; | |||
} catch (JAXBException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} |
@@ -1,10 +1,14 @@ | |||
package me.chanjar.weixin.mp.demo; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.mp.api.*; | |||
import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||
import org.apache.commons.lang3.StringUtils; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServlet; | |||
@@ -18,55 +22,29 @@ import java.util.Map; | |||
/** | |||
* @author Daniel Qian | |||
*/ | |||
public class WxMpDemoServlet extends HttpServlet { | |||
public class WxMpEndpointServlt extends HttpServlet { | |||
protected WxMpService wxMpService; | |||
protected WxMpConfigStorage wxMpConfigStorage; | |||
protected WxMpService wxMpService; | |||
protected WxMpMessageRouter wxMpMessageRouter; | |||
@Override public void init() throws ServletException { | |||
// | |||
super.init(); | |||
try { | |||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||
WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1); | |||
wxMpConfigStorage = config; | |||
wxMpService = new WxMpServiceImpl(); | |||
wxMpService.setWxMpConfigStorage(config); | |||
WxMpMessageHandler handler = new WxMpMessageHandler() { | |||
@Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) { | |||
WxMpXmlOutTextMessage m | |||
= WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName()) | |||
.toUser(wxMessage.getFromUserName()).build(); | |||
return m; | |||
} | |||
}; | |||
wxMpMessageRouter = new WxMpMessageRouter(); | |||
wxMpMessageRouter | |||
.rule() | |||
.async(false) | |||
.content("哈哈") // 拦截内容为“哈哈”的消息 | |||
.handler(handler) | |||
.end(); | |||
} catch (JAXBException e) { | |||
throw new RuntimeException(e); | |||
} | |||
public WxMpEndpointServlt(WxMpConfigStorage wxMpConfigStorage, WxMpService wxMpService, | |||
WxMpMessageRouter wxMpMessageRouter) { | |||
this.wxMpConfigStorage = wxMpConfigStorage; | |||
this.wxMpService = wxMpService; | |||
this.wxMpMessageRouter = wxMpMessageRouter; | |||
} | |||
@Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||
throws ServletException, IOException { | |||
response.setContentType("text/html;charset=utf-8"); | |||
response.setStatus(HttpServletResponse.SC_OK); | |||
String signature = request.getParameter("signature"); | |||
String nonce = request.getParameter("nonce"); | |||
String timestamp = request.getParameter("timestamp"); | |||
response.setContentType("text/html;charset=utf-8"); | |||
response.setStatus(HttpServletResponse.SC_OK); | |||
if (!wxMpService.checkSignature(timestamp, nonce, signature)) { | |||
// 消息签名不正确,说明不是公众平台发过来的消息 | |||
response.getWriter().println("非法请求"); | |||
@@ -84,31 +62,25 @@ public class WxMpDemoServlet extends HttpServlet { | |||
"raw" : | |||
request.getParameter("encrypt_type"); | |||
WxMpXmlMessage inMessage = null; | |||
if ("raw".equals(encryptType)) { | |||
// 明文传输的消息 | |||
inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); | |||
} else if ("aes".equals(encryptType)) { | |||
// 是aes加密的消息 | |||
String msgSignature = request.getParameter("msg_signature"); | |||
inMessage = WxMpXmlMessage.fromEncryptedXml(request.getInputStream(), wxMpConfigStorage, timestamp, nonce, msgSignature); | |||
} else { | |||
response.getWriter().println("不可识别的加密类型"); | |||
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); | |||
WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); | |||
response.getWriter().write(outMessage.toXml()); | |||
return; | |||
} | |||
WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); | |||
if (outMessage != null) { | |||
if ("raw".equals(encryptType)) { | |||
response.getWriter().write(outMessage.toXml()); | |||
} else if ("aes".equals(encryptType)) { | |||
response.getWriter().write(outMessage.toEncryptedXml(wxMpConfigStorage)); | |||
} | |||
if ("aes".equals(encryptType)) { | |||
// 是aes加密的消息 | |||
String msgSignature = request.getParameter("msg_signature"); | |||
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(request.getInputStream(), wxMpConfigStorage, timestamp, nonce, msgSignature); | |||
WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage); | |||
response.getWriter().write(outMessage.toEncryptedXml(wxMpConfigStorage)); | |||
return; | |||
} | |||
response.getWriter().println("不可识别的加密类型"); | |||
return; | |||
} | |||
} |
@@ -0,0 +1,65 @@ | |||
package me.chanjar.weixin.mp.demo; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.mp.api.*; | |||
import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||
import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||
import javax.servlet.ServletContext; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServlet; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.xml.bind.JAXBException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Map; | |||
public class WxMpOAuth2Servlet extends HttpServlet { | |||
protected WxMpService wxMpService; | |||
public WxMpOAuth2Servlet(WxMpService wxMpService) { | |||
this.wxMpService = wxMpService; | |||
} | |||
@Override protected void service(HttpServletRequest request, HttpServletResponse response) | |||
throws ServletException, IOException { | |||
response.setContentType("text/html;charset=utf-8"); | |||
response.setStatus(HttpServletResponse.SC_OK); | |||
String code = request.getParameter("code"); | |||
try { | |||
response.getWriter().println("<h1>code</h1>"); | |||
response.getWriter().println(code); | |||
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code); | |||
response.getWriter().println("<h1>access token</h1>"); | |||
response.getWriter().println(wxMpOAuth2AccessToken.toString()); | |||
WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null); | |||
response.getWriter().println("<h1>user info</h1>"); | |||
response.getWriter().println(wxMpUser.toString()); | |||
wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken()); | |||
response.getWriter().println("<h1>after refresh</h1>"); | |||
response.getWriter().println(wxMpOAuth2AccessToken.toString()); | |||
} catch (WxErrorException e) { | |||
e.printStackTrace(); | |||
} | |||
response.getWriter().flush(); | |||
response.getWriter().close(); | |||
} | |||
} |
@@ -6,4 +6,5 @@ | |||
<accessToken>可以不填写</accessToken> | |||
<expiresIn>可以不填写</expiresIn> | |||
<openId>某个加你公众号的用户的openId</openId> | |||
<oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri> | |||
</xml> |