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