| @@ -50,3 +50,6 @@ Temporary Items | |||
| .apdisk | |||
| /.sonar/ | |||
| sonar-project.properties | |||
| !/.mvn/wrapper/maven-wrapper.jar | |||
| *.versionsBackup | |||
| @@ -0,0 +1 @@ | |||
| distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip | |||
| @@ -8,15 +8,15 @@ language: java | |||
| jdk: | |||
| - oraclejdk8 | |||
| script: "mvn clean package -Dmaven.test.skip=true" | |||
| script: "./mvnw clean package -DskipTests=true" | |||
| #script: | |||
| # - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar | |||
| branches: | |||
| only: | |||
| - develop | |||
| cache: | |||
| directories: | |||
| - '$HOME/.m2/repository' | |||
| @@ -0,0 +1,2 @@ | |||
| #!/usr/bin/env bash | |||
| ./mvnw org.codehaus.mojo:versions-maven-plugin:display-dependency-updates | |||
| @@ -0,0 +1,2 @@ | |||
| #!/usr/bin/env bash | |||
| ./mvnw org.codehaus.mojo:versions-maven-plugin:display-plugin-updates | |||
| @@ -0,0 +1,2 @@ | |||
| #!/usr/bin/env bash | |||
| ./mvnw org.codehaus.mojo:versions-maven-plugin:display-property-updates | |||
| @@ -0,0 +1,236 @@ | |||
| #!/bin/sh | |||
| # ---------------------------------------------------------------------------- | |||
| # Licensed to the Apache Software Foundation (ASF) under one | |||
| # or more contributor license agreements. See the NOTICE file | |||
| # distributed with this work for additional information | |||
| # regarding copyright ownership. The ASF licenses this file | |||
| # to you under the Apache License, Version 2.0 (the | |||
| # "License"); you may not use this file except in compliance | |||
| # with the License. You may obtain a copy of the License at | |||
| # | |||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, | |||
| # software distributed under the License is distributed on an | |||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
| # KIND, either express or implied. See the License for the | |||
| # specific language governing permissions and limitations | |||
| # under the License. | |||
| # ---------------------------------------------------------------------------- | |||
| # ---------------------------------------------------------------------------- | |||
| # Maven2 Start Up Batch script | |||
| # | |||
| # Required ENV vars: | |||
| # ------------------ | |||
| # JAVA_HOME - location of a JDK home dir | |||
| # | |||
| # Optional ENV vars | |||
| # ----------------- | |||
| # M2_HOME - location of maven2's installed home dir | |||
| # MAVEN_OPTS - parameters passed to the Java VM when running Maven | |||
| # e.g. to debug Maven itself, use | |||
| # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 | |||
| # MAVEN_SKIP_RC - flag to disable loading of mavenrc files | |||
| # ---------------------------------------------------------------------------- | |||
| if [ -z "$MAVEN_SKIP_RC" ] ; then | |||
| if [ -f /etc/mavenrc ] ; then | |||
| . /etc/mavenrc | |||
| fi | |||
| if [ -f "$HOME/.mavenrc" ] ; then | |||
| . "$HOME/.mavenrc" | |||
| fi | |||
| fi | |||
| # OS specific support. $var _must_ be set to either true or false. | |||
| cygwin=false; | |||
| darwin=false; | |||
| mingw=false | |||
| case "`uname`" in | |||
| CYGWIN*) cygwin=true ;; | |||
| MINGW*) mingw=true;; | |||
| Darwin*) darwin=true | |||
| # | |||
| # Look for the Apple JDKs first to preserve the existing behaviour, and then look | |||
| # for the new JDKs provided by Oracle. | |||
| # | |||
| if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then | |||
| # | |||
| # Apple JDKs | |||
| # | |||
| export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home | |||
| fi | |||
| if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then | |||
| # | |||
| # Apple JDKs | |||
| # | |||
| export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home | |||
| fi | |||
| if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then | |||
| # | |||
| # Oracle JDKs | |||
| # | |||
| export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home | |||
| fi | |||
| if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then | |||
| # | |||
| # Apple JDKs | |||
| # | |||
| export JAVA_HOME=`/usr/libexec/java_home` | |||
| fi | |||
| ;; | |||
| esac | |||
| if [ -z "$JAVA_HOME" ] ; then | |||
| if [ -r /etc/gentoo-release ] ; then | |||
| JAVA_HOME=`java-config --jre-home` | |||
| fi | |||
| fi | |||
| if [ -z "$M2_HOME" ] ; then | |||
| ## resolve links - $0 may be a link to maven's home | |||
| PRG="$0" | |||
| # need this for relative symlinks | |||
| while [ -h "$PRG" ] ; do | |||
| ls=`ls -ld "$PRG"` | |||
| link=`expr "$ls" : '.*-> \(.*\)$'` | |||
| if expr "$link" : '/.*' > /dev/null; then | |||
| PRG="$link" | |||
| else | |||
| PRG="`dirname "$PRG"`/$link" | |||
| fi | |||
| done | |||
| saveddir=`pwd` | |||
| M2_HOME=`dirname "$PRG"`/.. | |||
| # make it fully qualified | |||
| M2_HOME=`cd "$M2_HOME" && pwd` | |||
| cd "$saveddir" | |||
| # echo Using m2 at $M2_HOME | |||
| fi | |||
| # For Cygwin, ensure paths are in UNIX format before anything is touched | |||
| if $cygwin ; then | |||
| [ -n "$M2_HOME" ] && | |||
| M2_HOME=`cygpath --unix "$M2_HOME"` | |||
| [ -n "$JAVA_HOME" ] && | |||
| JAVA_HOME=`cygpath --unix "$JAVA_HOME"` | |||
| [ -n "$CLASSPATH" ] && | |||
| CLASSPATH=`cygpath --path --unix "$CLASSPATH"` | |||
| fi | |||
| # For Migwn, ensure paths are in UNIX format before anything is touched | |||
| if $mingw ; then | |||
| [ -n "$M2_HOME" ] && | |||
| M2_HOME="`(cd "$M2_HOME"; pwd)`" | |||
| [ -n "$JAVA_HOME" ] && | |||
| JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" | |||
| # TODO classpath? | |||
| fi | |||
| if [ -z "$JAVA_HOME" ]; then | |||
| javaExecutable="`which javac`" | |||
| if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then | |||
| # readlink(1) is not available as standard on Solaris 10. | |||
| readLink=`which readlink` | |||
| if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then | |||
| if $darwin ; then | |||
| javaHome="`dirname \"$javaExecutable\"`" | |||
| javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" | |||
| else | |||
| javaExecutable="`readlink -f \"$javaExecutable\"`" | |||
| fi | |||
| javaHome="`dirname \"$javaExecutable\"`" | |||
| javaHome=`expr "$javaHome" : '\(.*\)/bin'` | |||
| JAVA_HOME="$javaHome" | |||
| export JAVA_HOME | |||
| fi | |||
| fi | |||
| fi | |||
| if [ -z "$JAVACMD" ] ; then | |||
| if [ -n "$JAVA_HOME" ] ; then | |||
| if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | |||
| # IBM's JDK on AIX uses strange locations for the executables | |||
| JAVACMD="$JAVA_HOME/jre/sh/java" | |||
| else | |||
| JAVACMD="$JAVA_HOME/bin/java" | |||
| fi | |||
| else | |||
| JAVACMD="`which java`" | |||
| fi | |||
| fi | |||
| if [ ! -x "$JAVACMD" ] ; then | |||
| echo "Error: JAVA_HOME is not defined correctly." >&2 | |||
| echo " We cannot execute $JAVACMD" >&2 | |||
| exit 1 | |||
| fi | |||
| if [ -z "$JAVA_HOME" ] ; then | |||
| echo "Warning: JAVA_HOME environment variable is not set." | |||
| fi | |||
| CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher | |||
| # traverses directory structure from process work directory to filesystem root | |||
| # first directory with .mvn subdirectory is considered project base directory | |||
| find_maven_basedir() { | |||
| local basedir=$(pwd) | |||
| local wdir=$(pwd) | |||
| while [ "$wdir" != '/' ] ; do | |||
| if [ -d "$wdir"/.mvn ] ; then | |||
| basedir=$wdir | |||
| break | |||
| fi | |||
| wdir=$(cd "$wdir/.."; pwd) | |||
| done | |||
| echo "${basedir}" | |||
| } | |||
| # concatenates all lines of a file | |||
| concat_lines() { | |||
| if [ -f "$1" ]; then | |||
| echo "$(tr -s '\n' ' ' < "$1")" | |||
| fi | |||
| } | |||
| export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} | |||
| MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" | |||
| # For Cygwin, switch paths to Windows format before running java | |||
| if $cygwin; then | |||
| [ -n "$M2_HOME" ] && | |||
| M2_HOME=`cygpath --path --windows "$M2_HOME"` | |||
| [ -n "$JAVA_HOME" ] && | |||
| JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` | |||
| [ -n "$CLASSPATH" ] && | |||
| CLASSPATH=`cygpath --path --windows "$CLASSPATH"` | |||
| [ -n "$MAVEN_PROJECTBASEDIR" ] && | |||
| MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` | |||
| fi | |||
| # Provide a "standardized" way to retrieve the CLI args that will | |||
| # work with both Windows and non-Windows executions. | |||
| MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" | |||
| export MAVEN_CMD_LINE_ARGS | |||
| WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain | |||
| # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ | |||
| exec "$JAVACMD" \ | |||
| $MAVEN_OPTS \ | |||
| -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ | |||
| "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ | |||
| ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" | |||
| @@ -0,0 +1,146 @@ | |||
| @REM ---------------------------------------------------------------------------- | |||
| @REM Licensed to the Apache Software Foundation (ASF) under one | |||
| @REM or more contributor license agreements. See the NOTICE file | |||
| @REM distributed with this work for additional information | |||
| @REM regarding copyright ownership. The ASF licenses this file | |||
| @REM to you under the Apache License, Version 2.0 (the | |||
| @REM "License"); you may not use this file except in compliance | |||
| @REM with the License. You may obtain a copy of the License at | |||
| @REM | |||
| @REM http://www.apache.org/licenses/LICENSE-2.0 | |||
| @REM | |||
| @REM Unless required by applicable law or agreed to in writing, | |||
| @REM software distributed under the License is distributed on an | |||
| @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
| @REM KIND, either express or implied. See the License for the | |||
| @REM specific language governing permissions and limitations | |||
| @REM under the License. | |||
| @REM ---------------------------------------------------------------------------- | |||
| @REM ---------------------------------------------------------------------------- | |||
| @REM Maven2 Start Up Batch script | |||
| @REM | |||
| @REM Required ENV vars: | |||
| @REM JAVA_HOME - location of a JDK home dir | |||
| @REM | |||
| @REM Optional ENV vars | |||
| @REM M2_HOME - location of maven2's installed home dir | |||
| @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands | |||
| @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending | |||
| @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven | |||
| @REM e.g. to debug Maven itself, use | |||
| @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 | |||
| @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files | |||
| @REM ---------------------------------------------------------------------------- | |||
| @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' | |||
| @echo off | |||
| @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' | |||
| @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% | |||
| @REM set %HOME% to equivalent of $HOME | |||
| if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") | |||
| @REM Execute a user defined script before this one | |||
| if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre | |||
| @REM check for pre script, once with legacy .bat ending and once with .cmd ending | |||
| if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" | |||
| if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" | |||
| :skipRcPre | |||
| @setlocal | |||
| set ERROR_CODE=0 | |||
| @REM To isolate internal variables from possible post scripts, we use another setlocal | |||
| @setlocal | |||
| @REM ==== START VALIDATION ==== | |||
| if not "%JAVA_HOME%" == "" goto OkJHome | |||
| echo. | |||
| echo Error: JAVA_HOME not found in your environment. >&2 | |||
| echo Please set the JAVA_HOME variable in your environment to match the >&2 | |||
| echo location of your Java installation. >&2 | |||
| echo. | |||
| goto error | |||
| :OkJHome | |||
| if exist "%JAVA_HOME%\bin\java.exe" goto init | |||
| echo. | |||
| echo Error: JAVA_HOME is set to an invalid directory. >&2 | |||
| echo JAVA_HOME = "%JAVA_HOME%" >&2 | |||
| echo Please set the JAVA_HOME variable in your environment to match the >&2 | |||
| echo location of your Java installation. >&2 | |||
| echo. | |||
| goto error | |||
| @REM ==== END VALIDATION ==== | |||
| :init | |||
| set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* | |||
| @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". | |||
| @REM Fallback to current working directory if not found. | |||
| set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% | |||
| IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir | |||
| set EXEC_DIR=%CD% | |||
| set WDIR=%EXEC_DIR% | |||
| :findBaseDir | |||
| IF EXIST "%WDIR%"\.mvn goto baseDirFound | |||
| cd .. | |||
| IF "%WDIR%"=="%CD%" goto baseDirNotFound | |||
| set WDIR=%CD% | |||
| goto findBaseDir | |||
| :baseDirFound | |||
| set MAVEN_PROJECTBASEDIR=%WDIR% | |||
| cd "%EXEC_DIR%" | |||
| goto endDetectBaseDir | |||
| :baseDirNotFound | |||
| set MAVEN_PROJECTBASEDIR=%EXEC_DIR% | |||
| cd "%EXEC_DIR%" | |||
| :endDetectBaseDir | |||
| IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig | |||
| @setlocal EnableExtensions EnableDelayedExpansion | |||
| for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a | |||
| @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% | |||
| :endReadAdditionalConfig | |||
| SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" | |||
| set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" | |||
| set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain | |||
| # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* | |||
| %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* | |||
| if ERRORLEVEL 1 goto error | |||
| goto end | |||
| :error | |||
| set ERROR_CODE=1 | |||
| :end | |||
| @endlocal & set ERROR_CODE=%ERROR_CODE% | |||
| if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost | |||
| @REM check for post script, once with legacy .bat ending and once with .cmd ending | |||
| if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" | |||
| if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" | |||
| :skipRcPost | |||
| @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' | |||
| if "%MAVEN_BATCH_PAUSE%" == "on" pause | |||
| if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% | |||
| exit /B %ERROR_CODE% | |||
| @@ -6,9 +6,9 @@ | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.9.0</version> | |||
| <version>2.9.9.BETA</version> | |||
| <packaging>pom</packaging> | |||
| <name>WeiXin Java Tools - Parent</name> | |||
| <name>Weixin Java Tools - Parent</name> | |||
| <description>微信公众号、企业号上级POM</description> | |||
| <url>https://github.com/wechat-group/weixin-java-tools</url> | |||
| @@ -108,8 +108,6 @@ | |||
| <maven.compiler.target>1.7</maven.compiler.target> | |||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
| <downloadJavadocs>true</downloadJavadocs> | |||
| <downloadSources>true</downloadSources> | |||
| <httpclient.version>4.5</httpclient.version> | |||
| <jetty.version>9.3.0.RC0</jetty.version> | |||
| </properties> | |||
| @@ -225,6 +223,13 @@ | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.assertj</groupId> | |||
| <artifactId>assertj-guava</artifactId> | |||
| <version>3.0.0</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>redis.clients</groupId> | |||
| <artifactId>jedis</artifactId> | |||
| @@ -235,7 +240,7 @@ | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| <version>1.16.18</version> | |||
| <scope>compile</scope> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| </dependencyManagement> | |||
| @@ -353,15 +358,6 @@ | |||
| <goals>deploy</goals> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-compiler-plugin</artifactId> | |||
| <version>3.6.0</version> | |||
| <configuration> | |||
| <encoding>UTF-8</encoding> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-checkstyle-plugin</artifactId> | |||
| @@ -32,7 +32,7 @@ | |||
| <module name="IllegalTokenText"> | |||
| <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> | |||
| <property name="format" value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/> | |||
| <property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/> | |||
| <property name="message" value="考虑使用特殊的转义字符,而非八进制值或者Unicode转义值。"/> | |||
| </module> | |||
| <module name="AvoidEscapedUnicodeCharacters"> | |||
| <property name="allowEscapesForControlCharacters" value="true"/> | |||
| @@ -69,9 +69,9 @@ | |||
| <property name="allowEmptyTypes" value="true"/> | |||
| <property name="allowEmptyLoops" value="true"/> | |||
| <message key="ws.notFollowed" | |||
| value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> | |||
| value="WhitespaceAround: ''{0}''后面没有空格。Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> | |||
| <message key="ws.notPreceded" | |||
| value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/> | |||
| value="WhitespaceAround: ''{0}''前面没有加空格。"/> | |||
| </module> | |||
| <module name="OneStatementPerLine"/> | |||
| <module name="MultipleVariableDeclarations"/> | |||
| @@ -100,11 +100,11 @@ | |||
| <module name="PackageName"> | |||
| <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> | |||
| <message key="name.invalidPattern" | |||
| value="Package name ''{0}'' must match pattern ''{1}''."/> | |||
| value="包名 ''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="TypeName"> | |||
| <message key="name.invalidPattern" | |||
| value="Type name ''{0}'' must match pattern ''{1}''."/> | |||
| value="类型名 ''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="MemberName"> | |||
| <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/> | |||
| @@ -114,28 +114,28 @@ | |||
| <module name="ParameterName"> | |||
| <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> | |||
| <message key="name.invalidPattern" | |||
| value="Parameter name ''{0}'' must match pattern ''{1}''."/> | |||
| value="参数名''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="LocalVariableName"> | |||
| <property name="tokens" value="VARIABLE_DEF"/> | |||
| <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> | |||
| <message key="name.invalidPattern" | |||
| value="Local variable name ''{0}'' must match pattern ''{1}''."/> | |||
| value="本地变量名 ''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="ClassTypeParameterName"> | |||
| <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> | |||
| <message key="name.invalidPattern" | |||
| value="Class type name ''{0}'' must match pattern ''{1}''."/> | |||
| value="类的类型名 ''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="MethodTypeParameterName"> | |||
| <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> | |||
| <message key="name.invalidPattern" | |||
| value="Method type name ''{0}'' must match pattern ''{1}''."/> | |||
| value="方法类型名 ''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="InterfaceTypeParameterName"> | |||
| <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> | |||
| <message key="name.invalidPattern" | |||
| value="Interface type name ''{0}'' must match pattern ''{1}''."/> | |||
| value="Interface type name ''{0}'' 必须匹配模式:''{1}''。"/> | |||
| </module> | |||
| <module name="NoFinalizer"/> | |||
| <module name="GenericWhitespace"> | |||
| @@ -148,14 +148,6 @@ | |||
| <message key="ws.notPreceded" | |||
| value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> | |||
| </module> | |||
| <module name="Indentation"> | |||
| <property name="basicOffset" value="2"/> | |||
| <property name="braceAdjustment" value="0"/> | |||
| <property name="caseIndent" value="2"/> | |||
| <property name="throwsIndent" value="4"/> | |||
| <property name="lineWrappingIndentation" value="4"/> | |||
| <property name="arrayInitIndent" value="2"/> | |||
| </module> | |||
| <module name="AbbreviationAsWordInName"> | |||
| <property name="ignoreFinal" value="false"/> | |||
| <property name="allowedAbbreviationLength" value="1"/> | |||
| @@ -186,7 +178,6 @@ | |||
| <property name="allowSamelineMultipleAnnotations" value="true"/> | |||
| </module> | |||
| <module name="NonEmptyAtclauseDescription"/> | |||
| <module name="JavadocTagContinuationIndentation"/> | |||
| <module name="SummaryJavadoc"> | |||
| <property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> | |||
| </module> | |||
| @@ -207,7 +198,7 @@ | |||
| <module name="MethodName"> | |||
| <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/> | |||
| <message key="name.invalidPattern" | |||
| value="Method name ''{0}'' must match pattern ''{1}''."/> | |||
| value="方法名 ''{0}'' 必须匹配模式:''{1}''."/> | |||
| </module> | |||
| <module name="SingleLineJavadoc"> | |||
| <property name="ignoreInlineTags" value="false"/> | |||
| @@ -215,6 +206,5 @@ | |||
| <module name="EmptyCatchBlock"> | |||
| <property name="exceptionVariableName" value="expected"/> | |||
| </module> | |||
| <module name="CommentsIndentation"/> | |||
| </module> | |||
| </module> | |||
| @@ -7,11 +7,11 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.9.0</version> | |||
| <version>2.9.9.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <name>WeiXin Java Tools - Common</name> | |||
| <name>Weixin Java Tools - Common</name> | |||
| <description>微信公众号、企业号Java SDK Common</description> | |||
| <dependencies> | |||
| @@ -5,9 +5,9 @@ | |||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |||
| * (the "License"); you may not use this file except in compliance with | |||
| * the License. You may obtain a copy of the License at | |||
| * | |||
| * | |||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||
| * | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, software | |||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| @@ -23,9 +23,6 @@ package me.chanjar.weixin.common.session; | |||
| * | |||
| * @author Craig R. McClanahan | |||
| */ | |||
| public class Constants { | |||
| public static final String Package = "me.chanjar.weixin.common.session"; | |||
| public static final String PACKAGE = "me.chanjar.weixin.common.session"; | |||
| } | |||
| @@ -11,13 +11,12 @@ public class StandardSession implements WxSession, InternalSession { | |||
| /** | |||
| * The string manager for this package. | |||
| */ | |||
| protected static final StringManager sm = StringManager.getManager(Constants.Package); | |||
| protected static final StringManager SM = StringManager.getManager(Constants.PACKAGE); | |||
| /** | |||
| * Type array. | |||
| */ | |||
| private static final String[] EMPTY_ARRAY = new String[0]; | |||
| // ------------------------------ WxSession | |||
| protected Map<String, Object> attributes = new ConcurrentHashMap<>(); | |||
| /** | |||
| * The session identifier of this Session. | |||
| @@ -73,7 +72,7 @@ public class StandardSession implements WxSession, InternalSession { | |||
| if (!isValidInternal()) { | |||
| throw new IllegalStateException | |||
| (sm.getString("sessionImpl.getAttribute.ise")); | |||
| (SM.getString("sessionImpl.getAttribute.ise")); | |||
| } | |||
| if (name == null) { | |||
| @@ -86,7 +85,7 @@ public class StandardSession implements WxSession, InternalSession { | |||
| @Override | |||
| public Enumeration<String> getAttributeNames() { | |||
| if (!isValidInternal()) { | |||
| throw new IllegalStateException(sm.getString("sessionImpl.getAttributeNames.ise")); | |||
| throw new IllegalStateException(SM.getString("sessionImpl.getAttributeNames.ise")); | |||
| } | |||
| Set<String> names = new HashSet<>(); | |||
| @@ -98,7 +97,7 @@ public class StandardSession implements WxSession, InternalSession { | |||
| public void setAttribute(String name, Object value) { | |||
| // Name cannot be null | |||
| if (name == null) { | |||
| throw new IllegalArgumentException(sm.getString("sessionImpl.setAttribute.namenull")); | |||
| throw new IllegalArgumentException(SM.getString("sessionImpl.setAttribute.namenull")); | |||
| } | |||
| // Null value is the same as removeAttribute() | |||
| @@ -109,7 +108,7 @@ public class StandardSession implements WxSession, InternalSession { | |||
| // Validate our current state | |||
| if (!isValidInternal()) { | |||
| throw new IllegalStateException(sm.getString("sessionImpl.setAttribute.ise", getIdInternal())); | |||
| throw new IllegalStateException(SM.getString("sessionImpl.setAttribute.ise", getIdInternal())); | |||
| } | |||
| this.attributes.put(name, value); | |||
| @@ -123,8 +122,9 @@ public class StandardSession implements WxSession, InternalSession { | |||
| @Override | |||
| public void invalidate() { | |||
| if (!isValidInternal()) | |||
| throw new IllegalStateException(sm.getString("sessionImpl.invalidate.ise")); | |||
| if (!isValidInternal()) { | |||
| throw new IllegalStateException(SM.getString("sessionImpl.invalidate.ise")); | |||
| } | |||
| // Cause this session to expire | |||
| expire(); | |||
| @@ -13,8 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||
| */ | |||
| public class StandardSessionManager implements WxSessionManager, InternalSessionManager { | |||
| protected static final StringManager sm = | |||
| StringManager.getManager(Constants.Package); | |||
| protected static final StringManager SM = StringManager.getManager(Constants.PACKAGE); | |||
| /** | |||
| * The descriptive name of this Manager implementation (for logging). | |||
| */ | |||
| @@ -82,7 +81,7 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
| public WxSession getSession(String sessionId, boolean create) { | |||
| if (sessionId == null) { | |||
| throw new IllegalStateException | |||
| (sm.getString("sessionManagerImpl.getSession.ise")); | |||
| (SM.getString("sessionManagerImpl.getSession.ise")); | |||
| } | |||
| InternalSession session = findSession(sessionId); | |||
| @@ -124,25 +123,24 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
| @Override | |||
| public InternalSession findSession(String id) { | |||
| if (id == null) | |||
| if (id == null) { | |||
| return (null); | |||
| } | |||
| return this.sessions.get(id); | |||
| } | |||
| @Override | |||
| public InternalSession createSession(String sessionId) { | |||
| if (sessionId == null) { | |||
| throw new IllegalStateException | |||
| (sm.getString("sessionManagerImpl.createSession.ise")); | |||
| (SM.getString("sessionManagerImpl.createSession.ise")); | |||
| } | |||
| if ((this.maxActiveSessions >= 0) && | |||
| (getActiveSessions() >= this.maxActiveSessions)) { | |||
| this.rejectedSessions++; | |||
| throw new TooManyActiveSessionsException( | |||
| sm.getString("sessionManagerImpl.createSession.tmase"), | |||
| SM.getString("sessionManagerImpl.createSession.tmase"), | |||
| this.maxActiveSessions); | |||
| } | |||
| @@ -192,7 +190,7 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
| while (true) { | |||
| try { | |||
| // 每秒清理一次 | |||
| Thread.sleep(StandardSessionManager.this.backgroundProcessorDelay * 1000l); | |||
| Thread.sleep(StandardSessionManager.this.backgroundProcessorDelay * 1000L); | |||
| backgroundProcess(); | |||
| } catch (InterruptedException e) { | |||
| StandardSessionManager.this.log.error("SessionManagerImpl.backgroundProcess error", e); | |||
| @@ -230,8 +228,9 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
| @Override | |||
| public void backgroundProcess() { | |||
| this.count = (this.count + 1) % this.processExpiresFrequency; | |||
| if (this.count == 0) | |||
| if (this.count == 0) { | |||
| processExpires(); | |||
| } | |||
| } | |||
| /** | |||
| @@ -243,16 +242,18 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
| InternalSession sessions[] = findSessions(); | |||
| int expireHere = 0; | |||
| if (this.log.isDebugEnabled()) | |||
| if (this.log.isDebugEnabled()) { | |||
| this.log.debug("Start expire sessions {} at {} sessioncount {}", getName(), timeNow, sessions.length); | |||
| for (int i = 0; i < sessions.length; i++) { | |||
| if (sessions[i] != null && !sessions[i].isValid()) { | |||
| } | |||
| for (InternalSession session : sessions) { | |||
| if (session != null && !session.isValid()) { | |||
| expireHere++; | |||
| } | |||
| } | |||
| long timeEnd = System.currentTimeMillis(); | |||
| if (this.log.isDebugEnabled()) | |||
| if (this.log.isDebugEnabled()) { | |||
| this.log.debug("End expire sessions {} processingTime {} expired sessions: {}", getName(), timeEnd - timeNow, expireHere); | |||
| } | |||
| this.processingTime += (timeEnd - timeNow); | |||
| } | |||
| @@ -32,7 +32,6 @@ public class BeanUtils { | |||
| * 检查bean里标记为@Required的field是否为空,为空则抛异常 | |||
| * | |||
| * @param bean 要检查的bean对象 | |||
| * @throws WxErrorException | |||
| */ | |||
| public static void checkRequiredFields(Object bean) throws WxErrorException { | |||
| List<String> requiredFields = Lists.newArrayList(); | |||
| @@ -68,39 +67,4 @@ public class BeanUtils { | |||
| } | |||
| } | |||
| /** | |||
| * 将bean按照@XStreamAlias标识的字符串内容生成以之为key的map对象 | |||
| * | |||
| * @param bean 包含@XStreamAlias的xml bean对象 | |||
| * @return map对象 | |||
| */ | |||
| public static Map<String, String> xmlBean2Map(Object bean) { | |||
| Map<String, String> result = Maps.newHashMap(); | |||
| List<Field> fields = new ArrayList<>(Arrays.asList(bean.getClass().getDeclaredFields())); | |||
| fields.addAll(Arrays.asList(bean.getClass().getSuperclass().getDeclaredFields())); | |||
| for (Field field : fields) { | |||
| try { | |||
| boolean isAccessible = field.isAccessible(); | |||
| field.setAccessible(true); | |||
| if (field.get(bean) == null) { | |||
| field.setAccessible(isAccessible); | |||
| continue; | |||
| } | |||
| if (field.isAnnotationPresent(XStreamAlias.class)) { | |||
| result.put(field.getAnnotation(XStreamAlias.class).value(), field.get(bean).toString()); | |||
| } else if (!Modifier.isStatic(field.getModifiers())) { | |||
| //忽略掉静态成员变量 | |||
| result.put(field.getName(), field.get(bean).toString()); | |||
| } | |||
| field.setAccessible(isAccessible); | |||
| } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) { | |||
| log.error(e.getMessage(), e); | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| @@ -28,10 +28,10 @@ import java.util.Random; | |||
| */ | |||
| public class WxCryptUtil { | |||
| private static final Base64 base64 = new Base64(); | |||
| private static final Base64 BASE64 = new Base64(); | |||
| private static final Charset CHARSET = StandardCharsets.UTF_8; | |||
| private static final ThreadLocal<DocumentBuilder> builderLocal = new ThreadLocal<DocumentBuilder>() { | |||
| private static final ThreadLocal<DocumentBuilder> BUILDER_LOCAL = new ThreadLocal<DocumentBuilder>() { | |||
| @Override | |||
| protected DocumentBuilder initialValue() { | |||
| try { | |||
| @@ -65,7 +65,7 @@ public class WxCryptUtil { | |||
| static String extractEncryptPart(String xml) { | |||
| try { | |||
| DocumentBuilder db = builderLocal.get(); | |||
| DocumentBuilder db = BUILDER_LOCAL.get(); | |||
| Document document = db.parse(new InputSource(new StringReader(xml))); | |||
| Element root = document.getDocumentElement(); | |||
| @@ -192,7 +192,7 @@ public class WxCryptUtil { | |||
| byte[] encrypted = cipher.doFinal(unencrypted); | |||
| // 使用BASE64对加密后的字符串进行编码 | |||
| return base64.encodeToString(encrypted); | |||
| return BASE64.encodeToString(encrypted); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| @@ -8,7 +8,7 @@ import java.nio.file.Files; | |||
| public class FileUtils { | |||
| /** | |||
| * 创建临时文件 | |||
| * 创建临时文件. | |||
| * | |||
| * @param inputStream 输入文件流 | |||
| * @param name 文件名 | |||
| @@ -24,7 +24,7 @@ public class FileUtils { | |||
| } | |||
| /** | |||
| * 创建临时文件 | |||
| * 创建临时文件. | |||
| * | |||
| * @param inputStream 输入文件流 | |||
| * @param name 文件名 | |||
| @@ -4,5 +4,16 @@ package me.chanjar.weixin.common.util.http; | |||
| * Created by ecoolper on 2017/4/28. | |||
| */ | |||
| public enum HttpType { | |||
| JODD_HTTP, APACHE_HTTP, OK_HTTP; | |||
| /** | |||
| * jodd-http. | |||
| */ | |||
| JODD_HTTP, | |||
| /** | |||
| * apache httpclient. | |||
| */ | |||
| APACHE_HTTP, | |||
| /** | |||
| * okhttp. | |||
| */ | |||
| OK_HTTP | |||
| } | |||
| @@ -56,11 +56,14 @@ public class OkHttpMediaDownloadRequestExecutor extends BaseMediaDownloadRequest | |||
| return null; | |||
| } | |||
| File file = File.createTempFile(FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), | |||
| super.tmpDirFile); | |||
| File file = File.createTempFile( | |||
| FilenameUtils.getBaseName(fileName), "." + FilenameUtils.getExtension(fileName), super.tmpDirFile | |||
| ); | |||
| try (BufferedSink sink = Okio.buffer(Okio.sink(file))) { | |||
| sink.writeAll(response.body().source()); | |||
| } | |||
| file.deleteOnExit(); | |||
| return file; | |||
| } | |||
| @@ -1,14 +1,5 @@ | |||
| /* | |||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||
| * | |||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||
| * arose from modification of the original source, or other redistribution of this source | |||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||
| */ | |||
| package me.chanjar.weixin.common.util.json; | |||
| import com.google.common.collect.Lists; | |||
| import com.google.gson.JsonArray; | |||
| import com.google.gson.JsonElement; | |||
| @@ -16,7 +7,6 @@ import com.google.gson.JsonObject; | |||
| import java.util.List; | |||
| public class GsonHelper { | |||
| public static boolean isNull(JsonElement element) { | |||
| @@ -77,7 +67,7 @@ public class GsonHelper { | |||
| public static long getAsPrimitiveLong(JsonElement element) { | |||
| Long r = getAsLong(element); | |||
| return r == null ? 0l : r; | |||
| return r == null ? 0L : r; | |||
| } | |||
| public static Integer getAsInteger(JsonElement element) { | |||
| @@ -95,7 +85,7 @@ public class GsonHelper { | |||
| public static boolean getAsPrimitiveBool(JsonElement element) { | |||
| Boolean r = getAsBoolean(element); | |||
| return r != null && r.booleanValue(); | |||
| return r != null && r; | |||
| } | |||
| public static Double getAsDouble(JsonElement element) { | |||
| @@ -130,6 +120,20 @@ public class GsonHelper { | |||
| return result.toArray(new Integer[0]); | |||
| } | |||
| public static String[] getStringArray(JsonObject o, String string) { | |||
| JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string)); | |||
| if (jsonArray == null) { | |||
| return null; | |||
| } | |||
| List<String> result = Lists.newArrayList(); | |||
| for (int i = 0; i < jsonArray.size(); i++) { | |||
| result.add(jsonArray.get(i).getAsString()); | |||
| } | |||
| return result.toArray(new String[0]); | |||
| } | |||
| public static Long[] getLongArray(JsonObject o, String string) { | |||
| JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string)); | |||
| if (jsonArray == null) { | |||
| @@ -1,11 +1,3 @@ | |||
| /* | |||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||
| * | |||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||
| * arose from modification of the original source, or other redistribution of this source | |||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||
| */ | |||
| package me.chanjar.weixin.common.util.json; | |||
| import com.google.gson.*; | |||
| @@ -1,11 +1,3 @@ | |||
| /* | |||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||
| * | |||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||
| * arose from modification of the original source, or other redistribution of this source | |||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||
| */ | |||
| package me.chanjar.weixin.common.util.json; | |||
| import com.google.gson.*; | |||
| @@ -1,11 +1,3 @@ | |||
| /* | |||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||
| * | |||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||
| * arose from modification of the original source, or other redistribution of this source | |||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||
| */ | |||
| package me.chanjar.weixin.common.util.json; | |||
| import com.google.gson.*; | |||
| @@ -46,8 +46,7 @@ import java.util.*; | |||
| */ | |||
| public class StringManager { | |||
| private static final Map<String, Map<Locale, StringManager>> managers = | |||
| new Hashtable<>(); | |||
| private static final Map<String, Map<Locale, StringManager>> MANAGERS = new Hashtable<>(); | |||
| private static int LOCALE_CACHE_SIZE = 10; | |||
| /** | |||
| * The ResourceBundle for this StringManager. | |||
| @@ -118,16 +117,16 @@ public class StringManager { | |||
| public static final synchronized StringManager getManager( | |||
| String packageName, Locale locale) { | |||
| Map<Locale, StringManager> map = managers.get(packageName); | |||
| Map<Locale, StringManager> map = MANAGERS.get(packageName); | |||
| if (map == null) { | |||
| /* | |||
| * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. | |||
| * Expansion occurs when size() exceeds capacity. Therefore keep | |||
| * size at or below capacity. | |||
| * removeEldestEntry() executes after insertion therefore the test | |||
| * for removal needs to use one less than the maximum desired size | |||
| * | |||
| */ | |||
| /* | |||
| * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. | |||
| * Expansion occurs when size() exceeds capacity. Therefore keep | |||
| * size at or below capacity. | |||
| * removeEldestEntry() executes after insertion therefore the test | |||
| * for removal needs to use one less than the maximum desired size | |||
| * | |||
| */ | |||
| map = new LinkedHashMap<Locale, StringManager>(LOCALE_CACHE_SIZE, 1, true) { | |||
| private static final long serialVersionUID = 1L; | |||
| @@ -137,7 +136,7 @@ public class StringManager { | |||
| return size() > (LOCALE_CACHE_SIZE - 1); | |||
| } | |||
| }; | |||
| managers.put(packageName, map); | |||
| MANAGERS.put(packageName, map); | |||
| } | |||
| StringManager mgr = map.get(locale); | |||
| @@ -1,6 +1,7 @@ | |||
| package me.chanjar.weixin.common.util.xml; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; | |||
| import com.thoughtworks.xstream.core.util.QuickWriter; | |||
| import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||
| import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; | |||
| @@ -13,7 +14,7 @@ import java.io.Writer; | |||
| public class XStreamInitializer { | |||
| public static XStream getInstance() { | |||
| XStream xstream = new XStream(new XppDriver() { | |||
| XStream xstream = new XStream(new PureJavaReflectionProvider(), new XppDriver() { | |||
| @Override | |||
| public HierarchicalStreamWriter createWriter(Writer out) { | |||
| @@ -42,6 +43,7 @@ public class XStreamInitializer { | |||
| }; | |||
| } | |||
| }); | |||
| xstream.ignoreUnknownElements(); | |||
| xstream.setMode(XStream.NO_REFERENCES); | |||
| xstream.addPermission(NullPermission.NULL); | |||
| @@ -3,15 +3,17 @@ package me.chanjar.weixin.common.api; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Test; | |||
| import java.util.concurrent.TimeUnit; | |||
| import static org.testng.Assert.assertFalse; | |||
| import static org.testng.Assert.assertTrue; | |||
| @Test | |||
| public class WxMessageInMemoryDuplicateCheckerTest { | |||
| private WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); | |||
| private WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000L, 1000L); | |||
| public void test() throws InterruptedException { | |||
| Long[] msgIds = new Long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l}; | |||
| Long[] msgIds = new Long[]{1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L}; | |||
| // 第一次检查 | |||
| for (Long msgId : msgIds) { | |||
| @@ -20,14 +22,14 @@ public class WxMessageInMemoryDuplicateCheckerTest { | |||
| } | |||
| // 过1秒再检查 | |||
| Thread.sleep(1000l); | |||
| TimeUnit.SECONDS.sleep(1); | |||
| for (Long msgId : msgIds) { | |||
| boolean result = checker.isDuplicate(String.valueOf(msgId)); | |||
| assertTrue(result); | |||
| } | |||
| // 过1.5秒再检查 | |||
| Thread.sleep(1500l); | |||
| TimeUnit.MILLISECONDS.sleep(1500L); | |||
| for (Long msgId : msgIds) { | |||
| boolean result = checker.isDuplicate(String.valueOf(msgId)); | |||
| assertFalse(result); | |||
| @@ -80,7 +80,7 @@ public class SessionTest { | |||
| InternalSession abc = ism.createSession("abc"); | |||
| abc.endAccess(); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| @@ -99,7 +99,7 @@ public class SessionTest { | |||
| abc.setMaxInactiveInterval(1); | |||
| abc.endAccess(); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| @@ -7,11 +7,11 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.9.0</version> | |||
| <version>2.9.9.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-cp</artifactId> | |||
| <name>WeiXin Java Tools - CP</name> | |||
| <name>Weixin Java Tools - CP</name> | |||
| <description>微信企业号Java SDK</description> | |||
| <dependencies> | |||
| @@ -69,6 +69,16 @@ | |||
| <artifactId>logback-classic</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.assertj</groupId> | |||
| <artifactId>assertj-guava</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -29,11 +29,12 @@ public interface WxCpDepartmentService { | |||
| /** | |||
| * <pre> | |||
| * 部门管理接口 - 查询所有部门 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||
| * 部门管理接口 - 查询部门 | |||
| * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E5.88.97.E8.A1.A8 | |||
| * </pre> | |||
| * @param id 部门id。获取指定部门及其下的子部门。非必需,可为null | |||
| */ | |||
| List<WxCpDepart> listAll() throws WxErrorException; | |||
| List<WxCpDepart> list(Integer id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| @@ -19,9 +19,9 @@ import java.util.List; | |||
| /** | |||
| * 微信API的Service | |||
| * @author chanjaster | |||
| */ | |||
| public interface WxCpService { | |||
| /** | |||
| * <pre> | |||
| * 验证推送过来的消息的正确性 | |||
| @@ -85,42 +85,6 @@ public interface WxCpService { | |||
| */ | |||
| WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMenuService#create(WxMenu)} | |||
| */ | |||
| @Deprecated | |||
| void menuCreate(WxMenu menu) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMenuService#create(Integer, WxMenu)} | |||
| */ | |||
| @Deprecated | |||
| void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMenuService#delete()} } | |||
| */ | |||
| @Deprecated | |||
| void menuDelete() throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMenuService#delete(Integer)} | |||
| */ | |||
| @Deprecated | |||
| void menuDelete(Integer agentId) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMenuService#get() } | |||
| */ | |||
| @Deprecated | |||
| WxMenu menuGet() throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMenuService#get(Integer)} | |||
| */ | |||
| @Deprecated | |||
| WxMenu menuGet(Integer agentId) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 发送消息 | |||
| @@ -131,175 +95,6 @@ public interface WxCpService { | |||
| */ | |||
| WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpDepartmentService#create(WxCpDepart)} | |||
| */ | |||
| @Deprecated | |||
| Integer departCreate(WxCpDepart depart) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpDepartmentService#update(WxCpDepart)} | |||
| */ | |||
| @Deprecated | |||
| void departUpdate(WxCpDepart group) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpDepartmentService#delete(Integer)} | |||
| */ | |||
| @Deprecated | |||
| void departDelete(Integer departId) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpDepartmentService#listAll() } | |||
| */ | |||
| @Deprecated | |||
| List<WxCpDepart> departGet() throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMediaService#upload(String, String, InputStream)} | |||
| */ | |||
| @Deprecated | |||
| WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | |||
| throws WxErrorException, IOException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMediaService#upload(String, File)} | |||
| */ | |||
| @Deprecated | |||
| WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpMediaService#download(String)} | |||
| */ | |||
| @Deprecated | |||
| File mediaDownload(String mediaId) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#authenticate(String)} | |||
| */ | |||
| @Deprecated | |||
| void userAuthenticated(String userId) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#create(WxCpUser)} | |||
| */ | |||
| @Deprecated | |||
| void userCreate(WxCpUser user) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#update(WxCpUser)} | |||
| */ | |||
| @Deprecated | |||
| void userUpdate(WxCpUser user) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#delete(String...)} | |||
| */ | |||
| @Deprecated | |||
| void userDelete(String userid) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#delete(String...)} | |||
| */ | |||
| @Deprecated | |||
| void userDelete(String[] userids) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#getById(String)} | |||
| */ | |||
| @Deprecated | |||
| WxCpUser userGet(String userid) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#listByDepartment(Integer, Boolean, Integer)} | |||
| */ | |||
| @Deprecated | |||
| List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpUserService#listSimpleByDepartment(Integer, Boolean, Integer)} | |||
| */ | |||
| @Deprecated | |||
| List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#create(String)} | |||
| */ | |||
| @Deprecated | |||
| String tagCreate(String tagName) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#update(String, String)} | |||
| */ | |||
| @Deprecated | |||
| void tagUpdate(String tagId, String tagName) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#delete(String)} | |||
| */ | |||
| @Deprecated | |||
| void tagDelete(String tagId) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#listAll()} | |||
| */ | |||
| @Deprecated | |||
| List<WxCpTag> tagGet() throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#listUsersByTagId(String)} | |||
| */ | |||
| @Deprecated | |||
| List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#addUsers2Tag(String, List, List)} | |||
| */ | |||
| @Deprecated | |||
| void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpTagService#removeUsersFromTag(String, List)} | |||
| */ | |||
| @Deprecated | |||
| void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpOAuth2Service#buildAuthorizationUrl(String)} | |||
| */ | |||
| @Deprecated | |||
| String oauth2buildAuthorizationUrl(String state); | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpOAuth2Service#buildAuthorizationUrl(String, String)} | |||
| */ | |||
| @Deprecated | |||
| String oauth2buildAuthorizationUrl(String redirectUri, String state); | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpOAuth2Service#getUserInfo(String)} | |||
| */ | |||
| @Deprecated | |||
| String[] oauth2getUserInfo(String code) throws WxErrorException; | |||
| /** | |||
| * @deprecated 请使用 {@link WxCpOAuth2Service#getUserInfo(Integer, String)} | |||
| */ | |||
| @Deprecated | |||
| String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 邀请成员关注 | |||
| * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8 | |||
| * </pre> | |||
| * | |||
| * @param userId 用户的userid | |||
| * @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 | |||
| * @return 1:微信邀请 2.邮件邀请 | |||
| */ | |||
| int invite(String userId, String inviteTips) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取微信服务器的ip段 | |||
| @@ -4,6 +4,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.cp.bean.WxCpUser; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * <pre> | |||
| @@ -80,4 +81,56 @@ public interface WxCpUserService { | |||
| * @param userid 用户id | |||
| */ | |||
| WxCpUser getById(String userid) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 邀请成员关注 | |||
| * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8 | |||
| * </pre> | |||
| * | |||
| * @param userId 用户的userid | |||
| * @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 | |||
| * @return 1:微信邀请 2.邮件邀请 | |||
| * @deprecated api obsoleted. 邀请关注的功能微信企业号已经下线了, | |||
| * 详细请参考该链接点击查看 https://qy.weixin.qq.com/cgi-bin/homepagenotify?action=get&id=46 | |||
| */ | |||
| @Deprecated | |||
| int invite(String userId, String inviteTips) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * userid转openid. | |||
| * 该接口使用场景为微信支付、微信红包和企业转账。 | |||
| * | |||
| * 在使用微信支付的功能时,需要自行将企业微信的userid转成openid。 | |||
| * 在使用微信红包功能时,需要将应用id和userid转成appid和openid才能使用。 | |||
| * 注:需要成员使用微信登录企业微信或者关注微信插件才能转成openid | |||
| * | |||
| * 文档地址:https://work.weixin.qq.com/api/doc#11279 | |||
| * </pre> | |||
| * | |||
| * @param userId 企业内的成员id | |||
| * @param agentId 非必填,整型,仅用于发红包。其它场景该参数不要填,如微信支付、企业转账、电子发票 | |||
| * @return map对象,可能包含以下值: | |||
| * - openid 企业微信成员userid对应的openid,若有传参agentid,则是针对该agentid的openid。否则是针对企业微信corpid的openid | |||
| * - appid 应用的appid,若请求包中不包含agentid则不返回appid。该appid在使用微信红包时会用到 | |||
| */ | |||
| Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * openid转userid | |||
| * | |||
| * 该接口主要应用于使用微信支付、微信红包和企业转账之后的结果查询。 | |||
| * 开发者需要知道某个结果事件的openid对应企业微信内成员的信息时,可以通过调用该接口进行转换查询。 | |||
| * 权限说明: | |||
| * 管理组需对openid对应的企业微信成员有查看权限。 | |||
| * | |||
| * 文档地址:https://work.weixin.qq.com/api/doc#11279 | |||
| * </pre> | |||
| * | |||
| * @param openid 在使用微信支付、微信红包和企业转账之后,返回结果的openid | |||
| * @return userid 该openid在企业微信对应的成员userid | |||
| */ | |||
| String openid2UserId(String openid) throws WxErrorException; | |||
| } | |||
| @@ -48,13 +48,13 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||
| } | |||
| @Override | |||
| public List<WxCpDepart> listAll() throws WxErrorException { | |||
| public List<WxCpDepart> list(Integer id) throws WxErrorException { | |||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; | |||
| if (id != null) { | |||
| url += "?id=" + id; | |||
| } | |||
| String responseContent = this.mainService.get(url, null); | |||
| /* | |||
| * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } | |||
| * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } | |||
| */ | |||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
| return WxCpGsonBuilder.INSTANCE.create() | |||
| .fromJson(tmpJsonElement.getAsJsonObject().get("department"), | |||
| @@ -47,7 +47,7 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||
| @Override | |||
| public String[] getUserInfo(String code) throws WxErrorException { | |||
| return getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); | |||
| return this.getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); | |||
| } | |||
| @Override | |||
| @@ -138,188 +138,6 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||
| return WxCpMessageSendResult.fromJson(this.post(url, message.toJson())); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void menuCreate(WxMenu menu) throws WxErrorException { | |||
| this.getMenuService().create(menu); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException { | |||
| this.getMenuService().create(agentId, menu); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void menuDelete() throws WxErrorException { | |||
| this.getMenuService().delete(); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void menuDelete(Integer agentId) throws WxErrorException { | |||
| this.getMenuService().delete(agentId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public WxMenu menuGet() throws WxErrorException { | |||
| return this.getMenuService().get(); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public WxMenu menuGet(Integer agentId) throws WxErrorException { | |||
| return this.getMenuService().get(agentId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | |||
| throws WxErrorException, IOException { | |||
| return this.getMediaService().upload(mediaType, fileType, inputStream); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { | |||
| return this.getMediaService().upload(mediaType, file); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public File mediaDownload(String mediaId) throws WxErrorException { | |||
| return this.getMediaService().download(mediaId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void userAuthenticated(String userId) throws WxErrorException { | |||
| this.getUserService().authenticate(userId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void userCreate(WxCpUser user) throws WxErrorException { | |||
| this.getUserService().create(user); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void userUpdate(WxCpUser user) throws WxErrorException { | |||
| this.getUserService().update(user); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void userDelete(String userid) throws WxErrorException { | |||
| this.getUserService().delete(userid); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void userDelete(String[] userids) throws WxErrorException { | |||
| this.getUserService().delete(userids); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public WxCpUser userGet(String userid) throws WxErrorException { | |||
| return this.getUserService().getById(userid); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { | |||
| return this.getUserService().listByDepartment(departId, fetchChild, status); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { | |||
| return this.getUserService().listSimpleByDepartment(departId, fetchChild, status); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public String tagCreate(String tagName) throws WxErrorException { | |||
| return this.getTagService().create(tagName); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void tagUpdate(String tagId, String tagName) throws WxErrorException { | |||
| this.getTagService().update(tagId, tagName); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void tagDelete(String tagId) throws WxErrorException { | |||
| this.getTagService().delete(tagId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public List<WxCpTag> tagGet() throws WxErrorException { | |||
| return this.getTagService().listAll(); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException { | |||
| return this.getTagService().listUsersByTagId(tagId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException { | |||
| this.getTagService().addUsers2Tag(tagId, userIds, partyIds); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException { | |||
| this.getTagService().removeUsersFromTag(tagId, userIds); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public String oauth2buildAuthorizationUrl(String state) { | |||
| return this.getOauth2Service().buildAuthorizationUrl(state); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public String oauth2buildAuthorizationUrl(String redirectUri, String state) { | |||
| return this.getOauth2Service().buildAuthorizationUrl(redirectUri, state); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public String[] oauth2getUserInfo(String code) throws WxErrorException { | |||
| return this.getOauth2Service().getUserInfo(code); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException { | |||
| return this.getOauth2Service().getUserInfo(agentId, code); | |||
| } | |||
| @Override | |||
| public int invite(String userId, String inviteTips) throws WxErrorException { | |||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send"; | |||
| JsonObject jsonObject = new JsonObject(); | |||
| jsonObject.addProperty("userid", userId); | |||
| if (StringUtils.isNotEmpty(inviteTips)) { | |||
| jsonObject.addProperty("invite_tips", inviteTips); | |||
| } | |||
| String responseContent = post(url, jsonObject.toString()); | |||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
| return tmpJsonElement.getAsJsonObject().get("type").getAsInt(); | |||
| } | |||
| @Override | |||
| public String[] getCallbackIp() throws WxErrorException { | |||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; | |||
| @@ -516,30 +334,6 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||
| return userService; | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public Integer departCreate(WxCpDepart depart) throws WxErrorException { | |||
| return this.getDepartmentService().create(depart); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void departUpdate(WxCpDepart depart) throws WxErrorException { | |||
| this.getDepartmentService().update(depart); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public void departDelete(Integer departId) throws WxErrorException { | |||
| this.getDepartmentService().delete(departId); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public List<WxCpDepart> departGet() throws WxErrorException { | |||
| return this.getDepartmentService().listAll(); | |||
| } | |||
| @Override | |||
| public RequestHttp getRequestHttp() { | |||
| return this; | |||
| @@ -1,5 +1,6 @@ | |||
| package me.chanjar.weixin.cp.api.impl; | |||
| import com.google.common.collect.Maps; | |||
| import com.google.gson.*; | |||
| import com.google.gson.reflect.TypeToken; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| @@ -7,8 +8,10 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||
| import me.chanjar.weixin.cp.api.WxCpUserService; | |||
| import me.chanjar.weixin.cp.bean.WxCpUser; | |||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| /** | |||
| * <pre> | |||
| @@ -112,4 +115,50 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
| ); | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public int invite(String userId, String inviteTips) throws WxErrorException { | |||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send"; | |||
| JsonObject jsonObject = new JsonObject(); | |||
| jsonObject.addProperty("userid", userId); | |||
| if (StringUtils.isNotEmpty(inviteTips)) { | |||
| jsonObject.addProperty("invite_tips", inviteTips); | |||
| } | |||
| String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
| return tmpJsonElement.getAsJsonObject().get("type").getAsInt(); | |||
| } | |||
| @Override | |||
| public Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException { | |||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid"; | |||
| JsonObject jsonObject = new JsonObject(); | |||
| jsonObject.addProperty("userid", userId); | |||
| if (agentId != null) { | |||
| jsonObject.addProperty("agentid", agentId); | |||
| } | |||
| String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
| Map<String, String> result = Maps.newHashMap(); | |||
| if (tmpJsonElement.getAsJsonObject().get("openid") != null) { | |||
| result.put("openid", tmpJsonElement.getAsJsonObject().get("openid").getAsString()); | |||
| } | |||
| if (tmpJsonElement.getAsJsonObject().get("appid") != null) { | |||
| result.put("appid", tmpJsonElement.getAsJsonObject().get("appid").getAsString()); | |||
| } | |||
| return result; | |||
| } | |||
| @Override | |||
| public String openid2UserId(String openid) throws WxErrorException { | |||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid"; | |||
| JsonObject jsonObject = new JsonObject(); | |||
| jsonObject.addProperty("openid", openid); | |||
| String responseContent = this.mainService.post(url, jsonObject.toString()); | |||
| JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
| return tmpJsonElement.getAsJsonObject().get("userid").getAsString(); | |||
| } | |||
| } | |||
| @@ -16,8 +16,14 @@ import java.util.List; | |||
| @Data | |||
| public class WxCpUser implements Serializable { | |||
| public enum Gender { | |||
| /** | |||
| * 男 | |||
| */ | |||
| MALE("男", "1"), | |||
| FEMAIL("女", "2"); | |||
| /** | |||
| * 女 | |||
| */ | |||
| FEMALE("女", "2"); | |||
| private String genderName; | |||
| private String code; | |||
| @@ -40,7 +46,7 @@ public class WxCpUser implements Serializable { | |||
| return Gender.MALE; | |||
| } | |||
| if ("2".equals(code)) { | |||
| return Gender.FEMAIL; | |||
| return Gender.FEMALE; | |||
| } | |||
| return null; | |||
| @@ -3,11 +3,13 @@ package me.chanjar.weixin.cp.bean; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; | |||
| @XStreamAlias("xml") | |||
| @Data | |||
| @EqualsAndHashCode(callSuper = false) | |||
| public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage { | |||
| private static final long serialVersionUID = -1099446240667237313L; | |||
| @@ -3,11 +3,13 @@ package me.chanjar.weixin.cp.bean; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
| @XStreamAlias("xml") | |||
| @Data | |||
| @EqualsAndHashCode(callSuper = false) | |||
| public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage { | |||
| private static final long serialVersionUID = 2569239617185930232L; | |||
| @@ -3,11 +3,13 @@ package me.chanjar.weixin.cp.bean; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
| @XStreamAlias("xml") | |||
| @Data | |||
| @EqualsAndHashCode(callSuper = false) | |||
| public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage { | |||
| private static final long serialVersionUID = -8672761162722733622L; | |||
| @@ -3,11 +3,13 @@ package me.chanjar.weixin.cp.bean; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; | |||
| @XStreamAlias("xml") | |||
| @Data | |||
| @EqualsAndHashCode(callSuper = false) | |||
| public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage { | |||
| private static final long serialVersionUID = -7947384031546099340L; | |||
| @@ -23,7 +23,7 @@ public abstract class BaseBuilder<BuilderType, ValueType> { | |||
| public void setCommon(WxCpXmlOutMessage m) { | |||
| m.setToUserName(this.toUserName); | |||
| m.setFromUserName(this.fromUserName); | |||
| m.setCreateTime(System.currentTimeMillis() / 1000l); | |||
| m.setCreateTime(System.currentTimeMillis() / 1000L); | |||
| } | |||
| } | |||
| @@ -63,7 +63,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||
| @Override | |||
| public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | |||
| this.accessToken = accessToken; | |||
| this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||
| this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
| } | |||
| @Override | |||
| @@ -92,7 +92,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||
| public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||
| this.jsapiTicket = jsapiTicket; | |||
| // 预留200秒的时间 | |||
| this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||
| this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
| } | |||
| @Override | |||
| @@ -19,12 +19,16 @@ import java.io.File; | |||
| */ | |||
| public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||
| /* Redis keys here */ | |||
| /** | |||
| * Redis keys here | |||
| */ | |||
| private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; | |||
| private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | |||
| private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | |||
| private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; | |||
| /* Redis clients pool */ | |||
| /** | |||
| * Redis clients pool | |||
| */ | |||
| private final JedisPool jedisPool; | |||
| private volatile String corpId; | |||
| private volatile String corpSecret; | |||
| @@ -196,8 +200,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||
| String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||
| if (expiresTimeStr != null) { | |||
| Long expiresTime = Long.parseLong(expiresTimeStr); | |||
| return expiresTime; | |||
| return Long.parseLong(expiresTimeStr); | |||
| } | |||
| return 0L; | |||
| @@ -1,13 +1,19 @@ | |||
| package me.chanjar.weixin.cp.util.xml; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.cp.bean.*; | |||
| import java.io.InputStream; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; | |||
| public class XStreamTransformer { | |||
| protected static final Map<Class, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); | |||
| @@ -38,7 +44,7 @@ public class XStreamTransformer { | |||
| } | |||
| /** | |||
| * pojo -> xml | |||
| * pojo -> xml. | |||
| */ | |||
| public static <T> String toXml(Class<T> clazz, T object) { | |||
| return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
| @@ -69,7 +69,7 @@ public class WxCpMessageRouterTest { | |||
| WxCpMessageRouter router = new WxCpMessageRouter(null); | |||
| prepare(true, sb, router); | |||
| router.route(message); | |||
| Thread.sleep(500l); | |||
| Thread.sleep(500); | |||
| Assert.assertEquals(sb.toString(), expected); | |||
| } | |||
| @@ -89,7 +89,7 @@ public class WxCpMessageRouterTest { | |||
| public void run() { | |||
| router.route(m); | |||
| try { | |||
| Thread.sleep(1000l); | |||
| Thread.sleep(1000); | |||
| } catch (InterruptedException e) { | |||
| } | |||
| } | |||
| @@ -98,7 +98,7 @@ public class WxCpMessageRouterTest { | |||
| new Thread(r).start(); | |||
| } | |||
| Thread.sleep(1000l * 2); | |||
| Thread.sleep(2000); | |||
| } | |||
| @DataProvider(name = "messages-1") | |||
| @@ -184,7 +184,7 @@ public class WxCpMessageRouterTest { | |||
| msg.setFromUserName("abc"); | |||
| router.route(msg); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| @@ -204,7 +204,7 @@ public class WxCpMessageRouterTest { | |||
| msg.setFromUserName("abc"); | |||
| router.route(msg); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| { | |||
| @@ -218,7 +218,7 @@ public class WxCpMessageRouterTest { | |||
| msg.setFromUserName("abc"); | |||
| router.route(msg); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| @@ -238,7 +238,7 @@ public class WxCpMessageRouterTest { | |||
| msg.setFromUserName("abc"); | |||
| router.route(msg); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| @@ -257,7 +257,7 @@ public class WxCpMessageRouterTest { | |||
| msg.setFromUserName("abc"); | |||
| router.route(msg); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| @@ -271,7 +271,7 @@ public class WxCpMessageRouterTest { | |||
| msg.setFromUserName("abc"); | |||
| router.route(msg); | |||
| Thread.sleep(2000l); | |||
| Thread.sleep(2000); | |||
| Assert.assertEquals(ism.getActiveSessions(), 0); | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ import org.testng.annotations.*; | |||
| import java.util.List; | |||
| import static org.assertj.core.api.Assertions.assertThat; | |||
| import static org.testng.Assert.*; | |||
| /** | |||
| @@ -34,16 +35,24 @@ public class WxCpDepartmentServiceImplTest { | |||
| System.out.println(departId); | |||
| } | |||
| @Test | |||
| public void testListAll() throws Exception { | |||
| @DataProvider | |||
| public Object[][] departIds(){ | |||
| return new Object[][]{ | |||
| {null}, | |||
| {1}, | |||
| {5} | |||
| }; | |||
| } | |||
| @Test(dataProvider = "departIds") | |||
| public void testList(Integer id) throws Exception { | |||
| System.out.println("=================获取部门"); | |||
| List<WxCpDepart> departList = this.wxCpService.getDepartmentService().listAll(); | |||
| assertNotNull(departList); | |||
| assertTrue(departList.size() > 0); | |||
| List<WxCpDepart> departList = this.wxCpService.getDepartmentService().list(id); | |||
| assertThat(departList).isNotEmpty(); | |||
| for (WxCpDepart g : departList) { | |||
| this.depart = g; | |||
| System.out.println(this.depart.getId() + ":" + this.depart.getName()); | |||
| assertNotNull(g.getName()); | |||
| assertThat(g.getName()).isNotBlank(); | |||
| } | |||
| } | |||
| @@ -6,11 +6,14 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||
| import me.chanjar.weixin.cp.bean.WxCpUser; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import org.testng.annotations.*; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import static org.testng.Assert.*; | |||
| import static org.testng.Assert.assertNotEquals; | |||
| import static org.testng.Assert.assertNotNull; | |||
| /** | |||
| * <pre> | |||
| @@ -37,7 +40,7 @@ public class WxCpUserServiceImplTest { | |||
| user.setName("Some Woman"); | |||
| user.setDepartIds(new Integer[]{2}); | |||
| user.setEmail("none@none.com"); | |||
| user.setGender(WxCpUser.Gender.FEMAIL); | |||
| user.setGender(WxCpUser.Gender.FEMALE); | |||
| user.setMobile("13560084979"); | |||
| user.setPosition("woman"); | |||
| user.setTelephone("3300393"); | |||
| @@ -83,4 +86,24 @@ public class WxCpUserServiceImplTest { | |||
| } | |||
| } | |||
| @Test | |||
| @Deprecated | |||
| public void testInvite() throws Exception { | |||
| int result = this.wxCpService.getUserService().invite(userId, ""); | |||
| System.out.println(result); | |||
| } | |||
| @Test | |||
| public void testUserId2Openid() throws Exception { | |||
| Map<String, String> result = this.wxCpService.getUserService().userId2Openid(userId, null); | |||
| System.out.println(result); | |||
| assertNotNull(result); | |||
| } | |||
| @Test | |||
| public void testOpenid2UserId() throws Exception { | |||
| String result = this.wxCpService.getUserService().openid2UserId(userId); | |||
| System.out.println(result); | |||
| assertNotNull(result); | |||
| } | |||
| } | |||
| @@ -57,10 +57,10 @@ public class WxCpXmlMessageTest { | |||
| WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||
| assertEquals(wxMessage.getToUserName(), "toUser"); | |||
| assertEquals(wxMessage.getFromUserName(), "fromUser"); | |||
| assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); | |||
| assertEquals(wxMessage.getCreateTime(), new Long(1348831860)); | |||
| assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); | |||
| assertEquals(wxMessage.getContent(), "this is a test"); | |||
| assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); | |||
| assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); | |||
| assertEquals(wxMessage.getPicUrl(), "this is a url"); | |||
| assertEquals(wxMessage.getMediaId(), "media_id"); | |||
| assertEquals(wxMessage.getFormat(), "Format"); | |||
| @@ -80,7 +80,7 @@ public class WxCpXmlMessageTest { | |||
| assertEquals(wxMessage.getPrecision(), 119.385040); | |||
| assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); | |||
| assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); | |||
| assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l)); | |||
| assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1)); | |||
| assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); | |||
| assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); | |||
| assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); | |||
| @@ -9,7 +9,7 @@ public class WxCpXmlOutImageMessageTest { | |||
| public void test() { | |||
| WxCpXmlOutImageMessage m = new WxCpXmlOutImageMessage(); | |||
| m.setMediaId("ddfefesfsdfef"); | |||
| m.setCreateTime(1122l); | |||
| m.setCreateTime(1122L); | |||
| m.setFromUserName("from"); | |||
| m.setToUserName("to"); | |||
| @@ -8,7 +8,7 @@ public class WxCpXmlOutNewsMessageTest { | |||
| public void test() { | |||
| WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | |||
| m.setCreateTime(1122l); | |||
| m.setCreateTime(1122L); | |||
| m.setFromUserName("fromUser"); | |||
| m.setToUserName("toUser"); | |||
| @@ -9,7 +9,7 @@ public class WxCpXmlOutTextMessageTest { | |||
| public void test() { | |||
| WxCpXmlOutTextMessage m = new WxCpXmlOutTextMessage(); | |||
| m.setContent("content"); | |||
| m.setCreateTime(1122l); | |||
| m.setCreateTime(1122L); | |||
| m.setFromUserName("from"); | |||
| m.setToUserName("to"); | |||
| @@ -11,7 +11,7 @@ public class WxCpXmlOutVideoMessageTest { | |||
| m.setMediaId("media_id"); | |||
| m.setTitle("title"); | |||
| m.setDescription("ddfff"); | |||
| m.setCreateTime(1122l); | |||
| m.setCreateTime(1122L); | |||
| m.setFromUserName("fromUser"); | |||
| m.setToUserName("toUser"); | |||
| @@ -9,7 +9,7 @@ public class WxCpXmlOutVoiceMessageTest { | |||
| public void test() { | |||
| WxCpXmlOutVoiceMessage m = new WxCpXmlOutVoiceMessage(); | |||
| m.setMediaId("ddfefesfsdfef"); | |||
| m.setCreateTime(1122l); | |||
| m.setCreateTime(1122L); | |||
| m.setFromUserName("from"); | |||
| m.setToUserName("to"); | |||
| @@ -7,10 +7,10 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.9.0</version> | |||
| <version>2.9.9.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-miniapp</artifactId> | |||
| <name>WeiXin Java Tools - MiniApp</name> | |||
| <name>Weixin Java Tools - MiniApp</name> | |||
| <description>微信小程序Java SDK</description> | |||
| <dependencies> | |||
| @@ -61,10 +61,19 @@ | |||
| <artifactId>joda-time</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.assertj</groupId> | |||
| <artifactId>assertj-guava</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>redis.clients</groupId> | |||
| <artifactId>jedis</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -7,7 +7,7 @@ import java.io.File; | |||
| /** | |||
| * <pre> | |||
| * 二维码相关操作接口 | |||
| * 二维码相关操作接口. | |||
| * | |||
| * 接口A(createWxCode)加上接口C(createQrcode),总共生成的码数量限制为100,000,请谨慎调用。 | |||
| * | |||
| @@ -22,9 +22,8 @@ public interface WxMaQrcodeService { | |||
| String GET_WXACODE_UNLIMIT_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit"; | |||
| /** | |||
| * 接口C | |||
| * 接口C: 获取小程序页面二维码. | |||
| * <pre> | |||
| * 获取小程序页面二维码 | |||
| * 适用于需要的码数量较少的业务场景 | |||
| * 通过该接口,仅能生成已发布的小程序的二维码。 | |||
| * 可以在开发者工具预览时生成开发版的带参二维码。 | |||
| @@ -39,8 +38,7 @@ public interface WxMaQrcodeService { | |||
| File createQrcode(String path) throws WxErrorException; | |||
| /** | |||
| * 接口A | |||
| * 获取小程序码 | |||
| * 接口A: 获取小程序码. | |||
| * | |||
| * @param path 不能为空,最大长度 128 字节 | |||
| * @param width 默认430 二维码的宽度 | |||
| @@ -54,14 +52,13 @@ public interface WxMaQrcodeService { | |||
| File createWxCode(String path) throws WxErrorException; | |||
| /** | |||
| * 接口B | |||
| * 获取小程序码(永久有效、数量暂无限制) | |||
| * <p> | |||
| * 接口B: 获取小程序码(永久有效、数量暂无限制). | |||
| * <pre> | |||
| * 通过该接口生成的小程序码,永久有效,数量暂无限制。 | |||
| * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。 | |||
| * 使用如下代码可以获取到二维码中的 scene 字段的值。 | |||
| * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode | |||
| * | |||
| * </pre> | |||
| * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) | |||
| * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 | |||
| * @param width 默认false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 | |||
| @@ -1,5 +1,6 @@ | |||
| package cn.binarywang.wx.miniapp.api; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
| @@ -11,20 +12,27 @@ import me.chanjar.weixin.common.util.http.RequestHttp; | |||
| */ | |||
| public interface WxMaService { | |||
| /** | |||
| * 获取access_token | |||
| * 获取access_token. | |||
| */ | |||
| String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | |||
| String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | |||
| /** | |||
| * 获取登录后的session信息 | |||
| * | |||
| * @param jsCode 登录时获取的 code | |||
| */ | |||
| WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 验证消息的确来自微信服务器 | |||
| * 验证消息的确来自微信服务器. | |||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN | |||
| * </pre> | |||
| */ | |||
| boolean checkSignature(String timestamp, String nonce, String signature); | |||
| /** | |||
| * 获取access_token, 不强制刷新access_token | |||
| * 获取access_token, 不强制刷新access_token. | |||
| * | |||
| * @see #getAccessToken(boolean) | |||
| */ | |||
| @@ -32,7 +40,7 @@ public interface WxMaService { | |||
| /** | |||
| * <pre> | |||
| * 获取access_token,本方法线程安全 | |||
| * 获取access_token,本方法线程安全. | |||
| * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 | |||
| * | |||
| * 另:本service的所有方法都会在access_token过期是调用此方法 | |||
| @@ -47,12 +55,12 @@ public interface WxMaService { | |||
| String getAccessToken(boolean forceRefresh) throws WxErrorException; | |||
| /** | |||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | |||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. | |||
| */ | |||
| String get(String url, String queryParam) throws WxErrorException; | |||
| /** | |||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | |||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. | |||
| */ | |||
| String post(String url, String postData) throws WxErrorException; | |||
| @@ -67,7 +75,7 @@ public interface WxMaService { | |||
| /** | |||
| * <pre> | |||
| * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试 | |||
| * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试. | |||
| * 默认:1000ms | |||
| * </pre> | |||
| */ | |||
| @@ -75,59 +83,65 @@ public interface WxMaService { | |||
| /** | |||
| * <pre> | |||
| * 设置当微信系统响应系统繁忙时,最大重试次数 | |||
| * 设置当微信系统响应系统繁忙时,最大重试次数. | |||
| * 默认:5次 | |||
| * </pre> | |||
| */ | |||
| void setMaxRetryTimes(int maxRetryTimes); | |||
| /** | |||
| * 获取WxMaConfig 对象 | |||
| * 获取WxMaConfig 对象. | |||
| * | |||
| * @return WxMaConfig | |||
| */ | |||
| WxMaConfig getWxMaConfig(); | |||
| /** | |||
| * 注入 {@link WxMaConfig} 的实现 | |||
| * 注入 {@link WxMaConfig} 的实现. | |||
| */ | |||
| void setWxMaConfig(WxMaConfig wxConfigProvider); | |||
| /** | |||
| * 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口 | |||
| * 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口. | |||
| * | |||
| * @return WxMaMsgService | |||
| */ | |||
| WxMaMsgService getMsgService(); | |||
| /** | |||
| * 返回素材相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * 返回素材相关接口方法的实现类对象,以方便调用其各个接口. | |||
| * | |||
| * @return WxMaMediaService | |||
| */ | |||
| WxMaMediaService getMediaService(); | |||
| /** | |||
| * 返回用户相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * 返回用户相关接口方法的实现类对象,以方便调用其各个接口. | |||
| * | |||
| * @return WxMaUserService | |||
| */ | |||
| WxMaUserService getUserService(); | |||
| /** | |||
| * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口. | |||
| * | |||
| * @return WxMaQrcodeService | |||
| */ | |||
| WxMaQrcodeService getQrcodeService(); | |||
| /** | |||
| * 初始化http请求对象 | |||
| * 返回模板配置相关接口方法的实现类对象, 以方便调用其各个接口. | |||
| * @return WxMaTemplateService | |||
| */ | |||
| WxMaTemplateService getTemplateService(); | |||
| /** | |||
| * 初始化http请求对象. | |||
| */ | |||
| void initHttp(); | |||
| /** | |||
| * 请求http请求相关信息 | |||
| * 请求http请求相关信息. | |||
| */ | |||
| RequestHttp getRequestHttp(); | |||
| @@ -0,0 +1,89 @@ | |||
| package cn.binarywang.wx.miniapp.api; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateAddResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateLibraryGetResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateLibraryListResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateListResult; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import java.util.List; | |||
| public interface WxMaTemplateService { | |||
| //获取小程序模板库标题列表 | |||
| String TEMPLATE_LIBRARY_LIST_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/template/library/list"; | |||
| //获取模板库某个模板标题下关键词库 | |||
| String TEMPLATE_LIBRARY_KEYWORD_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/template/library/get"; | |||
| //组合模板并添加至帐号下的个人模板库 | |||
| String TEMPLATE_ADD_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/template/add"; | |||
| //获取帐号下已存在的模板列表 | |||
| String TEMPLATE_LIST_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/template/list"; | |||
| //删除帐号下的某个模板 | |||
| String TEMPLATE_DEL_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/template/del"; | |||
| /** | |||
| * <pre> | |||
| * 获取小程序模板库标题列表 | |||
| * | |||
| * 详情请见: <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500465446_j4CgR&token=&lang=zh_CN">获取小程序模板库标题列表</a> | |||
| * 接口url格式: https://api.weixin.qq.com/cgi-bin/wxopen/template/library/list?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * @param offset | |||
| * @param count | |||
| * @return | |||
| */ | |||
| WxMaTemplateLibraryListResult findTemplateLibraryList(int offset, int count) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取模板库某个模板标题下关键词库 | |||
| * | |||
| * 详情请见: <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500465446_j4CgR&token=&lang=zh_CN">获取小程序模板库标题列表</a> | |||
| * 接口url格式: https://api.weixin.qq.com/cgi-bin/wxopen/template/library/get?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * @param id | |||
| * @return | |||
| */ | |||
| WxMaTemplateLibraryGetResult findTemplateLibraryKeywordList(String id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 组合模板并添加至帐号下的个人模板库 | |||
| * | |||
| * 详情请见: <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500465446_j4CgR&token=&lang=zh_CN">获取小程序模板库标题列表</a> | |||
| * 接口url格式: https://api.weixin.qq.com/cgi-bin/wxopen/template/add?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * @param id | |||
| * @param keywordIdList | |||
| * @return | |||
| */ | |||
| WxMaTemplateAddResult addTemplate(String id, List<Integer> keywordIdList) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取帐号下已存在的模板列表 | |||
| * | |||
| * 详情请见: <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500465446_j4CgR&token=&lang=zh_CN">获取小程序模板库标题列表</a> | |||
| * 接口url格式: https://api.weixin.qq.com/cgi-bin/wxopen/template/list?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * @param offset | |||
| * @param count | |||
| * @return | |||
| */ | |||
| WxMaTemplateListResult findTemplateList(int offset, int count) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 删除帐号下的某个模板 | |||
| * | |||
| * 详情请见: <a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500465446_j4CgR&token=&lang=zh_CN">获取小程序模板库标题列表</a> | |||
| * 接口url格式: https://api.weixin.qq.com/cgi-bin/wxopen/template/list?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * @param templateId | |||
| */ | |||
| boolean delTemplate(String templateId) throws WxErrorException; | |||
| } | |||
| @@ -1,26 +1,26 @@ | |||
| package cn.binarywang.wx.miniapp.api; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| /** | |||
| * 用户信息相关操作接口 | |||
| * 用户信息相关操作接口. | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public interface WxMaUserService { | |||
| String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | |||
| /** | |||
| * 获取登录后的session信息 | |||
| * 获取登录后的session信息. | |||
| * | |||
| * @param jsCode 登录时获取的 code | |||
| */ | |||
| WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException; | |||
| /** | |||
| * 解密用户敏感数据 | |||
| * 解密用户敏感数据. | |||
| * | |||
| * @param sessionKey 会话密钥 | |||
| * @param encryptedData 消息密文 | |||
| @@ -29,7 +29,16 @@ public interface WxMaUserService { | |||
| WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); | |||
| /** | |||
| * 验证用户信息完整性 | |||
| * 解密用户手机号信息. | |||
| * | |||
| * @param sessionKey 会话密钥 | |||
| * @param encryptedData 消息密文 | |||
| * @param ivStr 加密算法的初始向量 | |||
| */ | |||
| WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr); | |||
| /** | |||
| * 验证用户信息完整性. | |||
| * | |||
| * @param sessionKey 会话密钥 | |||
| * @param rawData 微信用户基本信息 | |||
| @@ -54,7 +54,8 @@ public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { | |||
| } | |||
| @Override | |||
| public File createWxCodeLimit(String scene, String page, int width, boolean autoColor, WxMaCodeLineColor lineColor) throws WxErrorException { | |||
| public File createWxCodeLimit(String scene, String page, int width, boolean autoColor, WxMaCodeLineColor lineColor) | |||
| throws WxErrorException { | |||
| WxMaWxcodeLimit wxMaWxcodeLimit = new WxMaWxcodeLimit(); | |||
| wxMaWxcodeLimit.setScene(scene); | |||
| wxMaWxcodeLimit.setPage(page); | |||
| @@ -1,16 +1,10 @@ | |||
| package cn.binarywang.wx.miniapp.api.impl; | |||
| import cn.binarywang.wx.miniapp.api.*; | |||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
| import cn.binarywang.wx.miniapp.constant.WxMaConstants; | |||
| import com.google.gson.JsonParser; | |||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.crypto.SHA1; | |||
| import me.chanjar.weixin.common.util.http.*; | |||
| import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
| import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | |||
| import java.io.IOException; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import java.util.concurrent.locks.Lock; | |||
| import org.apache.http.HttpHost; | |||
| import org.apache.http.client.config.RequestConfig; | |||
| import org.apache.http.client.methods.CloseableHttpResponse; | |||
| @@ -20,14 +14,32 @@ import org.apache.http.impl.client.CloseableHttpClient; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.io.IOException; | |||
| import java.util.concurrent.locks.Lock; | |||
| import cn.binarywang.wx.miniapp.api.WxMaMediaService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaMsgService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaTemplateService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaUserService; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
| import cn.binarywang.wx.miniapp.constant.WxMaConstants; | |||
| import com.google.common.base.Joiner; | |||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.crypto.SHA1; | |||
| import me.chanjar.weixin.common.util.http.HttpType; | |||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.RequestHttp; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
| import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> { | |||
| private static final JsonParser JSON_PARSER = new JsonParser(); | |||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
| private CloseableHttpClient httpClient; | |||
| @@ -38,6 +50,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
| private WxMaMediaService materialService = new WxMaMediaServiceImpl(this); | |||
| private WxMaUserService userService = new WxMaUserServiceImpl(this); | |||
| private WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this); | |||
| private WxMaTemplateService templateService = new WxMaTemplateServiceImpl(this); | |||
| private int retrySleepMillis = 1000; | |||
| private int maxRetryTimes = 5; | |||
| @@ -120,6 +133,19 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
| return this.getWxMaConfig().getAccessToken(); | |||
| } | |||
| @Override | |||
| public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | |||
| final WxMaConfig config = getWxMaConfig(); | |||
| Map<String, String> params = new HashMap<>(8); | |||
| params.put("appid", config.getAppid()); | |||
| params.put("secret", config.getSecret()); | |||
| params.put("js_code", jsCode); | |||
| params.put("grant_type", "authorization_code"); | |||
| String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||
| return WxMaJscode2SessionResult.fromJson(result); | |||
| } | |||
| @Override | |||
| public boolean checkSignature(String timestamp, String nonce, String signature) { | |||
| try { | |||
| @@ -259,4 +285,9 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
| public WxMaQrcodeService getQrcodeService() { | |||
| return this.qrCodeService; | |||
| } | |||
| @Override | |||
| public WxMaTemplateService getTemplateService() { | |||
| return this.templateService; | |||
| } | |||
| } | |||
| @@ -0,0 +1,101 @@ | |||
| package cn.binarywang.wx.miniapp.api.impl; | |||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaTemplateService; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateAddResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateLibraryGetResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateLibraryListResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateListResult; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
| private WxMaService wxMaService; | |||
| public WxMaTemplateServiceImpl(WxMaService wxMaService){ | |||
| this.wxMaService = wxMaService; | |||
| } | |||
| @Override | |||
| public WxMaTemplateLibraryListResult findTemplateLibraryList(int offset, int count) throws WxErrorException { | |||
| Map<String, Integer> params = new HashMap<>(); | |||
| params.put("offset", offset); | |||
| params.put("count", count); | |||
| String responseText = this.wxMaService.post(TEMPLATE_LIBRARY_LIST_URL, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if(wxError.getErrorCode() == 0){ | |||
| return WxMaTemplateLibraryListResult.fromJson(responseText); | |||
| }else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMaTemplateLibraryGetResult findTemplateLibraryKeywordList(String id) throws WxErrorException { | |||
| Map<String, String> params = new HashMap<>(); | |||
| params.put("id", id); | |||
| String responseText = this.wxMaService.post(TEMPLATE_LIBRARY_KEYWORD_URL, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if(wxError.getErrorCode() == 0){ | |||
| return WxMaTemplateLibraryGetResult.fromJson(responseText); | |||
| }else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMaTemplateAddResult addTemplate(String id, List<Integer> keywordIdList) throws WxErrorException { | |||
| Map<String, Object> params = new HashMap<>(); | |||
| params.put("id", id); | |||
| params.put("keyword_id_list", keywordIdList.toArray()); | |||
| String responseText = this.wxMaService.post(TEMPLATE_ADD_URL, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if(wxError.getErrorCode() == 0){ | |||
| return WxMaTemplateAddResult.fromJson(responseText); | |||
| }else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMaTemplateListResult findTemplateList(int offset, int count) throws WxErrorException { | |||
| Map<String, Integer> params = new HashMap<>(); | |||
| params.put("offset", offset); | |||
| params.put("count", count); | |||
| String responseText = this.wxMaService.post(TEMPLATE_LIST_URL, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if(wxError.getErrorCode() == 0){ | |||
| return WxMaTemplateListResult.fromJson(responseText); | |||
| }else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean delTemplate(String templateId) throws WxErrorException { | |||
| Map<String, String> params = new HashMap<>(); | |||
| params.put("template_id", templateId); | |||
| String responseText = this.wxMaService.post(TEMPLATE_DEL_URL, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if(wxError.getErrorCode() == 0){ | |||
| return true; | |||
| }else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,17 +1,14 @@ | |||
| package cn.binarywang.wx.miniapp.api.impl; | |||
| import org.apache.commons.codec.digest.DigestUtils; | |||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||
| import cn.binarywang.wx.miniapp.api.WxMaUserService; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
| import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | |||
| import com.google.common.base.Joiner; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import org.apache.commons.codec.digest.DigestUtils; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| @@ -25,15 +22,7 @@ public class WxMaUserServiceImpl implements WxMaUserService { | |||
| @Override | |||
| public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException { | |||
| final WxMaConfig config = service.getWxMaConfig(); | |||
| Map<String, String> params = new HashMap<>(8); | |||
| params.put("appid", config.getAppid()); | |||
| params.put("secret", config.getSecret()); | |||
| params.put("js_code", jsCode); | |||
| params.put("grant_type", "authorization_code"); | |||
| String result = this.service.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||
| return WxMaJscode2SessionResult.fromJson(result); | |||
| return service.jsCode2SessionInfo(jsCode); | |||
| } | |||
| @Override | |||
| @@ -41,10 +30,14 @@ public class WxMaUserServiceImpl implements WxMaUserService { | |||
| return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||
| } | |||
| @Override | |||
| public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) { | |||
| return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||
| } | |||
| @Override | |||
| public boolean checkUserInfo(String sessionKey, String rawData, String signature) { | |||
| final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey); | |||
| System.out.println(generatedSignature); | |||
| return generatedSignature.equals(signature); | |||
| } | |||
| @@ -8,10 +8,12 @@ import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
| * @author Element | |||
| */ | |||
| public abstract class AbstractWxMaQrcodeWrapper { | |||
| public String toJson() { | |||
| return WxMaGsonBuilder.create().toJson(this); | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return WxMaGsonBuilder.create().toJson(this); | |||
| return this.toJson(); | |||
| } | |||
| } | |||
| @@ -8,8 +8,11 @@ import lombok.EqualsAndHashCode; | |||
| import java.io.Serializable; | |||
| /** | |||
| * {"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","expires_in":2592000,"openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"} | |||
| * | |||
| * <pre> | |||
| * code换取session_key接口的响应 | |||
| * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject | |||
| * 微信返回报文:{"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"} | |||
| * </pre> | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| @Data | |||
| @@ -20,9 +23,6 @@ public class WxMaJscode2SessionResult implements Serializable { | |||
| @SerializedName("session_key") | |||
| private String sessionKey; | |||
| @SerializedName("expires_in") | |||
| private Integer expiresin; | |||
| @SerializedName("openid") | |||
| private String openid; | |||
| @@ -1,14 +1,19 @@ | |||
| package cn.binarywang.wx.miniapp.bean; | |||
| import cn.binarywang.wx.miniapp.builder.ImageBuilder; | |||
| import cn.binarywang.wx.miniapp.builder.TextBuilder; | |||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
| import cn.binarywang.wx.miniapp.builder.ImageMessageBuilder; | |||
| import cn.binarywang.wx.miniapp.builder.LinkMessageBuilder; | |||
| import cn.binarywang.wx.miniapp.builder.MaPageMessageBuilder; | |||
| import cn.binarywang.wx.miniapp.builder.TextMessageBuilder; | |||
| import com.google.gson.GsonBuilder; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.AllArgsConstructor; | |||
| import lombok.Builder; | |||
| import lombok.Data; | |||
| import java.io.Serializable; | |||
| /** | |||
| * 客服消息 | |||
| * 客服消息. | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| @@ -16,30 +21,90 @@ import java.io.Serializable; | |||
| public class WxMaKefuMessage implements Serializable { | |||
| private static final long serialVersionUID = -9196732086954365246L; | |||
| @SerializedName("touser") | |||
| private String toUser; | |||
| @SerializedName("msgtype") | |||
| private String msgType; | |||
| private String content; | |||
| private String mediaId; | |||
| private String thumbMediaId; | |||
| private String title; | |||
| private String description; | |||
| @SerializedName("text") | |||
| private KfText text; | |||
| @SerializedName("image") | |||
| private KfImage image; | |||
| @SerializedName("link") | |||
| private KfLink link; | |||
| @SerializedName("miniprogrampage") | |||
| private KfMaPage maPage; | |||
| @Data | |||
| @AllArgsConstructor | |||
| public static class KfText { | |||
| private String content; | |||
| } | |||
| @Data | |||
| @AllArgsConstructor | |||
| public static class KfImage { | |||
| @SerializedName("media_id") | |||
| private String mediaId; | |||
| } | |||
| @Data | |||
| @Builder | |||
| public static class KfLink { | |||
| private String title; | |||
| private String description; | |||
| private String url; | |||
| @SerializedName("thumb_url") | |||
| private String thumbUrl; | |||
| } | |||
| @Data | |||
| @Builder | |||
| public static class KfMaPage { | |||
| private String title; | |||
| @SerializedName("pagepath") | |||
| private String pagePath; | |||
| @SerializedName("thumb_media_id") | |||
| private String thumbMediaId; | |||
| } | |||
| /** | |||
| * 获得文本消息builder. | |||
| */ | |||
| public static TextMessageBuilder newTextBuilder() { | |||
| return new TextMessageBuilder(); | |||
| } | |||
| /** | |||
| * 获得图片消息builder. | |||
| */ | |||
| public static ImageMessageBuilder newImageBuilder() { | |||
| return new ImageMessageBuilder(); | |||
| } | |||
| /** | |||
| * 获得文本消息builder | |||
| * 获得图文链接消息builder. | |||
| */ | |||
| public static TextBuilder newTextBuilder() { | |||
| return new TextBuilder(); | |||
| public static LinkMessageBuilder newLinkBuilder() { | |||
| return new LinkMessageBuilder(); | |||
| } | |||
| /** | |||
| * 获得图片消息builder | |||
| * 获得图文链接消息builder. | |||
| */ | |||
| public static ImageBuilder newImageBuilder() { | |||
| return new ImageBuilder(); | |||
| public static MaPageMessageBuilder newMaPageBuilder() { | |||
| return new MaPageMessageBuilder(); | |||
| } | |||
| public String toJson() { | |||
| return WxMaGsonBuilder.create().toJson(this); | |||
| return new GsonBuilder().create().toJson(this); | |||
| } | |||
| } | |||
| @@ -45,12 +45,16 @@ public class WxMaMessage implements Serializable { | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private Integer createTime; | |||
| @SerializedName("MsgType") | |||
| @XStreamAlias("MsgType") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String msgType; | |||
| @SerializedName("MsgDataFormat") | |||
| @XStreamAlias("MsgDataFormat") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String msgType; | |||
| private String msgDataFormat; | |||
| // 文本消息 | |||
| @SerializedName("Content") | |||
| @XStreamAlias("Content") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| @@ -61,7 +65,6 @@ public class WxMaMessage implements Serializable { | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private Long msgId; | |||
| // 图片消息 | |||
| @SerializedName("PicUrl") | |||
| @XStreamAlias("PicUrl") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| @@ -72,7 +75,6 @@ public class WxMaMessage implements Serializable { | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String mediaId; | |||
| // 事件消息 | |||
| @SerializedName("Event") | |||
| @XStreamAlias("Event") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| @@ -92,7 +94,7 @@ public class WxMaMessage implements Serializable { | |||
| } | |||
| /** | |||
| * 从加密字符串转换 | |||
| * 从加密字符串转换. | |||
| * | |||
| * @param encryptedXml 密文 | |||
| * @param wxMaConfig 配置存储器对象 | |||
| @@ -0,0 +1,30 @@ | |||
| package cn.binarywang.wx.miniapp.bean; | |||
| import java.io.Serializable; | |||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
| import lombok.Data; | |||
| /** | |||
| * 微信用户绑定的手机号相关信息 | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| @Data | |||
| public class WxMaPhoneNumberInfo implements Serializable { | |||
| private static final long serialVersionUID = 6719822331555402137L; | |||
| private String phoneNumber; | |||
| private String purePhoneNumber; | |||
| private String countryCode; | |||
| private Watermark watermark; | |||
| public static WxMaPhoneNumberInfo fromJson(String json) { | |||
| return WxMaGsonBuilder.create().fromJson(json, WxMaPhoneNumberInfo.class); | |||
| } | |||
| @Data | |||
| public static class Watermark { | |||
| private String timestamp; | |||
| private String appid; | |||
| } | |||
| } | |||
| @@ -1,24 +1,28 @@ | |||
| package cn.binarywang.wx.miniapp.bean; | |||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
| import lombok.Builder; | |||
| import lombok.Data; | |||
| import lombok.*; | |||
| import java.io.Serializable; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * 模板消息. | |||
| * 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| @Data | |||
| @Getter | |||
| @Setter | |||
| @NoArgsConstructor | |||
| @AllArgsConstructor | |||
| @Builder | |||
| public class WxMaTemplateMessage implements Serializable { | |||
| private static final long serialVersionUID = 5063374783759519418L; | |||
| /** | |||
| * 接收者(用户)的 openid. | |||
| * <pre> | |||
| * 参数:touser | |||
| * 是否必填: 是 | |||
| @@ -28,6 +32,7 @@ public class WxMaTemplateMessage implements Serializable { | |||
| private String toUser; | |||
| /** | |||
| * 所需下发的模板消息的id. | |||
| * <pre> | |||
| * 参数:template_id | |||
| * 是否必填: 是 | |||
| @@ -37,6 +42,7 @@ public class WxMaTemplateMessage implements Serializable { | |||
| private String templateId; | |||
| /** | |||
| * 点击模板卡片后的跳转页面,仅限本小程序内的页面. | |||
| * <pre> | |||
| * 参数:page | |||
| * 是否必填: 否 | |||
| @@ -46,6 +52,7 @@ public class WxMaTemplateMessage implements Serializable { | |||
| private String page; | |||
| /** | |||
| * 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id. | |||
| * <pre> | |||
| * 参数:form_id | |||
| * 是否必填: 是 | |||
| @@ -55,16 +62,17 @@ public class WxMaTemplateMessage implements Serializable { | |||
| private String formId; | |||
| /** | |||
| * 模板内容,不填则下发空模板. | |||
| * <pre> | |||
| * 参数:data | |||
| * 是否必填: 是 | |||
| * 描述: 模板内容,不填则下发空模板 | |||
| * </pre> | |||
| */ | |||
| @Builder.Default | |||
| private final List<Data> data = new ArrayList<>(); | |||
| private List<Data> data; | |||
| /** | |||
| * 模板内容字体的颜色,不填默认黑色. | |||
| * <pre> | |||
| * 参数:color | |||
| * 是否必填: 否 | |||
| @@ -74,6 +82,7 @@ public class WxMaTemplateMessage implements Serializable { | |||
| private String color; | |||
| /** | |||
| * 模板需要放大的关键词,不填则默认无放大. | |||
| * <pre> | |||
| * 参数:emphasis_keyword | |||
| * 是否必填: 否 | |||
| @@ -82,6 +91,15 @@ public class WxMaTemplateMessage implements Serializable { | |||
| */ | |||
| private String emphasisKeyword; | |||
| public WxMaTemplateMessage addData(Data datum) { | |||
| if (this.data == null) { | |||
| this.data = new ArrayList<>(); | |||
| } | |||
| this.data.add(datum); | |||
| return this; | |||
| } | |||
| public String toJson() { | |||
| return WxMaGsonBuilder.create().toJson(this); | |||
| } | |||
| @@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode; | |||
| import java.io.Serializable; | |||
| /** | |||
| * 小程序码接口B. | |||
| * | |||
| * @author Element | |||
| * @date 2017/7/27 | |||
| @@ -0,0 +1,20 @@ | |||
| package cn.binarywang.wx.miniapp.bean.template; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.Data; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.io.Serializable; | |||
| @Data | |||
| public class WxMaTemplateAddResult implements Serializable{ | |||
| private static final long serialVersionUID = 872250961973834465L; | |||
| @SerializedName("template_id") | |||
| private String templateId; | |||
| public static WxMaTemplateAddResult fromJson(String json){ | |||
| return WxGsonBuilder.create().fromJson(json, WxMaTemplateAddResult.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| package cn.binarywang.wx.miniapp.bean.template; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.Data; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| @Data | |||
| public class WxMaTemplateLibraryGetResult implements Serializable{ | |||
| private static final long serialVersionUID = -190847592776636744L; | |||
| private String id; | |||
| private String title; | |||
| @SerializedName("keyword_list") | |||
| private List<KeywordInfo> keywordList; | |||
| @Data | |||
| public static class KeywordInfo{ | |||
| @SerializedName("keyword_id") | |||
| private int keywordId; | |||
| private String name; | |||
| private String example; | |||
| } | |||
| public static WxMaTemplateLibraryGetResult fromJson(String json){ | |||
| return WxGsonBuilder.create().fromJson(json, WxMaTemplateLibraryGetResult.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package cn.binarywang.wx.miniapp.bean.template; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.Data; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| @Data | |||
| public class WxMaTemplateLibraryListResult implements Serializable{ | |||
| private static final long serialVersionUID = -2780782521447602209L; | |||
| @SerializedName("total_count") | |||
| private int totalCount; | |||
| private List<TemplateItem> list; | |||
| public static WxMaTemplateLibraryListResult fromJson(String json){ | |||
| return WxGsonBuilder.create().fromJson(json, WxMaTemplateLibraryListResult.class); | |||
| } | |||
| @Data | |||
| public static class TemplateItem{ | |||
| private String id; | |||
| private String title; | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package cn.binarywang.wx.miniapp.bean.template; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.Data; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| @Data | |||
| public class WxMaTemplateListResult implements Serializable{ | |||
| private static final long serialVersionUID = -7430535579782184537L; | |||
| private List<TemplateInfo> list; | |||
| public static WxMaTemplateListResult fromJson(String json){ | |||
| return WxGsonBuilder.create().fromJson(json, WxMaTemplateListResult.class); | |||
| } | |||
| @Data | |||
| public static class TemplateInfo{ | |||
| @SerializedName("template_id") | |||
| private String templateId; | |||
| private String title; | |||
| private String content; | |||
| private String example; | |||
| } | |||
| } | |||
| @@ -1,27 +0,0 @@ | |||
| package cn.binarywang.wx.miniapp.builder; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import cn.binarywang.wx.miniapp.constant.WxMaConstants; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public final class ImageBuilder extends BaseBuilder<ImageBuilder> { | |||
| private String mediaId; | |||
| public ImageBuilder() { | |||
| this.msgType = WxMaConstants.KefuMsgType.IMAGE; | |||
| } | |||
| public ImageBuilder mediaId(String mediaId) { | |||
| this.mediaId = mediaId; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = super.build(); | |||
| m.setMediaId(this.mediaId); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package cn.binarywang.wx.miniapp.builder; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; | |||
| /** | |||
| * 图片消息builder. | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public final class ImageMessageBuilder extends BaseBuilder<ImageMessageBuilder> { | |||
| private String mediaId; | |||
| public ImageMessageBuilder() { | |||
| this.msgType = KefuMsgType.IMAGE; | |||
| } | |||
| public ImageMessageBuilder mediaId(String mediaId) { | |||
| this.mediaId = mediaId; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = super.build(); | |||
| m.setImage(new WxMaKefuMessage.KfImage(this.mediaId)); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| package cn.binarywang.wx.miniapp.builder; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; | |||
| /** | |||
| * 图文链接消息builder | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public class LinkMessageBuilder extends BaseBuilder<LinkMessageBuilder> { | |||
| private String title; | |||
| private String description; | |||
| private String url; | |||
| private String thumbUrl; | |||
| public LinkMessageBuilder() { | |||
| this.msgType = KefuMsgType.LINK; | |||
| } | |||
| public LinkMessageBuilder title(String title) { | |||
| this.title = title; | |||
| return this; | |||
| } | |||
| public LinkMessageBuilder description(String description) { | |||
| this.description = description; | |||
| return this; | |||
| } | |||
| public LinkMessageBuilder url(String url) { | |||
| this.url = url; | |||
| return this; | |||
| } | |||
| public LinkMessageBuilder thumbUrl(String thumbUrl) { | |||
| this.thumbUrl = thumbUrl; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = super.build(); | |||
| m.setLink(WxMaKefuMessage.KfLink.builder().title(this.title) | |||
| .description(this.description) | |||
| .url(this.url) | |||
| .thumbUrl(this.thumbUrl) | |||
| .build() | |||
| ); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| package cn.binarywang.wx.miniapp.builder; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; | |||
| /** | |||
| * 小程序卡片消息builder | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public class MaPageMessageBuilder extends BaseBuilder<MaPageMessageBuilder> { | |||
| private String title; | |||
| private String pagePath; | |||
| private String thumbMediaId; | |||
| public MaPageMessageBuilder() { | |||
| this.msgType = KefuMsgType.MA_PAGE; | |||
| } | |||
| public MaPageMessageBuilder title(String title) { | |||
| this.title = title; | |||
| return this; | |||
| } | |||
| public MaPageMessageBuilder pagePath(String pagePath) { | |||
| this.pagePath = pagePath; | |||
| return this; | |||
| } | |||
| public MaPageMessageBuilder thumbMediaId(String thumbMediaId) { | |||
| this.thumbMediaId = thumbMediaId; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = super.build(); | |||
| m.setMaPage(WxMaKefuMessage.KfMaPage.builder() | |||
| .title(this.title) | |||
| .pagePath(this.pagePath) | |||
| .thumbMediaId(this.thumbMediaId) | |||
| .build() | |||
| ); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -1,27 +0,0 @@ | |||
| package cn.binarywang.wx.miniapp.builder; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import cn.binarywang.wx.miniapp.constant.WxMaConstants; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public final class TextBuilder extends BaseBuilder<TextBuilder> { | |||
| private String content; | |||
| public TextBuilder() { | |||
| this.msgType = WxMaConstants.KefuMsgType.TEXT; | |||
| } | |||
| public TextBuilder content(String content) { | |||
| this.content = content; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = super.build(); | |||
| m.setContent(this.content); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package cn.binarywang.wx.miniapp.builder; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.KefuMsgType; | |||
| /** | |||
| * 文本消息builder. | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public final class TextMessageBuilder extends BaseBuilder<TextMessageBuilder> { | |||
| private String content; | |||
| public TextMessageBuilder() { | |||
| this.msgType = KefuMsgType.TEXT; | |||
| } | |||
| public TextMessageBuilder content(String content) { | |||
| this.content = content; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = super.build(); | |||
| m.setText(new WxMaKefuMessage.KfText(this.content)); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -2,29 +2,29 @@ package cn.binarywang.wx.miniapp.constant; | |||
| /** | |||
| * <pre> | |||
| * 小程序常量 | |||
| * 小程序常量. | |||
| * </pre> | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public class WxMaConstants { | |||
| /** | |||
| * 微信接口返回的参数errcode | |||
| * 微信接口返回的参数errcode. | |||
| */ | |||
| public static final String ERRCODE = "errcode"; | |||
| /** | |||
| * 素材类型 | |||
| * 素材类型. | |||
| */ | |||
| public static class MediaType { | |||
| /** | |||
| * 图片 | |||
| * 图片. | |||
| */ | |||
| public static final String IMAGE = "image"; | |||
| } | |||
| /** | |||
| * 消息格式 | |||
| * 消息格式. | |||
| */ | |||
| public static class MsgDataFormat { | |||
| public static final String XML = "XML"; | |||
| @@ -32,32 +32,40 @@ public class WxMaConstants { | |||
| } | |||
| /** | |||
| * 客服消息的消息类型 | |||
| * 客服消息的消息类型. | |||
| */ | |||
| public static class KefuMsgType { | |||
| /** | |||
| * 文本消息 | |||
| * 文本消息. | |||
| */ | |||
| public static final String TEXT = "text"; | |||
| /** | |||
| * 图片消息 | |||
| * 图片消息. | |||
| */ | |||
| public static final String IMAGE = "image"; | |||
| /** | |||
| * 图文链接. | |||
| */ | |||
| public static final String LINK = "link"; | |||
| /** | |||
| * 小程序卡片消息. | |||
| */ | |||
| public static final String MA_PAGE = "miniprogrampage"; | |||
| } | |||
| public static final class ErrorCode { | |||
| /** | |||
| * 40001 获取access_token时AppSecret错误,或者access_token无效 | |||
| * 40001 获取access_token时AppSecret错误,或者access_token无效. | |||
| */ | |||
| public static final int ERR_40001 = 40001; | |||
| /** | |||
| * 42001 access_token超时 | |||
| * 42001 access_token超时. | |||
| */ | |||
| public static final int ERR_42001 = 42001; | |||
| /** | |||
| * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期) | |||
| * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期). | |||
| */ | |||
| public static final int ERR_40014 = 40014; | |||
| } | |||
| @@ -37,17 +37,17 @@ public class QrCodeRequestExecutor implements RequestExecutor<File, AbstractWxMa | |||
| HttpPost httpPost = new HttpPost(uri); | |||
| if (requestHttp.getRequestHttpProxy() != null) { | |||
| httpPost.setConfig( | |||
| RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() | |||
| RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() | |||
| ); | |||
| } | |||
| httpPost.setEntity(new StringEntity(ticket.toString())); | |||
| httpPost.setEntity(new StringEntity(ticket.toJson())); | |||
| try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); | |||
| InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { | |||
| Header[] contentTypeHeader = response.getHeaders("Content-Type"); | |||
| if (contentTypeHeader != null && contentTypeHeader.length > 0 | |||
| && ContentType.APPLICATION_JSON.getMimeType() | |||
| .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { | |||
| && ContentType.APPLICATION_JSON.getMimeType() | |||
| .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { | |||
| String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||
| throw new WxErrorException(WxError.fromJson(responseContent)); | |||
| } | |||
| @@ -1,6 +1,5 @@ | |||
| package cn.binarywang.wx.miniapp.util.json; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; | |||
| import com.google.gson.Gson; | |||
| import com.google.gson.GsonBuilder; | |||
| @@ -13,7 +12,6 @@ public class WxMaGsonBuilder { | |||
| static { | |||
| INSTANCE.disableHtmlEscaping(); | |||
| INSTANCE.registerTypeAdapter(WxMaKefuMessage.class, new WxMaKefuMessageGsonAdapter()); | |||
| INSTANCE.registerTypeAdapter(WxMaTemplateMessage.class, new WxMaTemplateMessageGsonAdapter()); | |||
| } | |||
| @@ -1,46 +0,0 @@ | |||
| /* | |||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||
| * | |||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||
| * arose from modification of the original source, or other redistribution of this source | |||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||
| */ | |||
| package cn.binarywang.wx.miniapp.util.json; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; | |||
| import cn.binarywang.wx.miniapp.constant.WxMaConstants; | |||
| import com.google.gson.JsonElement; | |||
| import com.google.gson.JsonObject; | |||
| import com.google.gson.JsonSerializationContext; | |||
| import com.google.gson.JsonSerializer; | |||
| import java.lang.reflect.Type; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| public class WxMaKefuMessageGsonAdapter implements JsonSerializer<WxMaKefuMessage> { | |||
| @Override | |||
| public JsonElement serialize(WxMaKefuMessage message, Type typeOfSrc, JsonSerializationContext context) { | |||
| JsonObject messageJson = new JsonObject(); | |||
| messageJson.addProperty("touser", message.getToUser()); | |||
| messageJson.addProperty("msgtype", message.getMsgType()); | |||
| if (WxMaConstants.KefuMsgType.TEXT.equals(message.getMsgType())) { | |||
| JsonObject text = new JsonObject(); | |||
| text.addProperty("content", message.getContent()); | |||
| messageJson.add("text", text); | |||
| } | |||
| if (WxMaConstants.KefuMsgType.IMAGE.equals(message.getMsgType())) { | |||
| JsonObject image = new JsonObject(); | |||
| image.addProperty("media_id", message.getMediaId()); | |||
| messageJson.add("image", image); | |||
| } | |||
| return messageJson; | |||
| } | |||
| } | |||
| @@ -1,12 +1,16 @@ | |||
| package cn.binarywang.wx.miniapp.util.xml; | |||
| import java.io.InputStream; | |||
| import java.util.ArrayList; | |||
| import java.util.Arrays; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaMessage; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import java.io.InputStream; | |||
| import java.util.*; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| @@ -45,7 +49,7 @@ public class XStreamTransformer { | |||
| * @param clz 类型 | |||
| * @param xStream xml解析器 | |||
| */ | |||
| private static void register(Class<?> clz, XStream xStream) { | |||
| public static void register(Class<?> clz, XStream xStream) { | |||
| CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||
| } | |||
| @@ -7,16 +7,14 @@ import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||
| import cn.binarywang.wx.miniapp.test.TestConfig; | |||
| import com.google.common.collect.Lists; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import org.testng.annotations.*; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Date; | |||
| /** | |||
| * 测试客服相关接口 | |||
| * 测试消息相关接口 | |||
| * | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| */ | |||
| @@ -25,26 +23,14 @@ import java.util.Date; | |||
| public class WxMaMsgServiceImplTest { | |||
| @Inject | |||
| protected WxMaService wxService; | |||
| public void testSendKefuMpNewsMessage() throws WxErrorException { | |||
| TestConfig configStorage = (TestConfig) this.wxService | |||
| .getWxMaConfig(); | |||
| WxMaKefuMessage message = new WxMaKefuMessage(); | |||
| message.setMsgType(WxConsts.KefuMsgType.MPNEWS); | |||
| message.setToUser(configStorage.getOpenid()); | |||
| this.wxService.getMsgService().sendKefuMsg(message); | |||
| } | |||
| private WxMaService wxService; | |||
| public void testSendKefuMessage() throws WxErrorException { | |||
| TestConfig config = (TestConfig) this.wxService | |||
| .getWxMaConfig(); | |||
| WxMaKefuMessage message = new WxMaKefuMessage(); | |||
| message.setMsgType(WxConsts.KefuMsgType.TEXT); | |||
| message.setToUser(config.getOpenid()); | |||
| message.setContent( | |||
| "欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>"); | |||
| TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); | |||
| WxMaKefuMessage message = WxMaKefuMessage.newTextBuilder() | |||
| .toUser(config.getOpenid()) | |||
| .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>") | |||
| .build(); | |||
| this.wxService.getMsgService().sendKefuMsg(message); | |||
| } | |||
| @@ -66,7 +52,7 @@ public class WxMaMsgServiceImplTest { | |||
| .templateId(config.getTemplateId()) | |||
| .emphasisKeyword("keyword1.DATA") | |||
| .build(); | |||
| //templateMessage.addData( new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177")); | |||
| this.wxService.getMsgService().sendTemplateMsg(templateMessage); | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| package cn.binarywang.wx.miniapp.api.impl; | |||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateAddResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateLibraryGetResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateLibraryListResult; | |||
| import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateListResult; | |||
| import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||
| import com.google.inject.Inject; | |||
| import org.assertj.core.util.Lists; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import java.util.List; | |||
| @Test | |||
| @Guice(modules = ApiTestModule.class) | |||
| public class WxMaTemplateServiceImplTest { | |||
| @Inject | |||
| protected WxMaService wxService; | |||
| @Test | |||
| public void testFindTemplateLibraryList() throws Exception { | |||
| WxMaTemplateLibraryListResult result = this.wxService.getTemplateService().findTemplateLibraryList(0, 20); | |||
| Assert.assertEquals(20, result.getList().size()); | |||
| } | |||
| @Test | |||
| public void testFindTemplateLibraryKeywordList() throws Exception { | |||
| WxMaTemplateLibraryGetResult result = this.wxService.getTemplateService().findTemplateLibraryKeywordList("AT0004"); | |||
| Assert.assertEquals("AT0004", result.getId()); | |||
| Assert.assertEquals("交易提醒", result.getTitle()); | |||
| Assert.assertEquals(100, result.getKeywordList().size()); | |||
| } | |||
| @Test | |||
| public void testAddTemplate() throws Exception{ | |||
| List<Integer> list = Lists.newArrayList(); | |||
| list.add(1); | |||
| list.add(20); | |||
| list.add(84); | |||
| WxMaTemplateAddResult result = this.wxService.getTemplateService().addTemplate("AT0004", list); | |||
| Assert.assertNotNull(result.getTemplateId()); | |||
| System.out.println(result); | |||
| } | |||
| @Test | |||
| public void testFindTemplateList() throws Exception{ | |||
| WxMaTemplateListResult result = this.wxService.getTemplateService().findTemplateList(0, 20); | |||
| System.out.println(result); | |||
| } | |||
| @Test | |||
| public void testDelTemplate() throws Exception { | |||
| //add | |||
| List<Integer> list = Lists.newArrayList(); | |||
| list.add(1); | |||
| list.add(20); | |||
| list.add(84); | |||
| WxMaTemplateAddResult result = this.wxService.getTemplateService().addTemplate("AT0004", list); | |||
| //delete | |||
| this.wxService.getTemplateService().delTemplate(result.getTemplateId()); | |||
| } | |||
| } | |||
| @@ -1,14 +1,14 @@ | |||
| package cn.binarywang.wx.miniapp.api.impl; | |||
| import org.testng.annotations.*; | |||
| import cn.binarywang.wx.miniapp.api.WxMaService; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||
| import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||
| import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||
| import com.google.inject.Inject; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import static org.testng.Assert.assertNotNull; | |||
| import static org.testng.Assert.assertTrue; | |||
| import static org.testng.Assert.*; | |||
| /** | |||
| * 测试用户相关的接口 | |||
| @@ -28,7 +28,7 @@ public class WxMaUserServiceImplTest { | |||
| } | |||
| @Test | |||
| public void testGetUserInfo() throws Exception { | |||
| public void testGetUserInfo() { | |||
| WxMaUserInfo userInfo = this.wxService.getUserService().getUserInfo("tiihtNczf5v6AKRyjwEUhQ==", | |||
| "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", | |||
| "r7BXXKkLb8qrSNn05n0qiA=="); | |||
| @@ -37,10 +37,21 @@ public class WxMaUserServiceImplTest { | |||
| } | |||
| @Test | |||
| public void testCheckUserInfo() throws Exception { | |||
| public void testCheckUserInfo() { | |||
| assertTrue(this.wxService.getUserService().checkUserInfo("HyVFkGl5F5OQWJZZaNzBBg==", | |||
| "{\"nickName\":\"Band\",\"gender\":1,\"language\":\"zh_CN\",\"city\":\"Guangzhou\",\"province\":\"Guangdong\",\"country\":\"CN\",\"avatarUrl\":\"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0\"}", | |||
| "75e81ceda165f4ffa64f4068af58c64b8f54b88c")); | |||
| } | |||
| /** | |||
| * TODO 测试数据有问题,需要替换为正确的数据 | |||
| */ | |||
| @Test | |||
| public void testGetPhoneNoInfo() { | |||
| WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNoInfo("tiihtNczf5v6AKRyjwEUhQ==", | |||
| "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", | |||
| "r7BXXKkLb8qrSNn05n0qiA=="); | |||
| assertNotNull(phoneNoInfo); | |||
| System.out.println(phoneNoInfo.toString()); | |||
| } | |||
| } | |||
| @@ -1,8 +1,8 @@ | |||
| package cn.binarywang.wx.miniapp.bean; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Test; | |||
| import org.testng.annotations.*; | |||
| import static org.assertj.core.api.Assertions.assertThat; | |||
| /** | |||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
| @@ -10,30 +10,47 @@ import org.testng.annotations.Test; | |||
| @Test | |||
| public class WxMaKefuMessageTest { | |||
| public void testTextReply() { | |||
| WxMaKefuMessage reply = new WxMaKefuMessage(); | |||
| reply.setToUser("OPENID"); | |||
| reply.setMsgType(WxConsts.KefuMsgType.TEXT); | |||
| reply.setContent("sfsfdsdf"); | |||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||
| public void testTextBuilder() { | |||
| WxMaKefuMessage reply = WxMaKefuMessage.newTextBuilder() | |||
| .toUser("OPENID") | |||
| .content("sfsfdsdf") | |||
| .build(); | |||
| assertThat(reply.toJson()) | |||
| .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||
| } | |||
| public void testTextBuild() { | |||
| WxMaKefuMessage reply = WxMaKefuMessage.newTextBuilder().toUser("OPENID").content("sfsfdsdf").build(); | |||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||
| public void testImageBuilder() { | |||
| WxMaKefuMessage reply = WxMaKefuMessage.newImageBuilder() | |||
| .toUser("OPENID") | |||
| . mediaId("MEDIA_ID") | |||
| .build(); | |||
| assertThat(reply.toJson()) | |||
| .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||
| } | |||
| public void testImageReply() { | |||
| WxMaKefuMessage reply = new WxMaKefuMessage(); | |||
| reply.setToUser("OPENID"); | |||
| reply.setMsgType(WxConsts.KefuMsgType.IMAGE); | |||
| reply.setMediaId("MEDIA_ID"); | |||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||
| public void testLinkBuilder() { | |||
| WxMaKefuMessage reply = WxMaKefuMessage.newLinkBuilder() | |||
| .toUser("OPENID") | |||
| .url("url") | |||
| .description("description") | |||
| .title("title") | |||
| .thumbUrl("thumbUrl") | |||
| .build(); | |||
| assertThat(reply.toJson()) | |||
| .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"link\"," + | |||
| "\"link\":{\"title\":\"title\",\"description\":\"description\",\"url\":\"url\",\"thumb_url\":\"thumbUrl\"}}"); | |||
| } | |||
| public void testImageBuild() { | |||
| WxMaKefuMessage reply = WxMaKefuMessage.newImageBuilder().toUser("OPENID").mediaId("MEDIA_ID").build(); | |||
| Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||
| public void testMaPageBuilder() { | |||
| WxMaKefuMessage reply = WxMaKefuMessage.newMaPageBuilder() | |||
| .toUser("OPENID") | |||
| .title("title") | |||
| .pagePath("pagePath") | |||
| .thumbMediaId("thumbMediaId") | |||
| .build(); | |||
| assertThat(reply.toJson()) | |||
| .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"miniprogrampage\"," + | |||
| "\"miniprogrampage\":{\"title\":\"title\",\"pagepath\":\"pagePath\",\"thumb_media_id\":\"thumbMediaId\"}}"); | |||
| } | |||
| @@ -7,10 +7,10 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.9.0</version> | |||
| <version>2.9.9.BETA</version> | |||
| </parent> | |||
| <artifactId>weixin-java-mp</artifactId> | |||
| <name>WeiXin Java Tools - MP</name> | |||
| <name>Weixin Java Tools - MP</name> | |||
| <description>微信公众号Java SDK</description> | |||
| <dependencies> | |||
| @@ -65,6 +65,15 @@ | |||
| <artifactId>logback-classic</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.assertj</groupId> | |||
| <artifactId>assertj-guava</artifactId> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -85,6 +85,8 @@ public interface WxMpConfigStorage { | |||
| String getAesKey(); | |||
| String getTemplateId(); | |||
| long getExpiresTime(); | |||
| String getOauth2redirectUri(); | |||
| @@ -18,6 +18,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| protected volatile String appId; | |||
| protected volatile String secret; | |||
| protected volatile String token; | |||
| protected volatile String templateId; | |||
| protected volatile String accessToken; | |||
| protected volatile String aesKey; | |||
| protected volatile long expiresTime; | |||
| @@ -173,6 +174,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| this.token = token; | |||
| } | |||
| @Override | |||
| public String getTemplateId() { | |||
| return this.templateId; | |||
| } | |||
| public void setTemplateId(String templateId) { | |||
| this.templateId = templateId; | |||
| } | |||
| @Override | |||
| public long getExpiresTime() { | |||
| return this.expiresTime; | |||
| @@ -51,7 +51,7 @@ public class WxMpInRedisConfigStorage extends WxMpInMemoryConfigStorage { | |||
| @Override | |||
| public String getAccessToken() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| return jedis.get(accessTokenKey); | |||
| return jedis.get(this.accessTokenKey); | |||
| } | |||
| } | |||
| @@ -65,70 +65,70 @@ public class WxMpInRedisConfigStorage extends WxMpInMemoryConfigStorage { | |||
| @Override | |||
| public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| jedis.setex(accessTokenKey, expiresInSeconds - 200, accessToken); | |||
| jedis.setex(this.accessTokenKey, expiresInSeconds - 200, accessToken); | |||
| } | |||
| } | |||
| @Override | |||
| public void expireAccessToken() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| jedis.expire(accessTokenKey, 0); | |||
| jedis.expire(this.accessTokenKey, 0); | |||
| } | |||
| } | |||
| @Override | |||
| public String getJsapiTicket() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| return jedis.get(jsapiTicketKey); | |||
| return jedis.get(this.jsapiTicketKey); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean isJsapiTicketExpired() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| return jedis.ttl(jsapiTicketKey) < 2; | |||
| return jedis.ttl(this.jsapiTicketKey) < 2; | |||
| } | |||
| } | |||
| @Override | |||
| public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| jedis.setex(jsapiTicketKey, expiresInSeconds - 200, jsapiTicket); | |||
| jedis.setex(this.jsapiTicketKey, expiresInSeconds - 200, jsapiTicket); | |||
| } | |||
| } | |||
| @Override | |||
| public void expireJsapiTicket() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| jedis.expire(jsapiTicketKey, 0); | |||
| jedis.expire(this.jsapiTicketKey, 0); | |||
| } | |||
| } | |||
| @Override | |||
| public String getCardApiTicket() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| return jedis.get(cardapiTicketKey); | |||
| return jedis.get(this.cardapiTicketKey); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean isCardApiTicketExpired() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| return jedis.ttl(cardapiTicketKey) < 2; | |||
| return jedis.ttl(this.cardapiTicketKey) < 2; | |||
| } | |||
| } | |||
| @Override | |||
| public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| jedis.setex(cardapiTicketKey, expiresInSeconds - 200, cardApiTicket); | |||
| jedis.setex(this.cardapiTicketKey, expiresInSeconds - 200, cardApiTicket); | |||
| } | |||
| } | |||
| @Override | |||
| public void expireCardApiTicket() { | |||
| try (Jedis jedis = this.jedisPool.getResource()) { | |||
| jedis.expire(cardapiTicketKey, 0); | |||
| jedis.expire(this.cardapiTicketKey, 0); | |||
| } | |||
| } | |||
| } | |||
| @@ -7,7 +7,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; | |||
| /** | |||
| * <pre> | |||
| * 群发消息服务类 | |||
| * 群发消息服务类. | |||
| * Created by Binary Wang on 2017-8-16. | |||
| * </pre> | |||
| * | |||
| @@ -15,33 +15,33 @@ import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; | |||
| */ | |||
| public interface WxMpMassMessageService { | |||
| /** | |||
| * 上传群发用的图文消息 | |||
| * 上传群发用的图文消息. | |||
| */ | |||
| String MEDIA_UPLOAD_NEWS_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; | |||
| /** | |||
| * 上传群发用的视频 | |||
| * 上传群发用的视频. | |||
| */ | |||
| String MEDIA_UPLOAD_VIDEO_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo"; | |||
| /** | |||
| * 分组群发消息 | |||
| * 分组群发消息. | |||
| */ | |||
| String MESSAGE_MASS_SENDALL_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; | |||
| /** | |||
| * 按openId列表群发消息 | |||
| * 按openId列表群发消息. | |||
| */ | |||
| String MESSAGE_MASS_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; | |||
| /** | |||
| * 群发消息预览接口 | |||
| * 群发消息预览接口. | |||
| */ | |||
| String MESSAGE_MASS_PREVIEW_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"; | |||
| /** | |||
| * 删除群发接口 | |||
| * 删除群发接口. | |||
| */ | |||
| String MESSAGE_MASS_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/delete"; | |||
| /** | |||
| * <pre> | |||
| * 上传群发用的图文消息,上传后才能群发图文消息 | |||
| * 上传群发用的图文消息,上传后才能群发图文消息. | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||
| * </pre> | |||
| @@ -53,7 +53,7 @@ public interface WxMpMassMessageService { | |||
| /** | |||
| * <pre> | |||
| * 上传群发用的视频,上传后才能群发视频消息 | |||
| * 上传群发用的视频,上传后才能群发视频消息. | |||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||
| * </pre> | |||
| * | |||
| @@ -64,7 +64,7 @@ public interface WxMpMassMessageService { | |||
| /** | |||
| * <pre> | |||
| * 分组群发消息 | |||
| * 分组群发消息. | |||
| * 如果发送图文消息,必须先使用 {@link #massNewsUpload(WxMpMassNews)} 获得media_id,然后再发送 | |||
| * 如果发送视频消息,必须先使用 {@link #massVideoUpload(WxMpMassVideo)} 获得media_id,然后再发送 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||
| @@ -74,7 +74,7 @@ public interface WxMpMassMessageService { | |||
| /** | |||
| * <pre> | |||
| * 按openId列表群发消息 | |||
| * 按openId列表群发消息. | |||
| * 如果发送图文消息,必须先使用 {@link #massNewsUpload(WxMpMassNews)} 获得media_id,然后再发送 | |||
| * 如果发送视频消息,必须先使用 {@link #massVideoUpload(WxMpMassVideo)} 获得media_id,然后再发送 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN | |||
| @@ -84,7 +84,7 @@ public interface WxMpMassMessageService { | |||
| /** | |||
| * <pre> | |||
| * 群发消息预览接口 | |||
| * 群发消息预览接口. | |||
| * 开发者可通过该接口发送消息给指定用户,在手机端查看消息的样式和排版。为了满足第三方平台开发者的需求,在保留对openID预览能力的同时,增加了对指定微信号发送预览的能力,但该能力每日调用次数有限制(100次),请勿滥用。 | |||
| * 接口调用请求说明 | |||
| * http请求方式: POST | |||
| @@ -98,7 +98,7 @@ public interface WxMpMassMessageService { | |||
| /** | |||
| * <pre> | |||
| * 删除群发 | |||
| * 删除群发. | |||
| * 群发之后,随时可以通过该接口删除群发。 | |||
| * 请注意: | |||
| * 1、只有已经发送成功的消息才能删除 | |||
| @@ -114,6 +114,6 @@ public interface WxMpMassMessageService { | |||
| * @param msgId 发送出去的消息ID | |||
| * @param articleIndex 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章 | |||
| */ | |||
| void delete(Integer msgId, Integer articleIndex) throws WxErrorException; | |||
| void delete(Long msgId, Integer articleIndex) throws WxErrorException; | |||
| } | |||
| @@ -130,6 +130,16 @@ public class WxMpMessageRouter { | |||
| * 处理微信消息 | |||
| */ | |||
| public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map<String, Object> context) { | |||
| return route(wxMessage, context, null); | |||
| } | |||
| /** | |||
| * 处理微信消息 | |||
| */ | |||
| public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map<String, Object> context, WxMpService wxMpService) { | |||
| if(wxMpService == null){ | |||
| wxMpService = this.wxMpService; | |||
| } | |||
| final WxMpService mpService = wxMpService; | |||
| if (isMsgDuplicated(wxMessage)) { | |||
| // 如果是重复消息,那么就不做处理 | |||
| return null; | |||
| @@ -159,12 +169,12 @@ public class WxMpMessageRouter { | |||
| this.executorService.submit(new Runnable() { | |||
| @Override | |||
| public void run() { | |||
| rule.service(wxMessage, context, WxMpMessageRouter.this.wxMpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); | |||
| rule.service(wxMessage, context, mpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); | |||
| } | |||
| }) | |||
| ); | |||
| } else { | |||
| res = rule.service(wxMessage, context, this.wxMpService, this.sessionManager, this.exceptionHandler); | |||
| res = rule.service(wxMessage, context, mpService, this.sessionManager, this.exceptionHandler); | |||
| // 在同步操作结束,session访问结束 | |||
| this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); | |||
| sessionEndAccess(wxMessage); | |||
| @@ -90,7 +90,7 @@ public interface WxMpService { | |||
| * 获取access_token,本方法线程安全 | |||
| * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 | |||
| * | |||
| * 另:本service的所有方法都会在access_token过期是调用此方法 | |||
| * 另:本service的所有方法都会在access_token过期时调用此方法 | |||
| * | |||
| * 程序员在非必要情况下尽量不要主动调用此方法 | |||
| * | |||
| @@ -363,6 +363,13 @@ public interface WxMpService { | |||
| */ | |||
| WxMpTemplateMsgService getTemplateMsgService(); | |||
| /** | |||
| * 返回一次性订阅消息相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * | |||
| * @return WxMpSubscribeMsgService | |||
| */ | |||
| WxMpSubscribeMsgService getSubscribeMsgService(); | |||
| /** | |||
| * 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口 | |||
| * | |||
| @@ -0,0 +1,40 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; | |||
| /** | |||
| * <pre> | |||
| * 一次性订阅消息接口 | |||
| * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB | |||
| * </pre> | |||
| * | |||
| * @author Mklaus | |||
| * @date 2018-01-22 上午11:07 | |||
| */ | |||
| public interface WxMpSubscribeMsgService { | |||
| /** | |||
| * <pre> | |||
| * 构造用户订阅一条模板消息授权的url连接 | |||
| * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB | |||
| * </pre> | |||
| * | |||
| * @param redirectURI 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode | |||
| * @param scene 重定向后会带上scene参数,开发者可以填0-10000的整形值,用来标识订阅场景值 | |||
| * @param reserved 用于保持请求和回调的状态,授权请后原样带回给第三方 (最多128字节,要求做urlencode) | |||
| * @return url | |||
| */ | |||
| String subscribeMsgAuthorizationUrl(String redirectURI, int scene, String reserved); | |||
| /** | |||
| * <pre> | |||
| * 发送一次性订阅消息 | |||
| * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1500374289_66bvB | |||
| * </pre> | |||
| * | |||
| * @return 消息Id | |||
| */ | |||
| boolean sendSubscribeMessage(WxMpSubscribeMessage message) throws WxErrorException; | |||
| } | |||
| @@ -213,7 +213,7 @@ public class WxMpCardServiceImpl implements WxMpCardService { | |||
| WxMpCardResult cardResult = WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, | |||
| new TypeToken<WxMpCardResult>() { | |||
| }.getType()); | |||
| if (!cardResult.getErrorCode().equals("0")) { | |||
| if (!"0".equals(cardResult.getErrorCode())) { | |||
| this.log.warn("朋友的券mark失败:{}", cardResult.getErrorMsg()); | |||
| } | |||
| } | |||
| @@ -57,7 +57,7 @@ public class WxMpMassMessageServiceImpl implements WxMpMassMessageService { | |||
| } | |||
| @Override | |||
| public void delete(Integer msgId, Integer articleIndex) throws WxErrorException { | |||
| public void delete(Long msgId, Integer articleIndex) throws WxErrorException { | |||
| JsonObject jsonObject = new JsonObject(); | |||
| jsonObject.addProperty("msg_id", msgId); | |||
| jsonObject.addProperty("article_idx", articleIndex); | |||