Browse Source

Merge branch 'develop'

master
Daniel Qian 10 years ago
parent
commit
867abeaac7
64 changed files with 1499 additions and 283 deletions
  1. +2
    -2
      README.md
  2. +1
    -6
      pom.xml
  3. +229
    -0
      pom.xml.versionsBackup
  4. +1
    -1
      weixin-java-common/pom.xml
  5. +68
    -0
      weixin-java-common/pom.xml.versionsBackup
  6. +8
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
  7. +100
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/StringUtils.java
  8. +2
    -5
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java
  9. +47
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java
  10. +1
    -1
      weixin-java-cp/pom.xml
  11. +73
    -0
      weixin-java-cp/pom.xml.versionsBackup
  12. +2
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
  13. +11
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
  14. +2
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java
  15. +2
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java
  16. +37
    -6
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
  17. +21
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
  18. +56
    -26
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
  19. +2
    -2
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java
  20. +5
    -5
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java
  21. +7
    -10
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java
  22. +2
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XmlTransformer.java
  23. +1
    -1
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java
  24. +5
    -5
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java
  25. +4
    -4
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
  26. +2
    -4
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java
  27. +6
    -8
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java
  28. +2
    -4
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java
  29. +2
    -4
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java
  30. +2
    -4
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java
  31. +80
    -6
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java
  32. +15
    -44
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java
  33. +48
    -0
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java
  34. +1
    -0
      weixin-java-cp/src/test/resources/test-config.sample.xml
  35. +1
    -1
      weixin-java-mp/pom.xml
  36. +72
    -0
      weixin-java-mp/pom.xml.versionsBackup
  37. +2
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
  38. +11
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
  39. +2
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java
  40. +2
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java
  41. +41
    -10
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
  42. +48
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
  43. +93
    -3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
  44. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java
  45. +7
    -7
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/NewsBuilder.java
  46. +75
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java
  47. +23
    -7
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
  48. +3
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCustomMessageGsonAdapter.java
  49. +2
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java
  50. +38
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpOAuth2AccessTokenAdapter.java
  51. +2
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java
  52. +1
    -1
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java
  53. +5
    -5
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java
  54. +3
    -3
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpCustomMessageTest.java
  55. +2
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java
  56. +2
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessageTest.java
  57. +6
    -8
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutNewsMessageTest.java
  58. +2
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessageTest.java
  59. +2
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessageTest.java
  60. +2
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessageTest.java
  61. +111
    -7
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java
  62. +26
    -54
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlt.java
  63. +65
    -0
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java
  64. +1
    -0
      weixin-java-mp/src/test/resources/test-config.sample.xml

+ 2
- 2
README.md View File

@@ -19,7 +19,7 @@ weixin-java-tools
<dependency> <dependency>
<groupId>me.chanjar</groupId> <groupId>me.chanjar</groupId>
<artifactId>weixin-java-mp</artifactId> <artifactId>weixin-java-mp</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</dependency> </dependency>
``` ```


@@ -29,7 +29,7 @@ weixin-java-tools
<dependency> <dependency>
<groupId>me.chanjar</groupId> <groupId>me.chanjar</groupId>
<artifactId>weixin-java-cp</artifactId> <artifactId>weixin-java-cp</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</dependency> </dependency>
``` ```




+ 1
- 6
pom.xml View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>me.chanjar</groupId> <groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId> <artifactId>weixin-java-parent</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>WeiXin Java Tools - Parent</name> <name>WeiXin Java Tools - Parent</name>
<description>微信公众号、企业号上级POM</description> <description>微信公众号、企业号上级POM</description>
@@ -70,11 +70,6 @@
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.2.2</version> <version>2.2.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency> <dependency>
<groupId>commons-codec</groupId> <groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>


+ 229
- 0
pom.xml.versionsBackup View File

@@ -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>

+ 1
- 1
weixin-java-common/pom.xml View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>me.chanjar</groupId> <groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId> <artifactId>weixin-java-parent</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</parent> </parent>


<artifactId>weixin-java-common</artifactId> <artifactId>weixin-java-common</artifactId>


+ 68
- 0
weixin-java-common/pom.xml.versionsBackup View File

@@ -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>

+ 8
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java View File

@@ -128,4 +128,12 @@ public class WxConsts {
/** 弹出地理位置选择器 */ /** 弹出地理位置选择器 */
public static final String LOCATION_SELECT = "location_select"; 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";

} }

+ 100
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/StringUtils.java View File

@@ -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);
}

}

+ 2
- 5
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java View File

@@ -1,12 +1,9 @@
package me.chanjar.weixin.common.util.http; package me.chanjar.weixin.common.util.http;


import me.chanjar.weixin.common.bean.result.WxError; 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.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.Header;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ClientProtocolException;


+ 47
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/URIUtil.java View File

@@ -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();
}
}

+ 1
- 1
weixin-java-cp/pom.xml View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>me.chanjar</groupId> <groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId> <artifactId>weixin-java-parent</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</parent> </parent>


<artifactId>weixin-java-cp</artifactId> <artifactId>weixin-java-cp</artifactId>


+ 73
- 0
weixin-java-cp/pom.xml.versionsBackup View File

@@ -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>

+ 2
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java View File

@@ -27,6 +27,8 @@ public interface WxCpConfigStorage {


public int getExpiresIn(); public int getExpiresIn();


public String getOauth2redirectUri();

public String getHttp_proxy_host(); public String getHttp_proxy_host();


public int getHttp_proxy_port(); public int getHttp_proxy_port();


+ 11
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java View File

@@ -18,6 +18,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
protected String agentId; protected String agentId;
protected int expiresIn; protected int expiresIn;


protected String oauth2redirectUri;

protected String http_proxy_host; protected String http_proxy_host;
protected int http_proxy_port; protected int http_proxy_port;
protected String http_proxy_username; protected String http_proxy_username;
@@ -88,6 +90,15 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
this.agentId = agentId; this.agentId = agentId;
} }


@Override
public String getOauth2redirectUri() {
return this.oauth2redirectUri;
}

public void setOauth2redirectUri(String oauth2redirectUri) {
this.oauth2redirectUri = oauth2redirectUri;
}

public String getHttp_proxy_host() { public String getHttp_proxy_host() {
return http_proxy_host; return http_proxy_host;
} }


+ 2
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java View File

@@ -16,8 +16,9 @@ public interface WxCpMessageHandler {
* *
* @param wxMessage * @param wxMessage
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxCpService
* @return xml格式的消息,如果在异步规则里处理的话,可以返回null * @return xml格式的消息,如果在异步规则里处理的话,可以返回null
*/ */
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context);
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService);
} }

+ 2
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java View File

@@ -15,8 +15,9 @@ public interface WxCpMessageInterceptor {
* 拦截微信消息 * 拦截微信消息
* @param wxMessage * @param wxMessage
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxCpService
* @return true代表OK,false代表不OK * @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);
} }

+ 37
- 6
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java View File

@@ -41,16 +41,30 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
*/ */
public class WxCpMessageRouter { public class WxCpMessageRouter {


private static final int DEFAULT_THREAD_POOL_SIZE = 20;

private final List<Rule> rules = new ArrayList<Rule>(); 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规则 * 开始一个新的Route规则
* @return * @return
*/ */
public Rule rule() { public Rule rule() {
return new Rule(this);
return new Rule(this, wxCpService);
} }


/** /**
@@ -73,7 +87,7 @@ public class WxCpMessageRouter {
if (matchRules.get(0).async) { if (matchRules.get(0).async) {
// 只要第一个是异步的,那就异步执行 // 只要第一个是异步的,那就异步执行
// 在另一个线程里执行 // 在另一个线程里执行
es.submit(new Runnable() {
executorService.submit(new Runnable() {
public void run() { public void run() {
for (final Rule rule : matchRules) { for (final Rule rule : matchRules) {
rule.service(wxMessage); rule.service(wxMessage);
@@ -101,8 +115,12 @@ public class WxCpMessageRouter {


private final WxCpMessageRouter routerBuilder; private final WxCpMessageRouter routerBuilder;


private final WxCpService wxCpService;

private boolean async = true; private boolean async = true;


private String fromUser;

private String msgType; private String msgType;


private String event; private String event;
@@ -121,8 +139,9 @@ public class WxCpMessageRouter {


private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>(); private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>();


protected Rule(WxCpMessageRouter routerBuilder) {
protected Rule(WxCpMessageRouter routerBuilder, WxCpService wxCpService) {
this.routerBuilder = routerBuilder; this.routerBuilder = routerBuilder;
this.wxCpService = wxCpService;
} }


/** /**
@@ -195,6 +214,16 @@ public class WxCpMessageRouter {
return this; return this;
} }


/**
* 如果fromUser等于某值
* @param fromUser
* @return
*/
public Rule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}

/** /**
* 设置微信消息拦截器 * 设置微信消息拦截器
* @param interceptor * @param interceptor
@@ -265,6 +294,8 @@ public class WxCpMessageRouter {


protected boolean test(WxCpXmlMessage wxMessage) { protected boolean test(WxCpXmlMessage wxMessage) {
return return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
&&
(this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) (this.agentId == null || this.agentId.equals(wxMessage.getAgentId()))
&& &&
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) (this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
@@ -288,7 +319,7 @@ public class WxCpMessageRouter {
Map<String, Object> context = new HashMap<String, Object>(); Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过 // 如果拦截器不通过
for (WxCpMessageInterceptor interceptor : this.interceptors) { for (WxCpMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context)) {
if (!interceptor.intercept(wxMessage, context, wxCpService)) {
return null; return null;
} }
} }
@@ -297,7 +328,7 @@ public class WxCpMessageRouter {
WxCpXmlOutMessage res = null; WxCpXmlOutMessage res = null;
for (WxCpMessageHandler handler : this.handlers) { for (WxCpMessageHandler handler : this.handlers) {
// 返回最后handler的结果 // 返回最后handler的结果
res = handler.handle(wxMessage, context);
res = handler.handle(wxMessage, context, wxCpService);
} }
return res; return res;
} }


+ 21
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java View File

@@ -274,6 +274,27 @@ public interface WxCpService {
*/ */
public void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException; 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;

/** /**
* 移除标签成员 * 移除标签成员
* *


+ 56
- 26
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java View File

@@ -1,25 +1,27 @@
package me.chanjar.weixin.cp.api; 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.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive; 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.WxAccessToken;
import me.chanjar.weixin.common.bean.WxMenu; 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.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.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 me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials; 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.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; 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 { public class WxCpServiceImpl implements WxCpService {


@@ -321,6 +316,41 @@ public class WxCpServiceImpl implements WxCpService {
execute(new SimplePostRequestExecutor(), url, jsonObject.toString()); 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 { public String get(String url, String queryParam) throws WxErrorException {
return execute(new SimpleGetRequestExecutor(), url, queryParam); return execute(new SimpleGetRequestExecutor(), url, queryParam);
} }


weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMewsMessage.java → weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java View File

@@ -10,7 +10,7 @@ import java.util.List;


@XmlRootElement(name = "xml") @XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
public class WxCpXmlOutMewsMessage extends WxCpXmlOutMessage {
public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage {


@XmlElement(name = "ArticleCount") @XmlElement(name = "ArticleCount")
protected int articleCount; protected int articleCount;
@@ -19,7 +19,7 @@ public class WxCpXmlOutMewsMessage extends WxCpXmlOutMessage {
@XmlElement(name = "item") @XmlElement(name = "item")
protected final List<Item> articles = new ArrayList<Item>(); protected final List<Item> articles = new ArrayList<Item>();
public WxCpXmlOutMewsMessage() {
public WxCpXmlOutNewsMessage() {
this.msgType = WxConsts.XML_MSG_NEWS; this.msgType = WxConsts.XML_MSG_NEWS;
} }



+ 5
- 5
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java View File

@@ -3,14 +3,14 @@ package me.chanjar.weixin.cp.bean.outxmlbuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 * 图文消息builder
* @author Daniel Qian * @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>(); protected final List<Item> articles = new ArrayList<Item>();
@@ -19,8 +19,8 @@ public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutMewsMe
return this; return this;
} }
public WxCpXmlOutMewsMessage build() {
WxCpXmlOutMewsMessage m = new WxCpXmlOutMewsMessage();
public WxCpXmlOutNewsMessage build() {
WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage();
for(Item item : articles) { for(Item item : articles) {
m.addArticle(item); m.addArticle(item);
} }


+ 7
- 10
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java View File

@@ -8,17 +8,12 @@
*/ */
package me.chanjar.weixin.cp.util.json; 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.api.WxConsts;
import me.chanjar.weixin.common.util.StringUtils;
import me.chanjar.weixin.cp.bean.WxCpMessage; 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()); messageJson.addProperty("msgtype", message.getMsgType());


if (StringUtils.isNotBlank(message.getToParty())) { if (StringUtils.isNotBlank(message.getToParty())) {
messageJson.addProperty("toparty", message.getToUser());
messageJson.addProperty("toparty", message.getToParty());
} }
if (StringUtils.isNotBlank(message.getToTag())) { if (StringUtils.isNotBlank(message.getToTag())) {
messageJson.addProperty("totag", message.getToUser()); messageJson.addProperty("totag", message.getToUser());
@@ -75,6 +70,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> {
} }


if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray(); JsonArray articleJsonArray = new JsonArray();
for (WxCpMessage.WxArticle article : message.getArticles()) { for (WxCpMessage.WxArticle article : message.getArticles()) {
JsonObject articleJson = new JsonObject(); JsonObject articleJson = new JsonObject();
@@ -84,7 +80,8 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> {
articleJson.addProperty("picurl", article.getPicUrl()); articleJson.addProperty("picurl", article.getPicUrl());
articleJsonArray.add(articleJson); articleJsonArray.add(articleJson);
} }
messageJson.add("articles", articleJsonArray);
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("news", newsJsonObject);
} }
return messageJson; return messageJson;


+ 2
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XmlTransformer.java View File

@@ -59,6 +59,7 @@ public class XmlTransformer {
Marshaller m = JAXB_CONTEXT.createMarshaller(); Marshaller m = JAXB_CONTEXT.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER);
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
m.marshal(object, writer); m.marshal(object, writer);
} }


@@ -79,7 +80,7 @@ public class XmlTransformer {
return JAXBContext.newInstance( return JAXBContext.newInstance(
WxCpXmlOutMessage.class, WxCpXmlOutMessage.class,
WxCpXmlOutImageMessage.class, WxCpXmlOutImageMessage.class,
WxCpXmlOutMewsMessage.class,
WxCpXmlOutNewsMessage.class,
WxCpXmlOutTextMessage.class, WxCpXmlOutTextMessage.class,
WxCpXmlOutVideoMessage.class, WxCpXmlOutVideoMessage.class,
WxCpXmlOutVoiceMessage.class, WxCpXmlOutVoiceMessage.class,


+ 1
- 1
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java View File

@@ -1,6 +1,6 @@
package me.chanjar.weixin.cp.api; 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.Assert;
import org.testng.annotations.Guice; import org.testng.annotations.Guice;
import org.testng.annotations.Test; import org.testng.annotations.Test;


+ 5
- 5
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java View File

@@ -47,7 +47,7 @@ public class WxCpMessageRouterTest {
@Test(dataProvider="messages-1") @Test(dataProvider="messages-1")
public void testSync(WxCpXmlMessage message, String expected) { public void testSync(WxCpXmlMessage message, String expected) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
WxCpMessageRouter router = new WxCpMessageRouter();
WxCpMessageRouter router = new WxCpMessageRouter(null);
prepare(false, sb, router); prepare(false, sb, router);
router.route(message); router.route(message);
Assert.assertEquals(sb.toString(), expected); Assert.assertEquals(sb.toString(), expected);
@@ -56,7 +56,7 @@ public class WxCpMessageRouterTest {
@Test(dataProvider="messages-1") @Test(dataProvider="messages-1")
public void testAsync(WxCpXmlMessage message, String expected) throws InterruptedException { public void testAsync(WxCpXmlMessage message, String expected) throws InterruptedException {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
WxCpMessageRouter router = new WxCpMessageRouter();
WxCpMessageRouter router = new WxCpMessageRouter(null);
prepare(true, sb, router); prepare(true, sb, router);
router.route(message); router.route(message);
Thread.sleep(500l); Thread.sleep(500l);
@@ -64,10 +64,10 @@ public class WxCpMessageRouterTest {
} }
public void testConcurrency() throws InterruptedException { public void testConcurrency() throws InterruptedException {
final WxCpMessageRouter router = new WxCpMessageRouter();
final WxCpMessageRouter router = new WxCpMessageRouter(null);
router.rule().handler(new WxCpMessageHandler() { router.rule().handler(new WxCpMessageHandler() {
@Override @Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context) {
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService) {
return null; return null;
} }
}).end(); }).end();
@@ -149,7 +149,7 @@ public class WxCpMessageRouterTest {
} }


@Override @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(','); sb.append(this.echoStr).append(',');
return null; return null;
} }


+ 4
- 4
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java View File

@@ -83,8 +83,8 @@ public class WxCpMessageTest {
article2.setTitle("Happy Day"); article2.setTitle("Happy Day");
reply.getArticles().add(article2); 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() { public void testNewsBuild() {
@@ -101,8 +101,8 @@ public class WxCpMessageTest {
article2.setTitle("Happy Day"); article2.setTitle("Happy Day");


WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); 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\"}]}}");
} }
} }

+ 2
- 4
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java View File

@@ -13,8 +13,7 @@ public class WxCpXmlOutImageMessageTest {
m.setFromUserName("from"); m.setFromUserName("from");
m.setToUserName("to"); m.setToUserName("to");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -27,8 +26,7 @@ public class WxCpXmlOutImageMessageTest {
public void testBuild() { public void testBuild() {
WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); 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>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 6
- 8
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java View File

@@ -7,20 +7,19 @@ import org.testng.annotations.Test;
public class WxCpXmlOutNewsMessageTest { public class WxCpXmlOutNewsMessageTest {


public void test() { public void test() {
WxCpXmlOutMewsMessage m = new WxCpXmlOutMewsMessage();
WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage();
m.setCreateTime(1122l); m.setCreateTime(1122l);
m.setFromUserName("fromUser"); m.setFromUserName("fromUser");
m.setToUserName("toUser"); m.setToUserName("toUser");
WxCpXmlOutMewsMessage.Item item = new WxCpXmlOutMewsMessage.Item();
WxCpXmlOutNewsMessage.Item item = new WxCpXmlOutNewsMessage.Item();
item.setDescription("description"); item.setDescription("description");
item.setPicUrl("picUrl"); item.setPicUrl("picUrl");
item.setTitle("title"); item.setTitle("title");
item.setUrl("url"); item.setUrl("url");
m.addArticle(item); m.addArticle(item);
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>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -46,20 +45,19 @@ public class WxCpXmlOutNewsMessageTest {
} }
public void testBuild() { public void testBuild() {
WxCpXmlOutMewsMessage.Item item = new WxCpXmlOutMewsMessage.Item();
WxCpXmlOutNewsMessage.Item item = new WxCpXmlOutNewsMessage.Item();
item.setDescription("description"); item.setDescription("description");
item.setPicUrl("picUrl"); item.setPicUrl("picUrl");
item.setTitle("title"); item.setTitle("title");
item.setUrl("url"); item.setUrl("url");
WxCpXmlOutMewsMessage m = WxCpXmlOutMessage.NEWS()
WxCpXmlOutNewsMessage m = WxCpXmlOutMessage.NEWS()
.fromUser("fromUser") .fromUser("fromUser")
.toUser("toUser") .toUser("toUser")
.addArticle(item) .addArticle(item)
.addArticle(item) .addArticle(item)
.build(); .build();
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java View File

@@ -13,8 +13,7 @@ public class WxCpXmlOutTextMessageTest {
m.setFromUserName("from"); m.setFromUserName("from");
m.setToUserName("to"); m.setToUserName("to");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -27,8 +26,7 @@ public class WxCpXmlOutTextMessageTest {
public void testBuild() { public void testBuild() {
WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); 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>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java View File

@@ -15,8 +15,7 @@ public class WxCpXmlOutVideoMessageTest {
m.setFromUserName("fromUser"); m.setFromUserName("fromUser");
m.setToUserName("toUser"); m.setToUserName("toUser");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -39,8 +38,7 @@ public class WxCpXmlOutVideoMessageTest {
.title("title") .title("title")
.description("ddfff") .description("ddfff")
.build(); .build();
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java View File

@@ -13,8 +13,7 @@ public class WxCpXmlOutVoiceMessageTest {
m.setFromUserName("from"); m.setFromUserName("from");
m.setToUserName("to"); m.setToUserName("to");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -27,8 +26,7 @@ public class WxCpXmlOutVoiceMessageTest {
public void testBuild() { public void testBuild() {
WxCpXmlOutVoiceMessage m = WxCpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); 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>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 80
- 6
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java View File

@@ -1,20 +1,94 @@
package me.chanjar.weixin.cp.demo; 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.server.Server;
import org.eclipse.jetty.servlet.ServletHandler; 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 { public class WxCpDemoServer {

private static WxCpConfigStorage wxCpConfigStorage;
private static WxCpService wxCpService;
private static WxCpMessageRouter wxCpMessageRouter;

public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
initWeixin();

Server server = new Server(8080); 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.start();
server.join(); 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);
}
}
} }

weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServlet.java → weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java View File

@@ -1,11 +1,11 @@
package me.chanjar.weixin.cp.demo; package me.chanjar.weixin.cp.demo;


import me.chanjar.weixin.common.util.StringUtils;
import me.chanjar.weixin.cp.api.*; import me.chanjar.weixin.cp.api.*;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import org.apache.commons.lang3.StringUtils;


import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@@ -19,58 +19,31 @@ import java.util.Map;
/** /**
* @author Daniel Qian * @author Daniel Qian
*/ */
public class WxCpDemoServlet extends HttpServlet {
public class WxCpEndpointServlet extends HttpServlet {


protected WxCpService wxCpService;
protected WxCpConfigStorage wxCpConfigStorage; protected WxCpConfigStorage wxCpConfigStorage;
protected WxCpService wxCpService;
protected WxCpMessageRouter wxCpMessageRouter; 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 { throws ServletException, IOException {


response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);

String msgSignature = request.getParameter("msg_signature"); String msgSignature = request.getParameter("msg_signature");
String nonce = request.getParameter("nonce"); String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp"); String timestamp = request.getParameter("timestamp");
String echostr = request.getParameter("echostr"); String echostr = request.getParameter("echostr");


response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
if (StringUtils.isNotBlank(echostr)) { if (StringUtils.isNotBlank(echostr)) {
if (!wxCpService.checkSignature(msgSignature, timestamp, nonce, echostr)) { if (!wxCpService.checkSignature(msgSignature, timestamp, nonce, echostr)) {
// 消息签名不正确,说明不是公众平台发过来的消息 // 消息签名不正确,说明不是公众平台发过来的消息
@@ -84,11 +57,9 @@ public class WxCpDemoServlet extends HttpServlet {
return; 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); WxCpXmlOutMessage outMessage = wxCpMessageRouter.route(inMessage);

if (outMessage != null) { if (outMessage != null) {
response.getWriter().write(outMessage.toEncryptedXml(wxCpConfigStorage)); response.getWriter().write(outMessage.toEncryptedXml(wxCpConfigStorage));
} }

+ 48
- 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java View File

@@ -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();

}


}

+ 1
- 0
weixin-java-cp/src/test/resources/test-config.sample.xml View File

@@ -9,4 +9,5 @@
<userId>企业号通讯录里的某个userid</userId> <userId>企业号通讯录里的某个userid</userId>
<departmentId>企业号通讯录的某个部门id</departmentId> <departmentId>企业号通讯录的某个部门id</departmentId>
<tagId>企业号通讯录里的某个tagid</tagId> <tagId>企业号通讯录里的某个tagid</tagId>
<oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri>
</xml> </xml>

+ 1
- 1
weixin-java-mp/pom.xml View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>me.chanjar</groupId> <groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId> <artifactId>weixin-java-parent</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</parent> </parent>
<artifactId>weixin-java-mp</artifactId> <artifactId>weixin-java-mp</artifactId>
<name>WeiXin Java Tools - MP</name> <name>WeiXin Java Tools - MP</name>


+ 72
- 0
weixin-java-mp/pom.xml.versionsBackup View File

@@ -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>

+ 2
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java View File

@@ -25,6 +25,8 @@ public interface WxMpConfigStorage {


public int getExpiresIn(); public int getExpiresIn();


public String getOauth2redirectUri();

public String getHttp_proxy_host(); public String getHttp_proxy_host();


public int getHttp_proxy_port(); public int getHttp_proxy_port();


+ 11
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java View File

@@ -16,6 +16,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
protected String aesKey; protected String aesKey;
protected int expiresIn; protected int expiresIn;


protected String oauth2redirectUri;

protected String http_proxy_host; protected String http_proxy_host;
protected int http_proxy_port; protected int http_proxy_port;
protected String http_proxy_username; protected String http_proxy_username;
@@ -78,6 +80,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
} }


@Override
public String getOauth2redirectUri() {
return this.oauth2redirectUri;
}

public void setOauth2redirectUri(String oauth2redirectUri) {
this.oauth2redirectUri = oauth2redirectUri;
}

public String getHttp_proxy_host() { public String getHttp_proxy_host() {
return http_proxy_host; return http_proxy_host;
} }


+ 2
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java View File

@@ -16,8 +16,9 @@ public interface WxMpMessageHandler {
* *
* @param wxMessage * @param wxMessage
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxMpService
* @return xml格式的消息,如果在异步规则里处理的话,可以返回null * @return xml格式的消息,如果在异步规则里处理的话,可以返回null
*/ */
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context);
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService);
} }

+ 2
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java View File

@@ -15,8 +15,9 @@ public interface WxMpMessageInterceptor {
* 拦截微信消息 * 拦截微信消息
* @param wxMessage * @param wxMessage
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxMpService
* @return true代表OK,false代表不OK * @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);
} }

+ 41
- 10
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java View File

@@ -40,17 +40,31 @@ import java.util.regex.Pattern;
* *
*/ */
public class WxMpMessageRouter { public class WxMpMessageRouter {

private static final int DEFAULT_THREAD_POOL_SIZE = 20;

private final List<Rule> rules = new ArrayList<Rule>(); 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规则 * 开始一个新的Route规则
* @return * @return
*/ */
public Rule rule() { public Rule rule() {
return new Rule(this);
return new Rule(this, wxMpService);
} }


/** /**
@@ -73,7 +87,7 @@ public class WxMpMessageRouter {
if (matchRules.get(0).async) { if (matchRules.get(0).async) {
// 只要第一个是异步的,那就异步执行 // 只要第一个是异步的,那就异步执行
// 在另一个线程里执行 // 在另一个线程里执行
es.submit(new Runnable() {
executorService.execute(new Runnable() {
public void run() { public void run() {
for (final Rule rule : matchRules) { for (final Rule rule : matchRules) {
rule.service(wxMessage); rule.service(wxMessage);
@@ -101,7 +115,11 @@ public class WxMpMessageRouter {
private final WxMpMessageRouter routerBuilder; private final WxMpMessageRouter routerBuilder;


private final WxMpService wxMpService;

private boolean async = true; private boolean async = true;

private String fromUser;
private String msgType; private String msgType;


@@ -119,8 +137,9 @@ public class WxMpMessageRouter {
private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>(); private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>();
protected Rule(WxMpMessageRouter routerBuilder) {
protected Rule(WxMpMessageRouter routerBuilder, WxMpService wxMpService) {
this.routerBuilder = routerBuilder; this.routerBuilder = routerBuilder;
this.wxMpService = wxMpService;
} }
/** /**
@@ -182,7 +201,17 @@ public class WxMpMessageRouter {
this.rContent = regex; this.rContent = regex;
return this; return this;
} }

/**
* 如果fromUser等于某值
* @param fromUser
* @return
*/
public Rule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}

/** /**
* 设置微信消息拦截器 * 设置微信消息拦截器
* @param interceptor * @param interceptor
@@ -252,7 +281,9 @@ public class WxMpMessageRouter {
} }
protected boolean test(WxMpXmlMessage wxMessage) { protected boolean test(WxMpXmlMessage wxMessage) {
return
return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
&&
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) (this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
&& &&
(this.event == null || this.event.equals(wxMessage.getEvent())) (this.event == null || this.event.equals(wxMessage.getEvent()))
@@ -274,7 +305,7 @@ public class WxMpMessageRouter {
Map<String, Object> context = new HashMap<String, Object>(); Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过 // 如果拦截器不通过
for (WxMpMessageInterceptor interceptor : this.interceptors) { for (WxMpMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context)) {
if (!interceptor.intercept(wxMessage, context, wxMpService)) {
return null; return null;
} }
} }
@@ -283,7 +314,7 @@ public class WxMpMessageRouter {
WxMpXmlOutMessage res = null; WxMpXmlOutMessage res = null;
for (WxMpMessageHandler handler : this.handlers) { for (WxMpMessageHandler handler : this.handlers) {
// 返回最后handler的结果 // 返回最后handler的结果
res = handler.handle(wxMessage, context);
res = handler.handle(wxMessage, context, wxMpService);
} }
return res; return res;
} }


+ 48
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java View File

@@ -329,6 +329,54 @@ public interface WxMpService {
*/ */
WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; 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请求 * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求
* @param url * @param url


+ 93
- 3
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java View File

@@ -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.WxError;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException; 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.crypto.SHA1;
import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.*; 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.bean.result.*;
import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials; 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.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; 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.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
@@ -38,7 +37,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringReader; import java.io.StringReader;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;


@@ -299,6 +297,98 @@ public class WxMpServiceImpl implements WxMpService {
return WxMpSemanticQueryResult.fromJson(responseContent); 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 { public String get(String url, String queryParam) throws WxErrorException {
return execute(new SimpleGetRequestExecutor(), url, queryParam); return execute(new SimpleGetRequestExecutor(), url, queryParam);
} }


weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMewsMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java View File

@@ -10,7 +10,7 @@ import java.util.List;


@XmlRootElement(name = "xml") @XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
public class WxMpXmlOutMewsMessage extends WxMpXmlOutMessage {
public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {


@XmlElement(name = "ArticleCount") @XmlElement(name = "ArticleCount")
protected int articleCount; protected int articleCount;
@@ -19,7 +19,7 @@ public class WxMpXmlOutMewsMessage extends WxMpXmlOutMessage {
@XmlElement(name = "item") @XmlElement(name = "item")
protected final List<Item> articles = new ArrayList<Item>(); protected final List<Item> articles = new ArrayList<Item>();
public WxMpXmlOutMewsMessage() {
public WxMpXmlOutNewsMessage() {
this.msgType = WxConsts.XML_MSG_NEWS; this.msgType = WxConsts.XML_MSG_NEWS;
} }



+ 7
- 7
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/NewsBuilder.java View File

@@ -1,6 +1,6 @@
package me.chanjar.weixin.mp.bean.outxmlbuilder; 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.ArrayList;
import java.util.List; import java.util.List;
@@ -9,18 +9,18 @@ import java.util.List;
* 图文消息builder * 图文消息builder
* @author chanjarster * @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); this.articles.add(item);
return this; 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); m.addArticle(item);
} }
setCommon(m); setCommon(m);


+ 75
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java View File

@@ -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 + '\'' +
'}';
}
}

+ 23
- 7
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java View File

@@ -9,7 +9,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
*/ */
public class WxMpUser { public class WxMpUser {


protected boolean subscribe;
protected Boolean subscribe;
protected String openId; protected String openId;
protected String nickname; protected String nickname;
protected String sex; protected String sex;
@@ -18,13 +18,13 @@ public class WxMpUser {
protected String province; protected String province;
protected String country; protected String country;
protected String headImgUrl; protected String headImgUrl;
protected long subscribeTime;
protected Long subscribeTime;
protected String unionId; protected String unionId;
public boolean isSubscribe() {
public Boolean isSubscribe() {
return subscribe; return subscribe;
} }
public void setSubscribe(boolean subscribe) {
public void setSubscribe(Boolean subscribe) {
this.subscribe = subscribe; this.subscribe = subscribe;
} }
public String getOpenId() { public String getOpenId() {
@@ -75,10 +75,10 @@ public class WxMpUser {
public void setHeadImgUrl(String headImgUrl) { public void setHeadImgUrl(String headImgUrl) {
this.headImgUrl = headImgUrl; this.headImgUrl = headImgUrl;
} }
public long getSubscribeTime() {
public Long getSubscribeTime() {
return subscribeTime; return subscribeTime;
} }
public void setSubscribeTime(long subscribeTime) {
public void setSubscribeTime(Long subscribeTime) {
this.subscribeTime = subscribeTime; this.subscribeTime = subscribeTime;
} }
public String getUnionId() { public String getUnionId() {
@@ -91,5 +91,21 @@ public class WxMpUser {
public static WxMpUser fromJson(String json) { public static WxMpUser fromJson(String json) {
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); 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 + '\'' +
'}';
}
} }

+ 3
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCustomMessageGsonAdapter.java View File

@@ -64,6 +64,7 @@ public class WxMpCustomMessageGsonAdapter implements JsonSerializer<WxMpCustomMe
} }
if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray(); JsonArray articleJsonArray = new JsonArray();
for (WxMpCustomMessage.WxArticle article : message.getArticles()) { for (WxMpCustomMessage.WxArticle article : message.getArticles()) {
JsonObject articleJson = new JsonObject(); JsonObject articleJson = new JsonObject();
@@ -73,7 +74,8 @@ public class WxMpCustomMessageGsonAdapter implements JsonSerializer<WxMpCustomMe
articleJson.addProperty("picurl", article.getPicUrl()); articleJson.addProperty("picurl", article.getPicUrl());
articleJsonArray.add(articleJson); articleJsonArray.add(articleJson);
} }
messageJson.add("articles", articleJsonArray);
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("news", newsJsonObject);
} }
return messageJson; return messageJson;


+ 2
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java View File

@@ -4,6 +4,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import me.chanjar.weixin.mp.bean.*; import me.chanjar.weixin.mp.bean.*;
import me.chanjar.weixin.mp.bean.result.*; import me.chanjar.weixin.mp.bean.result.*;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;


public class WxMpGsonBuilder { public class WxMpGsonBuilder {


@@ -24,6 +25,7 @@ public class WxMpGsonBuilder {
INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter()); INSTANCE.registerTypeAdapter(WxMpQrCodeTicket.class, new WxQrCodeTicketAdapter());
INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter());
INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter());
INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter());
} }
public static Gson create() { public static Gson create() {


+ 38
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpOAuth2AccessTokenAdapter.java View File

@@ -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;
}

}

+ 2
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java View File

@@ -56,6 +56,7 @@ public class XmlTransformer {
Marshaller m = JAXB_CONTEXT.createMarshaller(); Marshaller m = JAXB_CONTEXT.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER);
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
m.marshal(object, writer); m.marshal(object, writer);
} }


@@ -76,7 +77,7 @@ public class XmlTransformer {
return JAXBContext.newInstance( return JAXBContext.newInstance(
WxMpXmlOutMessage.class, WxMpXmlOutMessage.class,
WxMpMpXmlOutImageMessage.class, WxMpMpXmlOutImageMessage.class,
WxMpXmlOutMewsMessage.class,
WxMpXmlOutNewsMessage.class,
WxMpXmlOutMusicMessage.class, WxMpXmlOutMusicMessage.class,
WxMpXmlOutTextMessage.class, WxMpXmlOutTextMessage.class,
WxMpXmlOutVideoMessage.class, WxMpXmlOutVideoMessage.class,


+ 1
- 1
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java View File

@@ -1,6 +1,6 @@
package me.chanjar.weixin.mp.api; 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.Assert;
import org.testng.annotations.Guice; import org.testng.annotations.Guice;
import org.testng.annotations.Test; import org.testng.annotations.Test;


+ 5
- 5
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java View File

@@ -47,7 +47,7 @@ public class WxMpMessageRouterTest {
@Test(dataProvider="messages-1") @Test(dataProvider="messages-1")
public void testSync(WxMpXmlMessage message, String expected) { public void testSync(WxMpXmlMessage message, String expected) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
WxMpMessageRouter router = new WxMpMessageRouter();
WxMpMessageRouter router = new WxMpMessageRouter(null);
prepare(false, sb, router); prepare(false, sb, router);
router.route(message); router.route(message);
Assert.assertEquals(sb.toString(), expected); Assert.assertEquals(sb.toString(), expected);
@@ -56,7 +56,7 @@ public class WxMpMessageRouterTest {
@Test(dataProvider="messages-1") @Test(dataProvider="messages-1")
public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException { public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
WxMpMessageRouter router = new WxMpMessageRouter();
WxMpMessageRouter router = new WxMpMessageRouter(null);
prepare(true, sb, router); prepare(true, sb, router);
router.route(message); router.route(message);
Thread.sleep(500l); Thread.sleep(500l);
@@ -64,10 +64,10 @@ public class WxMpMessageRouterTest {
} }
public void testConcurrency() throws InterruptedException { public void testConcurrency() throws InterruptedException {
final WxMpMessageRouter router = new WxMpMessageRouter();
final WxMpMessageRouter router = new WxMpMessageRouter(null);
router.rule().handler(new WxMpMessageHandler() { router.rule().handler(new WxMpMessageHandler() {
@Override @Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) {
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService) {
return null; return null;
} }
}).end(); }).end();
@@ -149,7 +149,7 @@ public class WxMpMessageRouterTest {
} }


@Override @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(','); sb.append(this.echoStr).append(',');
return null; return null;
} }


+ 3
- 3
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpCustomMessageTest.java View File

@@ -108,7 +108,7 @@ public class WxMpCustomMessageTest {
reply.getArticles().add(article2); 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() { public void testNewsBuild() {
@@ -125,8 +125,8 @@ public class WxMpCustomMessageTest {
article2.setTitle("Happy Day"); article2.setTitle("Happy Day");


WxMpCustomMessage reply = WxMpCustomMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); 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\"}]}}");
} }
} }

+ 2
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java View File

@@ -13,8 +13,7 @@ public class WxMpXmlOutImageMessageTest {
m.setFromUserName("from"); m.setFromUserName("from");
m.setToUserName("to"); m.setToUserName("to");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -27,8 +26,7 @@ public class WxMpXmlOutImageMessageTest {
public void testBuild() { public void testBuild() {
WxMpMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); 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>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessageTest.java View File

@@ -17,8 +17,7 @@ public class WxXmlOutMusicMessageTest {
m.setFromUserName("fromUser"); m.setFromUserName("fromUser");
m.setToUserName("toUser"); m.setToUserName("toUser");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -45,8 +44,7 @@ public class WxXmlOutMusicMessageTest {
.musicUrl("musicUrl") .musicUrl("musicUrl")
.thumbMediaId("thumbMediaId") .thumbMediaId("thumbMediaId")
.build(); .build();
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 6
- 8
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutNewsMessageTest.java View File

@@ -7,20 +7,19 @@ import org.testng.annotations.Test;
public class WxXmlOutNewsMessageTest { public class WxXmlOutNewsMessageTest {


public void test() { public void test() {
WxMpXmlOutMewsMessage m = new WxMpXmlOutMewsMessage();
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage();
m.setCreateTime(1122l); m.setCreateTime(1122l);
m.setFromUserName("fromUser"); m.setFromUserName("fromUser");
m.setToUserName("toUser"); m.setToUserName("toUser");
WxMpXmlOutMewsMessage.Item item = new WxMpXmlOutMewsMessage.Item();
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item();
item.setDescription("description"); item.setDescription("description");
item.setPicUrl("picUrl"); item.setPicUrl("picUrl");
item.setTitle("title"); item.setTitle("title");
item.setUrl("url"); item.setUrl("url");
m.addArticle(item); m.addArticle(item);
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>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -46,20 +45,19 @@ public class WxXmlOutNewsMessageTest {
} }
public void testBuild() { public void testBuild() {
WxMpXmlOutMewsMessage.Item item = new WxMpXmlOutMewsMessage.Item();
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item();
item.setDescription("description"); item.setDescription("description");
item.setPicUrl("picUrl"); item.setPicUrl("picUrl");
item.setTitle("title"); item.setTitle("title");
item.setUrl("url"); item.setUrl("url");
WxMpXmlOutMewsMessage m = WxMpXmlOutMessage.NEWS()
WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS()
.fromUser("fromUser") .fromUser("fromUser")
.toUser("toUser") .toUser("toUser")
.addArticle(item) .addArticle(item)
.addArticle(item) .addArticle(item)
.build(); .build();
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessageTest.java View File

@@ -13,8 +13,7 @@ public class WxXmlOutTextMessageTest {
m.setFromUserName("from"); m.setFromUserName("from");
m.setToUserName("to"); m.setToUserName("to");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -27,8 +26,7 @@ public class WxXmlOutTextMessageTest {
public void testBuild() { public void testBuild() {
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); 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>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessageTest.java View File

@@ -15,8 +15,7 @@ public class WxXmlOutVideoMessageTest {
m.setFromUserName("fromUser"); m.setFromUserName("fromUser");
m.setToUserName("toUser"); m.setToUserName("toUser");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -39,8 +38,7 @@ public class WxXmlOutVideoMessageTest {
.title("title") .title("title")
.description("ddfff") .description("ddfff")
.build(); .build();
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" + "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 2
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessageTest.java View File

@@ -13,8 +13,7 @@ public class WxXmlOutVoiceMessageTest {
m.setFromUserName("from"); m.setFromUserName("from");
m.setToUserName("to"); m.setToUserName("to");
String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<xml>"
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"
@@ -27,8 +26,7 @@ public class WxXmlOutVoiceMessageTest {
public void testBuild() { public void testBuild() {
WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); 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>" + "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>" + "<FromUserName><![CDATA[from]]></FromUserName>"
+ "<CreateTime>1122</CreateTime>" + "<CreateTime>1122</CreateTime>"


+ 111
- 7
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java View File

@@ -1,20 +1,124 @@
package me.chanjar.weixin.mp.demo; 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.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 { public class WxMpDemoServer {

private static WxMpConfigStorage wxMpConfigStorage;
private static WxMpService wxMpService;
private static WxMpMessageRouter wxMpMessageRouter;

public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
initWeixin();

Server server = new Server(8080); 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.start();
server.join(); 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);
}
}
} }

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServlet.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlt.java View File

@@ -1,10 +1,14 @@
package me.chanjar.weixin.mp.demo; 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.api.*;
import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage;
import org.apache.commons.lang3.StringUtils;


import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@@ -18,55 +22,29 @@ import java.util.Map;
/** /**
* @author Daniel Qian * @author Daniel Qian
*/ */
public class WxMpDemoServlet extends HttpServlet {
public class WxMpEndpointServlt extends HttpServlet {


protected WxMpService wxMpService;
protected WxMpConfigStorage wxMpConfigStorage; protected WxMpConfigStorage wxMpConfigStorage;
protected WxMpService wxMpService;
protected WxMpMessageRouter wxMpMessageRouter; 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) @Override protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {


response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);

String signature = request.getParameter("signature"); String signature = request.getParameter("signature");
String nonce = request.getParameter("nonce"); String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp"); String timestamp = request.getParameter("timestamp");


response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);

if (!wxMpService.checkSignature(timestamp, nonce, signature)) { if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
// 消息签名不正确,说明不是公众平台发过来的消息 // 消息签名不正确,说明不是公众平台发过来的消息
response.getWriter().println("非法请求"); response.getWriter().println("非法请求");
@@ -84,31 +62,25 @@ public class WxMpDemoServlet extends HttpServlet {
"raw" : "raw" :
request.getParameter("encrypt_type"); request.getParameter("encrypt_type");


WxMpXmlMessage inMessage = null;

if ("raw".equals(encryptType)) { 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; 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; return;
} }


response.getWriter().println("不可识别的加密类型");
return;
} }


} }

+ 65
- 0
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java View File

@@ -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();

}

}

+ 1
- 0
weixin-java-mp/src/test/resources/test-config.sample.xml View File

@@ -6,4 +6,5 @@
<accessToken>可以不填写</accessToken> <accessToken>可以不填写</accessToken>
<expiresIn>可以不填写</expiresIn> <expiresIn>可以不填写</expiresIn>
<openId>某个加你公众号的用户的openId</openId> <openId>某个加你公众号的用户的openId</openId>
<oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri>
</xml> </xml>

Loading…
Cancel
Save